Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
midi.c
Go to the documentation of this file.
1 /*
2  * Linux driver for TerraTec DMX 6Fire USB
3  *
4  * Rawmidi driver
5  *
6  * Author: Torsten Schenk <[email protected]>
7  * Created: Jan 01, 2011
8  * Copyright: (C) Torsten Schenk
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  */
15 
16 #include <sound/rawmidi.h>
17 
18 #include "midi.h"
19 #include "chip.h"
20 #include "comm.h"
21 
22 static void usb6fire_midi_out_handler(struct urb *urb)
23 {
24  struct midi_runtime *rt = urb->context;
25  int ret;
26  unsigned long flags;
27 
28  spin_lock_irqsave(&rt->out_lock, flags);
29 
30  if (rt->out) {
31  ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
32  MIDI_BUFSIZE - 4);
33  if (ret > 0) { /* more data available, send next packet */
34  rt->out_buffer[1] = ret + 2;
35  rt->out_buffer[3] = rt->out_serial++;
36  urb->transfer_buffer_length = ret + 4;
37 
38  ret = usb_submit_urb(urb, GFP_ATOMIC);
39  if (ret < 0)
40  snd_printk(KERN_ERR PREFIX "midi out urb "
41  "submit failed: %d\n", ret);
42  } else /* no more data to transmit */
43  rt->out = NULL;
44  }
45  spin_unlock_irqrestore(&rt->out_lock, flags);
46 }
47 
48 static void usb6fire_midi_in_received(
49  struct midi_runtime *rt, u8 *data, int length)
50 {
51  unsigned long flags;
52 
53  spin_lock_irqsave(&rt->in_lock, flags);
54  if (rt->in)
55  snd_rawmidi_receive(rt->in, data, length);
56  spin_unlock_irqrestore(&rt->in_lock, flags);
57 }
58 
59 static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
60 {
61  return 0;
62 }
63 
64 static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
65 {
66  return 0;
67 }
68 
69 static void usb6fire_midi_out_trigger(
70  struct snd_rawmidi_substream *alsa_sub, int up)
71 {
72  struct midi_runtime *rt = alsa_sub->rmidi->private_data;
73  struct urb *urb = &rt->out_urb;
74  __s8 ret;
75  unsigned long flags;
76 
77  spin_lock_irqsave(&rt->out_lock, flags);
78  if (up) { /* start transfer */
79  if (rt->out) { /* we are already transmitting so just return */
80  spin_unlock_irqrestore(&rt->out_lock, flags);
81  return;
82  }
83 
84  ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
85  MIDI_BUFSIZE - 4);
86  if (ret > 0) {
87  rt->out_buffer[1] = ret + 2;
88  rt->out_buffer[3] = rt->out_serial++;
89  urb->transfer_buffer_length = ret + 4;
90 
91  ret = usb_submit_urb(urb, GFP_ATOMIC);
92  if (ret < 0)
93  snd_printk(KERN_ERR PREFIX "midi out urb "
94  "submit failed: %d\n", ret);
95  else
96  rt->out = alsa_sub;
97  }
98  } else if (rt->out == alsa_sub)
99  rt->out = NULL;
100  spin_unlock_irqrestore(&rt->out_lock, flags);
101 }
102 
103 static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
104 {
105  struct midi_runtime *rt = alsa_sub->rmidi->private_data;
106  int retry = 0;
107 
108  while (rt->out && retry++ < 100)
109  msleep(10);
110 }
111 
112 static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
113 {
114  return 0;
115 }
116 
117 static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
118 {
119  return 0;
120 }
121 
122 static void usb6fire_midi_in_trigger(
123  struct snd_rawmidi_substream *alsa_sub, int up)
124 {
125  struct midi_runtime *rt = alsa_sub->rmidi->private_data;
126  unsigned long flags;
127 
128  spin_lock_irqsave(&rt->in_lock, flags);
129  if (up)
130  rt->in = alsa_sub;
131  else
132  rt->in = NULL;
133  spin_unlock_irqrestore(&rt->in_lock, flags);
134 }
135 
136 static struct snd_rawmidi_ops out_ops = {
137  .open = usb6fire_midi_out_open,
138  .close = usb6fire_midi_out_close,
139  .trigger = usb6fire_midi_out_trigger,
140  .drain = usb6fire_midi_out_drain
141 };
142 
143 static struct snd_rawmidi_ops in_ops = {
144  .open = usb6fire_midi_in_open,
145  .close = usb6fire_midi_in_close,
146  .trigger = usb6fire_midi_in_trigger
147 };
148 
150 {
151  int ret;
152  struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
153  GFP_KERNEL);
154  struct comm_runtime *comm_rt = chip->comm;
155 
156  if (!rt)
157  return -ENOMEM;
158 
159  rt->chip = chip;
160  rt->in_received = usb6fire_midi_in_received;
161  rt->out_buffer[0] = 0x80; /* 'send midi' command */
162  rt->out_buffer[1] = 0x00; /* size of data */
163  rt->out_buffer[2] = 0x00; /* always 0 */
164  spin_lock_init(&rt->in_lock);
165  spin_lock_init(&rt->out_lock);
166 
167  comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
168  usb6fire_midi_out_handler);
169 
170  ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
171  if (ret < 0) {
172  kfree(rt);
173  snd_printk(KERN_ERR PREFIX "unable to create midi.\n");
174  return ret;
175  }
176  rt->instance->private_data = rt;
177  strcpy(rt->instance->name, "DMX6FireUSB MIDI");
178  rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
182  &out_ops);
184  &in_ops);
185 
186  chip->midi = rt;
187  return 0;
188 }
189 
191 {
192  struct midi_runtime *rt = chip->midi;
193 
194  if (rt)
195  usb_poison_urb(&rt->out_urb);
196 }
197 
199 {
200  kfree(chip->midi);
201  chip->midi = NULL;
202 }