All of lore.kernel.org
 help / color / mirror / Atom feed
* Help requested: new HSS1394 MIDI back-end
@ 2012-05-25 19:43 Sean M. Pappalardo - D.J. Pegasus
  2012-05-27 13:50 ` Clemens Ladisch
  0 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-05-25 19:43 UTC (permalink / raw)
  To: alsa-devel


[-- Attachment #1.1: Type: text/plain, Size: 2402 bytes --]

Hello everyone.

I am a developer for Mixxx, GPL cross-platform digital DJ software: 
http://www.mixxx.org. My focus is the controller subsystem. I am ready 
to finally add Linux support for HSS1394 DJ controllers such as 
Stanton's SCS.1m and SCS.1d.

HSS1394 is just MIDI over IEEE1394 (Firewire) at wire speed. The Windows 
and OSX library source code is LGPL and available on LaunchPad here: 
https://launchpad.net/hss1394

The Stanton SCS.1m is a sound interface integrated with a two-way 
control surface that speaks HSS1394. (The sound card uses an Oxford chip 
set and works fine with FFADO 2.0.1 today up to 96kHz. I use it with 
Mixxx via JACK.)

The Stanton SCS.1d is a two-way control surface (no sound card) that 
speaks HSS1394 and is capable of sending ALOT of latency-sensitive data: 
about 4000 messages per platter rotation. (Now imagine if you had four 
or even two of them on the same network running at +50% speed. This 
would quickly overwhelm USB, at least at the time these devices were 
designed.)

Read about them here:
http://www.stantondj.com/stanton-controllers-systems/system1.html

After a lengthy discussion on the FFADO mailing list, we determined that 
the best (cleanest and most flexible) way to get Linux support for these 
devices is to write an ALSA kernel driver that speaks HSS1394 with the 
devices and presents an ALSA MIDI device (with no data rate limits) for 
each to Linux applications.

The advantage of doing this is that the devices will appear to all MIDI 
applications without them having to have specific support for HSS1394. 
This means they could be used in DAW applications as well.

(FWIW, FFADO trunk has a program called test-scs that does exactly this, 
but using that is impractical because it depends on various internal 
parts of the FFADO source so would require bundling all of FFADO with 
the application, and it's unable to see non-audio devices due to FFADO's 
intended use (so it sees the SCS.1m but not the .1d.))

So my questions are: where can I find documentation on writing a new 
MIDI driver for ALSA? And is anyone else interested in helping me do this?

Thank you very much for your time and work. Help me take the last step 
to my dream DJ setup! (Linux, FLAC and SCS.1 controllers.)

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4545 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-25 19:43 Help requested: new HSS1394 MIDI back-end Sean M. Pappalardo - D.J. Pegasus
@ 2012-05-27 13:50 ` Clemens Ladisch
  2012-05-27 20:59   ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-27 13:50 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> where can I find documentation on writing a new MIDI driver for ALSA?

See the document "Writing an ALSA Driver", and look at the source code
of other MIDI drivers, such as, for example, sound/usb/midi.c or the
(untested) patch below.

BTW: What is the output of "lsfirewire -v" for these devices?


Regards,
Clemens


