Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e0ddd07 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/line6' into for-next

parents 1001fb81 247d95ee
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ config SND_USB_PODHD
config SND_USB_TONEPORT
	tristate "TonePort GX, UX1 and UX2 USB support"
	select SND_USB_LINE6
	select NEW_LEDS
	select LEDS_CLASS
	help
	  This is a driver for TonePort GX, UX1 and UX2 devices.

+39 −184
Original line number Diff line number Diff line
@@ -20,26 +20,24 @@

/*
	Find a free URB and submit it.
	must be called in line6pcm->in.lock context
*/
static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
{
	int index;
	unsigned long flags;
	int i, urb_size;
	int ret;
	struct urb *urb_in;

	spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
	index =
	    find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS);
	    find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);

	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
		spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
		return -EINVAL;
	}

	urb_in = line6pcm->urb_audio_in[index];
	urb_in = line6pcm->in.urbs[index];
	urb_size = 0;

	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
@@ -51,7 +49,7 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
	}

	urb_in->transfer_buffer =
	    line6pcm->buffer_in +
	    line6pcm->in.buffer +
	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
	urb_in->transfer_buffer_length = urb_size;
	urb_in->context = line6pcm;
@@ -59,81 +57,29 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
	ret = usb_submit_urb(urb_in, GFP_ATOMIC);

	if (ret == 0)
		set_bit(index, &line6pcm->active_urb_in);
		set_bit(index, &line6pcm->in.active_urbs);
	else
		dev_err(line6pcm->line6->ifcdev,
			"URB in #%d submission failed (%d)\n", index, ret);

	spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
	return 0;
}

/*
	Submit all currently available capture URBs.
	must be called in line6pcm->in.lock context
*/
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
{
	int ret, i;
	int ret = 0, i;

	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
		ret = submit_audio_in_urb(line6pcm);
		if (ret < 0)
			return ret;
	}

	return 0;
}

/*
	Unlink all currently active capture URBs.
*/
void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
	unsigned int i;

	for (i = LINE6_ISO_BUFFERS; i--;) {
		if (test_bit(i, &line6pcm->active_urb_in)) {
			if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) {
				struct urb *u = line6pcm->urb_audio_in[i];

				usb_unlink_urb(u);
			}
		}
	}
}

/*
	Wait until unlinking of all currently active capture URBs has been
	finished.
*/
void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
	int timeout = HZ;
	unsigned int i;
	int alive;

	do {
		alive = 0;
		for (i = LINE6_ISO_BUFFERS; i--;) {
			if (test_bit(i, &line6pcm->active_urb_in))
				alive++;
		}
		if (!alive)
			break;
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule_timeout(1);
	} while (--timeout > 0);
	if (alive)
		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
	}

/*
	Unlink all currently active capture URBs, and wait for finishing.
*/
void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm)
{
	line6_unlink_audio_in_urbs(line6pcm);
	line6_wait_clear_audio_in_urbs(line6pcm);
	return ret;
}

/*
@@ -150,18 +96,18 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
	if (runtime == NULL)
		return;

	if (line6pcm->pos_in_done + frames > runtime->buffer_size) {
	if (line6pcm->in.pos_done + frames > runtime->buffer_size) {
		/*
		   The transferred area goes over buffer boundary,
		   copy two separate chunks.
		 */
		int len;

		len = runtime->buffer_size - line6pcm->pos_in_done;
		len = runtime->buffer_size - line6pcm->in.pos_done;

		if (len > 0) {
			memcpy(runtime->dma_area +
			       line6pcm->pos_in_done * bytes_per_frame, fbuf,
			       line6pcm->in.pos_done * bytes_per_frame, fbuf,
			       len * bytes_per_frame);
			memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
			       (frames - len) * bytes_per_frame);
@@ -173,12 +119,12 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
	} else {
		/* copy single chunk */
		memcpy(runtime->dma_area +
		       line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize);
		       line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize);
	}

	line6pcm->pos_in_done += frames;
	if (line6pcm->pos_in_done >= runtime->buffer_size)
		line6pcm->pos_in_done -= runtime->buffer_size;
	line6pcm->in.pos_done += frames;
	if (line6pcm->in.pos_done >= runtime->buffer_size)
		line6pcm->in.pos_done -= runtime->buffer_size;
}

void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
@@ -186,19 +132,15 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
	struct snd_pcm_substream *substream =
	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);

	line6pcm->bytes_in += length;
	if (line6pcm->bytes_in >= line6pcm->period_in) {
		line6pcm->bytes_in %= line6pcm->period_in;
	line6pcm->in.bytes += length;
	if (line6pcm->in.bytes >= line6pcm->in.period) {
		line6pcm->in.bytes %= line6pcm->in.period;
		spin_unlock(&line6pcm->in.lock);
		snd_pcm_period_elapsed(substream);
		spin_lock(&line6pcm->in.lock);
	}
}

