162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Line 6 Linux USB driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/usb.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <sound/core.h>
1562306a36Sopenharmony_ci#include <sound/initval.h>
1662306a36Sopenharmony_ci#include <sound/hwdep.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "capture.h"
1962306a36Sopenharmony_ci#include "driver.h"
2062306a36Sopenharmony_ci#include "midi.h"
2162306a36Sopenharmony_ci#include "playback.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define DRIVER_AUTHOR  "Markus Grabner <grabner@icg.tugraz.at>"
2462306a36Sopenharmony_ci#define DRIVER_DESC    "Line 6 USB Driver"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci	This is Line 6's MIDI manufacturer ID.
2862306a36Sopenharmony_ci*/
2962306a36Sopenharmony_ciconst unsigned char line6_midi_id[3] = {
3062306a36Sopenharmony_ci	0x00, 0x01, 0x0c
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_midi_id);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci	Code to request version of POD, Variax interface
3662306a36Sopenharmony_ci	(and maybe other devices).
3762306a36Sopenharmony_ci*/
3862306a36Sopenharmony_cistatic const char line6_request_version[] = {
3962306a36Sopenharmony_ci	0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci	 Class for asynchronous messages.
4462306a36Sopenharmony_ci*/
4562306a36Sopenharmony_cistruct message {
4662306a36Sopenharmony_ci	struct usb_line6 *line6;
4762306a36Sopenharmony_ci	const char *buffer;
4862306a36Sopenharmony_ci	int size;
4962306a36Sopenharmony_ci	int done;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci	Forward declarations.
5462306a36Sopenharmony_ci*/
5562306a36Sopenharmony_cistatic void line6_data_received(struct urb *urb);
5662306a36Sopenharmony_cistatic int line6_send_raw_message_async_part(struct message *msg,
5762306a36Sopenharmony_ci					     struct urb *urb);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci	Start to listen on endpoint.
6162306a36Sopenharmony_ci*/
6262306a36Sopenharmony_cistatic int line6_start_listen(struct usb_line6 *line6)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int err;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
6762306a36Sopenharmony_ci		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
6862306a36Sopenharmony_ci			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
6962306a36Sopenharmony_ci			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
7062306a36Sopenharmony_ci			line6_data_received, line6, line6->interval);
7162306a36Sopenharmony_ci	} else {
7262306a36Sopenharmony_ci		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
7362306a36Sopenharmony_ci			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
7462306a36Sopenharmony_ci			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
7562306a36Sopenharmony_ci			line6_data_received, line6);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* sanity checks of EP before actually submitting */
7962306a36Sopenharmony_ci	if (usb_urb_ep_type_check(line6->urb_listen)) {
8062306a36Sopenharmony_ci		dev_err(line6->ifcdev, "invalid control EP\n");
8162306a36Sopenharmony_ci		return -EINVAL;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	line6->urb_listen->actual_length = 0;
8562306a36Sopenharmony_ci	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
8662306a36Sopenharmony_ci	return err;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci	Stop listening on endpoint.
9162306a36Sopenharmony_ci*/
9262306a36Sopenharmony_cistatic void line6_stop_listen(struct usb_line6 *line6)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	usb_kill_urb(line6->urb_listen);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci	Send raw message in pieces of wMaxPacketSize bytes.
9962306a36Sopenharmony_ci*/
10062306a36Sopenharmony_ciint line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
10162306a36Sopenharmony_ci				  int size)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int i, done = 0;
10462306a36Sopenharmony_ci	const struct line6_properties *properties = line6->properties;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (i = 0; i < size; i += line6->max_packet_size) {
10762306a36Sopenharmony_ci		int partial;
10862306a36Sopenharmony_ci		const char *frag_buf = buffer + i;
10962306a36Sopenharmony_ci		int frag_size = min(line6->max_packet_size, size - i);
11062306a36Sopenharmony_ci		int retval;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
11362306a36Sopenharmony_ci			retval = usb_interrupt_msg(line6->usbdev,
11462306a36Sopenharmony_ci						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
11562306a36Sopenharmony_ci						(char *)frag_buf, frag_size,
11662306a36Sopenharmony_ci						&partial, LINE6_TIMEOUT);
11762306a36Sopenharmony_ci		} else {
11862306a36Sopenharmony_ci			retval = usb_bulk_msg(line6->usbdev,
11962306a36Sopenharmony_ci						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
12062306a36Sopenharmony_ci						(char *)frag_buf, frag_size,
12162306a36Sopenharmony_ci						&partial, LINE6_TIMEOUT);
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (retval) {
12562306a36Sopenharmony_ci			dev_err(line6->ifcdev,
12662306a36Sopenharmony_ci				"usb_bulk_msg failed (%d)\n", retval);
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		done += frag_size;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return done;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_send_raw_message);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci	Notification of completion of asynchronous request transmission.
13962306a36Sopenharmony_ci*/
14062306a36Sopenharmony_cistatic void line6_async_request_sent(struct urb *urb)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct message *msg = (struct message *)urb->context;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (msg->done >= msg->size) {
14562306a36Sopenharmony_ci		usb_free_urb(urb);
14662306a36Sopenharmony_ci		kfree(msg);
14762306a36Sopenharmony_ci	} else
14862306a36Sopenharmony_ci		line6_send_raw_message_async_part(msg, urb);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci	Asynchronously send part of a raw message.
15362306a36Sopenharmony_ci*/
15462306a36Sopenharmony_cistatic int line6_send_raw_message_async_part(struct message *msg,
15562306a36Sopenharmony_ci					     struct urb *urb)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int retval;
15862306a36Sopenharmony_ci	struct usb_line6 *line6 = msg->line6;
15962306a36Sopenharmony_ci	int done = msg->done;
16062306a36Sopenharmony_ci	int bytes = min(msg->size - done, line6->max_packet_size);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
16362306a36Sopenharmony_ci		usb_fill_int_urb(urb, line6->usbdev,
16462306a36Sopenharmony_ci			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
16562306a36Sopenharmony_ci			(char *)msg->buffer + done, bytes,
16662306a36Sopenharmony_ci			line6_async_request_sent, msg, line6->interval);
16762306a36Sopenharmony_ci	} else {
16862306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, line6->usbdev,
16962306a36Sopenharmony_ci			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
17062306a36Sopenharmony_ci			(char *)msg->buffer + done, bytes,
17162306a36Sopenharmony_ci			line6_async_request_sent, msg);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	msg->done += bytes;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* sanity checks of EP before actually submitting */
17762306a36Sopenharmony_ci	retval = usb_urb_ep_type_check(urb);
17862306a36Sopenharmony_ci	if (retval < 0)
17962306a36Sopenharmony_ci		goto error;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
18262306a36Sopenharmony_ci	if (retval < 0)
18362306a36Sopenharmony_ci		goto error;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci error:
18862306a36Sopenharmony_ci	dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n",
18962306a36Sopenharmony_ci		__func__, retval);
19062306a36Sopenharmony_ci	usb_free_urb(urb);
19162306a36Sopenharmony_ci	kfree(msg);
19262306a36Sopenharmony_ci	return retval;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci	Asynchronously send raw message.
19762306a36Sopenharmony_ci*/
19862306a36Sopenharmony_ciint line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer,
19962306a36Sopenharmony_ci				 int size)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct message *msg;
20262306a36Sopenharmony_ci	struct urb *urb;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* create message: */
20562306a36Sopenharmony_ci	msg = kmalloc(sizeof(struct message), GFP_ATOMIC);
20662306a36Sopenharmony_ci	if (msg == NULL)
20762306a36Sopenharmony_ci		return -ENOMEM;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* create URB: */
21062306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (urb == NULL) {
21362306a36Sopenharmony_ci		kfree(msg);
21462306a36Sopenharmony_ci		return -ENOMEM;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* set message data: */
21862306a36Sopenharmony_ci	msg->line6 = line6;
21962306a36Sopenharmony_ci	msg->buffer = buffer;
22062306a36Sopenharmony_ci	msg->size = size;
22162306a36Sopenharmony_ci	msg->done = 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* start sending: */
22462306a36Sopenharmony_ci	return line6_send_raw_message_async_part(msg, urb);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_send_raw_message_async);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*
22962306a36Sopenharmony_ci	Send asynchronous device version request.
23062306a36Sopenharmony_ci*/
23162306a36Sopenharmony_ciint line6_version_request_async(struct usb_line6 *line6)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	char *buffer;
23462306a36Sopenharmony_ci	int retval;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	buffer = kmemdup(line6_request_version,
23762306a36Sopenharmony_ci			sizeof(line6_request_version), GFP_ATOMIC);
23862306a36Sopenharmony_ci	if (buffer == NULL)
23962306a36Sopenharmony_ci		return -ENOMEM;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	retval = line6_send_raw_message_async(line6, buffer,
24262306a36Sopenharmony_ci					      sizeof(line6_request_version));
24362306a36Sopenharmony_ci	kfree(buffer);
24462306a36Sopenharmony_ci	return retval;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_version_request_async);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci	Send sysex message in pieces of wMaxPacketSize bytes.
25062306a36Sopenharmony_ci*/
25162306a36Sopenharmony_ciint line6_send_sysex_message(struct usb_line6 *line6, const char *buffer,
25262306a36Sopenharmony_ci			     int size)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	return line6_send_raw_message(line6, buffer,
25562306a36Sopenharmony_ci				      size + SYSEX_EXTRA_SIZE) -
25662306a36Sopenharmony_ci	    SYSEX_EXTRA_SIZE;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_send_sysex_message);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci	Allocate buffer for sysex message and prepare header.
26262306a36Sopenharmony_ci	@param code sysex message code
26362306a36Sopenharmony_ci	@param size number of bytes between code and sysex end
26462306a36Sopenharmony_ci*/
26562306a36Sopenharmony_cichar *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2,
26662306a36Sopenharmony_ci			       int size)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!buffer)
27162306a36Sopenharmony_ci		return NULL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	buffer[0] = LINE6_SYSEX_BEGIN;
27462306a36Sopenharmony_ci	memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id));
27562306a36Sopenharmony_ci	buffer[sizeof(line6_midi_id) + 1] = code1;
27662306a36Sopenharmony_ci	buffer[sizeof(line6_midi_id) + 2] = code2;
27762306a36Sopenharmony_ci	buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END;
27862306a36Sopenharmony_ci	return buffer;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_alloc_sysex_buffer);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci	Notification of data received from the Line 6 device.
28462306a36Sopenharmony_ci*/
28562306a36Sopenharmony_cistatic void line6_data_received(struct urb *urb)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
28862306a36Sopenharmony_ci	struct midi_buffer *mb = &line6->line6midi->midibuf_in;
28962306a36Sopenharmony_ci	int done;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (urb->status == -ESHUTDOWN)
29262306a36Sopenharmony_ci		return;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
29562306a36Sopenharmony_ci		done =
29662306a36Sopenharmony_ci			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		if (done < urb->actual_length) {
29962306a36Sopenharmony_ci			line6_midibuf_ignore(mb, done);
30062306a36Sopenharmony_ci			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
30162306a36Sopenharmony_ci				done, urb->actual_length);
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		for (;;) {
30562306a36Sopenharmony_ci			done =
30662306a36Sopenharmony_ci				line6_midibuf_read(mb, line6->buffer_message,
30762306a36Sopenharmony_ci						   LINE6_MIDI_MESSAGE_MAXLEN,
30862306a36Sopenharmony_ci						   LINE6_MIDIBUF_READ_RX);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci			if (done <= 0)
31162306a36Sopenharmony_ci				break;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci			line6->message_length = done;
31462306a36Sopenharmony_ci			line6_midi_receive(line6, line6->buffer_message, done);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci			if (line6->process_message)
31762306a36Sopenharmony_ci				line6->process_message(line6);
31862306a36Sopenharmony_ci		}
31962306a36Sopenharmony_ci	} else {
32062306a36Sopenharmony_ci		line6->buffer_message = urb->transfer_buffer;
32162306a36Sopenharmony_ci		line6->message_length = urb->actual_length;
32262306a36Sopenharmony_ci		if (line6->process_message)
32362306a36Sopenharmony_ci			line6->process_message(line6);
32462306a36Sopenharmony_ci		line6->buffer_message = NULL;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	line6_start_listen(line6);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci#define LINE6_READ_WRITE_STATUS_DELAY 2  /* milliseconds */
33162306a36Sopenharmony_ci#define LINE6_READ_WRITE_MAX_RETRIES 50
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/*
33462306a36Sopenharmony_ci	Read data from device.
33562306a36Sopenharmony_ci*/
33662306a36Sopenharmony_ciint line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
33762306a36Sopenharmony_ci		    unsigned datalen)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct usb_device *usbdev = line6->usbdev;
34062306a36Sopenharmony_ci	int ret;
34162306a36Sopenharmony_ci	u8 len;
34262306a36Sopenharmony_ci	unsigned count;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (address > 0xffff || datalen > 0xff)
34562306a36Sopenharmony_ci		return -EINVAL;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* query the serial number: */
34862306a36Sopenharmony_ci	ret = usb_control_msg_send(usbdev, 0, 0x67,
34962306a36Sopenharmony_ci				   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
35062306a36Sopenharmony_ci				   (datalen << 8) | 0x21, address, NULL, 0,
35162306a36Sopenharmony_ci				   LINE6_TIMEOUT, GFP_KERNEL);
35262306a36Sopenharmony_ci	if (ret) {
35362306a36Sopenharmony_ci		dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
35462306a36Sopenharmony_ci		goto exit;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* Wait for data length. We'll get 0xff until length arrives. */
35862306a36Sopenharmony_ci	for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) {
35962306a36Sopenharmony_ci		mdelay(LINE6_READ_WRITE_STATUS_DELAY);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		ret = usb_control_msg_recv(usbdev, 0, 0x67,
36262306a36Sopenharmony_ci					   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
36362306a36Sopenharmony_ci					   0x0012, 0x0000, &len, 1,
36462306a36Sopenharmony_ci					   LINE6_TIMEOUT, GFP_KERNEL);
36562306a36Sopenharmony_ci		if (ret) {
36662306a36Sopenharmony_ci			dev_err(line6->ifcdev,
36762306a36Sopenharmony_ci				"receive length failed (error %d)\n", ret);
36862306a36Sopenharmony_ci			goto exit;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		if (len != 0xff)
37262306a36Sopenharmony_ci			break;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = -EIO;
37662306a36Sopenharmony_ci	if (len == 0xff) {
37762306a36Sopenharmony_ci		dev_err(line6->ifcdev, "read failed after %d retries\n",
37862306a36Sopenharmony_ci			count);
37962306a36Sopenharmony_ci		goto exit;
38062306a36Sopenharmony_ci	} else if (len != datalen) {
38162306a36Sopenharmony_ci		/* should be equal or something went wrong */
38262306a36Sopenharmony_ci		dev_err(line6->ifcdev,
38362306a36Sopenharmony_ci			"length mismatch (expected %d, got %d)\n",
38462306a36Sopenharmony_ci			(int)datalen, len);
38562306a36Sopenharmony_ci		goto exit;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* receive the result: */
38962306a36Sopenharmony_ci	ret = usb_control_msg_recv(usbdev, 0, 0x67,
39062306a36Sopenharmony_ci				   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
39162306a36Sopenharmony_ci				   0x0013, 0x0000, data, datalen, LINE6_TIMEOUT,
39262306a36Sopenharmony_ci				   GFP_KERNEL);
39362306a36Sopenharmony_ci	if (ret)
39462306a36Sopenharmony_ci		dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ciexit:
39762306a36Sopenharmony_ci	return ret;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_read_data);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/*
40262306a36Sopenharmony_ci	Write data to device.
40362306a36Sopenharmony_ci*/
40462306a36Sopenharmony_ciint line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
40562306a36Sopenharmony_ci		     unsigned datalen)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct usb_device *usbdev = line6->usbdev;
40862306a36Sopenharmony_ci	int ret;
40962306a36Sopenharmony_ci	unsigned char *status;
41062306a36Sopenharmony_ci	int count;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (address > 0xffff || datalen > 0xffff)
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	status = kmalloc(1, GFP_KERNEL);
41662306a36Sopenharmony_ci	if (!status)
41762306a36Sopenharmony_ci		return -ENOMEM;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret = usb_control_msg_send(usbdev, 0, 0x67,
42062306a36Sopenharmony_ci				   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
42162306a36Sopenharmony_ci				   0x0022, address, data, datalen, LINE6_TIMEOUT,
42262306a36Sopenharmony_ci				   GFP_KERNEL);
42362306a36Sopenharmony_ci	if (ret) {
42462306a36Sopenharmony_ci		dev_err(line6->ifcdev,
42562306a36Sopenharmony_ci			"write request failed (error %d)\n", ret);
42662306a36Sopenharmony_ci		goto exit;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) {
43062306a36Sopenharmony_ci		mdelay(LINE6_READ_WRITE_STATUS_DELAY);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		ret = usb_control_msg_recv(usbdev, 0, 0x67,
43362306a36Sopenharmony_ci					   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
43462306a36Sopenharmony_ci					   0x0012, 0x0000, status, 1, LINE6_TIMEOUT,
43562306a36Sopenharmony_ci					   GFP_KERNEL);
43662306a36Sopenharmony_ci		if (ret) {
43762306a36Sopenharmony_ci			dev_err(line6->ifcdev,
43862306a36Sopenharmony_ci				"receiving status failed (error %d)\n", ret);
43962306a36Sopenharmony_ci			goto exit;
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		if (*status != 0xff)
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (*status == 0xff) {
44762306a36Sopenharmony_ci		dev_err(line6->ifcdev, "write failed after %d retries\n",
44862306a36Sopenharmony_ci			count);
44962306a36Sopenharmony_ci		ret = -EIO;
45062306a36Sopenharmony_ci	} else if (*status != 0) {
45162306a36Sopenharmony_ci		dev_err(line6->ifcdev, "write failed (error %d)\n", ret);
45262306a36Sopenharmony_ci		ret = -EIO;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ciexit:
45562306a36Sopenharmony_ci	kfree(status);
45662306a36Sopenharmony_ci	return ret;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_write_data);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci	Read Line 6 device serial number.
46262306a36Sopenharmony_ci	(POD, TonePort, GuitarPort)
46362306a36Sopenharmony_ci*/
46462306a36Sopenharmony_ciint line6_read_serial_number(struct usb_line6 *line6, u32 *serial_number)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	return line6_read_data(line6, 0x80d0, serial_number,
46762306a36Sopenharmony_ci			       sizeof(*serial_number));
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_read_serial_number);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci	Card destructor.
47362306a36Sopenharmony_ci*/
47462306a36Sopenharmony_cistatic void line6_destruct(struct snd_card *card)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct usb_line6 *line6 = card->private_data;
47762306a36Sopenharmony_ci	struct usb_device *usbdev = line6->usbdev;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Free buffer memory first. We cannot depend on the existence of private
48062306a36Sopenharmony_ci	 * data from the (podhd) module, it may be gone already during this call
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	kfree(line6->buffer_message);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	kfree(line6->buffer_listen);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* then free URBs: */
48762306a36Sopenharmony_ci	usb_free_urb(line6->urb_listen);
48862306a36Sopenharmony_ci	line6->urb_listen = NULL;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* decrement reference counters: */
49162306a36Sopenharmony_ci	usb_put_dev(usbdev);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void line6_get_usb_properties(struct usb_line6 *line6)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct usb_device *usbdev = line6->usbdev;
49762306a36Sopenharmony_ci	const struct line6_properties *properties = line6->properties;
49862306a36Sopenharmony_ci	int pipe;
49962306a36Sopenharmony_ci	struct usb_host_endpoint *ep = NULL;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (properties->capabilities & LINE6_CAP_CONTROL) {
50262306a36Sopenharmony_ci		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
50362306a36Sopenharmony_ci			pipe = usb_rcvintpipe(line6->usbdev,
50462306a36Sopenharmony_ci				line6->properties->ep_ctrl_r);
50562306a36Sopenharmony_ci		} else {
50662306a36Sopenharmony_ci			pipe = usb_rcvbulkpipe(line6->usbdev,
50762306a36Sopenharmony_ci				line6->properties->ep_ctrl_r);
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci		ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Control data transfer properties */
51362306a36Sopenharmony_ci	if (ep) {
51462306a36Sopenharmony_ci		line6->interval = ep->desc.bInterval;
51562306a36Sopenharmony_ci		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
51662306a36Sopenharmony_ci	} else {
51762306a36Sopenharmony_ci		if (properties->capabilities & LINE6_CAP_CONTROL) {
51862306a36Sopenharmony_ci			dev_err(line6->ifcdev,
51962306a36Sopenharmony_ci				"endpoint not available, using fallback values");
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci		line6->interval = LINE6_FALLBACK_INTERVAL;
52262306a36Sopenharmony_ci		line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Isochronous transfer properties */
52662306a36Sopenharmony_ci	if (usbdev->speed == USB_SPEED_LOW) {
52762306a36Sopenharmony_ci		line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
52862306a36Sopenharmony_ci		line6->iso_buffers = USB_LOW_ISO_BUFFERS;
52962306a36Sopenharmony_ci	} else {
53062306a36Sopenharmony_ci		line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
53162306a36Sopenharmony_ci		line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/* Enable buffering of incoming messages, flush the buffer */
53662306a36Sopenharmony_cistatic int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct usb_line6 *line6 = hw->private_data;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* NOTE: hwdep layer provides atomicity here */
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	line6->messages.active = 1;
54362306a36Sopenharmony_ci	line6->messages.nonblock = file->f_flags & O_NONBLOCK ? 1 : 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci/* Stop buffering */
54962306a36Sopenharmony_cistatic int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct usb_line6 *line6 = hw->private_data;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	line6->messages.active = 0;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/* Read from circular buffer, return to user */
55962306a36Sopenharmony_cistatic long
56062306a36Sopenharmony_ciline6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
56162306a36Sopenharmony_ci					loff_t *offset)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct usb_line6 *line6 = hwdep->private_data;
56462306a36Sopenharmony_ci	long rv = 0;
56562306a36Sopenharmony_ci	unsigned int out_count;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (mutex_lock_interruptible(&line6->messages.read_lock))
56862306a36Sopenharmony_ci		return -ERESTARTSYS;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	while (kfifo_len(&line6->messages.fifo) == 0) {
57162306a36Sopenharmony_ci		mutex_unlock(&line6->messages.read_lock);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (line6->messages.nonblock)
57462306a36Sopenharmony_ci			return -EAGAIN;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		rv = wait_event_interruptible(
57762306a36Sopenharmony_ci			line6->messages.wait_queue,
57862306a36Sopenharmony_ci			kfifo_len(&line6->messages.fifo) != 0);
57962306a36Sopenharmony_ci		if (rv < 0)
58062306a36Sopenharmony_ci			return rv;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		if (mutex_lock_interruptible(&line6->messages.read_lock))
58362306a36Sopenharmony_ci			return -ERESTARTSYS;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (kfifo_peek_len(&line6->messages.fifo) > count) {
58762306a36Sopenharmony_ci		/* Buffer too small; allow re-read of the current item... */
58862306a36Sopenharmony_ci		rv = -EINVAL;
58962306a36Sopenharmony_ci	} else {
59062306a36Sopenharmony_ci		rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count);
59162306a36Sopenharmony_ci		if (rv == 0)
59262306a36Sopenharmony_ci			rv = out_count;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	mutex_unlock(&line6->messages.read_lock);
59662306a36Sopenharmony_ci	return rv;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/* Write directly (no buffering) to device by user*/
60062306a36Sopenharmony_cistatic long
60162306a36Sopenharmony_ciline6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
60262306a36Sopenharmony_ci					loff_t *offset)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct usb_line6 *line6 = hwdep->private_data;
60562306a36Sopenharmony_ci	int rv;
60662306a36Sopenharmony_ci	char *data_copy;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) {
60962306a36Sopenharmony_ci		/* This is an arbitrary limit - still better than nothing... */
61062306a36Sopenharmony_ci		return -EINVAL;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	data_copy = memdup_user(data, count);
61462306a36Sopenharmony_ci	if (IS_ERR(data_copy))
61562306a36Sopenharmony_ci		return PTR_ERR(data_copy);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	rv = line6_send_raw_message(line6, data_copy, count);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	kfree(data_copy);
62062306a36Sopenharmony_ci	return rv;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic __poll_t
62462306a36Sopenharmony_ciline6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	__poll_t rv;
62762306a36Sopenharmony_ci	struct usb_line6 *line6 = hwdep->private_data;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	poll_wait(file, &line6->messages.wait_queue, wait);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	mutex_lock(&line6->messages.read_lock);
63262306a36Sopenharmony_ci	rv = kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM;
63362306a36Sopenharmony_ci	mutex_unlock(&line6->messages.read_lock);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return rv;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic const struct snd_hwdep_ops hwdep_ops = {
63962306a36Sopenharmony_ci	.open    = line6_hwdep_open,
64062306a36Sopenharmony_ci	.release = line6_hwdep_release,
64162306a36Sopenharmony_ci	.read    = line6_hwdep_read,
64262306a36Sopenharmony_ci	.write   = line6_hwdep_write,
64362306a36Sopenharmony_ci	.poll    = line6_hwdep_poll,
64462306a36Sopenharmony_ci};
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci/* Insert into circular buffer */
64762306a36Sopenharmony_cistatic void line6_hwdep_push_message(struct usb_line6 *line6)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	if (!line6->messages.active)
65062306a36Sopenharmony_ci		return;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) {
65362306a36Sopenharmony_ci		/* No race condition here, there's only one writer */
65462306a36Sopenharmony_ci		kfifo_in(&line6->messages.fifo,
65562306a36Sopenharmony_ci			line6->buffer_message, line6->message_length);
65662306a36Sopenharmony_ci	} /* else TODO: signal overflow */
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	wake_up_interruptible(&line6->messages.wait_queue);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int line6_hwdep_init(struct usb_line6 *line6)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	int err;
66462306a36Sopenharmony_ci	struct snd_hwdep *hwdep;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* TODO: usb_driver_claim_interface(); */
66762306a36Sopenharmony_ci	line6->process_message = line6_hwdep_push_message;
66862306a36Sopenharmony_ci	line6->messages.active = 0;
66962306a36Sopenharmony_ci	init_waitqueue_head(&line6->messages.wait_queue);
67062306a36Sopenharmony_ci	mutex_init(&line6->messages.read_lock);
67162306a36Sopenharmony_ci	INIT_KFIFO(line6->messages.fifo);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
67462306a36Sopenharmony_ci	if (err < 0)
67562306a36Sopenharmony_ci		goto end;
67662306a36Sopenharmony_ci	strcpy(hwdep->name, "config");
67762306a36Sopenharmony_ci	hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
67862306a36Sopenharmony_ci	hwdep->ops = hwdep_ops;
67962306a36Sopenharmony_ci	hwdep->private_data = line6;
68062306a36Sopenharmony_ci	hwdep->exclusive = true;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ciend:
68362306a36Sopenharmony_ci	return err;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int line6_init_cap_control(struct usb_line6 *line6)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	int ret;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* initialize USB buffers: */
69162306a36Sopenharmony_ci	line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL);
69262306a36Sopenharmony_ci	if (!line6->buffer_listen)
69362306a36Sopenharmony_ci		return -ENOMEM;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
69662306a36Sopenharmony_ci	if (!line6->urb_listen)
69762306a36Sopenharmony_ci		return -ENOMEM;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
70062306a36Sopenharmony_ci		line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL);
70162306a36Sopenharmony_ci		if (!line6->buffer_message)
70262306a36Sopenharmony_ci			return -ENOMEM;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		ret = line6_init_midi(line6);
70562306a36Sopenharmony_ci		if (ret < 0)
70662306a36Sopenharmony_ci			return ret;
70762306a36Sopenharmony_ci	} else {
70862306a36Sopenharmony_ci		ret = line6_hwdep_init(line6);
70962306a36Sopenharmony_ci		if (ret < 0)
71062306a36Sopenharmony_ci			return ret;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	ret = line6_start_listen(line6);
71462306a36Sopenharmony_ci	if (ret < 0) {
71562306a36Sopenharmony_ci		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
71662306a36Sopenharmony_ci		return ret;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	return 0;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic void line6_startup_work(struct work_struct *work)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct usb_line6 *line6 =
72562306a36Sopenharmony_ci		container_of(work, struct usb_line6, startup_work.work);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (line6->startup)
72862306a36Sopenharmony_ci		line6->startup(line6);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/*
73262306a36Sopenharmony_ci	Probe USB device.
73362306a36Sopenharmony_ci*/
73462306a36Sopenharmony_ciint line6_probe(struct usb_interface *interface,
73562306a36Sopenharmony_ci		const struct usb_device_id *id,
73662306a36Sopenharmony_ci		const char *driver_name,
73762306a36Sopenharmony_ci		const struct line6_properties *properties,
73862306a36Sopenharmony_ci		int (*private_init)(struct usb_line6 *, const struct usb_device_id *id),
73962306a36Sopenharmony_ci		size_t data_size)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(interface);
74262306a36Sopenharmony_ci	struct snd_card *card;
74362306a36Sopenharmony_ci	struct usb_line6 *line6;
74462306a36Sopenharmony_ci	int interface_number;
74562306a36Sopenharmony_ci	int ret;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (WARN_ON(data_size < sizeof(*line6)))
74862306a36Sopenharmony_ci		return -EINVAL;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* we don't handle multiple configurations */
75162306a36Sopenharmony_ci	if (usbdev->descriptor.bNumConfigurations != 1)
75262306a36Sopenharmony_ci		return -ENODEV;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ret = snd_card_new(&interface->dev,
75562306a36Sopenharmony_ci			   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
75662306a36Sopenharmony_ci			   THIS_MODULE, data_size, &card);
75762306a36Sopenharmony_ci	if (ret < 0)
75862306a36Sopenharmony_ci		return ret;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/* store basic data: */
76162306a36Sopenharmony_ci	line6 = card->private_data;
76262306a36Sopenharmony_ci	line6->card = card;
76362306a36Sopenharmony_ci	line6->properties = properties;
76462306a36Sopenharmony_ci	line6->usbdev = usbdev;
76562306a36Sopenharmony_ci	line6->ifcdev = &interface->dev;
76662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	strcpy(card->id, properties->id);
76962306a36Sopenharmony_ci	strcpy(card->driver, driver_name);
77062306a36Sopenharmony_ci	strcpy(card->shortname, properties->name);
77162306a36Sopenharmony_ci	sprintf(card->longname, "Line 6 %s at USB %s", properties->name,
77262306a36Sopenharmony_ci		dev_name(line6->ifcdev));
77362306a36Sopenharmony_ci	card->private_free = line6_destruct;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	usb_set_intfdata(interface, line6);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* increment reference counters: */
77862306a36Sopenharmony_ci	usb_get_dev(usbdev);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* initialize device info: */
78162306a36Sopenharmony_ci	dev_info(&interface->dev, "Line 6 %s found\n", properties->name);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* query interface number */
78462306a36Sopenharmony_ci	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* TODO reserves the bus bandwidth even without actual transfer */
78762306a36Sopenharmony_ci	ret = usb_set_interface(usbdev, interface_number,
78862306a36Sopenharmony_ci				properties->altsetting);
78962306a36Sopenharmony_ci	if (ret < 0) {
79062306a36Sopenharmony_ci		dev_err(&interface->dev, "set_interface failed\n");
79162306a36Sopenharmony_ci		goto error;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	line6_get_usb_properties(line6);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (properties->capabilities & LINE6_CAP_CONTROL) {
79762306a36Sopenharmony_ci		ret = line6_init_cap_control(line6);
79862306a36Sopenharmony_ci		if (ret < 0)
79962306a36Sopenharmony_ci			goto error;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* initialize device data based on device: */
80362306a36Sopenharmony_ci	ret = private_init(line6, id);
80462306a36Sopenharmony_ci	if (ret < 0)
80562306a36Sopenharmony_ci		goto error;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* creation of additional special files should go here */
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	dev_info(&interface->dev, "Line 6 %s now attached\n",
81062306a36Sopenharmony_ci		 properties->name);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci error:
81562306a36Sopenharmony_ci	/* we can call disconnect callback here because no close-sync is
81662306a36Sopenharmony_ci	 * needed yet at this point
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	line6_disconnect(interface);
81962306a36Sopenharmony_ci	return ret;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_probe);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/*
82462306a36Sopenharmony_ci	Line 6 device disconnected.
82562306a36Sopenharmony_ci*/
82662306a36Sopenharmony_civoid line6_disconnect(struct usb_interface *interface)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct usb_line6 *line6 = usb_get_intfdata(interface);
82962306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(interface);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (!line6)
83262306a36Sopenharmony_ci		return;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (WARN_ON(usbdev != line6->usbdev))
83562306a36Sopenharmony_ci		return;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	cancel_delayed_work_sync(&line6->startup_work);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (line6->urb_listen != NULL)
84062306a36Sopenharmony_ci		line6_stop_listen(line6);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	snd_card_disconnect(line6->card);
84362306a36Sopenharmony_ci	if (line6->line6pcm)
84462306a36Sopenharmony_ci		line6_pcm_disconnect(line6->line6pcm);
84562306a36Sopenharmony_ci	if (line6->disconnect)
84662306a36Sopenharmony_ci		line6->disconnect(line6);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	dev_info(&interface->dev, "Line 6 %s now disconnected\n",
84962306a36Sopenharmony_ci		 line6->properties->name);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* make sure the device isn't destructed twice: */
85262306a36Sopenharmony_ci	usb_set_intfdata(interface, NULL);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	snd_card_free_when_closed(line6->card);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_disconnect);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci#ifdef CONFIG_PM
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci/*
86162306a36Sopenharmony_ci	Suspend Line 6 device.
86262306a36Sopenharmony_ci*/
86362306a36Sopenharmony_ciint line6_suspend(struct usb_interface *interface, pm_message_t message)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct usb_line6 *line6 = usb_get_intfdata(interface);
86662306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = line6->line6pcm;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
87162306a36Sopenharmony_ci		line6_stop_listen(line6);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (line6pcm != NULL)
87462306a36Sopenharmony_ci		line6pcm->flags = 0;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_suspend);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci/*
88162306a36Sopenharmony_ci	Resume Line 6 device.
88262306a36Sopenharmony_ci*/
88362306a36Sopenharmony_ciint line6_resume(struct usb_interface *interface)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct usb_line6 *line6 = usb_get_intfdata(interface);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
88862306a36Sopenharmony_ci		line6_start_listen(line6);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
89162306a36Sopenharmony_ci	return 0;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_resume);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci#endif /* CONFIG_PM */
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
89862306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
89962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
90062306a36Sopenharmony_ci
901