1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Line 6 Linux USB driver 4 * 5 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 6 */ 7 8 #include <linux/slab.h> 9 #include <linux/usb.h> 10 #include <linux/export.h> 11 #include <sound/core.h> 12 #include <sound/rawmidi.h> 13 14 #include "driver.h" 15 #include "midi.h" 16 17 #define line6_rawmidi_substream_midi(substream) \ 18 ((struct snd_line6_midi *)((substream)->rmidi->private_data)) 19 20 static int send_midi_async(struct usb_line6 *line6, unsigned char *data, 21 int length); 22 23 /* 24 Pass data received via USB to MIDI. 25 */ 26 void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, 27 int length) 28 { 29 if (line6->line6midi->substream_receive) 30 snd_rawmidi_receive(line6->line6midi->substream_receive, 31 data, length); 32 } 33 34 /* 35 Read data from MIDI buffer and transmit them via USB. 36 */ 37 static void line6_midi_transmit(struct snd_rawmidi_substream *substream) 38 { 39 struct usb_line6 *line6 = 40 line6_rawmidi_substream_midi(substream)->line6; 41 struct snd_line6_midi *line6midi = line6->line6midi; 42 struct midi_buffer *mb = &line6midi->midibuf_out; 43 unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; 44 int req, done; 45 46 for (;;) { 47 req = min3(line6_midibuf_bytes_free(mb), line6->max_packet_size, 48 LINE6_FALLBACK_MAXPACKETSIZE); 49 done = snd_rawmidi_transmit_peek(substream, chunk, req); 50 51 if (done == 0) 52 break; 53 54 line6_midibuf_write(mb, chunk, done); 55 snd_rawmidi_transmit_ack(substream, done); 56 } 57 58 for (;;) { 59 done = line6_midibuf_read(mb, chunk, 60 LINE6_FALLBACK_MAXPACKETSIZE, 61 LINE6_MIDIBUF_READ_TX); 62 63 if (done == 0) 64 break; 65 66 send_midi_async(line6, chunk, done); 67 } 68 } 69 70 /* 71 Notification of completion of MIDI transmission. 72 */ 73 static void midi_sent(struct urb *urb) 74 { 75 unsigned long flags; 76 int status; 77 int num; 78 struct usb_line6 *line6 = (struct usb_line6 *)urb->context; 79 80 status = urb->status; 81 kfree(urb->transfer_buffer); 82 usb_free_urb(urb); 83 84 if (status == -ESHUTDOWN) 85 return; 86 87 spin_lock_irqsave(&line6->line6midi->lock, flags); 88 num = --line6->line6midi->num_active_send_urbs; 89 90 if (num == 0) { 91 line6_midi_transmit(line6->line6midi->substream_transmit); 92 num = line6->line6midi->num_active_send_urbs; 93 } 94 95 if (num == 0) 96 wake_up(&line6->line6midi->send_wait); 97 98 spin_unlock_irqrestore(&line6->line6midi->lock, flags); 99 } 100 101 /* 102 Send an asynchronous MIDI message. 103 Assumes that line6->line6midi->lock is held 104 (i.e., this function is serialized). 105 */ 106 static int send_midi_async(struct usb_line6 *line6, unsigned char *data, 107 int length) 108 { 109 struct urb *urb; 110 int retval; 111 unsigned char *transfer_buffer; 112 113 urb = usb_alloc_urb(0, GFP_ATOMIC); 114 115 if (urb == NULL) 116 return -ENOMEM; 117 118 transfer_buffer = kmemdup(data, length, GFP_ATOMIC); 119 120 if (transfer_buffer == NULL) { 121 usb_free_urb(urb); 122 return -ENOMEM; 123 } 124 125 usb_fill_int_urb(urb, line6->usbdev, 126 usb_sndintpipe(line6->usbdev, 127 line6->properties->ep_ctrl_w), 128 transfer_buffer, length, midi_sent, line6, 129 line6->interval); 130 urb->actual_length = 0; 131 retval = usb_urb_ep_type_check(urb); 132 if (retval < 0) 133 goto error; 134 135 retval = usb_submit_urb(urb, GFP_ATOMIC); 136 if (retval < 0) 137 goto error; 138 139 ++line6->line6midi->num_active_send_urbs; 140 return 0; 141 142 error: 143 dev_err(line6->ifcdev, "usb_submit_urb failed\n"); 144 usb_free_urb(urb); 145 return retval; 146 } 147 148 static int line6_midi_output_open(struct snd_rawmidi_substream *substream) 149 { 150 return 0; 151 } 152 153 static int line6_midi_output_close(struct snd_rawmidi_substream *substream) 154 { 155 return 0; 156 } 157 158 static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, 159 int up) 160 { 161 unsigned long flags; 162 struct usb_line6 *line6 = 163 line6_rawmidi_substream_midi(substream)->line6; 164 165 line6->line6midi->substream_transmit = substream; 166 spin_lock_irqsave(&line6->line6midi->lock, flags); 167 168 if (line6->line6midi->num_active_send_urbs == 0) 169 line6_midi_transmit(substream); 170 171 spin_unlock_irqrestore(&line6->line6midi->lock, flags); 172 } 173 174 static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) 175 { 176 struct usb_line6 *line6 = 177 line6_rawmidi_substream_midi(substream)->line6; 178 struct snd_line6_midi *midi = line6->line6midi; 179 180 wait_event_interruptible(midi->send_wait, 181 midi->num_active_send_urbs == 0); 182 } 183 184 static int line6_midi_input_open(struct snd_rawmidi_substream *substream) 185 { 186 return 0; 187 } 188 189 static int line6_midi_input_close(struct snd_rawmidi_substream *substream) 190 { 191 return 0; 192 } 193 194 static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, 195 int up) 196 { 197 struct usb_line6 *line6 = 198 line6_rawmidi_substream_midi(substream)->line6; 199 200 if (up) 201 line6->line6midi->substream_receive = substream; 202 else 203 line6->line6midi->substream_receive = NULL; 204 } 205 206 static const struct snd_rawmidi_ops line6_midi_output_ops = { 207 .open = line6_midi_output_open, 208 .close = line6_midi_output_close, 209 .trigger = line6_midi_output_trigger, 210 .drain = line6_midi_output_drain, 211 }; 212 213 static const struct snd_rawmidi_ops line6_midi_input_ops = { 214 .open = line6_midi_input_open, 215 .close = line6_midi_input_close, 216 .trigger = line6_midi_input_trigger, 217 }; 218 219 /* Create a MIDI device */ 220 static int snd_line6_new_midi(struct usb_line6 *line6, 221 struct snd_rawmidi **rmidi_ret) 222 { 223 struct snd_rawmidi *rmidi; 224 int err; 225 226 err = snd_rawmidi_new(line6->card, "Line 6 MIDI", 0, 1, 1, rmidi_ret); 227 if (err < 0) 228 return err; 229 230 rmidi = *rmidi_ret; 231 strcpy(rmidi->id, line6->properties->id); 232 strcpy(rmidi->name, line6->properties->name); 233 234 rmidi->info_flags = 235 SNDRV_RAWMIDI_INFO_OUTPUT | 236 SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 237 238 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 239 &line6_midi_output_ops); 240 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 241 &line6_midi_input_ops); 242 return 0; 243 } 244 245 /* MIDI device destructor */ 246 static void snd_line6_midi_free(struct snd_rawmidi *rmidi) 247 { 248 struct snd_line6_midi *line6midi = rmidi->private_data; 249 250 line6_midibuf_destroy(&line6midi->midibuf_in); 251 line6_midibuf_destroy(&line6midi->midibuf_out); 252 kfree(line6midi); 253 } 254 255 /* 256 Initialize the Line 6 MIDI subsystem. 257 */ 258 int line6_init_midi(struct usb_line6 *line6) 259 { 260 int err; 261 struct snd_rawmidi *rmidi; 262 struct snd_line6_midi *line6midi; 263 264 if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) { 265 /* skip MIDI initialization and report success */ 266 return 0; 267 } 268 269 err = snd_line6_new_midi(line6, &rmidi); 270 if (err < 0) 271 return err; 272 273 line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); 274 if (!line6midi) 275 return -ENOMEM; 276 277 rmidi->private_data = line6midi; 278 rmidi->private_free = snd_line6_midi_free; 279 280 init_waitqueue_head(&line6midi->send_wait); 281 spin_lock_init(&line6midi->lock); 282 line6midi->line6 = line6; 283 284 err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); 285 if (err < 0) 286 return err; 287 288 err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); 289 if (err < 0) 290 return err; 291 292 line6->line6midi = line6midi; 293 return 0; 294 } 295 EXPORT_SYMBOL_GPL(line6_init_midi); 296
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.