ALSA: firewire/bebob: Add a workaround for M-Audio special Firewire series

In post commit, a quirk of this firmware about transactions is reported.
This commit apply a workaround for this quirk.

They often fail transactions due to gap_count mismatch. This state is changed
by generating bus reset.

The fw_schedule_bus_reset() is an exported symbol in firewire-core. But there
are no header for public. This commit moves its prototype from
drivers/firewire/core.h to include/linux/firewire.h.

This mismatch still affects bus management before generating this bus reset.
It still takes a time to call driver's probe() because transactions are still
often failed.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index e1dd421..31b96b7 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -247,10 +247,26 @@
 	if (err < 0)
 		goto error;
 
-	err = snd_card_register(card);
-	if (err < 0) {
-		snd_bebob_stream_destroy_duplex(bebob);
-		goto error;
+	if (!bebob->maudio_special_quirk) {
+		err = snd_card_register(card);
+		if (err < 0) {
+			snd_bebob_stream_destroy_duplex(bebob);
+			goto error;
+		}
+	} else {
+		/*
+		 * This is a workaround. This bus reset seems to have an effect
+		 * to make devices correctly handling transactions. Without
+		 * this, the devices have gap_count mismatch. This causes much
+		 * failure of transaction.
+		 *
+		 * Just after registration, user-land application receive
+		 * signals from dbus and starts I/Os. To avoid I/Os till the
+		 * future bus reset, registration is done in next update().
+		 */
+		bebob->deferred_registration = true;
+		fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
+				      false, true);
 	}
 
 	dev_set_drvdata(&unit->device, bebob);
@@ -273,6 +289,14 @@
 
 	fcp_bus_reset(bebob->unit);
 	snd_bebob_stream_update_duplex(bebob);
+
+	if (bebob->deferred_registration) {
+		if (snd_card_register(bebob->card) < 0) {
+			snd_bebob_stream_destroy_duplex(bebob);
+			snd_card_free(bebob->card);
+		}
+		bebob->deferred_registration = false;
+	}
 }
 
 static void bebob_remove(struct fw_unit *unit)