| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (c) 2006,2007 Daniel Mack |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/usb.h> |
| #include <linux/gfp.h> |
| #include <sound/rawmidi.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| |
| #include "device.h" |
| #include "midi.h" |
| |
| static int snd_usb_caiaq_midi_input_open(struct snd_rawmidi_substream *substream) |
| { |
| return 0; |
| } |
| |
| static int snd_usb_caiaq_midi_input_close(struct snd_rawmidi_substream *substream) |
| { |
| return 0; |
| } |
| |
| static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) |
| { |
| struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data; |
| |
| if (!cdev) |
| return; |
| |
| cdev->midi_receive_substream = up ? substream : NULL; |
| } |
| |
| |
| static int snd_usb_caiaq_midi_output_open(struct snd_rawmidi_substream *substream) |
| { |
| return 0; |
| } |
| |
| static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substream) |
| { |
| struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data; |
| if (cdev->midi_out_active) { |
| usb_kill_urb(&cdev->midi_out_urb); |
| cdev->midi_out_active = 0; |
| } |
| return 0; |
| } |
| |
| static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev, |
| struct snd_rawmidi_substream *substream) |
| { |
| int len, ret; |
| struct device *dev = caiaqdev_to_dev(cdev); |
| |
| cdev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE; |
| cdev->midi_out_buf[1] = 0; /* port */ |
| len = snd_rawmidi_transmit(substream, cdev->midi_out_buf + 3, |
| EP1_BUFSIZE - 3); |
| |
| if (len <= 0) |
| return; |
| |
| cdev->midi_out_buf[2] = len; |
| cdev->midi_out_urb.transfer_buffer_length = len+3; |
| |
| ret = usb_submit_urb(&cdev->midi_out_urb, GFP_ATOMIC); |
| if (ret < 0) |
| dev_err(dev, |
| "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed," |
| "ret=%d, len=%d\n", substream, ret, len); |
| else |
| cdev->midi_out_active = 1; |
| } |
| |
| static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) |
| { |
| struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data; |
| |
| if (up) { |
| cdev->midi_out_substream = substream; |
| if (!cdev->midi_out_active) |
| snd_usb_caiaq_midi_send(cdev, substream); |
| } else { |
| cdev->midi_out_substream = NULL; |
| } |
| } |
| |
| |
| static const struct snd_rawmidi_ops snd_usb_caiaq_midi_output = |
| { |
| .open = snd_usb_caiaq_midi_output_open, |
| .close = snd_usb_caiaq_midi_output_close, |
| .trigger = snd_usb_caiaq_midi_output_trigger, |
| }; |
| |
| static const struct snd_rawmidi_ops snd_usb_caiaq_midi_input = |
| { |
| .open = snd_usb_caiaq_midi_input_open, |
| .close = snd_usb_caiaq_midi_input_close, |
| .trigger = snd_usb_caiaq_midi_input_trigger, |
| }; |
| |
| void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *cdev, |
| int port, const char *buf, int len) |
| { |
| if (!cdev->midi_receive_substream) |
| return; |
| |
| snd_rawmidi_receive(cdev->midi_receive_substream, buf, len); |
| } |
| |
| int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) |
| { |
| int ret; |
| struct snd_rawmidi *rmidi; |
| |
| ret = snd_rawmidi_new(device->chip.card, device->product_name, 0, |
| device->spec.num_midi_out, |
| device->spec.num_midi_in, |
| &rmidi); |
| |
| if (ret < 0) |
| return ret; |
| |
| strlcpy(rmidi->name, device->product_name, sizeof(rmidi->name)); |
| |
| rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX; |
| rmidi->private_data = device; |
| |
| if (device->spec.num_midi_out > 0) { |
| rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; |
| snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, |
| &snd_usb_caiaq_midi_output); |
| } |
| |
| if (device->spec.num_midi_in > 0) { |
| rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; |
| snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, |
| &snd_usb_caiaq_midi_input); |
| } |
| |
| device->rmidi = rmidi; |
| |
| return 0; |
| } |
| |
| void snd_usb_caiaq_midi_output_done(struct urb* urb) |
| { |
| struct snd_usb_caiaqdev *cdev = urb->context; |
| |
| cdev->midi_out_active = 0; |
| if (urb->status != 0) |
| return; |
| |
| if (!cdev->midi_out_substream) |
| return; |
| |
| snd_usb_caiaq_midi_send(cdev, cdev->midi_out_substream); |
| } |