--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -33,4 +33,17 @@ 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_PCM
+	select SND_RAWMIDI
+	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.
+
 endif # SND_FIREWIRE
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
+snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
--- /dev/null
+++ b/sound/firewire/scs1x.c
@@ -0,0 +1,447 @@
+/*
+ * 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/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 HSS1394_ADDRESS			0xc007dedadadaULL
+#define HSS1394_MAX_PACKET_SIZE		64
+
+#define HSS1394_TAG_USER_DATA		0x00
+#define HSS1394_TAG_CHANGE_ADDRESS	0xf1
+#define HSS1394_TAG_PING_RESPONSE	0xf3
+
+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 midi_status;
+	u8 midi_bytes;
+	struct snd_rawmidi_substream *output;
+	struct snd_rawmidi_substream *input;
+	struct tasklet_struct tasklet;
+	wait_queue_head_t idle_wait;
+	u8 *buffer;
+};
+
+static int scs_output_open(struct snd_rawmidi_substream *stream)
+{
+	struct scs *scs = stream->rmidi->private_data;
+
+	scs->midi_status = 0;
+	scs->midi_bytes = 0;
+
+	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;
+}
+
+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->midi_bytes;
+	for (;;) {
+		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+			scs->midi_bytes = i;
+			scs->output_idle = true;
+			wake_up(&scs->idle_wait);
+			return;
+		}
+		/*
+		 * Convert from real MIDI to what the device expects (no
+		 * running status, one command per packet).
+		 */
+		if (byte < 0x80) {
+			if (i == 0) {
+				if (!is_valid_running_status(scs->midi_status))
+					continue;
+				scs->buffer[++i] = scs->midi_status;
+			}
+			scs->buffer[++i] = byte;
+			if ((i == 2 && is_two_bytes_cmd(scs->midi_status)) ||
+			    (i == 3 && is_three_bytes_cmd(scs->midi_status)))
+				break;
+			if (i >= HSS1394_MAX_PACKET_SIZE - 1)
+				i = 0;
+		} else if (byte == 0xf7) {
+			if (i > 0 && scs->midi_status == 0xf0) {
+				scs->buffer[++i] = 0xf7;
+				break;
+			}
+		} else if (!is_invalid_cmd(byte) &&
+			   byte < 0xf8) {
+			i = 0;
+			scs->buffer[++i] = byte;
+			scs->midi_status = byte;
+			if (is_one_byte_cmd(byte))
+				break;
+		}
+	}
+	scs->midi_bytes = 0;
+
+	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, 1 + 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)
+{
+	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 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 && ((const u8 *)data)[0] == HSS1394_TAG_USER_DATA) {
+		stream = ACCESS_ONCE(scs->input);
+		if (stream)
+			snd_rawmidi_receive(stream, data + 1, length - 1);
+	}
+
+	fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static int scs_check_version(struct scs *scs)
+{
+	u8 data[4];
+	int err;
+
+	err = snd_fw_transaction(scs->unit, TCODE_READ_BLOCK_REQUEST,
+				 HSS1394_ADDRESS, data, 4);
+	if (err < 0)
+		return err;
+
+	if (data[0] != HSS1394_TAG_PING_RESPONSE) {
+		dev_err(&scs->unit->device,
+			"wrong ping response: expected %#02x, got %#02x\n",
+			HSS1394_TAG_PING_RESPONSE, data[0]);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int scs_init_hss_address(struct scs *scs)
+{
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	return snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+				  HSS1394_ADDRESS, data, 8);
+}
+
+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 device *unit_dev)
+{
+	struct fw_unit *unit = fw_unit(unit_dev);
+	struct fw_device *fw_dev = fw_parent_device(unit);
+	struct snd_card *card;
+	struct scs *scs;
+	int err;
+
+	err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, unit_dev);
+
+	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)
+		goto err_card;
+	scs->buffer[0] = HSS1394_TAG_USER_DATA;
+
+	err = scs_check_version(scs);
+	if (err < 0)
+		goto err_buffer;
+
+	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 %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_dev, scs);
+
+	return 0;
+
+err_buffer:
+	kfree(scs->buffer);
+err_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int scs_remove(struct device *dev)
+{
+	struct scs *scs = dev_get_drvdata(dev);
+
+	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);
+
+	return 0;
+}
+
+static void scs_update(struct fw_unit *unit)
+{
+	struct scs *scs = dev_get_drvdata(&unit->device);
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+			   HSS1394_ADDRESS, data, 8);
+}
+
+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,
+	},
+	/* TODO: add ID for 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,
+		.remove = scs_remove,
+	},
+	.update   = scs_update,
+	.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);

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-27 13:50 ` Clemens Ladisch
@ 2012-05-27 20:59   ` Sean M. Pappalardo - D.J. Pegasus
  2012-05-28 14:58     ` Clemens Ladisch
  0 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-05-27 20:59 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 2232 bytes --]

Hello again.

On 05/27/2012 03:50 PM, Clemens Ladisch wrote:
> See the document "Writing an ALSA Driver", and look at the source code
> of other MIDI drivers, such as, for example, sound/usb/midi.c or the
> (untested) patch below.

Holy crap!! Thank you so much for that patch, Clemens! I just updated my 
kernel and tried it with the SCS.1m but unfortunately it reports an error:

firewire_ohci: isochronous cycle inconsistent
firewire_core: created device fw1: GUID 0012600000000000, S400
firewire_core: phy config: card 0, new root=ffc0, gap_count=5
snd_scs1x fw1.0: transaction failed: type error
snd_scs1x: probe of fw1.0 failed with error -5

Turning on the SCS.1d then yields:
firewire_core: phy config: card 0, new root=ffc2, gap_count=7
firewire_core: phy config: card 0, new root=ffc2, gap_count=7
firewire_core: created device fw2: GUID 0012600100000000, S400


I'm guessing the type error is around line 254 in scs1x.c, but where 
does tcode come from? (I'm totally green with systems and Firewire 
programming.) I glanced at the MIDI section of that "writing a driver" 
PDF before I wrote to this list. I guess I'll go actually read it now. :)

FYI, I'm now on kernel 3.2.18-rt23 #2 SMP PREEMPT x86_64.

> BTW: What is the output of "lsfirewire -v" for these devices?

device fw1:
   vendor ID: 0x001260
   model ID: 0x001000
   vendor: Stanton DJ
   model: SCS.1m
   guid: 0x0012600000000000
   units: 0x00a02d:0x010001
   unit fw1.0:
     model ID: 0x001000
     model: SCS.1m
     specifier ID: 0x00a02d
     version: 0x010001
device fw2:
   vendor ID: 0x001260
   model ID: 0x002000
   vendor: Stanton DJ
   model: SCS.1d
   guid: 0x0012600100000000
   units: 0x00a02d:0x010001
   unit fw2.0:
     model ID: 0x002000
     model: SCS.1d
     specifier ID: 0x00a02d
     version: 0x010001


bus 0, node 0: 080028:424296  Texas Instruments TSB41AB1/2
bus 0, node 1: 080028:424296  Texas Instruments TSB41AB1/2

I tried poking around with firewire-request read but didn't find 
anything interesting. (Then again, I don't really know what I'm looking 
at or for.)

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4545 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-27 20:59   ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-05-28 14:58     ` Clemens Ladisch
  2012-05-28 20:40       ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-28 14:58 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> I just updated my kernel and tried it with the SCS.1m but unfortunately it reports an error:
>
> firewire_core: created device fw1: GUID 0012600000000000, S400
> snd_scs1x fw1.0: transaction failed: type error

This is from a snd_fw_transaction call, either the ping or the address
setting.

Please check if these commands work:

	firewire-request /dev/fwX read c007dedadada 4
	firewire-request /dev/fwX write c007dedadada f100123456789abc

In theory, you should be able to write MIDI commands by prefixing them
with 00, like this for a note-on:
	firewire-request /dev/fwX write c007dedadada 00912345

> Turning on the SCS.1d then yields:
> firewire_core: created device fw2: GUID 0012600100000000, S400

FFADO had only the 1m's ID, so the driver does not yet know about the 1d.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-28 14:58     ` Clemens Ladisch
@ 2012-05-28 20:40       ` Sean M. Pappalardo - D.J. Pegasus
  2012-05-29 17:05         ` Clemens Ladisch
  0 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-05-28 20:40 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 760 bytes --]



On 05/28/2012 04:58 PM, Clemens Ladisch wrote:
> Please check if these commands work:
>
> 	firewire-request /dev/fwX read c007dedadada 4

type error (on both devices)

> 	firewire-request /dev/fwX write c007dedadada f100123456789abc

no response (on both devices)

> with 00, like this for a note-on:
> 	firewire-request /dev/fwX write c007dedadada 00912345

That worked just fine. I lit up a few LEDs in different colors on both 
devices, and was able to stop the platter and move the pitch slider on 
the SCS.1d (yes, its slider is motorized) using CC messages. (I need 
sysex for the LCD displays though.)

So I feel like we're very close!

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4545 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-28 20:40       ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-05-29 17:05         ` Clemens Ladisch
  2012-05-29 21:52           ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-29 17:05 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
>>     firewire-request /dev/fwX write c007dedadada f100123456789abc
>
> no response (on both devices)

No error, which is good.

>>     firewire-request /dev/fwX read c007dedadada 4
>
> type error (on both devices)

This is supposed to be used to confirm that this is indeed a HSS1394 device.
In theory.  But we can just omit this check.  :)

New patch below (also with .1d support).


Regards,
Clemens


--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -33,4 +33,17 @@ 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_PCM
+	select SND_RAWMIDI
+	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.
+
 endif # SND_FIREWIRE
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
+snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
--- /dev/null
+++ b/sound/firewire/scs1x.c
@@ -0,0 +1,436 @@
+/*
+ * 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/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 midi_status;
+	u8 midi_bytes;
+	struct snd_rawmidi_substream *output;
+	struct snd_rawmidi_substream *input;
+	struct tasklet_struct tasklet;
+	wait_queue_head_t idle_wait;
+	u8 *buffer;
+};
+
+static int scs_output_open(struct snd_rawmidi_substream *stream)
+{
+	struct scs *scs = stream->rmidi->private_data;
+
+	scs->midi_status = 0;
+	scs->midi_bytes = 0;
+
+	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;
+}
+
+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->midi_bytes;
+	for (;;) {
+		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+			scs->midi_bytes = i;
+			scs->output_idle = true;
+			wake_up(&scs->idle_wait);
+			return;
+		}
+		/*
+		 * Convert from real MIDI to what the device expects (no
+		 * running status, one command per packet).
+		 */
+		if (byte < 0x80) {
+			if (i == 0) {
+				if (!is_valid_running_status(scs->midi_status))
+					continue;
+				scs->buffer[++i] = scs->midi_status;
+			}
+			scs->buffer[++i] = byte;
+			if ((i == 2 && is_two_bytes_cmd(scs->midi_status)) ||
+			    (i == 3 && is_three_bytes_cmd(scs->midi_status)))
+				break;
+			if (i >= HSS1394_MAX_PACKET_SIZE - 1)
+				i = 0;
+		} else if (byte == 0xf7) {
+			if (i > 0 && scs->midi_status == 0xf0) {
+				scs->buffer[++i] = 0xf7;
+				break;
+			}
+		} else if (!is_invalid_cmd(byte) &&
+			   byte < 0xf8) {
+			i = 0;
+			scs->buffer[++i] = byte;
+			scs->midi_status = byte;
+			if (is_one_byte_cmd(byte))
+				break;
+		}
+	}
+	scs->midi_bytes = 0;
+
+	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, 1 + 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)
+{
+	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 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 && ((const u8 *)data)[0] == HSS1394_TAG_USER_DATA) {
+		stream = ACCESS_ONCE(scs->input);
+		if (stream)
+			snd_rawmidi_receive(stream, data + 1, length - 1);
+	}
+
+	fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static int scs_init_hss_address(struct scs *scs)
+{
+	u8 data[8];
+	int err;
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+				 HSS1394_ADDRESS, data, 8);
+	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 device *unit_dev)
+{
+	struct fw_unit *unit = fw_unit(unit_dev);
+	struct fw_device *fw_dev = fw_parent_device(unit);
+	struct snd_card *card;
+	struct scs *scs;
+	int err;
+
+	err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, unit_dev);
+
+	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)
+		goto err_card;
+	scs->buffer[0] = HSS1394_TAG_USER_DATA;
+
+	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_dev, scs);
+
+	return 0;
+
+err_buffer:
+	kfree(scs->buffer);
+err_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int scs_remove(struct device *dev)
+{
+	struct scs *scs = dev_get_drvdata(dev);
+
+	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);
+
+	return 0;
+}
+
+static void scs_update(struct fw_unit *unit)
+{
+	struct scs *scs = dev_get_drvdata(&unit->device);
+	int generation;
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	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);
+}
+
+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,
+		.remove = scs_remove,
+	},
+	.update   = scs_update,
+	.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);

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-29 17:05         ` Clemens Ladisch
@ 2012-05-29 21:52           ` Sean M. Pappalardo - D.J. Pegasus
  2012-07-24 11:44             ` Clemens Ladisch
  0 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-05-29 21:52 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1.1: Type: text/plain, Size: 705 bytes --]



On 05/29/2012 07:05 PM, Clemens Ladisch wrote:
> New patch below (also with .1d support).

Sweet! It works perfectly with the SCS.1m!! The .1d works, but some of 
the non-standard data it sends appears to be getting corrupted. In 
particular, the device sends four-byte platter messages that start with 
0xF9 and the remaining three can range from 0x00 to 0xFF. These are 
coming across as random three- or one-byte messages which cause all 
kinds of mayhem. (See attached amidi dump. Each line should begin with 
F9 and have three more bytes after it.)

Outbound SYSEX seems fine on both devices.

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist

[-- Attachment #1.1.2: SCS.1dCorruption --]
[-- Type: text/plain, Size: 2202 bytes --]


F9
EC
F2 6B
F9
F0
DC 6B
F9
F4
C5 6B
F9
F8
AE 6B
F9
FC
98 6B
F9 00
81 6B
F9 04
   6A 6B
F9
   08 53
   6B
F9 0C
   3C 6B
F9
   10 25
   6B
F9 14
   0E 6B
F9
   17
F8 6B
F9
   1B
E1 6B
F9 1F
CA 6B
F9
   23
B3 6B
F9 27
9C 6B
F9 2B
85 6B
F9 2F
   6E 6B
F9
   33 57
   6B
F9 37
   40 6B
F9
   3B 29
   6B
F9 3F
   12 6B
F9
   42
FC 6B
F9
   46
E5 6B
F9 4A
CE 6B
F9
   4E
B7 6B
F9 52
A0 6B
F9 56
89 6B
F9 5A
   72 6B
F9
   5E 5B
   6B
F9 62
   44 6B
F9
   66 2D
   6B
F9 6A
   16 6B
F9
   6D
FF 6B
F9
   71
E8 6B
F9 75
D1 6B
F9
   79
BB 6B
F9 7D
A4 6B
F9
81
8D 6B
F9
85 77 6B
F9
89 60 6B
F9
8D 4A 6B
F9
91 33 6B
F9
95 1C 6B
F9
99 06 6B
F9
9C
EF 6B
F9
A0
D8 6B
F9
A4
C2 6B
F9
A8
AB 6B
F9
AC
95 6B
F9
B0 7E 6B
F9
B4 67 6B
F9
B8 51 6B
F9
BC 3A 6B
F9
C0 23
   6B
F9
C4 0D
   6B
F9
C7
F6
6B
F9
CB
E0 6B
F9
CF
C9 6B
F9
D3
B2 6B
F9
D7
9C 6B
F9
DB
85 6B
F9
DF 6F
   6B
F9
E3 58 6B
F9
E7 41 6B
F9
EB 2B 6B
F9
EF 14 6B
F9
F2
FD 6B
F9
F6
E7 6B
F9
FA
D0 6B
F9
BA 6B
F9 02
A3 6B
F9 06
8C 6B
F9 0A
   75 6B
F9
   0E 5E
   6B
F9 12
   47 6B
F9
   16 30
   6B
F9 1A
   19 6B
F9
   1E 02
   6B
F9 21
EB 6B
F9 25
D4 6B
F9
   29
BE 6B
F9 2D
A7 6B
F9 31
90 6B
F9 35
   79 6B
F9
   39 62
   6B
F9 3D
   4B 6B
F9
   41 34
   6B
F9 45
   1D 6B
F9
   49 06
   6B
F9 4C
EF 6B
F9 50
D9 6B
F9
   54
C2 6B
F9
   58
AB 6B
F9 5C
94 6B
F9 60
   7D 6B
F9
   64 66
   6B
F9 68
   50 6B
F9
   6C 3A
   6B
F9 70
   23 6B
F9
   74 0C
   6B
F9 77
F5
6B
F9
7B
DF 6B
F9
   7F
C8 6B
F9
83
B2 6B
F9
87
9B 6B
F9
8B
84 6B
F9
8F 6E 6B
F9
93 57 6B
F9
97 40 6B
F9
9B 2A 6B
F9
9F 13 6B
F9
A2
FD 6B
F9
A6
E6 6B
F9
AA
CF 6B
F9
AE
B9 6B
F9
B2
A2 6B
F9
B6
8B 6B
F9
BA 75 6B
F9
BE 5E 6B
F9
C2 48
   6B
F9
C6 31
   6B
F9
CA 1A
   6B
F9
CE 04
   6B
F9
D1
ED 6B
F9
D5
D7 6B
F9
D9
C0 6B
F9
DD
A9 6B
F9
E1
93 6B
F9
E5 7C 6B
F9
E9 65 6B
F9
ED 4F 6B
F9
F1 38
   6B
F9
F5
22
6B
F9
F9
0B
6B
F9
FC
F4
6B
F9
00
DE 6B
F9
   04
C7 6B
F9
   08
B0 6B
F9 0C
99 6B
F9 10
82 6B
F9 14
   6B 6B
F9
   18 54
   6B
F9 1C
   3E 6B
F9
   20 27
   6B
F9 24
   10 6B
F9
   27
F9 6B
F9
   2B
E2 6B
F9 2F
CB 6B
F9
   33
B4 6B
F9 37
9E 6B
F9 3B
87 6B
F9 3F
   70 6B
F9
   43 59
   6B
F9 47
   42 6B
F9
   4B 2B
   6B
F9 4F
   14 6B
F9
   52
FD 6B
F9
   56
E6 6B
F9 5A
CF 6B
F9
   5E
B8 6B

[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4545 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-29 21:52           ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-07-24 11:44             ` Clemens Ladisch
  2012-08-01 17:16               ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-07-24 11:44 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

