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