diff options
Diffstat (limited to 'sound/firewire/fireface')
-rw-r--r-- | sound/firewire/fireface/amdtp-ff.c | 28 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-hwdep.c | 41 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-protocol-former.c | 192 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-protocol-latter.c | 6 | ||||
-rw-r--r-- | sound/firewire/fireface/ff-transaction.c | 17 | ||||
-rw-r--r-- | sound/firewire/fireface/ff.c | 10 | ||||
-rw-r--r-- | sound/firewire/fireface/ff.h | 9 |
7 files changed, 232 insertions, 71 deletions
diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c index 98177b0666d3..76c9d33ed572 100644 --- a/sound/firewire/fireface/amdtp-ff.c +++ b/sound/firewire/fireface/amdtp-ff.c @@ -24,7 +24,7 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate, p->pcm_channels = pcm_channels; data_channels = pcm_channels; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -112,16 +112,13 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -131,21 +128,18 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, } else { write_pcm_silence(s, buf, data_blocks); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -153,9 +147,9 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); pcm_frames += data_blocks; } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index ea64a2a41eea..8a741b3b0436 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -15,16 +15,23 @@ #include "ff.h" +static bool has_msg(struct snd_ff *ff) +{ + if (ff->spec->protocol->has_msg) + return ff->spec->protocol->has_msg(ff); + else + return 0; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_ff *ff = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event; spin_lock_irq(&ff->lock); - while (!ff->dev_lock_changed) { + while (!ff->dev_lock_changed && !has_msg(ff)) { prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&ff->lock); schedule(); @@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&ff->lock); } - memset(&event, 0, sizeof(event)); - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = (ff->dev_lock_count > 0); - ff->dev_lock_changed = false; + if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) { + struct snd_firewire_event_lock_status ev = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + .status = (ff->dev_lock_count > 0), + }; - count = min_t(long, count, sizeof(event.lock_status)); + ff->dev_lock_changed = false; - spin_unlock_irq(&ff->lock); + spin_unlock_irq(&ff->lock); - if (copy_to_user(buf, &event, count)) - return -EFAULT; + if (copy_to_user(buf, &ev, sizeof(ev))) + return -EFAULT; + count = sizeof(ev); + } else if (has_msg(ff)) { + // NOTE: Acquired spin lock should be released before accessing to user space in the + // callback since the access can cause page fault. + count = ff->spec->protocol->copy_msg_to_user(ff, buf, count); + spin_unlock_irq(&ff->lock); + } else { + spin_unlock_irq(&ff->lock); + + count = 0; + } return count; } @@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &ff->hwdep_wait, wait); spin_lock_irq(&ff->lock); - if (ff->dev_lock_changed) + if (ff->dev_lock_changed || has_msg(ff)) events = EPOLLIN | EPOLLRDNORM; else events = 0; diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8900ffe517ed..efd59e9d9935 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff) // 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, - __le32 *buf, size_t length) +static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { int i; @@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, } const struct snd_ff_protocol snd_ff_protocol_ff800 = { - .handle_midi_msg = ff800_handle_midi_msg, + .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, @@ -534,6 +534,35 @@ static void ff400_finish_session(struct snd_ff *ff) 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: @@ -553,46 +582,147 @@ static void ff400_finish_session(struct snd_ff *ff) // input attenuation. This driver allocates destination address with '0000'0000 // in its lower offset and expects userspace application to configure the // register for it. -static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) + +// 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]); - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } + if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) { + struct ff400_msg_parser *parser = ff->msg_parser; - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } + 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 = { - .handle_midi_msg = ff400_handle_midi_msg, + .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, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 76c3eab36d4e..9947e0c2e0aa 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer // input attenuation. This driver allocates for the first option // (0x'....'....'0000'0000) and expects userspace application to configure the // register for it. -static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { u32 data = le32_to_cpu(*buf); unsigned int index = (data & 0x000000f0) >> 4; @@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff, } const struct snd_ff_protocol snd_ff_protocol_latter = { - .handle_midi_msg = latter_handle_midi_msg, + .handle_msg = latter_handle_midi_msg, .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index ee7122c461d4..6b89e39f4a43 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -125,19 +125,22 @@ static void transmit_midi1_msg(struct work_struct *work) transmit_midi_msg(ff, 1); } -static void handle_midi_msg(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) +static void handle_msg(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 snd_ff *ff = callback_data; __le32 *buf = data; + u32 tstamp = fw_request_get_timestamp(request); + unsigned long flag; fw_send_response(card, request, RCODE_COMPLETE); offset -= ff->async_handler.offset; - ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, - length); + + spin_lock_irqsave(&ff->lock, flag); + ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp); + spin_unlock_irqrestore(&ff->lock, flag); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -146,7 +149,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) int err; ff->async_handler.length = ff->spec->midi_addr_range; - ff->async_handler.address_callback = handle_midi_msg; + ff->async_handler.address_callback = handle_msg; ff->async_handler.callback_data = ff; midi_msg_region.start = 0x000100000000ull * i; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 7bf51d062021..448e972028d9 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card) snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); + kfree(ff->msg_parser); + mutex_destroy(&ff->mutex); fw_unit_put(ff->unit); } @@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e if (err < 0) goto error; + if (ff->spec->protocol->msg_parser_size > 0) { + ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL); + if (!ff->msg_parser) { + err = -ENOMEM; + goto error; + } + } + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 0535f0b58b67..7e42f5778a8a 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -97,6 +97,8 @@ struct snd_ff { wait_queue_head_t hwdep_wait; struct amdtp_domain domain; + + void *msg_parser; }; enum snd_ff_clock_src { @@ -110,8 +112,11 @@ enum snd_ff_clock_src { }; struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length); + size_t msg_parser_size; + bool (*has_msg)(struct snd_ff *ff); + long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count); + void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp); int (*fill_midi_msg)(struct snd_ff *ff, struct snd_rawmidi_substream *substream, unsigned int port); |