I wrote:
> I wrote:
>> Sean M. Pappalardo - D.J. Pegasus wrote:
>>> I also see an F9 message slip through once in awhile according to amidi -d:
>>
>> The driver currently assumes that there is exactly one MIDI message per
>> packet.  It appears that this is not true, and if F9 and real MIDI
>> commands are mixed in one packet, the result is not correct.
>
> New patch below.

Ping?  Does it work?  Wouldn't it be time to move it into the kernel?
Would you be OK with this tag on the patch?
  Tested-by: Sean M. Pappalardo - D.J. Pegasus <spappalardo@mixxx.org>


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-07-24 11:44             ` Clemens Ladisch
@ 2012-08-01 17:16               ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-08-01 17:16 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 525 bytes --]

Hello again.

On 07/24/2012 01:44 PM, Clemens Ladisch wrote:
> Ping?  Does it work?  Wouldn't it be time to move it into the kernel?

I haven't tested the latest one fully and won't be able to for a couple 
of weeks due to traveling.

> Would you be OK with this tag on the patch?
>    Tested-by: Sean M. Pappalardo - D.J. Pegasus<spappalardo@mixxx.org>

That's fine. I'm happy to provide help to other users if needed.

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-11-12 11:33                     ` Clemens Ladisch
@ 2012-11-12 11:40                       ` Takashi Iwai
  0 siblings, 0 replies; 30+ messages in thread
From: Takashi Iwai @ 2012-11-12 11:40 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Sean M. Pappalardo - D.J. Pegasus, alsa-devel, linux1394-devel

At Mon, 12 Nov 2012 12:33:47 +0100,
Clemens Ladisch wrote:
> 
> Takashi Iwai wrote:
> > Clemens Ladisch wrote:
> >>
> >> +static int scs_init_hss_address(struct scs *scs)
> >> +{
> >> +	u8 data[8];
> >> +	int err;
> >> +
> >> +	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
> >
> > Wouldn't it be safer to use unaligned access or a union?
> 
> Oops, indeed.  But safest would be to avoid playing byte-array games:
> 
>   __be64 data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
>                             scs->hss_handler.offset);
> 
> Now fixed in the same branch.

OK, let me know when you push out the changes, so that I'll pull it.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-11-12  9:45                   ` Takashi Iwai
@ 2012-11-12 11:33                     ` Clemens Ladisch
  2012-11-12 11:40                       ` Takashi Iwai
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-11-12 11:33 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Sean M. Pappalardo - D.J. Pegasus, alsa-devel, linux1394-devel

