18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Line 6 Linux USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/usb.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "driver.h" 158c2ecf20Sopenharmony_ci#include "midi.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define line6_rawmidi_substream_midi(substream) \ 188c2ecf20Sopenharmony_ci ((struct snd_line6_midi *)((substream)->rmidi->private_data)) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int send_midi_async(struct usb_line6 *line6, unsigned char *data, 218c2ecf20Sopenharmony_ci int length); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci Pass data received via USB to MIDI. 258c2ecf20Sopenharmony_ci*/ 268c2ecf20Sopenharmony_civoid line6_midi_receive(struct usb_line6 *line6, unsigned char *data, 278c2ecf20Sopenharmony_ci int length) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci if (line6->line6midi->substream_receive) 308c2ecf20Sopenharmony_ci snd_rawmidi_receive(line6->line6midi->substream_receive, 318c2ecf20Sopenharmony_ci data, length); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci Read data from MIDI buffer and transmit them via USB. 368c2ecf20Sopenharmony_ci*/ 378c2ecf20Sopenharmony_cistatic void line6_midi_transmit(struct snd_rawmidi_substream *substream) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct usb_line6 *line6 = 408c2ecf20Sopenharmony_ci line6_rawmidi_substream_midi(substream)->line6; 418c2ecf20Sopenharmony_ci struct snd_line6_midi *line6midi = line6->line6midi; 428c2ecf20Sopenharmony_ci struct midi_buffer *mb = &line6midi->midibuf_out; 438c2ecf20Sopenharmony_ci unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; 448c2ecf20Sopenharmony_ci int req, done; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci for (;;) { 478c2ecf20Sopenharmony_ci req = min3(line6_midibuf_bytes_free(mb), line6->max_packet_size, 488c2ecf20Sopenharmony_ci LINE6_FALLBACK_MAXPACKETSIZE); 498c2ecf20Sopenharmony_ci done = snd_rawmidi_transmit_peek(substream, chunk, req); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (done == 0) 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci line6_midibuf_write(mb, chunk, done); 558c2ecf20Sopenharmony_ci snd_rawmidi_transmit_ack(substream, done); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (;;) { 598c2ecf20Sopenharmony_ci done = line6_midibuf_read(mb, chunk, 608c2ecf20Sopenharmony_ci LINE6_FALLBACK_MAXPACKETSIZE, 618c2ecf20Sopenharmony_ci LINE6_MIDIBUF_READ_TX); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (done == 0) 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci send_midi_async(line6, chunk, done); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci Notification of completion of MIDI transmission. 728c2ecf20Sopenharmony_ci*/ 738c2ecf20Sopenharmony_cistatic void midi_sent(struct urb *urb) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned long flags; 768c2ecf20Sopenharmony_ci int status; 778c2ecf20Sopenharmony_ci int num; 788c2ecf20Sopenharmony_ci struct usb_line6 *line6 = (struct usb_line6 *)urb->context; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci status = urb->status; 818c2ecf20Sopenharmony_ci kfree(urb->transfer_buffer); 828c2ecf20Sopenharmony_ci usb_free_urb(urb); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (status == -ESHUTDOWN) 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci spin_lock_irqsave(&line6->line6midi->lock, flags); 888c2ecf20Sopenharmony_ci num = --line6->line6midi->num_active_send_urbs; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (num == 0) { 918c2ecf20Sopenharmony_ci line6_midi_transmit(line6->line6midi->substream_transmit); 928c2ecf20Sopenharmony_ci num = line6->line6midi->num_active_send_urbs; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (num == 0) 968c2ecf20Sopenharmony_ci wake_up(&line6->line6midi->send_wait); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line6->line6midi->lock, flags); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci Send an asynchronous MIDI message. 1038c2ecf20Sopenharmony_ci Assumes that line6->line6midi->lock is held 1048c2ecf20Sopenharmony_ci (i.e., this function is serialized). 1058c2ecf20Sopenharmony_ci*/ 1068c2ecf20Sopenharmony_cistatic int send_midi_async(struct usb_line6 *line6, unsigned char *data, 1078c2ecf20Sopenharmony_ci int length) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct urb *urb; 1108c2ecf20Sopenharmony_ci int retval; 1118c2ecf20Sopenharmony_ci unsigned char *transfer_buffer; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (urb == NULL) 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci transfer_buffer = kmemdup(data, length, GFP_ATOMIC); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (transfer_buffer == NULL) { 1218c2ecf20Sopenharmony_ci usb_free_urb(urb); 1228c2ecf20Sopenharmony_ci return -ENOMEM; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci usb_fill_int_urb(urb, line6->usbdev, 1268c2ecf20Sopenharmony_ci usb_sndintpipe(line6->usbdev, 1278c2ecf20Sopenharmony_ci line6->properties->ep_ctrl_w), 1288c2ecf20Sopenharmony_ci transfer_buffer, length, midi_sent, line6, 1298c2ecf20Sopenharmony_ci line6->interval); 1308c2ecf20Sopenharmony_ci urb->actual_length = 0; 1318c2ecf20Sopenharmony_ci retval = usb_urb_ep_type_check(urb); 1328c2ecf20Sopenharmony_ci if (retval < 0) 1338c2ecf20Sopenharmony_ci goto error; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 1368c2ecf20Sopenharmony_ci if (retval < 0) 1378c2ecf20Sopenharmony_ci goto error; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ++line6->line6midi->num_active_send_urbs; 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci error: 1438c2ecf20Sopenharmony_ci dev_err(line6->ifcdev, "usb_submit_urb failed\n"); 1448c2ecf20Sopenharmony_ci usb_free_urb(urb); 1458c2ecf20Sopenharmony_ci return retval; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int line6_midi_output_open(struct snd_rawmidi_substream *substream) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int line6_midi_output_close(struct snd_rawmidi_substream *substream) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, 1598c2ecf20Sopenharmony_ci int up) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned long flags; 1628c2ecf20Sopenharmony_ci struct usb_line6 *line6 = 1638c2ecf20Sopenharmony_ci line6_rawmidi_substream_midi(substream)->line6; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci line6->line6midi->substream_transmit = substream; 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(&line6->line6midi->lock, flags); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (line6->line6midi->num_active_send_urbs == 0) 1698c2ecf20Sopenharmony_ci line6_midi_transmit(substream); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&line6->line6midi->lock, flags); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void line6_midi_output_drain(struct snd_rawmidi_substream *substream) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct usb_line6 *line6 = 1778c2ecf20Sopenharmony_ci line6_rawmidi_substream_midi(substream)->line6; 1788c2ecf20Sopenharmony_ci struct snd_line6_midi *midi = line6->line6midi; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci wait_event_interruptible(midi->send_wait, 1818c2ecf20Sopenharmony_ci midi->num_active_send_urbs == 0); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int line6_midi_input_open(struct snd_rawmidi_substream *substream) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int line6_midi_input_close(struct snd_rawmidi_substream *substream) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, 1958c2ecf20Sopenharmony_ci int up) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct usb_line6 *line6 = 1988c2ecf20Sopenharmony_ci line6_rawmidi_substream_midi(substream)->line6; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (up) 2018c2ecf20Sopenharmony_ci line6->line6midi->substream_receive = substream; 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci line6->line6midi->substream_receive = NULL; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops line6_midi_output_ops = { 2078c2ecf20Sopenharmony_ci .open = line6_midi_output_open, 2088c2ecf20Sopenharmony_ci .close = line6_midi_output_close, 2098c2ecf20Sopenharmony_ci .trigger = line6_midi_output_trigger, 2108c2ecf20Sopenharmony_ci .drain = line6_midi_output_drain, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops line6_midi_input_ops = { 2148c2ecf20Sopenharmony_ci .open = line6_midi_input_open, 2158c2ecf20Sopenharmony_ci .close = line6_midi_input_close, 2168c2ecf20Sopenharmony_ci .trigger = line6_midi_input_trigger, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Create a MIDI device */ 2208c2ecf20Sopenharmony_cistatic int snd_line6_new_midi(struct usb_line6 *line6, 2218c2ecf20Sopenharmony_ci struct snd_rawmidi **rmidi_ret) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2248c2ecf20Sopenharmony_ci int err; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci err = snd_rawmidi_new(line6->card, "Line 6 MIDI", 0, 1, 1, rmidi_ret); 2278c2ecf20Sopenharmony_ci if (err < 0) 2288c2ecf20Sopenharmony_ci return err; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci rmidi = *rmidi_ret; 2318c2ecf20Sopenharmony_ci strcpy(rmidi->id, line6->properties->id); 2328c2ecf20Sopenharmony_ci strcpy(rmidi->name, line6->properties->name); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci rmidi->info_flags = 2358c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_OUTPUT | 2368c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 2398c2ecf20Sopenharmony_ci &line6_midi_output_ops); 2408c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 2418c2ecf20Sopenharmony_ci &line6_midi_input_ops); 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* MIDI device destructor */ 2468c2ecf20Sopenharmony_cistatic void snd_line6_midi_free(struct snd_rawmidi *rmidi) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct snd_line6_midi *line6midi = rmidi->private_data; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci line6_midibuf_destroy(&line6midi->midibuf_in); 2518c2ecf20Sopenharmony_ci line6_midibuf_destroy(&line6midi->midibuf_out); 2528c2ecf20Sopenharmony_ci kfree(line6midi); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci Initialize the Line 6 MIDI subsystem. 2578c2ecf20Sopenharmony_ci*/ 2588c2ecf20Sopenharmony_ciint line6_init_midi(struct usb_line6 *line6) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int err; 2618c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2628c2ecf20Sopenharmony_ci struct snd_line6_midi *line6midi; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) { 2658c2ecf20Sopenharmony_ci /* skip MIDI initialization and report success */ 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci err = snd_line6_new_midi(line6, &rmidi); 2708c2ecf20Sopenharmony_ci if (err < 0) 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); 2748c2ecf20Sopenharmony_ci if (!line6midi) 2758c2ecf20Sopenharmony_ci return -ENOMEM; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci rmidi->private_data = line6midi; 2788c2ecf20Sopenharmony_ci rmidi->private_free = snd_line6_midi_free; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci init_waitqueue_head(&line6midi->send_wait); 2818c2ecf20Sopenharmony_ci spin_lock_init(&line6midi->lock); 2828c2ecf20Sopenharmony_ci line6midi->line6 = line6; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); 2858c2ecf20Sopenharmony_ci if (err < 0) 2868c2ecf20Sopenharmony_ci return err; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); 2898c2ecf20Sopenharmony_ci if (err < 0) 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci line6->line6midi = line6midi; 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_init_midi); 296