1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com> 3 4 #include "us144mkii.h" 5 6 /** 7 * tascam_midi_in_work_handler() - Deferred work for processing MIDI input. 8 * @work: The work_struct instance. 9 * 10 * This function runs in a thread context. It safely reads raw USB data from 11 * the kfifo, processes it by stripping protocol-specific padding bytes, and 12 * passes the clean MIDI data to the ALSA rawmidi subsystem. 13 */ 14 static void tascam_midi_in_work_handler(struct work_struct *work) 15 { 16 struct tascam_card *tascam = 17 container_of(work, struct tascam_card, midi_in_work); 18 u8 buf[9]; 19 u8 clean_buf[8]; 20 unsigned int count, clean_count; 21 22 if (!tascam->midi_in_substream) 23 return; 24 25 while (kfifo_out_spinlocked(&tascam->midi_in_fifo, buf, sizeof(buf), 26 &tascam->midi_in_lock) == sizeof(buf)) { 27 clean_count = 0; 28 for (count = 0; count < 8; ++count) { 29 if (buf[count] != 0xfd) 30 clean_buf[clean_count++] = buf[count]; 31 } 32 33 if (clean_count > 0) 34 snd_rawmidi_receive(tascam->midi_in_substream, 35 clean_buf, clean_count); 36 } 37 } 38 39 void tascam_midi_in_urb_complete(struct urb *urb) 40 { 41 struct tascam_card *tascam = urb->context; 42 int ret; 43 44 if (!tascam) 45 goto out; 46 47 if (urb->status) { 48 if (urb->status != -ENOENT && urb->status != -ECONNRESET && 49 urb->status != -ESHUTDOWN && urb->status != -EPROTO) { 50 dev_err_ratelimited(tascam->card->dev, 51 "MIDI IN URB failed: status %d\n", 52 urb->status); 53 } 54 goto out; 55 } 56 57 if (atomic_read(&tascam->midi_in_active) && 58 urb->actual_length > 0) { 59 kfifo_in_spinlocked(&tascam->midi_in_fifo, urb->transfer_buffer, 60 urb->actual_length, &tascam->midi_in_lock); 61 schedule_work(&tascam->midi_in_work); 62 } 63 64 usb_get_urb(urb); 65 usb_anchor_urb(urb, &tascam->midi_in_anchor); 66 ret = usb_submit_urb(urb, GFP_ATOMIC); 67 if (ret < 0) { 68 dev_err(tascam->card->dev, 69 "Failed to resubmit MIDI IN URB: error %d\n", ret); 70 usb_unanchor_urb(urb); 71 goto out; 72 } 73 74 out: 75 usb_put_urb(urb); 76 } 77 78 /** 79 * tascam_midi_in_open() - Opens the MIDI input substream. 80 * @substream: The ALSA rawmidi substream to open. 81 * 82 * This function stores a reference to the MIDI input substream in the 83 * driver's private data. 84 * 85 * Return: 0 on success. 86 */ 87 static int tascam_midi_in_open(struct snd_rawmidi_substream *substream) 88 { 89 struct tascam_card *tascam = substream->rmidi->private_data; 90 91 tascam->midi_in_substream = substream; 92 return 0; 93 } 94 95 /** 96 * tascam_midi_in_close() - Closes the MIDI input substream. 97 * @substream: The ALSA rawmidi substream to close. 98 * 99 * Return: 0 on success. 100 */ 101 static int tascam_midi_in_close(struct snd_rawmidi_substream *substream) 102 { 103 return 0; 104 } 105 106 /** 107 * tascam_midi_in_trigger() - Triggers MIDI input stream activity. 108 * @substream: The ALSA rawmidi substream. 109 * @up: Boolean indicating whether to start (1) or stop (0) the stream. 110 * 111 * This function starts or stops the MIDI input URBs based on the 'up' 112 * parameter. When starting, it resets the kfifo and submits all MIDI input 113 * URBs. When stopping, it kills all anchored MIDI input URBs and cancels the 114 * associated workqueue. 115 */ 116 static void tascam_midi_in_trigger(struct snd_rawmidi_substream *substream, 117 int up) 118 { 119 struct tascam_card *tascam = substream->rmidi->private_data; 120 int i, err; 121 122 if (up) { 123 if (atomic_xchg(&tascam->midi_in_active, 1) == 0) { 124 scoped_guard(spinlock_irqsave, &tascam->midi_in_lock) 125 { 126 kfifo_reset(&tascam->midi_in_fifo); 127 } 128 129 for (i = 0; i < NUM_MIDI_IN_URBS; i++) { 130 usb_get_urb(tascam->midi_in_urbs[i]); 131 usb_anchor_urb(tascam->midi_in_urbs[i], 132 &tascam->midi_in_anchor); 133 err = usb_submit_urb(tascam->midi_in_urbs[i], 134 GFP_KERNEL); 135 if (err < 0) { 136 dev_err(tascam->card->dev, 137 "Failed to submit MIDI IN URB %d: %d\n", 138 i, err); 139 usb_unanchor_urb( 140 tascam->midi_in_urbs[i]); 141 usb_put_urb(tascam->midi_in_urbs[i]); 142 } 143 } 144 } 145 } else { 146 if (atomic_xchg(&tascam->midi_in_active, 0) == 1) { 147 usb_kill_anchored_urbs(&tascam->midi_in_anchor); 148 cancel_work_sync(&tascam->midi_in_work); 149 } 150 } 151 } 152 153 /** 154 * tascam_midi_in_ops - ALSA rawmidi operations for MIDI input. 155 * 156 * This structure defines the callback functions for MIDI input stream 157 * operations, including open, close, and trigger. 158 */ 159 static const struct snd_rawmidi_ops tascam_midi_in_ops = { 160 .open = tascam_midi_in_open, 161 .close = tascam_midi_in_close, 162 .trigger = tascam_midi_in_trigger, 163 }; 164 165 void tascam_midi_out_urb_complete(struct urb *urb) 166 { 167 struct tascam_card *tascam = urb->context; 168 int i, urb_index = -1; 169 170 if (urb->status) { 171 if (urb->status != -ENOENT && urb->status != -ECONNRESET && 172 urb->status != -ESHUTDOWN) { 173 dev_err_ratelimited(tascam->card->dev, 174 "MIDI OUT URB failed: %d\n", 175 urb->status); 176 } 177 goto out; 178 } 179 180 if (!tascam) 181 goto out; 182 183 for (i = 0; i < NUM_MIDI_OUT_URBS; i++) { 184 if (tascam->midi_out_urbs[i] == urb) { 185 urb_index = i; 186 break; 187 } 188 } 189 190 if (urb_index < 0) { 191 dev_err_ratelimited(tascam->card->dev, 192 "Unknown MIDI OUT URB completed!\n"); 193 goto out; 194 } 195 196 scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) 197 { 198 clear_bit(urb_index, &tascam->midi_out_urbs_in_flight); 199 } 200 201 if (atomic_read(&tascam->midi_out_active)) 202 schedule_work(&tascam->midi_out_work); 203 204 out: 205 usb_put_urb(urb); 206 } 207 208 /** 209 * tascam_midi_out_work_handler() - Deferred work for sending MIDI data 210 * @work: The work_struct instance. 211 * 212 * This function handles the proprietary output protocol: take the raw MIDI 213 * message bytes from the application, place them at the start of a 9-byte 214 * buffer, pad the rest with 0xFD, and add a terminator byte (0x00). 215 * This function pulls as many bytes as will fit into one packet from the 216 * ALSA buffer and sends them. 217 */ 218 static void tascam_midi_out_work_handler(struct work_struct *work) 219 { 220 struct tascam_card *tascam = 221 container_of(work, struct tascam_card, midi_out_work); 222 struct snd_rawmidi_substream *substream = tascam->midi_out_substream; 223 int i; 224 225 if (!substream || !atomic_read(&tascam->midi_out_active)) 226 return; 227 228 while (snd_rawmidi_transmit_peek(substream, (u8[]){ 0 }, 1) == 1) { 229 int urb_index; 230 struct urb *urb; 231 u8 *buf; 232 int bytes_to_send; 233 234 scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) { 235 urb_index = -1; 236 for (i = 0; i < NUM_MIDI_OUT_URBS; i++) { 237 if (!test_bit( 238 i, 239 &tascam->midi_out_urbs_in_flight)) { 240 urb_index = i; 241 break; 242 } 243 } 244 245 if (urb_index < 0) 246 return; /* No free URBs, will be rescheduled by 247 * completion handler 248 */ 249 250 urb = tascam->midi_out_urbs[urb_index]; 251 buf = urb->transfer_buffer; 252 bytes_to_send = snd_rawmidi_transmit(substream, buf, 8); 253 254 if (bytes_to_send <= 0) 255 break; /* No more data */ 256 257 if (bytes_to_send < 9) 258 memset(buf + bytes_to_send, 0xfd, 259 9 - bytes_to_send); 260 buf[8] = 0xe0; 261 262 set_bit(urb_index, &tascam->midi_out_urbs_in_flight); 263 urb->transfer_buffer_length = 9; 264 } 265 266 usb_get_urb(urb); 267 usb_anchor_urb(urb, &tascam->midi_out_anchor); 268 if (usb_submit_urb(urb, GFP_KERNEL) < 0) { 269 dev_err_ratelimited( 270 tascam->card->dev, 271 "Failed to submit MIDI OUT URB %d\n", 272 urb_index); 273 scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) 274 { 275 clear_bit(urb_index, 276 &tascam->midi_out_urbs_in_flight); 277 } 278 usb_unanchor_urb(urb); 279 usb_put_urb(urb); 280 break; /* Stop on error */ 281 } 282 } 283 } 284 285 /** 286 * tascam_midi_out_open() - Opens the MIDI output substream. 287 * @substream: The ALSA rawmidi substream to open. 288 * 289 * This function stores a reference to the MIDI output substream in the 290 * driver's private data and initializes the MIDI running status. 291 * 292 * Return: 0 on success. 293 */ 294 static int tascam_midi_out_open(struct snd_rawmidi_substream *substream) 295 { 296 struct tascam_card *tascam = substream->rmidi->private_data; 297 298 tascam->midi_out_substream = substream; 299 /* Initialize the running status state for the packet packer. */ 300 tascam->midi_running_status = 0; 301 return 0; 302 } 303 304 /** 305 * tascam_midi_out_close() - Closes the MIDI output substream. 306 * @substream: The ALSA rawmidi substream to close. 307 * 308 * Return: 0 on success. 309 */ 310 static int tascam_midi_out_close(struct snd_rawmidi_substream *substream) 311 { 312 return 0; 313 } 314 315 /** 316 * tascam_midi_out_drain() - Drains the MIDI output stream. 317 * @substream: The ALSA rawmidi substream. 318 * 319 * This function cancels any pending MIDI output work and kills all 320 * anchored MIDI output URBs, ensuring all data is sent or discarded. 321 */ 322 static void tascam_midi_out_drain(struct snd_rawmidi_substream *substream) 323 { 324 struct tascam_card *tascam = substream->rmidi->private_data; 325 bool in_flight = true; 326 327 while (in_flight) { 328 in_flight = false; 329 for (int i = 0; i < NUM_MIDI_OUT_URBS; i++) { 330 if (test_bit(i, &tascam->midi_out_urbs_in_flight)) { 331 in_flight = true; 332 break; 333 } 334 } 335 if (in_flight) 336 schedule_timeout_uninterruptible(1); 337 } 338 339 cancel_work_sync(&tascam->midi_out_work); 340 usb_kill_anchored_urbs(&tascam->midi_out_anchor); 341 } 342 343 /** 344 * tascam_midi_out_trigger() - Triggers MIDI output stream activity. 345 * @substream: The ALSA rawmidi substream. 346 * @up: Boolean indicating whether to start (1) or stop (0) the stream. 347 * 348 * This function starts or stops the MIDI output workqueue based on the 349 * 'up' parameter. 350 */ 351 static void tascam_midi_out_trigger(struct snd_rawmidi_substream *substream, 352 int up) 353 { 354 struct tascam_card *tascam = substream->rmidi->private_data; 355 356 if (up) { 357 atomic_set(&tascam->midi_out_active, 1); 358 schedule_work(&tascam->midi_out_work); 359 } else { 360 atomic_set(&tascam->midi_out_active, 0); 361 } 362 } 363 364 /** 365 * tascam_midi_out_ops - ALSA rawmidi operations for MIDI output. 366 * 367 * This structure defines the callback functions for MIDI output stream 368 * operations, including open, close, trigger, and drain. 369 */ 370 static const struct snd_rawmidi_ops tascam_midi_out_ops = { 371 .open = tascam_midi_out_open, 372 .close = tascam_midi_out_close, 373 .trigger = tascam_midi_out_trigger, 374 .drain = tascam_midi_out_drain, 375 }; 376 377 int tascam_create_midi(struct tascam_card *tascam) 378 { 379 int err; 380 381 err = snd_rawmidi_new(tascam->card, "US144MKII MIDI", 0, 1, 1, 382 &tascam->rmidi); 383 if (err < 0) 384 return err; 385 386 strscpy(tascam->rmidi->name, "US144MKII MIDI", 387 sizeof(tascam->rmidi->name)); 388 tascam->rmidi->private_data = tascam; 389 390 snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 391 &tascam_midi_in_ops); 392 snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 393 &tascam_midi_out_ops); 394 395 tascam->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT | 396 SNDRV_RAWMIDI_INFO_OUTPUT | 397 SNDRV_RAWMIDI_INFO_DUPLEX; 398 399 INIT_WORK(&tascam->midi_in_work, tascam_midi_in_work_handler); 400 INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler); 401 402 return 0; 403 } 404