Takashi Iwai wrote:
> Clemens Ladisch wrote:
>>
>> +static int scs_init_hss_address(struct scs *scs)
>> +{
>> +	u8 data[8];
>> +	int err;
>> +
>> +	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
>
> Wouldn't it be safer to use unaligned access or a union?

Oops, indeed.  But safest would be to avoid playing byte-array games:

  __be64 data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
                            scs->hss_handler.offset);

Now fixed in the same branch.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-25 19:23                 ` Clemens Ladisch
                                     ` (2 preceding siblings ...)
  2012-11-09  6:41                   ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-11-12  9:45                   ` Takashi Iwai
  2012-11-12 11:33                     ` Clemens Ladisch
  3 siblings, 1 reply; 30+ messages in thread
From: Takashi Iwai @ 2012-11-12  9:45 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Sean M. Pappalardo - D.J. Pegasus, alsa-devel, linux1394-devel

At Thu, 25 Oct 2012 21:23:41 +0200,
Clemens Ladisch wrote:
> 
> +static int scs_init_hss_address(struct scs *scs)
> +{
> +	u8 data[8];
> +	int err;
> +
> +	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);

Wouldn't it be safer to use unaligned access or a union?

> +static void scs_update(struct fw_unit *unit)
> +{
> +	struct scs *scs = dev_get_drvdata(&unit->device);
> +	int generation;
> +	u8 data[8];
> +
> +	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);

Ditto.


Takashi

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-25 19:23                 ` Clemens Ladisch
  2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
  2012-10-31 10:00                   ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-11-09  6:41                   ` Sean M. Pappalardo - D.J. Pegasus
  2012-11-12  9:45                   ` Takashi Iwai
  3 siblings, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-11-09  6:41 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 290 bytes --]

Hello again.

Since your patch works fine, I just wanted to know in which ALSA and 
kernel version it will appear, so I can tell end users what they need.

Thank you very much for your time.

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-25 19:23                 ` Clemens Ladisch
  2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-10-31 10:00                   ` Sean M. Pappalardo - D.J. Pegasus
  2012-11-09  6:41                   ` Sean M. Pappalardo - D.J. Pegasus
  2012-11-12  9:45                   ` Takashi Iwai
  3 siblings, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-10-31 10:00 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 394 bytes --]



On 10/25/2012 09:23 PM, Clemens Ladisch wrote:
> Silly off-by-one error.  New patch below.

Okay! That looks and works great! (I had to remove the extra parameter 
on the snd_fw_transaction calls since I'm still using kernel 3.2.18.) 
Thank you very much for all your time and work on this!

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-10-26  7:48                     ` Clemens Ladisch
  0 siblings, 0 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-10-26  7:48 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> On 10/25/2012 09:23 PM, Clemens Ladisch wrote:
>> I was waiting for your Tested-by ...
>
> Oopsie... I thought I replied saying that
>
> Tested-by: Sean M. Pappalardo - D.J. Pegasus <spappalardo@mixxx.org>
>
> ...was fine.