void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm)
{
	kfree(line6pcm->buffer_in);
	line6pcm->buffer_in = NULL;
}

/*
 * Callback for completed capture URB.
 */
@@ -209,14 +151,14 @@ static void audio_in_callback(struct urb *urb)

	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;

	line6pcm->last_frame_in = urb->start_frame;
	line6pcm->in.last_frame = urb->start_frame;

	/* find index of URB */
	for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
		if (urb == line6pcm->urb_audio_in[index])
		if (urb == line6pcm->in.urbs[index])
			break;

	spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
	spin_lock_irqsave(&line6pcm->in.lock, flags);

	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
		char *fbuf;
@@ -243,27 +185,26 @@ static void audio_in_callback(struct urb *urb)
		line6pcm->prev_fbuf = fbuf;
		line6pcm->prev_fsize = fsize;

		if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
			if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
				     &line6pcm->flags) && (fsize > 0))
		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
		    fsize > 0)
			line6_capture_copy(line6pcm, fbuf, fsize);
	}

	clear_bit(index, &line6pcm->active_urb_in);
	clear_bit(index, &line6pcm->in.active_urbs);

	if (test_and_clear_bit(index, &line6pcm->unlink_urb_in))
	if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs))
		shutdown = 1;

	spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);

	if (!shutdown) {
		submit_audio_in_urb(line6pcm);

		if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
			if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
				     &line6pcm->flags))
		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
			line6_capture_check_period(line6pcm, length);
	}

	spin_unlock_irqrestore(&line6pcm->in.lock, flags);
}

/* open capture callback */
@@ -290,102 +231,16 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream)
	return 0;
}

/* hw_params capture callback */
static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
				       struct snd_pcm_hw_params *hw_params)
{
	int ret;
	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);

	/* -- Florian Demski [FD] */
	/* don't ask me why, but this fixes the bug on my machine */
	if (line6pcm == NULL) {
		if (substream->pcm == NULL)
			return -ENOMEM;
		if (substream->pcm->private_data == NULL)
			return -ENOMEM;
		substream->private_data = substream->pcm->private_data;
		line6pcm = snd_pcm_substream_chip(substream);
	}
	/* -- [FD] end */

	ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);

	if (ret < 0)
		return ret;

	ret = snd_pcm_lib_malloc_pages(substream,
				       params_buffer_bytes(hw_params));
	if (ret < 0) {
		line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
		return ret;
	}

	line6pcm->period_in = params_period_bytes(hw_params);
	return 0;
}

/* hw_free capture callback */
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
{
	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);

	line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
	return snd_pcm_lib_free_pages(substream);
}

/* trigger callback */
int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
{
	int err;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
		err = line6_pcm_acquire(line6pcm,
					LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);

		if (err < 0)
			return err;

		break;

	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		err = line6_pcm_release(line6pcm,
					LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);

		if (err < 0)
			return err;

		break;

	default:
		return -EINVAL;
	}

	return 0;
}

/* capture pointer callback */
static snd_pcm_uframes_t
snd_line6_capture_pointer(struct snd_pcm_substream *substream)
{
	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);

	return line6pcm->pos_in_done;
}

/* capture operators */
struct snd_pcm_ops snd_line6_capture_ops = {
	.open = snd_line6_capture_open,
	.close = snd_line6_capture_close,
	.ioctl = snd_pcm_lib_ioctl,
	.hw_params = snd_line6_capture_hw_params,
	.hw_free = snd_line6_capture_hw_free,
	.hw_params = snd_line6_hw_params,
	.hw_free = snd_line6_hw_free,
	.prepare = snd_line6_prepare,
	.trigger = snd_line6_trigger,
	.pointer = snd_line6_capture_pointer,
	.pointer = snd_line6_pointer,
};

int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
@@ -398,7 +253,7 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
		struct urb *urb;

		/* URB for audio in: */
		urb = line6pcm->urb_audio_in[i] =
		urb = line6pcm->in.urbs[i] =
		    usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);

		if (urb == NULL)
+0 −6
Original line number Diff line number Diff line
@@ -24,12 +24,6 @@ extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
				       int length);
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm);
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm
						  *line6pcm);
extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);

#endif
+92 −110
Original line number Diff line number Diff line
@@ -412,27 +412,13 @@ int line6_read_serial_number(struct usb_line6 *line6, int *serial_number)
}
EXPORT_SYMBOL_GPL(line6_read_serial_number);

