18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for TerraTec DMX 6Fire USB 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Rawmidi driver 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Torsten Schenk <torsten.schenk@zoho.com> 88c2ecf20Sopenharmony_ci * Created: Jan 01, 2011 98c2ecf20Sopenharmony_ci * Copyright: (C) Torsten Schenk 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "midi.h" 158c2ecf20Sopenharmony_ci#include "chip.h" 168c2ecf20Sopenharmony_ci#include "comm.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cienum { 198c2ecf20Sopenharmony_ci MIDI_BUFSIZE = 64 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void usb6fire_midi_out_handler(struct urb *urb) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct midi_runtime *rt = urb->context; 258c2ecf20Sopenharmony_ci int ret; 268c2ecf20Sopenharmony_ci unsigned long flags; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt->out_lock, flags); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (rt->out) { 318c2ecf20Sopenharmony_ci ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4, 328c2ecf20Sopenharmony_ci MIDI_BUFSIZE - 4); 338c2ecf20Sopenharmony_ci if (ret > 0) { /* more data available, send next packet */ 348c2ecf20Sopenharmony_ci rt->out_buffer[1] = ret + 2; 358c2ecf20Sopenharmony_ci rt->out_buffer[3] = rt->out_serial++; 368c2ecf20Sopenharmony_ci urb->transfer_buffer_length = ret + 4; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 398c2ecf20Sopenharmony_ci if (ret < 0) 408c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, 418c2ecf20Sopenharmony_ci "midi out urb submit failed: %d\n", 428c2ecf20Sopenharmony_ci ret); 438c2ecf20Sopenharmony_ci } else /* no more data to transmit */ 448c2ecf20Sopenharmony_ci rt->out = NULL; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt->out_lock, flags); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void usb6fire_midi_in_received( 508c2ecf20Sopenharmony_ci struct midi_runtime *rt, u8 *data, int length) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt->in_lock, flags); 558c2ecf20Sopenharmony_ci if (rt->in) 568c2ecf20Sopenharmony_ci snd_rawmidi_receive(rt->in, data, length); 578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt->in_lock, flags); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void usb6fire_midi_out_trigger( 718c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *alsa_sub, int up) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct midi_runtime *rt = alsa_sub->rmidi->private_data; 748c2ecf20Sopenharmony_ci struct urb *urb = &rt->out_urb; 758c2ecf20Sopenharmony_ci __s8 ret; 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt->out_lock, flags); 798c2ecf20Sopenharmony_ci if (up) { /* start transfer */ 808c2ecf20Sopenharmony_ci if (rt->out) { /* we are already transmitting so just return */ 818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt->out_lock, flags); 828c2ecf20Sopenharmony_ci return; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4, 868c2ecf20Sopenharmony_ci MIDI_BUFSIZE - 4); 878c2ecf20Sopenharmony_ci if (ret > 0) { 888c2ecf20Sopenharmony_ci rt->out_buffer[1] = ret + 2; 898c2ecf20Sopenharmony_ci rt->out_buffer[3] = rt->out_serial++; 908c2ecf20Sopenharmony_ci urb->transfer_buffer_length = ret + 4; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 938c2ecf20Sopenharmony_ci if (ret < 0) 948c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, 958c2ecf20Sopenharmony_ci "midi out urb submit failed: %d\n", 968c2ecf20Sopenharmony_ci ret); 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci rt->out = alsa_sub; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } else if (rt->out == alsa_sub) 1018c2ecf20Sopenharmony_ci rt->out = NULL; 1028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt->out_lock, flags); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct midi_runtime *rt = alsa_sub->rmidi->private_data; 1088c2ecf20Sopenharmony_ci int retry = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci while (rt->out && retry++ < 100) 1118c2ecf20Sopenharmony_ci msleep(10); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void usb6fire_midi_in_trigger( 1258c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *alsa_sub, int up) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct midi_runtime *rt = alsa_sub->rmidi->private_data; 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt->in_lock, flags); 1318c2ecf20Sopenharmony_ci if (up) 1328c2ecf20Sopenharmony_ci rt->in = alsa_sub; 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci rt->in = NULL; 1358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt->in_lock, flags); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops out_ops = { 1398c2ecf20Sopenharmony_ci .open = usb6fire_midi_out_open, 1408c2ecf20Sopenharmony_ci .close = usb6fire_midi_out_close, 1418c2ecf20Sopenharmony_ci .trigger = usb6fire_midi_out_trigger, 1428c2ecf20Sopenharmony_ci .drain = usb6fire_midi_out_drain 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops in_ops = { 1468c2ecf20Sopenharmony_ci .open = usb6fire_midi_in_open, 1478c2ecf20Sopenharmony_ci .close = usb6fire_midi_in_close, 1488c2ecf20Sopenharmony_ci .trigger = usb6fire_midi_in_trigger 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciint usb6fire_midi_init(struct sfire_chip *chip) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int ret; 1548c2ecf20Sopenharmony_ci struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), 1558c2ecf20Sopenharmony_ci GFP_KERNEL); 1568c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = chip->comm; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!rt) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); 1628c2ecf20Sopenharmony_ci if (!rt->out_buffer) { 1638c2ecf20Sopenharmony_ci kfree(rt); 1648c2ecf20Sopenharmony_ci return -ENOMEM; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci rt->chip = chip; 1688c2ecf20Sopenharmony_ci rt->in_received = usb6fire_midi_in_received; 1698c2ecf20Sopenharmony_ci rt->out_buffer[0] = 0x80; /* 'send midi' command */ 1708c2ecf20Sopenharmony_ci rt->out_buffer[1] = 0x00; /* size of data */ 1718c2ecf20Sopenharmony_ci rt->out_buffer[2] = 0x00; /* always 0 */ 1728c2ecf20Sopenharmony_ci spin_lock_init(&rt->in_lock); 1738c2ecf20Sopenharmony_ci spin_lock_init(&rt->out_lock); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, 1768c2ecf20Sopenharmony_ci usb6fire_midi_out_handler); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); 1798c2ecf20Sopenharmony_ci if (ret < 0) { 1808c2ecf20Sopenharmony_ci kfree(rt->out_buffer); 1818c2ecf20Sopenharmony_ci kfree(rt); 1828c2ecf20Sopenharmony_ci dev_err(&chip->dev->dev, "unable to create midi.\n"); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci rt->instance->private_data = rt; 1868c2ecf20Sopenharmony_ci strcpy(rt->instance->name, "DMX6FireUSB MIDI"); 1878c2ecf20Sopenharmony_ci rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | 1888c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | 1898c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 1908c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT, 1918c2ecf20Sopenharmony_ci &out_ops); 1928c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT, 1938c2ecf20Sopenharmony_ci &in_ops); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci chip->midi = rt; 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_civoid usb6fire_midi_abort(struct sfire_chip *chip) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct midi_runtime *rt = chip->midi; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (rt) 2048c2ecf20Sopenharmony_ci usb_poison_urb(&rt->out_urb); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_civoid usb6fire_midi_destroy(struct sfire_chip *chip) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct midi_runtime *rt = chip->midi; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci kfree(rt->out_buffer); 2128c2ecf20Sopenharmony_ci kfree(rt); 2138c2ecf20Sopenharmony_ci chip->midi = NULL; 2148c2ecf20Sopenharmony_ci} 215