Sorry, you did; what I meant to say was that I was waiting for the test
*result*.  (Which turned out to be negative after all.)


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-25 19:23                 ` Clemens Ladisch
@ 2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
  2012-10-26  7:48                     ` Clemens Ladisch
  2012-10-31 10:00                   ` Sean M. Pappalardo - D.J. Pegasus
                                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-10-25 20:26 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 377 bytes --]



On 10/25/2012 09:23 PM, Clemens Ladisch wrote:
> I was waiting for your Tested-by ...

Oopsie... I thought I replied saying that

Tested-by: Sean M. Pappalardo - D.J. Pegasus <spappalardo@mixxx.org>

...was fine. Ah well.


Thanks again for all your hard work on this!

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-10-24 11:49               ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-10-25 19:23                 ` Clemens Ladisch
  2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
                                     ` (3 more replies)
  0 siblings, 4 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-10-25 19:23 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> the amidi dump still shows an extra 00 byte after every message

Silly off-by-one error.  New patch below.

> BTW, which ALSA version (and Linux kernel version) did the last patch
> go into, if any?

I was waiting for your Tested-by ...


Regards,
Clemens


 sound/firewire/Kconfig  |   13 ++
 sound/firewire/Makefile |    2 +
 sound/firewire/scs1x.c  |  531 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 546 insertions(+), 0 deletions(-)
 create mode 100644 sound/firewire/scs1x.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 2607148..ea063e1 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -33,4 +33,17 @@ 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_PCM
+	select SND_RAWMIDI
+	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.
+
 endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index d71ed89..460179d 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
+snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c
new file mode 100644
index 0000000..40b4798
--- /dev/null
+++ b/sound/firewire/scs1x.c
@@ -0,0 +1,531 @@
+/*
+ * 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)
+{
+	u8 data[8];
+	int err;
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	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 device *unit_dev)
+{
+	struct fw_unit *unit = fw_unit(unit_dev);
+	struct fw_device *fw_dev = fw_parent_device(unit);
+	struct snd_card *card;
+	struct scs *scs;
+	int err;
+
+	err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, unit_dev);
+
+	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)
+		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_dev, scs);
+
+	return 0;
+
+err_buffer:
+	kfree(scs->buffer);
+err_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int scs_remove(struct device *dev)
+{
+	struct scs *scs = dev_get_drvdata(dev);
+
+	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);
+
+	return 0;
+}
+
+static void scs_update(struct fw_unit *unit)
+{
+	struct scs *scs = dev_get_drvdata(&unit->device);
+	int generation;
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	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 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,
+		.remove = scs_remove,
+	},
+	.update   = scs_update,
+	.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);

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-06-10 13:00             ` Clemens Ladisch
@ 2012-10-24 11:49               ` Sean M. Pappalardo - D.J. Pegasus
  2012-10-25 19:23                 ` Clemens Ladisch
  0 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-10-24 11:49 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 1053 bytes --]

Hello again.

I'm very sorry for my absence. As usual with works of passion, life has 
gotten in the way the past few months.

On 06/10/2012 03:00 PM, Clemens Ladisch wrote:
> New patch below.

This one seems to work perfectly with Mixxx! However, the amidi dump 
still shows an extra 00 byte after every message:

F0 00 01 60 48 53 53 00 00 0F 09 02 0C 09 0A 05 0C F7
00
F0 00 01 60 48 53 53 00 00 0F 09 03 00 08 03 05 0C F7
00
F0 00 01 60 48 53 53 00 00 0F 09 03 04 06 0D 05 0C F7
00
80 20 00
    00
F0 00 01 60 48 53 53 00 00 0F 09 03 08 05 06 05 0C F7
00
F0 00 01 60 48 53 53 00 00 0F 09 03 0C 03 0F 05 0C F7
00
90 20 0D
    00
F0 00 01 60 48 53 53 00 00 0F 09 04 00 02 09 05 0C F7
00
F0 00 01 60 48 53 53 00 00 0F 09 04 04 01 02 05 0C F7
00
F0 00 01 60 48 53 53 00 00 0F 09 04 07 0F 0B 05 0C F7
00


We should probably fix that.

BTW, which ALSA version (and Linux kernel version) did the last patch go 
into, if any?

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-06-09 11:07           ` Clemens Ladisch
  2012-06-09 12:41             ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-06-10 13:00             ` Clemens Ladisch
  2012-10-24 11:49               ` Sean M. Pappalardo - D.J. Pegasus
  1 sibling, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-06-10 13:00 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

I wrote:
> Sean M. Pappalardo - D.J. Pegasus wrote:
>> I also see an F9 message slip through once in awhile according to amidi -d:
>
> The driver currently assumes that there is exactly one MIDI message per
> packet.  It appears that this is not true, and if F9 and real MIDI
> commands are mixed in one packet, the result is not correct.

New patch below.


Regards,
Clemens


--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -33,4 +33,17 @@ 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_PCM
+	select SND_RAWMIDI
+	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.
+
 endif # SND_FIREWIRE
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
+snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
--- /dev/null
+++ b/sound/firewire/scs1x.c
@@ -0,0 +1,527 @@
+/*
+ * 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 = 0; 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)
+{
+	u8 data[8];
+	int err;
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+				 HSS1394_ADDRESS, data, 8);
+	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 device *unit_dev)
+{
+	struct fw_unit *unit = fw_unit(unit_dev);
+	struct fw_device *fw_dev = fw_parent_device(unit);
+	struct snd_card *card;
+	struct scs *scs;
+	int err;
+
+	err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, unit_dev);
+
+	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)
+		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_dev, scs);
+
+	return 0;
+
+err_buffer:
+	kfree(scs->buffer);
+err_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int scs_remove(struct device *dev)
+{
+	struct scs *scs = dev_get_drvdata(dev);
+
+	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);
+
+	return 0;
+}
+
+static void scs_update(struct fw_unit *unit)
+{
+	struct scs *scs = dev_get_drvdata(&unit->device);
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+			   HSS1394_ADDRESS, data, 8);
+}
+
+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,
+		.remove = scs_remove,
+	},
+	.update   = scs_update,
+	.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);

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
       [not found] <mailman.786.1339244201.2490.alsa-devel@alsa-project.org>
@ 2012-06-09 12:56 ` Jonathan Woithe
  0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Woithe @ 2012-06-09 12:56 UTC (permalink / raw)
  To: alsa-devel; +Cc: Jonathan Woithe, linux1394-devel

Clemens Ladisch wrote:
> Sean M. Pappalardo - D.J. Pegasus wrote:
> > As soon as I start the JACK server to handle the audio interface on
> > the SCS.1m, the ALSA driver stops sending MIDI for that device unless
> > I stop JACK and power-cycle the device. Its last words are:
> > F0 00 01 60 48 53 53 0F 03 00 03 00 00 0B 01 F7
> 
> This is a ping response (tag F3).  Apparently, Jack's FFADO driver
> reinitializes the device and registers its own address for received
> packets.  Since it does _not_ handle MIDI messages, it really should
> not do this.

If we're talking about the ARM handler for HSS1394 messages, then I suspect
the reason it sets this up is that Pieter initially fully intended for FFADO
to pass these messages back.  Obviously if this were to be done then an ARM
handler would be necessary, but that's not how things are panning out.  To
prevent the ARM handler being registered by FFADO it should only be ncessary
to comment out the initMessageHandler() call in the ScsDevice::discover()
method.  I'm happy to make this change since it seems to make sense to do so
given these latest developments.  A preliminary approach is in FFADO svn
revision 2163 if you want to check it out.  Note that this will break
FFADO's test-scs program - a detail I'll tidy up if the overall approach
seems to work.

The other related issue is whether the other device setup done by FFADO
interferes with the MIDI stream transmission.  I guess that's most easily
tested by trying FFADO rev 2163 and seeing if there are other issues
remaining.

