/* * Copyright (c) 2006,2007 Daniel Mack * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #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 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 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); } /option>
authorTony Lindgren <tony@atomide.com>2017-01-24 09:18:57 -0600
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-25 11:02:29 +0100
commit407788b51db6f6aab499d02420082f436abf3238 (patch)
treeb2fb3c885baf47cd2bd8ee0429f423b352e11905 /sound/firewire
parent7a308bb3016f57e5be11a677d15b821536419d36 (diff)
usb: musb: Fix host mode error -71 regression
Commit 467d5c980709 ("usb: musb: Implement session bit based runtime PM for musb-core") started implementing musb generic runtime PM support by introducing devctl register session bit based state control. This caused a regression where if a USB mass storage device is connected to a USB hub, we can get: usb 1-1: reset high-speed USB device number 2 using musb-hdrc usb 1-1: device descriptor read/64, error -71 usb 1-1.1: new high-speed USB device number 4 using musb-hdrc This is because before the USB storage device is connected, musb is in OTG_STATE_A_SUSPEND. And we currently only set need_finish_resume in musb_stage0_irq() and the related code calling finish_resume_work in musb_resume() and musb_runtime_resume() never gets called. To fix the issue, we can call schedule_delayed_work() directly in musb_stage0_irq() to have finish_resume_work run. And we should no longer never get interrupts when when suspended. We have changed musb to no longer need pm_runtime_irqsafe(). The need_finish_resume flag was added in commit 9298b4aad37e ("usb: musb: fix device hotplug behind hub") and no longer applies as far as I can tell. So let's just remove the earlier code that no longer is needed. Fixes: 467d5c980709 ("usb: musb: Implement session bit based runtime PM for musb-core") Reported-by: Bin Liu <b-liu@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Bin Liu <b-liu@ti.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound/firewire')