| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733 |
- // SPDX-License-Identifier: GPL-2.0
- // ff-protocol-former.c - a part of driver for RME Fireface series
- //
- // Copyright (c) 2019 Takashi Sakamoto
- #include <linux/delay.h>
- #include "ff.h"
- #define FORMER_REG_SYNC_STATUS 0x0000801c0000ull
- /* For block write request. */
- #define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull
- #define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull
- static int parse_clock_bits(u32 data, unsigned int *rate,
- enum snd_ff_clock_src *src)
- {
- static const struct {
- unsigned int rate;
- u32 mask;
- } *rate_entry, rate_entries[] = {
- { 32000, 0x00000002, },
- { 44100, 0x00000000, },
- { 48000, 0x00000006, },
- { 64000, 0x0000000a, },
- { 88200, 0x00000008, },
- { 96000, 0x0000000e, },
- { 128000, 0x00000012, },
- { 176400, 0x00000010, },
- { 192000, 0x00000016, },
- };
- static const struct {
- enum snd_ff_clock_src src;
- u32 mask;
- } *clk_entry, clk_entries[] = {
- { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, },
- { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, },
- { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, },
- { SND_FF_CLOCK_SRC_WORD, 0x00001000, },
- { SND_FF_CLOCK_SRC_LTC, 0x00001800, },
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
- rate_entry = rate_entries + i;
- if ((data & 0x0000001e) == rate_entry->mask) {
- *rate = rate_entry->rate;
- break;
- }
- }
- if (i == ARRAY_SIZE(rate_entries))
- return -EIO;
- if (data & 0x00000001) {
- *src = SND_FF_CLOCK_SRC_INTERNAL;
- } else {
- for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
- clk_entry = clk_entries + i;
- if ((data & 0x00001c00) == clk_entry->mask) {
- *src = clk_entry->src;
- break;
- }
- }
- if (i == ARRAY_SIZE(clk_entries))
- return -EIO;
- }
- return 0;
- }
- static int former_get_clock(struct snd_ff *ff, unsigned int *rate,
- enum snd_ff_clock_src *src)
- {
- __le32 reg;
- u32 data;
- int err;
- err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
- FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- data = le32_to_cpu(reg);
- return parse_clock_bits(data, rate, src);
- }
- static int former_switch_fetching_mode(struct snd_ff *ff, bool enable)
- {
- unsigned int count;
- __le32 *reg;
- int i;
- int err;
- count = 0;
- for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
- count = max(count, ff->spec->pcm_playback_channels[i]);
- reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
- if (!reg)
- return -ENOMEM;
- if (!enable) {
- /*
- * Each quadlet is corresponding to data channels in a data
- * blocks in reverse order. Precisely, quadlets for available
- * data channels should be enabled. Here, I take second best
- * to fetch PCM frames from all of data channels regardless of
- * stf.
- */
- for (i = 0; i < count; ++i)
- reg[i] = cpu_to_le32(0x00000001);
- }
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
- FORMER_REG_FETCH_PCM_FRAMES, reg,
- sizeof(__le32) * count, 0);
- kfree(reg);
- return err;
- }
- static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
- {
- __le32 reg;
- u32 data;
- unsigned int rate;
- enum snd_ff_clock_src src;
- const char *label;
- int err;
- err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
- FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0);
- if (err < 0)
- return;
- data = le32_to_cpu(reg);
- snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
- (data & 0x00000020) ? "Professional" : "Consumer",
- (data & 0x00000040) ? "on" : "off");
- snd_iprintf(buffer, "Optical output interface format: %s\n",
- (data & 0x00000100) ? "S/PDIF" : "ADAT");
- snd_iprintf(buffer, "Word output single speed: %s\n",
- (data & 0x00002000) ? "on" : "off");
- snd_iprintf(buffer, "S/PDIF input interface: %s\n",
- (data & 0x00000200) ? "Optical" : "Coaxial");
- err = parse_clock_bits(data, &rate, &src);
- if (err < 0)
- return;
- label = snd_ff_proc_get_clk_label(src);
- if (!label)
- return;
- snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label);
- }
- static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
- {
- static const struct {
- char *const label;
- u32 locked_mask;
- u32 synced_mask;
- } *clk_entry, clk_entries[] = {
- { "WDClk", 0x40000000, 0x20000000, },
- { "S/PDIF", 0x00080000, 0x00040000, },
- { "ADAT1", 0x00000400, 0x00001000, },
- { "ADAT2", 0x00000800, 0x00002000, },
- };
- static const struct {
- char *const label;
- u32 mask;
- } *referred_entry, referred_entries[] = {
- { "ADAT1", 0x00000000, },
- { "ADAT2", 0x00400000, },
- { "S/PDIF", 0x00c00000, },
- { "WDclk", 0x01000000, },
- { "TCO", 0x01400000, },
- };
- static const struct {
- unsigned int rate;
- u32 mask;
- } *rate_entry, rate_entries[] = {
- { 32000, 0x02000000, },
- { 44100, 0x04000000, },
- { 48000, 0x06000000, },
- { 64000, 0x08000000, },
- { 88200, 0x0a000000, },
- { 96000, 0x0c000000, },
- { 128000, 0x0e000000, },
- { 176400, 0x10000000, },
- { 192000, 0x12000000, },
- };
- __le32 reg[2];
- u32 data[2];
- int i;
- int err;
- err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
- FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0);
- if (err < 0)
- return;
- data[0] = le32_to_cpu(reg[0]);
- data[1] = le32_to_cpu(reg[1]);
- snd_iprintf(buffer, "External source detection:\n");
- for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
- const char *state;
- clk_entry = clk_entries + i;
- if (data[0] & clk_entry->locked_mask) {
- if (data[0] & clk_entry->synced_mask)
- state = "sync";
- else
- state = "lock";
- } else {
- state = "none";
- }
- snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state);
- }
- snd_iprintf(buffer, "Referred clock:\n");
- if (data[1] & 0x00000001) {
- snd_iprintf(buffer, "Internal\n");
- } else {
- unsigned int rate;
- const char *label;
- for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) {
- referred_entry = referred_entries + i;
- if ((data[0] & 0x1e0000) == referred_entry->mask) {
- label = referred_entry->label;
- break;
- }
- }
- if (i == ARRAY_SIZE(referred_entries))
- label = "none";
- for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
- rate_entry = rate_entries + i;
- if ((data[0] & 0x1e000000) == rate_entry->mask) {
- rate = rate_entry->rate;
- break;
- }
- }
- if (i == ARRAY_SIZE(rate_entries))
- rate = 0;
- snd_iprintf(buffer, "%s %d\n", label, rate);
- }
- }
- static void former_dump_status(struct snd_ff *ff,
- struct snd_info_buffer *buffer)
- {
- dump_clock_config(ff, buffer);
- dump_sync_status(ff, buffer);
- }
- static int former_fill_midi_msg(struct snd_ff *ff,
- struct snd_rawmidi_substream *substream,
- unsigned int port)
- {
- u8 *buf = (u8 *)ff->msg_buf[port];
- int len;
- int i;
- len = snd_rawmidi_transmit_peek(substream, buf,
- SND_FF_MAXIMIM_MIDI_QUADS);
- if (len <= 0)
- return len;
- // One quadlet includes one byte.
- for (i = len - 1; i >= 0; --i)
- ff->msg_buf[port][i] = cpu_to_le32(buf[i]);
- ff->rx_bytes[port] = len;
- return len;
- }
- #define FF800_STF 0x0000fc88f000
- #define FF800_RX_PACKET_FORMAT 0x0000fc88f004
- #define FF800_ALLOC_TX_STREAM 0x0000fc88f008
- #define FF800_ISOC_COMM_START 0x0000fc88f00c
- #define FF800_TX_S800_FLAG 0x00000800
- #define FF800_ISOC_COMM_STOP 0x0000fc88f010
- #define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
- static int allocate_tx_resources(struct snd_ff *ff)
- {
- __le32 reg;
- unsigned int count;
- unsigned int tx_isoc_channel;
- int err;
- reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- // Wait till the format of tx packet is available.
- count = 0;
- while (count++ < 10) {
- u32 data;
- err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
- FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- data = le32_to_cpu(reg);
- if (data != 0xffffffff) {
- tx_isoc_channel = data;
- break;
- }
- msleep(50);
- }
- if (count >= 10)
- return -ETIMEDOUT;
- // NOTE: this is a makeshift to start OHCI 1394 IR context in the
- // channel. On the other hand, 'struct fw_iso_resources.allocated' is
- // not true and it's not deallocated at stop.
- ff->tx_resources.channel = tx_isoc_channel;
- return 0;
- }
- static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate)
- {
- u32 data;
- __le32 reg;
- int err;
- reg = cpu_to_le32(rate);
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF800_STF, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- // If starting isochronous communication immediately, change of STF has
- // no effect. In this case, the communication runs based on former STF.
- // Let's sleep for a bit.
- msleep(100);
- // Controllers should allocate isochronous resources for rx stream.
- err = fw_iso_resources_allocate(&ff->rx_resources,
- amdtp_stream_get_max_payload(&ff->rx_stream),
- fw_parent_device(ff->unit)->max_speed);
- if (err < 0)
- return err;
- // Set isochronous channel and the number of quadlets of rx packets.
- // This should be done before the allocation of tx resources to avoid
- // periodical noise.
- data = ff->rx_stream.data_block_quadlets << 3;
- data = (data << 8) | ff->rx_resources.channel;
- reg = cpu_to_le32(data);
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- return allocate_tx_resources(ff);
- }
- static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
- {
- unsigned int generation = ff->rx_resources.generation;
- __le32 reg;
- if (generation != fw_parent_device(ff->unit)->card->generation) {
- int err = fw_iso_resources_update(&ff->rx_resources);
- if (err < 0)
- return err;
- }
- reg = cpu_to_le32(0x80000000);
- reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
- if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
- reg |= cpu_to_le32(FF800_TX_S800_FLAG);
- return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF800_ISOC_COMM_START, ®, sizeof(reg), 0);
- }
- static void ff800_finish_session(struct snd_ff *ff)
- {
- __le32 reg;
- reg = cpu_to_le32(0x80000000);
- snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0);
- }
- // Fireface 800 doesn't allow drivers to register lower 4 bytes of destination
- // address.
- // A write transaction to clear registered higher 4 bytes of destination address
- // has an effect to suppress asynchronous transaction from device.
- static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
- size_t length, u32 tstamp)
- {
- int i;
- for (i = 0; i < length / 4; i++) {
- u8 byte = le32_to_cpu(buf[i]) & 0xff;
- struct snd_rawmidi_substream *substream;
- substream = READ_ONCE(ff->tx_midi_substreams[0]);
- if (substream)
- snd_rawmidi_receive(substream, &byte, 1);
- }
- }
- const struct snd_ff_protocol snd_ff_protocol_ff800 = {
- .handle_msg = ff800_handle_midi_msg,
- .fill_midi_msg = former_fill_midi_msg,
- .get_clock = former_get_clock,
- .switch_fetching_mode = former_switch_fetching_mode,
- .allocate_resources = ff800_allocate_resources,
- .begin_session = ff800_begin_session,
- .finish_session = ff800_finish_session,
- .dump_status = former_dump_status,
- };
- #define FF400_STF 0x000080100500ull
- #define FF400_RX_PACKET_FORMAT 0x000080100504ull
- #define FF400_ISOC_COMM_START 0x000080100508ull
- #define FF400_TX_PACKET_FORMAT 0x00008010050cull
- #define FF400_ISOC_COMM_STOP 0x000080100510ull
- // Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
- // we can allocate between 0 and 7 channel.
- static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate)
- {
- __le32 reg;
- enum snd_ff_stream_mode mode;
- int i;
- int err;
- // Check whether the given value is supported or not.
- for (i = 0; i < CIP_SFC_COUNT; i++) {
- if (amdtp_rate_table[i] == rate)
- break;
- }
- if (i >= CIP_SFC_COUNT)
- return -EINVAL;
- // Set the number of data blocks transferred in a second.
- reg = cpu_to_le32(rate);
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF400_STF, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- msleep(100);
- err = snd_ff_stream_get_multiplier_mode(i, &mode);
- if (err < 0)
- return err;
- // Keep resources for in-stream.
- ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
- err = fw_iso_resources_allocate(&ff->tx_resources,
- amdtp_stream_get_max_payload(&ff->tx_stream),
- fw_parent_device(ff->unit)->max_speed);
- if (err < 0)
- return err;
- // Keep resources for out-stream.
- ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
- err = fw_iso_resources_allocate(&ff->rx_resources,
- amdtp_stream_get_max_payload(&ff->rx_stream),
- fw_parent_device(ff->unit)->max_speed);
- if (err < 0)
- fw_iso_resources_free(&ff->tx_resources);
- return err;
- }
- static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
- {
- unsigned int generation = ff->rx_resources.generation;
- __le32 reg;
- int err;
- if (generation != fw_parent_device(ff->unit)->card->generation) {
- err = fw_iso_resources_update(&ff->tx_resources);
- if (err < 0)
- return err;
- err = fw_iso_resources_update(&ff->rx_resources);
- if (err < 0)
- return err;
- }
- // Set isochronous channel and the number of quadlets of received
- // packets.
- reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
- ff->rx_resources.channel);
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- // Set isochronous channel and the number of quadlets of transmitted
- // packet.
- // TODO: investigate the purpose of this 0x80.
- reg = cpu_to_le32((0x80 << 24) |
- (ff->tx_resources.channel << 5) |
- (ff->tx_stream.data_block_quadlets));
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- // Allow to transmit packets.
- reg = cpu_to_le32(0x00000001);
- return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF400_ISOC_COMM_START, ®, sizeof(reg), 0);
- }
- static void ff400_finish_session(struct snd_ff *ff)
- {
- __le32 reg;
- reg = cpu_to_le32(0x80000000);
- snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
- FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0);
- }
- static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
- {
- struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]);
- if (substream != NULL) {
- u8 byte = (quad >> (16 * port)) & 0x000000ff;
- snd_rawmidi_receive(substream, &byte, 1);
- }
- }
- #define FF400_QUEUE_SIZE 32
- struct ff400_msg_parser {
- struct {
- u32 msg;
- u32 tstamp;
- } msgs[FF400_QUEUE_SIZE];
- size_t push_pos;
- size_t pull_pos;
- };
- static bool ff400_has_msg(struct snd_ff *ff)
- {
- struct ff400_msg_parser *parser = ff->msg_parser;
- return (parser->push_pos != parser->pull_pos);
- }
- // For Fireface 400, lower 4 bytes of destination address is configured by bit
- // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
- // select one of 4 options:
- //
- // bit flags: offset of destination address
- // - 0x04000000: 0x'....'....'0000'0000
- // - 0x08000000: 0x'....'....'0000'0080
- // - 0x10000000: 0x'....'....'0000'0100
- // - 0x20000000: 0x'....'....'0000'0180
- //
- // Drivers can suppress the device to transfer asynchronous transactions by
- // using below 2 bits.
- // - 0x01000000: suppress transmission
- // - 0x02000000: suppress transmission
- //
- // Actually, the register is write-only and includes the other options such as
- // input attenuation. This driver allocates destination address with '0000'0000
- // in its lower offset and expects userspace application to configure the
- // register for it.
- // When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of
- // stereo physical port.
- // - 0: Microphone input 0/1
- // - 1: Line input 0/1
- // - [2-4]: Line output 0-5
- // - 5: Headphone output 0/1
- // - 6: S/PDIF output 0/1
- // - [7-10]: ADAT output 0-7
- //
- // The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone
- // input:
- //
- // - 0: 0.0 dB
- // - 10: +10.0 dB
- // - 11: +11.0 dB
- // - 12: +12.0 dB
- // - ...
- // - 63: +63.0 dB:
- // - 64: +64.0 dB:
- // - 65: +65.0 dB:
- //
- // For signal level of line input:
- //
- // - 0: 0.0 dB
- // - 1: +0.5 dB
- // - 2: +1.0 dB
- // - 3: +1.5 dB
- // - ...
- // - 34: +17.0 dB:
- // - 35: +17.5 dB:
- // - 36: +18.0 dB:
- //
- // For signal level of any type of output:
- //
- // - 63: -infinite
- // - 62: -58.0 dB
- // - 61: -56.0 dB
- // - 60: -54.0 dB
- // - 59: -53.0 dB
- // - 58: -52.0 dB
- // - ...
- // - 7: -1.0 dB
- // - 6: 0.0 dB
- // - 5: +1.0 dB
- // - ...
- // - 2: +4.0 dB
- // - 1: +5.0 dB
- // - 0: +6.0 dB
- //
- // When the message is not for signal level operation, it's for MIDI bytes. When matching to
- // FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When
- // matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000.
- #define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000
- #define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000
- #define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000
- #define FF400_MSG_MASK_STEREO_PAIR 0xf0000000
- #define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00
- #define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100
- #define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff
- #define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000
- #define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000
- static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
- size_t length, u32 tstamp)
- {
- bool need_hwdep_wake_up = false;
- int i;
- for (i = 0; i < length / 4; i++) {
- u32 quad = le32_to_cpu(buf[i]);
- if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) {
- struct ff400_msg_parser *parser = ff->msg_parser;
- parser->msgs[parser->push_pos].msg = quad;
- parser->msgs[parser->push_pos].tstamp = tstamp;
- ++parser->push_pos;
- if (parser->push_pos >= FF400_QUEUE_SIZE)
- parser->push_pos = 0;
- need_hwdep_wake_up = true;
- } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) {
- parse_midi_msg(ff, quad, 0);
- } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) {
- parse_midi_msg(ff, quad, 1);
- }
- }
- if (need_hwdep_wake_up)
- wake_up(&ff->hwdep_wait);
- }
- static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count)
- {
- struct snd_firewire_event_ff400_message ev = {
- .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE,
- .message_count = 0,
- };
- struct ff400_msg_parser *parser = ff->msg_parser;
- long consumed = 0;
- long ret = 0;
- if (count < sizeof(ev) || parser->pull_pos == parser->push_pos)
- return 0;
- count -= sizeof(ev);
- consumed += sizeof(ev);
- while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) {
- spin_unlock_irq(&ff->lock);
- if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos,
- sizeof(*parser->msgs)))
- ret = -EFAULT;
- spin_lock_irq(&ff->lock);
- if (ret)
- return ret;
- ++parser->pull_pos;
- if (parser->pull_pos >= FF400_QUEUE_SIZE)
- parser->pull_pos = 0;
- ++ev.message_count;
- count -= sizeof(*parser->msgs);
- consumed += sizeof(*parser->msgs);
- }
- spin_unlock_irq(&ff->lock);
- if (copy_to_user(buf, &ev, sizeof(ev)))
- ret = -EFAULT;
- spin_lock_irq(&ff->lock);
- if (ret)
- return ret;
- return consumed;
- }
- const struct snd_ff_protocol snd_ff_protocol_ff400 = {
- .msg_parser_size = sizeof(struct ff400_msg_parser),
- .has_msg = ff400_has_msg,
- .copy_msg_to_user = ff400_copy_msg_to_user,
- .handle_msg = ff400_handle_msg,
- .fill_midi_msg = former_fill_midi_msg,
- .get_clock = former_get_clock,
- .switch_fetching_mode = former_switch_fetching_mode,
- .allocate_resources = ff400_allocate_resources,
- .begin_session = ff400_begin_session,
- .finish_session = ff400_finish_session,
- .dump_status = former_dump_status,
- };
|