Regards
  jonathan

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-06-09 11:07           ` Clemens Ladisch
@ 2012-06-09 12:41             ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-10 13:00             ` Clemens Ladisch
  1 sibling, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-06-09 12:41 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel, ffado-devel


[-- Attachment #1.1: Type: text/plain, Size: 1645 bytes --]



On 06/09/2012 01:07 PM, Clemens Ladisch wrote:
>> I just modified Mixxx's SCS.1d script to work with your custom sysex
>> messages and tested with your latest patch. The .1d doesn't actually
>> work 100% with this since Mixxx never receives some of the standard
>> MIDI messages amid all of the sysex ones, and it's much worse when the
>> platter is spinning and sending the tons of messages it does. This
>> could be due to PortMIDI's lack of a callback mechanism, forcing Mixxx
>> to poll it every 1ms because it's much more difficult to reproduce the
>> problem using amidi -d.
>
> That's possible.  However, at 4 KB, ALSA's buffer should be big enough
> for those messages.

Yes, I subsequently found out that it was a bug in Mixxx's PortMIDI 
code. I've corrected that and it works much better now. (Nothing like a 
demanding device to ferret out bugs, eh?)

>> As soon as I start the JACK server to handle the audio interface on
>> the SCS.1m, the ALSA driver stops sending MIDI for that device unless
>> I stop JACK and power-cycle the device. Its last words are:
>> F0 00 01 60 48 53 53 0F 03 00 03 00 00 0B 01 F7
>
> This is a ping response (tag F3).  Apparently, Jack's FFADO driver
> reinitializes the device and registers its own address for received
> packets.  Since it does _not_ handle MIDI messages, it really should
> not do this.

I think the test-scs program uses JACK-MIDI, so that might be why. But 
test-scs is obsoleted by your driver. I'm CC'ing the FFADO-devel list so 
they can take a look.

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-06-09 11:07           ` Clemens Ladisch
  2012-06-09 12:41             ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-10 13:00             ` Clemens Ladisch
  0 siblings, 2 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-06-09 11:07 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> On 05/31/2012 10:00 PM, Clemens Ladisch wrote:
>> I wrote:
>>> 00 F9 xx yy zz ->  F0 00 01 60 48 53 53 00 00 0F 09 0x 0x 0y 0y 0z 0z F7
>
> This appears to work fine. But where did you get the leading 00 from?

This is how the FireWire packets are constructed.  The first byte is
a tag; 00 means that the rest of the packet contains MIDI data, ...

>>> 13 xx yy zz<- F0 00 01 60 48 53 53 01 03 0x 0x 0y 0y 0z 0z F7
>>> 14 xx yy zz<- F0 00 01 60 48 53 53 01 04 0x 0x 0y 0y 0z 0z F7
>>> (0x13 = 0x10 + 3 = kUserTagBase + uUserTag)

... while tags in the range 10..EF are "user tags".

When you use this function:

> // uUserTag is valid in the range 0x00 - 0xDF.
> virtual uint SendUserControl(uint8 uUserTag, const uint8 *pUserData,
>                              uint uDataBytes) = 0;

libhss1394 will add 0x10 to the tag.

> I just modified Mixxx's SCS.1d script to work with your custom sysex
> messages and tested with your latest patch. The .1d doesn't actually
> work 100% with this since Mixxx never receives some of the standard
> MIDI messages amid all of the sysex ones, and it's much worse when the
> platter is spinning and sending the tons of messages it does. This
> could be due to PortMIDI's lack of a callback mechanism, forcing Mixxx
> to poll it every 1ms because it's much more difficult to reproduce the
> problem using amidi -d.

That's possible.  However, at 4 KB, ALSA's buffer should be big enough
for those messages.

> I also see an F9 message slip through once in awhile according to amidi -d:
> F9
>    55
> 91
> C4

The driver currently assumes that there is exactly one MIDI message per
packet.  It appears that this is not true, and if F9 and real MIDI
commands are mixed in one packet, the result is not correct.

> As soon as I start the JACK server to handle the audio interface on
> the SCS.1m, the ALSA driver stops sending MIDI for that device unless
> I stop JACK and power-cycle the device. Its last words are:
> F0 00 01 60 48 53 53 0F 03 00 03 00 00 0B 01 F7

This is a ping response (tag F3).  Apparently, Jack's FFADO driver
reinitializes the device and registers its own address for received
packets.  Since it does _not_ handle MIDI messages, it really should
not do this.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-31 20:00       ` Clemens Ladisch
  2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-09  8:42         ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-06-09 10:12         ` Sean M. Pappalardo - D.J. Pegasus
  2 siblings, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-06-09 10:12 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 661 bytes --]

Oh one more thing, sorry for the multiple messages.

As soon as I start the JACK server to handle the audio interface on the 
SCS.1m, the ALSA driver stops sending MIDI for that device unless I stop 
JACK and power-cycle the device. Its last words are:
F0 00 01 60 48 53 53 0F 03 00 03 00 00 0B 01 F7
It does, however, continue to respond to MIDI messages send to it.

The SCS.1d (connected through the SCS.1m) continues as before.

(On Windows via libhss1394, it's possible to use the audio interface and 
HSS1394 control on both devices simultaneously.)

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-31 20:00       ` Clemens Ladisch
  2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-06-09  8:42         ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-09 10:12         ` Sean M. Pappalardo - D.J. Pegasus
  2 siblings, 0 replies; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-06-09  8:42 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 1338 bytes --]

Hello again.

I just modified Mixxx's SCS.1d script to work with your custom sysex 
messages and tested with your latest patch. The .1d doesn't actually 
work 100% with this since Mixxx never receives some of the standard MIDI 
messages amid all of the sysex ones, and it's much worse when the 
platter is spinning and sending the tons of messages it does. This could 
be due to PortMIDI's lack of a callback mechanism, forcing Mixxx to poll 
it every 1ms because it's much more difficult to reproduce the problem 
using amidi -d. (Of course, amidi isn't sending data to the controller 
at the same time like Mixxx does.)

That said, the scratching works better in Linux that it does on Windows 
via libhss1394! (Not really a surprise since Windows has issues anyway.)

I also see an F9 message slip through once in awhile according to amidi -d:
F9
    55
91
C4

That's most likely to happen if the unit is sending alot of standard 
MIDI messages (such as when I quickly move a slider that sends a CC) 
since the device is always sending F9 messages as timestamps. More F9 
messages slip through when the platter is spinning because then they 
come more frequently.