/*
	No operation (i.e., unsupported).
*/
ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr,
		       char *buf)
{
	return 0;
}
EXPORT_SYMBOL_GPL(line6_nop_read);

/*
	Card destructor.
*/
static void line6_destruct(struct snd_card *card)
{
	struct usb_line6 *line6 = card->private_data;
	struct usb_device *usbdev;

	if (!line6)
		return;
	usbdev = line6->usbdev;
	struct usb_device *usbdev = line6->usbdev;

	/* free buffer memory first: */
	kfree(line6->buffer_message);
@@ -441,82 +427,96 @@ static void line6_destruct(struct snd_card *card)
	/* then free URBs: */
	usb_free_urb(line6->urb_listen);

	/* free interface data: */
	kfree(line6);

	/* decrement reference counters: */
	usb_put_dev(usbdev);
}

/* get data from endpoint descriptor (see usb_maxpacket): */
static void line6_get_interval(struct usb_line6 *line6)
{
	struct usb_device *usbdev = line6->usbdev;
	struct usb_host_endpoint *ep;
	unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
	unsigned epnum = usb_pipeendpoint(pipe);

	ep = usbdev->ep_in[epnum];
	if (ep) {
		line6->interval = ep->desc.bInterval;
		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
	} else {
		dev_err(line6->ifcdev,
			"endpoint not available, using fallback values");
		line6->interval = LINE6_FALLBACK_INTERVAL;
		line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE;
	}
}

static int line6_init_cap_control(struct usb_line6 *line6)
{
	int ret;

	/* initialize USB buffers: */
	line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL);
	if (!line6->buffer_listen)
		return -ENOMEM;

	line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
	if (!line6->buffer_message)
		return -ENOMEM;

	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
	if (!line6->urb_listen)
		return -ENOMEM;

	ret = line6_start_listen(line6);
	if (ret < 0) {
		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
		return ret;
	}

	return 0;
}

