diff options
Diffstat (limited to 'sound/usb/line6/driver.c')
| -rw-r--r-- | sound/usb/line6/driver.c | 203 |
1 files changed, 111 insertions, 92 deletions
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index c1376bfdc90b..e97368c31417 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Line 6 Linux USB driver * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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, version 2. - * + * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at) */ #include <linux/kernel.h> @@ -24,7 +20,7 @@ #include "midi.h" #include "playback.h" -#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" +#define DRIVER_AUTHOR "Markus Grabner <line6@grabner-graz.at>" #define DRIVER_DESC "Line 6 USB Driver" /* @@ -101,7 +97,7 @@ static void line6_stop_listen(struct usb_line6 *line6) /* Send raw message in pieces of wMaxPacketSize bytes. */ -static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, +int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, int size) { int i, done = 0; @@ -117,12 +113,12 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w), (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + &partial, LINE6_TIMEOUT); } else { retval = usb_bulk_msg(line6->usbdev, usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w), (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + &partial, LINE6_TIMEOUT); } if (retval) { @@ -136,6 +132,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, return done; } +EXPORT_SYMBOL_GPL(line6_send_raw_message); /* Notification of completion of asynchronous request transmission. @@ -196,17 +193,6 @@ static int line6_send_raw_message_async_part(struct message *msg, } /* - Setup and start timer. -*/ -void line6_start_timer(struct timer_list *timer, unsigned long msecs, - void (*function)(struct timer_list *t)) -{ - timer->function = function; - mod_timer(timer, jiffies + msecs_to_jiffies(msecs)); -} -EXPORT_SYMBOL_GPL(line6_start_timer); - -/* Asynchronously send raw message. */ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, @@ -216,7 +202,7 @@ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, struct urb *urb; /* create message: */ - msg = kmalloc(sizeof(struct message), GFP_ATOMIC); + msg = kzalloc(sizeof(struct message), GFP_ATOMIC); if (msg == NULL) return -ENOMEM; @@ -306,21 +292,26 @@ static void line6_data_received(struct urb *urb) return; if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { - done = - line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); + scoped_guard(spinlock_irqsave, &line6->line6midi->lock) { + done = + line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); - if (done < urb->actual_length) { - line6_midibuf_ignore(mb, done); - dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", - done, urb->actual_length); + if (done < urb->actual_length) { + line6_midibuf_ignore(mb, done); + dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", + done, urb->actual_length); + } } for (;;) { - done = - line6_midibuf_read(mb, line6->buffer_message, - LINE6_MIDI_MESSAGE_MAXLEN); - - if (done == 0) + scoped_guard(spinlock_irqsave, &line6->line6midi->lock) { + done = + line6_midibuf_read(mb, line6->buffer_message, + LINE6_MIDI_MESSAGE_MAXLEN, + LINE6_MIDIBUF_READ_RX); + } + + if (done <= 0) break; line6->message_length = done; @@ -351,66 +342,63 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, { struct usb_device *usbdev = line6->usbdev; int ret; - unsigned char len; + u8 len; unsigned count; if (address > 0xffff || datalen > 0xff) return -EINVAL; /* query the serial number: */ - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - (datalen << 8) | 0x21, address, - NULL, 0, LINE6_TIMEOUT * HZ); - - if (ret < 0) { + ret = usb_control_msg_send(usbdev, 0, 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + (datalen << 8) | 0x21, address, NULL, 0, + LINE6_TIMEOUT, GFP_KERNEL); + if (ret) { dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); - return ret; + goto exit; } /* Wait for data length. We'll get 0xff until length arrives. */ for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) { mdelay(LINE6_READ_WRITE_STATUS_DELAY); - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, &len, 1, - LINE6_TIMEOUT * HZ); - if (ret < 0) { + ret = usb_control_msg_recv(usbdev, 0, 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0012, 0x0000, &len, 1, + LINE6_TIMEOUT, GFP_KERNEL); + if (ret) { dev_err(line6->ifcdev, "receive length failed (error %d)\n", ret); - return ret; + goto exit; } if (len != 0xff) break; } + ret = -EIO; if (len == 0xff) { dev_err(line6->ifcdev, "read failed after %d retries\n", count); - return -EIO; + goto exit; } else if (len != datalen) { /* should be equal or something went wrong */ dev_err(line6->ifcdev, "length mismatch (expected %d, got %d)\n", - (int)datalen, (int)len); - return -EIO; + (int)datalen, len); + goto exit; } /* receive the result: */ - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0013, 0x0000, data, datalen, - LINE6_TIMEOUT * HZ); - - if (ret < 0) { + ret = usb_control_msg_recv(usbdev, 0, 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT, + GFP_KERNEL); + if (ret) dev_err(line6->ifcdev, "read failed (error %d)\n", ret); - return ret; - } - return 0; +exit: + return ret; } EXPORT_SYMBOL_GPL(line6_read_data); @@ -422,53 +410,54 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, { struct usb_device *usbdev = line6->usbdev; int ret; - unsigned char status; + unsigned char *status; int count; if (address > 0xffff || datalen > 0xffff) return -EINVAL; - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x0022, address, data, datalen, - LINE6_TIMEOUT * HZ); + status = kmalloc(1, GFP_KERNEL); + if (!status) + return -ENOMEM; - if (ret < 0) { + ret = usb_control_msg_send(usbdev, 0, 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x0022, address, data, datalen, LINE6_TIMEOUT, + GFP_KERNEL); + if (ret) { dev_err(line6->ifcdev, "write request failed (error %d)\n", ret); - return ret; + goto exit; } for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) { mdelay(LINE6_READ_WRITE_STATUS_DELAY); - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), - 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, - &status, 1, LINE6_TIMEOUT * HZ); - - if (ret < 0) { + ret = usb_control_msg_recv(usbdev, 0, 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0012, 0x0000, status, 1, LINE6_TIMEOUT, + GFP_KERNEL); + if (ret) { dev_err(line6->ifcdev, "receiving status failed (error %d)\n", ret); - return ret; + goto exit; } - if (status != 0xff) + if (*status != 0xff) break; } - if (status == 0xff) { + if (*status == 0xff) { dev_err(line6->ifcdev, "write failed after %d retries\n", count); - return -EIO; - } else if (status != 0) { + ret = -EIO; + } else if (*status != 0) { dev_err(line6->ifcdev, "write failed (error %d)\n", ret); - return -EIO; + ret = -EIO; } - - return 0; +exit: + kfree(status); + return ret; } EXPORT_SYMBOL_GPL(line6_write_data); @@ -555,6 +544,7 @@ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) /* NOTE: hwdep layer provides atomicity here */ line6->messages.active = 1; + line6->messages.nonblock = file->f_flags & O_NONBLOCK ? 1 : 0; return 0; } @@ -584,6 +574,9 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, while (kfifo_len(&line6->messages.fifo) == 0) { mutex_unlock(&line6->messages.read_lock); + if (line6->messages.nonblock) + return -EAGAIN; + rv = wait_event_interruptible( line6->messages.wait_queue, kfifo_len(&line6->messages.fifo) != 0); @@ -631,11 +624,23 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, return rv; } +static __poll_t +line6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) +{ + struct usb_line6 *line6 = hwdep->private_data; + + poll_wait(file, &line6->messages.wait_queue, wait); + + guard(mutex)(&line6->messages.read_lock); + return kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM; +} + static const struct snd_hwdep_ops hwdep_ops = { .open = line6_hwdep_open, .release = line6_hwdep_release, .read = line6_hwdep_read, .write = line6_hwdep_write, + .poll = line6_hwdep_poll, }; /* Insert into circular buffer */ @@ -668,7 +673,7 @@ static int line6_hwdep_init(struct usb_line6 *line6) err = snd_hwdep_new(line6->card, "config", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "config"); + strscpy(hwdep->name, "config"); hwdep->iface = SNDRV_HWDEP_IFACE_LINE6; hwdep->ops = hwdep_ops; hwdep->private_data = line6; @@ -683,7 +688,7 @@ static int line6_init_cap_control(struct usb_line6 *line6) int ret; /* initialize USB buffers: */ - line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); + line6->buffer_listen = kzalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); if (!line6->buffer_listen) return -ENOMEM; @@ -692,9 +697,13 @@ static int line6_init_cap_control(struct usb_line6 *line6) return -ENOMEM; if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) { - line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL); + line6->buffer_message = kzalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL); if (!line6->buffer_message) return -ENOMEM; + + ret = line6_init_midi(line6); + if (ret < 0) + return ret; } else { ret = line6_hwdep_init(line6); if (ret < 0) @@ -710,6 +719,15 @@ static int line6_init_cap_control(struct usb_line6 *line6) return 0; } +static void line6_startup_work(struct work_struct *work) +{ + struct usb_line6 *line6 = + container_of(work, struct usb_line6, startup_work.work); + + if (line6->startup) + line6->startup(line6); +} + /* Probe USB device. */ @@ -745,10 +763,11 @@ int line6_probe(struct usb_interface *interface, line6->properties = properties; line6->usbdev = usbdev; line6->ifcdev = &interface->dev; + INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work); - strcpy(card->id, properties->id); - strcpy(card->driver, driver_name); - strcpy(card->shortname, properties->name); + strscpy(card->id, properties->id); + strscpy(card->driver, driver_name); + strscpy(card->shortname, properties->name); sprintf(card->longname, "Line 6 %s at USB %s", properties->name, dev_name(line6->ifcdev)); card->private_free = line6_destruct; @@ -815,6 +834,8 @@ void line6_disconnect(struct usb_interface *interface) if (WARN_ON(usbdev != line6->usbdev)) return; + cancel_delayed_work_sync(&line6->startup_work); + if (line6->urb_listen != NULL) line6_stop_listen(line6); @@ -849,10 +870,8 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message) if (line6->properties->capabilities & LINE6_CAP_CONTROL) line6_stop_listen(line6); - if (line6pcm != NULL) { - snd_pcm_suspend_all(line6pcm->pcm); + if (line6pcm != NULL) line6pcm->flags = 0; - } return 0; } |