Thank you again very much for your time and work!

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-31 20:00       ` Clemens Ladisch
@ 2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-09 11:07           ` Clemens Ladisch
  2012-06-09  8:42         ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-09 10:12         ` Sean M. Pappalardo - D.J. Pegasus
  2 siblings, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-06-09  6:54 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 1165 bytes --]

Hello again.

Sorry for the delay. I've been quite busy in the last week.

On 05/31/2012 10:00 PM, Clemens Ladisch wrote:
> I wrote:
>> 00 F9 xx yy zz ->  F0 00 01 60 48 53 53 00 00 0F 09 0x 0x 0y 0y 0z 0z F7

This appears to work fine. But where did you get the leading 00 from? 
libhss1394 just sends F9 xx yy zz.

>> 13 xx yy zz<- F0 00 01 60 48 53 53 01 03 0x 0x 0y 0y 0z 0z F7
>> 14 xx yy zz<- F0 00 01 60 48 53 53 01 04 0x 0x 0y 0y 0z 0z F7
 >> (0x13 = 0x10 + 3 = kUserTagBase + uUserTag)

I'm not clear on how you arrived at that formula. HSS1394.h shows:

//! Send a user control message via the channel to the target
//! node. Returns the number of bytes sent on success, zero on
//! failure. uUserTag is valid in the range 0x00 - 0xDF. All other
//! tag values will be rejected (0 return).
virtual uint SendUserControl(uint8 uUserTag, const uint8 *pUserData,
                              uint uDataBytes) = 0;

It appears to work with arbitrary-length user data strings, though the 
Stanton docs only specify 3-byte ones at this time.

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4558 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-31 22:04       ` Sean M. Pappalardo - D.J. Pegasus
@ 2012-06-01  8:22         ` Clemens Ladisch
  0 siblings, 0 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-06-01  8:22 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> On 05/30/2012 09:18 AM, Clemens Ladisch wrote:
>> As it happens, the actual SysEx commands use the wrong manufacturer ID
>> ("00 01 02" is Crystal Semiconductor); I could just use the real ID
>> (Stanton is "00 01 60") to escape non-MIDI HSS1394 messages.  Let's add
>> "HSS" to identify this, and to allow the full byte range, each HSS1394
>> byte is split into two nibbles.
>
> That all sounds good, but that now means that applications that want
> to use these device features must have different code for Linux than
> they do for Windows or OSX.

IIRC it was decided to use a MIDI driver in order to allow other
applications to access these devices with standard MIDI APIs.
(Does this imply that on Windows and OS X, it is not possible to
access them without using the HSS1394 library?)

You could still port the HSS1394 library so that it sits on top of the
MIDI driver.  (In other words, the Linux MIDI backend would be the
equivalent of the Windows and OS X FireWire backends.)


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-30  7:18     ` Clemens Ladisch
  2012-05-31 20:00       ` Clemens Ladisch
@ 2012-05-31 22:04       ` Sean M. Pappalardo - D.J. Pegasus
  2012-06-01  8:22         ` Clemens Ladisch
  1 sibling, 1 reply; 30+ messages in thread
From: Sean M. Pappalardo - D.J. Pegasus @ 2012-05-31 22:04 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, linux1394-devel


[-- Attachment #1.1: Type: text/plain, Size: 828 bytes --]



On 05/30/2012 09:18 AM, Clemens Ladisch wrote:
> As it happens, the actual SysEx commands use the wrong manufacturer ID
> ("00 01 02" is Crystal Semiconductor); I could just use the real ID
> (Stanton is "00 01 60") to escape non-MIDI HSS1394 messages.  Let's add
> "HSS" to identify this, and to allow the full byte range, each HSS1394
> byte is split into two nibbles.

That all sounds good, but that now means that applications that want to 
use these device features must have different code for Linux than they 
do for Windows or OSX. In the case of Mixxx, that also means a different 
controller preset. But I guess that's not the end of the world if it's a 
choice between this and no support at all.

Sigh...

Sincerely,
Sean M. Pappalardo
"D.J. Pegasus"
Mixxx Developer - Controller Specialist


[-- Attachment #1.2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4545 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-30  7:18     ` Clemens Ladisch
@ 2012-05-31 20:00       ` Clemens Ladisch
  2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
                           ` (2 more replies)
  2012-05-31 22:04       ` Sean M. Pappalardo - D.J. Pegasus
  1 sibling, 3 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-31 20:00 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

I wrote:
> 00 F9 xx yy zz -> F0 00 01 60 48 53 53 00 00 0F 09 0x 0x 0y 0y 0z 0z F7
> 13 xx yy zz    <- F0 00 01 60 48 53 53 01 03 0x 0x 0y 0y 0z 0z F7
> 14 xx yy zz    <- F0 00 01 60 48 53 53 01 04 0x 0x 0y 0y 0z 0z F7

New patch below.


Regards,
Clemens


--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -33,4 +33,17 @@ 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_PCM
+	select SND_RAWMIDI
+	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.
+
 endif # SND_FIREWIRE
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
 			 fcp.o cmp.o amdtp.o
 snd-firewire-speakers-objs := speakers.o
 snd-isight-objs := isight.o
+snd-scs1x-objs := scs1x.o

 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
--- /dev/null
+++ b/sound/firewire/scs1x.c
@@ -0,0 +1,498 @@
+/*
+ * 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 midi_status;
+	u8 midi_bytes;
+	bool output_escaped;
+	bool escape_high_nibble;
+	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->midi_status = 0;
+	scs->midi_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->midi_bytes;
+	for (;;) {
+		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+			scs->midi_bytes = i;
+			scs->output_idle = true;
+			wake_up(&scs->idle_wait);
+			return;
+		}
+		/*
+		 * Convert from real MIDI to what the device expects (no
+		 * running status, one command per packet, unescaped SysExs).
+		 */
+		if (scs->output_escaped && byte < 0x80) {
+			if (scs->escape_high_nibble) {
+				if (i < HSS1394_MAX_PACKET_SIZE) {
+					scs->buffer[i] = byte << 4;
+					scs->escape_high_nibble = false;
+				}
+			} else {
+				scs->buffer[i++] |= byte & 0x0f;
+				scs->escape_high_nibble = true;
+			}
+		} else if (byte < 0x80) {
+			if (i == 1) {
+				if (!is_valid_running_status(scs->midi_status))
+					continue;
+				scs->buffer[0] = HSS1394_TAG_USER_DATA;
+				scs->buffer[i++] = scs->midi_status;
+			}
+			scs->buffer[i++] = byte;
+			if ((i == 3 && is_two_bytes_cmd(scs->midi_status)) ||
+			    (i == 4 && is_three_bytes_cmd(scs->midi_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->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->escape_high_nibble &&
+				    scs->buffer[0] != HSS1394_TAG_CHANGE_ADDRESS)
+					break;
+			} else {
+				if (i > 1 && scs->midi_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->midi_status = byte;
+			scs->output_escaped = false;
+			if (is_one_byte_cmd(byte))
+				break;
+		}
+	}
+	scs->midi_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)
+{
+	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_packet(struct snd_rawmidi_substream *stream,
+			     const u8 *data, unsigned int bytes)
+{
+	unsigned int i;
+	u8 nibbles[2];
+
+	if (data[0] == HSS1394_TAG_USER_DATA && bytes >= 2 &&
+	    data[1] >= 0x80 && !is_invalid_cmd(data[1])) {
+		snd_rawmidi_receive(stream, data + 1, bytes - 1);
+	} else {
+		snd_rawmidi_receive(stream, sysex_escape_prefix,
+				    ARRAY_SIZE(sysex_escape_prefix));
+		for (i = 0; i < bytes; ++i) {
+			nibbles[0] = data[i] >> 4;
+			nibbles[1] = data[i] & 0x0f;
+			snd_rawmidi_receive(stream, nibbles, 2);
+		}
+		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(stream, data, length);
+	}
+
+	fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static int scs_init_hss_address(struct scs *scs)
+{
+	u8 data[8];
+	int err;
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
+				 HSS1394_ADDRESS, data, 8);
+	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 device *unit_dev)
+{
+	struct fw_unit *unit = fw_unit(unit_dev);
+	struct fw_device *fw_dev = fw_parent_device(unit);
+	struct snd_card *card;
+	struct scs *scs;
+	int err;
+
+	err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card);
+	if (err < 0)
+		return err;
+	snd_card_set_dev(card, unit_dev);
+
+	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)
+		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_dev, scs);
+
+	return 0;
+
+err_buffer:
+	kfree(scs->buffer);
+err_card:
+	snd_card_free(card);
+	return err;
+}
+
+static int scs_remove(struct device *dev)
+{
+	struct scs *scs = dev_get_drvdata(dev);
+
+	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);
+
+	return 0;
+}
+
+static void scs_update(struct fw_unit *unit)
+{
+	struct scs *scs = dev_get_drvdata(&unit->device);
+	int generation;
+	u8 data[8];
+
+	*(__be64 *)data = cpu_to_be64(scs->hss_handler.offset);
+	data[0] = HSS1394_TAG_CHANGE_ADDRESS;
+	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);
+}
+
+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,
+		.remove = scs_remove,
+	},
+	.update   = scs_update,
+	.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);

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
  2012-05-30  5:12   ` [alsa-devel] " Sean M. Pappalardo - D.J. Pegasus