/*
	Probe USB device.
*/
int line6_probe(struct usb_interface *interface,
		struct usb_line6 *line6,
		const struct usb_device_id *id,
		const struct line6_properties *properties,
		int (*private_init)(struct usb_interface *, struct usb_line6 *))
		int (*private_init)(struct usb_line6 *, const struct usb_device_id *id),
		size_t data_size)
{
	struct usb_device *usbdev = interface_to_usbdev(interface);
	struct snd_card *card;
	struct usb_line6 *line6;
	int interface_number;
	int ret;

	/* we don't handle multiple configurations */
	if (usbdev->descriptor.bNumConfigurations != 1) {
		ret = -ENODEV;
		goto err_put;
	}

	/* initialize device info: */
	dev_info(&interface->dev, "Line 6 %s found\n", properties->name);
	if (WARN_ON(data_size < sizeof(*line6)))
		return -EINVAL;

	/* query interface number */
	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
	/* we don't handle multiple configurations */
	if (usbdev->descriptor.bNumConfigurations != 1)
		return -ENODEV;

	ret = usb_set_interface(usbdev, interface_number,
			properties->altsetting);
	if (ret < 0) {
		dev_err(&interface->dev, "set_interface failed\n");
		goto err_put;
	}
	ret = snd_card_new(&interface->dev,
			   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			   THIS_MODULE, data_size, &card);
	if (ret < 0)
		return ret;

	/* store basic data: */
	line6 = card->private_data;
	line6->card = card;
	line6->properties = properties;
	line6->usbdev = usbdev;
	line6->ifcdev = &interface->dev;

	/* get data from endpoint descriptor (see usb_maxpacket): */
	{
		struct usb_host_endpoint *ep;
		unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r);
		unsigned epnum = usb_pipeendpoint(pipe);
		ep = usbdev->ep_in[epnum];

		if (ep != NULL) {
			line6->interval = ep->desc.bInterval;
			line6->max_packet_size =
			    le16_to_cpu(ep->desc.wMaxPacketSize);
		} else {
			line6->interval = LINE6_FALLBACK_INTERVAL;
			line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE;
			dev_err(line6->ifcdev,
				"endpoint not available, using fallback values");
		}
	}

	ret = snd_card_new(line6->ifcdev,
			   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			   THIS_MODULE, 0, &card);
	if (ret < 0)
		goto err_put;

	line6->card = card;
	strcpy(card->id, line6->properties->id);
	strcpy(card->id, properties->id);
	strcpy(card->driver, DRIVER_NAME);
	strcpy(card->shortname, line6->properties->name);
	sprintf(card->longname, "Line 6 %s at USB %s", line6->properties->name,
	strcpy(card->shortname, properties->name);
	sprintf(card->longname, "Line 6 %s at USB %s", properties->name,
		dev_name(line6->ifcdev));
	card->private_data = line6;
	card->private_free = line6_destruct;

	usb_set_intfdata(interface, line6);
@@ -524,52 +524,43 @@ int line6_probe(struct usb_interface *interface,
	/* increment reference counters: */
	usb_get_dev(usbdev);

	if (properties->capabilities & LINE6_CAP_CONTROL) {
		/* initialize USB buffers: */
		line6->buffer_listen =
		    kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL);
		if (line6->buffer_listen == NULL) {
			ret = -ENOMEM;
			goto err_destruct;
		}

		line6->buffer_message =
		    kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
		if (line6->buffer_message == NULL) {
			ret = -ENOMEM;
			goto err_destruct;
		}

		line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
	/* initialize device info: */
	dev_info(&interface->dev, "Line 6 %s found\n", properties->name);

		if (line6->urb_listen == NULL) {
			ret = -ENOMEM;
			goto err_destruct;
		}
	/* query interface number */
	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;

		ret = line6_start_listen(line6);
	ret = usb_set_interface(usbdev, interface_number,
				properties->altsetting);
	if (ret < 0) {
			dev_err(&interface->dev, "%s: usb_submit_urb failed\n",
				__func__);
			goto err_destruct;
		dev_err(&interface->dev, "set_interface failed\n");
		goto error;
	}

	line6_get_interval(line6);

	if (properties->capabilities & LINE6_CAP_CONTROL) {
		ret = line6_init_cap_control(line6);
		if (ret < 0)
			goto error;
	}

	/* initialize device data based on device: */
	ret = private_init(interface, line6);
	ret = private_init(line6, id);
	if (ret < 0)
		goto err_destruct;
		goto error;

	/* creation of additional special files should go here */

	dev_info(&interface->dev, "Line 6 %s now attached\n",
		 line6->properties->name);
		 properties->name);

	return 0;

 err_destruct:
 error:
	if (line6->disconnect)
		line6->disconnect(line6);
	snd_card_free(card);
 err_put:
	return ret;
}
EXPORT_SYMBOL_GPL(line6_probe);
@@ -579,32 +570,23 @@ EXPORT_SYMBOL_GPL(line6_probe);
*/
void line6_disconnect(struct usb_interface *interface)
{
	struct usb_line6 *line6;
	struct usb_device *usbdev;
	int interface_number;
	struct usb_line6 *line6 = usb_get_intfdata(interface);
	struct usb_device *usbdev = interface_to_usbdev(interface);

	if (interface == NULL)
		return;
	usbdev = interface_to_usbdev(interface);
	if (usbdev == NULL)
	if (!line6)
		return;

	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
	line6 = usb_get_intfdata(interface);
	if (!line6)
	if (WARN_ON(usbdev != line6->usbdev))
		return;

	if (line6->urb_listen != NULL)
		line6_stop_listen(line6);

	if (usbdev != line6->usbdev)
		dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n");

	snd_card_disconnect(line6->card);
	if (line6->line6pcm)
		line6_pcm_disconnect(line6->line6pcm);
	if (line6->disconnect)
		line6->disconnect(interface);
		line6->disconnect(line6);

	dev_info(&interface->dev, "Line 6 %s now disconnected\n",
		 line6->properties->name);
+5 −5
Original line number Diff line number Diff line
@@ -157,13 +157,11 @@ struct usb_line6 {
	int message_length;

	void (*process_message)(struct usb_line6 *);
	void (*disconnect)(struct usb_interface *);
	void (*disconnect)(struct usb_line6 *line6);
};

extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1,
				      int code2, int size);
extern ssize_t line6_nop_read(struct device *dev,
			      struct device_attribute *attr, char *buf);
extern int line6_read_data(struct usb_line6 *line6, int address, void *data,
			   size_t datalen);
extern int line6_read_serial_number(struct usb_line6 *line6,
@@ -182,9 +180,11 @@ extern int line6_write_data(struct usb_line6 *line6, int address, void *data,
			    size_t datalen);

int line6_probe(struct usb_interface *interface,
		struct usb_line6 *line6,
		const struct usb_device_id *id,
		const struct line6_properties *properties,
		int (*private_init)(struct usb_interface *, struct usb_line6 *));
		int (*private_init)(struct usb_line6 *, const struct usb_device_id *id),
		size_t data_size);

void line6_disconnect(struct usb_interface *interface);

#ifdef CONFIG_PM
Loading