@ 2012-05-30  7:18     ` Clemens Ladisch
  2012-05-31 20:00       ` Clemens Ladisch
  2012-05-31 22:04       ` Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 2 replies; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-30  7:18 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
> On 05/30/2012 06:51 AM, Clemens Ladisch wrote:
>> Sean M. Pappalardo - D.J. Pegasus wrote:
>>> four-byte platter messages that start with
>>> 0xF9 and the remaining three can range from 0x00 to 0xFF.
>>
>> This is not MIDI. Into what MIDI messages should
>> the driver convert this?
>
> It shouldn't convert it at all. Just deliver it as-is in a single
> four-byte message, if possible.

The problem is the communication between the driver and userspace;
_that_ must be valid MIDI.

> 0xF9 is a valid, if undefined, MIDI status.

Undefined means invalid.  Furthermore, data bytes must not have the
eighth bit set.

> I will also need the ability to use the HSS1394 SendUserControl
> function to set the device's timers, and that is definitely not MIDI
> at all.
> I wonder if it would be a good idea to define a special SYSEX message
> that the SCS1x driver would interpret and convert to a call to that
> function?

As it happens, the actual SysEx commands use the wrong manufacturer ID
("00 01 02" is Crystal Semiconductor); I could just use the real ID
(Stanton is "00 01 60") to escape non-MIDI HSS1394 messages.  Let's add
"HSS" to identify this, and to allow the full byte range, each HSS1394
byte is split into two nibbles.  So:

00 F9 xx yy zz -> F0 00 01 60 48 53 53 00 00 0F 09 0x 0x 0y 0y 0z 0z F7
13 xx yy zz    <- F0 00 01 60 48 53 53 01 03 0x 0x 0y 0y 0z 0z F7
14 xx yy zz    <- F0 00 01 60 48 53 53 01 04 0x 0x 0y 0y 0z 0z F7
(0x13 = 0x10 + 3 = kUserTagBase + uUserTag)


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Help requested: new HSS1394 MIDI back-end
       [not found] <94aa86f3-5257-402a-a094-f58fccdeb846@email.android.com>
@ 2012-05-30  4:51 ` Clemens Ladisch
  2012-05-30  5:12   ` [alsa-devel] " Sean M. Pappalardo - D.J. Pegasus
  0 siblings, 1 reply; 30+ messages in thread
From: Clemens Ladisch @ 2012-05-30  4:51 UTC (permalink / raw)
  To: Sean M. Pappalardo - D.J. Pegasus; +Cc: alsa-devel, linux1394-devel

Sean M. Pappalardo - D.J. Pegasus wrote:
>It works perfectly with the SCS.1m!! The .1d works, but some of 
>the non-standard data it sends appears to be getting corrupted. In 
>particular, the device sends four-byte platter messages that start with
>0xF9 and the remaining three can range from 0x00 to 0xFF.

This is not MIDI. Into what MIDI messages should
the driver convert this?
Is this documented anywhere?


Regards,
Clemens

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2012-11-12 11:40 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-25 19:43 Help requested: new HSS1394 MIDI back-end Sean M. Pappalardo - D.J. Pegasus
2012-05-27 13:50 ` Clemens Ladisch
2012-05-27 20:59   ` Sean M. Pappalardo - D.J. Pegasus
2012-05-28 14:58     ` Clemens Ladisch
2012-05-28 20:40       ` Sean M. Pappalardo - D.J. Pegasus
2012-05-29 17:05         ` Clemens Ladisch
2012-05-29 21:52           ` Sean M. Pappalardo - D.J. Pegasus
2012-07-24 11:44             ` Clemens Ladisch
2012-08-01 17:16               ` Sean M. Pappalardo - D.J. Pegasus
     [not found] <94aa86f3-5257-402a-a094-f58fccdeb846@email.android.com>
2012-05-30  4:51 ` Clemens Ladisch
2012-05-30  5:12   ` [alsa-devel] " Sean M. Pappalardo - D.J. Pegasus
2012-05-30  7:18     ` Clemens Ladisch
2012-05-31 20:00       ` Clemens Ladisch
2012-06-09  6:54         ` Sean M. Pappalardo - D.J. Pegasus
2012-06-09 11:07           ` Clemens Ladisch
2012-06-09 12:41             ` Sean M. Pappalardo - D.J. Pegasus
2012-06-10 13:00             ` Clemens Ladisch
2012-10-24 11:49               ` Sean M. Pappalardo - D.J. Pegasus
2012-10-25 19:23                 ` Clemens Ladisch
2012-10-25 20:26                   ` Sean M. Pappalardo - D.J. Pegasus
2012-10-26  7:48                     ` Clemens Ladisch
2012-10-31 10:00                   ` Sean M. Pappalardo - D.J. Pegasus
2012-11-09  6:41                   ` Sean M. Pappalardo - D.J. Pegasus
2012-11-12  9:45                   ` Takashi Iwai
2012-11-12 11:33                     ` Clemens Ladisch
2012-11-12 11:40                       ` Takashi Iwai
2012-06-09  8:42         ` Sean M. Pappalardo - D.J. Pegasus
2012-06-09 10:12         ` Sean M. Pappalardo - D.J. Pegasus
2012-05-31 22:04       ` Sean M. Pappalardo - D.J. Pegasus
2012-06-01  8:22         ` Clemens Ladisch
     [not found] <mailman.786.1339244201.2490.alsa-devel@alsa-project.org>
2012-06-09 12:56 ` Jonathan Woithe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.