@@ -9,18 +9,30 @@
struct snd_ump_endpoint;
struct snd_ump_block;
+struct snd_ump_ops;
struct snd_ump_endpoint {
struct snd_rawmidi core; /* raw UMP access */
struct snd_ump_endpoint_info info;
+ const struct snd_ump_ops *ops; /* UMP ops set by the driver */
+ struct snd_rawmidi_substream *substreams[2]; /* opened substreams */
+
void *private_data;
void (*private_free)(struct snd_ump_endpoint *ump);
struct list_head block_list; /* list of snd_ump_block objects */
};
+/* ops filled by UMP drivers */
+struct snd_ump_ops {
+ int (*open)(struct snd_ump_endpoint *ump, int dir);
+ void (*close)(struct snd_ump_endpoint *ump, int dir);
+ void (*trigger)(struct snd_ump_endpoint *ump, int dir, int up);
+ void (*drain)(struct snd_ump_endpoint *ump, int dir);
+};
+
struct snd_ump_block {
struct snd_ump_block_info info;
struct snd_ump_endpoint *ump;
@@ -39,6 +51,8 @@ int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
unsigned int direction, unsigned int first_group,
unsigned int num_groups, struct snd_ump_block **blk_ret);
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count);
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count);
/*
* Some definitions for UMP
@@ -23,6 +23,11 @@ static long snd_ump_ioctl(struct snd_rawmidi_file *rfile, unsigned int cmd,
void __user *argp);
static void snd_ump_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up);
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
.dev_register = snd_ump_dev_register,
@@ -31,6 +36,19 @@ static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
.proc_read = snd_ump_proc_read,
};
+static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+ .drain = snd_ump_rawmidi_drain,
+};
+
static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
{
struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
@@ -104,6 +122,12 @@ int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
ump->core.private_free = snd_ump_endpoint_free;
ump->core.ops = &snd_ump_rawmidi_ops;
+ if (input)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_ump_rawmidi_input_ops);
+ if (output)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_ump_rawmidi_output_ops);
ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
*ump_ret = ump;
@@ -137,6 +161,93 @@ snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
return NULL;
}
+/*
+ * rawmidi ops for UMP endpoint
+ */
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+ int err;
+
+ if (ump->substreams[dir])
+ return -EBUSY;
+ err = ump->ops->open(ump, dir);
+ if (err < 0)
+ return err;
+ ump->substreams[dir] = substream;
+ return 0;
+}
+
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->substreams[dir] = NULL;
+ ump->ops->close(ump, dir);
+ return 0;
+}
+
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+
+ if (ump->ops->drain)
+ ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+/**
+ * snd_ump_receive - transfer UMP packets from the device
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to submit the received UMP packets from the device
+ * to user-space. It's essentially a wrapper of rawmidi_receive().
+ * The data to receive is in CPU-native endianness.
+ */
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream =
+ ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ if (!substream)
+ return 0;
+ return snd_rawmidi_receive(substream, (const char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive);
+
+/**
+ * snd_ump_transmit - transmit UMP packets
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to obtain the UMP packets from user-space to the
+ * device. It's essentially a wrapper of rawmidi_transmit().
+ * The data to transmit is in CPU-native endianness.
+ */
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream =
+ ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ if (!substream)
+ return -ENODEV;
+ return snd_rawmidi_transmit(substream, (char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_transmit);
+
/**
* snd_ump_block_new - Create a UMP block
* @ump: UMP object
@@ -52,7 +52,8 @@ struct snd_usb_midi2_endpoint {
struct usb_device *dev;
const struct usb_ms20_endpoint_descriptor *ms_ep; /* reference to EP descriptor */
struct snd_usb_midi2_endpoint *pair; /* bidirectional pair EP */
- struct snd_usb_midi2_ump *rmidi; /* assigned UMP EP */
+ struct snd_usb_midi2_ump *rmidi; /* assigned UMP EP pair */
+ struct snd_ump_endpoint *ump; /* assigned UMP EP */
int direction; /* direction (STR_IN/OUT) */
unsigned int endpoint; /* EP number */
unsigned int pipe; /* URB pipe */
@@ -133,12 +134,8 @@ static int prepare_output_urb(struct snd_usb_midi2_endpoint *ep,
{
int count;
- if (ep->substream)
- count = snd_rawmidi_transmit(ep->substream,
- urb->transfer_buffer,
- ep->packets);
- else
- count = -ENODEV;
+ count = snd_ump_transmit(ep->ump, urb->transfer_buffer,
+ ep->packets);
if (count < 0) {
dev_dbg(&ep->dev->dev, "rawmidi transmit error %d\n", count);
return count;
@@ -197,9 +194,9 @@ static void input_urb_complete(struct urb *urb)
len &= ~3; /* align UMP */
if (len > ep->packets)
len = ep->packets;
- if (len > 0 && ep->substream) {
+ if (len > 0) {
le32_to_cpu_array((u32 *)urb->transfer_buffer, len >> 2);
- snd_rawmidi_receive(ep->substream, urb->transfer_buffer, len);
+ snd_ump_receive(ep->ump, (u32 *)urb->transfer_buffer, len);
}
dequeue:
set_bit(ctx->index, &ep->urb_free);
@@ -330,68 +327,58 @@ static int alloc_midi_urbs(struct snd_usb_midi2_endpoint *ep)
}
static struct snd_usb_midi2_endpoint *
-substream_to_endpoint(struct snd_rawmidi_substream *substream)
+ump_to_endpoint(struct snd_ump_endpoint *ump, int dir)
{
- struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
struct snd_usb_midi2_ump *rmidi = ump->private_data;
- return rmidi->eps[substream->stream];
+ return rmidi->eps[dir];
}
-/* rawmidi open callback */
-static int snd_usb_midi_v2_open(struct snd_rawmidi_substream *substream)
+/* ump open callback */
+static int snd_usb_midi_v2_open(struct snd_ump_endpoint *ump, int dir)
{
- struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
int err = 0;
if (!ep || !ep->endpoint)
return -ENODEV;
if (ep->disconnected)
return -EIO;
- if (ep->substream)
- return -EBUSY;
if (ep->direction == STR_OUT) {
err = alloc_midi_urbs(ep);
if (err)
return err;
}
- spin_lock_irq(&ep->lock);
- ep->substream = substream;
- spin_unlock_irq(&ep->lock);
return 0;
}
-/* rawmidi close callback */
-static int snd_usb_midi_v2_close(struct snd_rawmidi_substream *substream)
+/* ump close callback */
+static void snd_usb_midi_v2_close(struct snd_ump_endpoint *ump, int dir)
{
- struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
- spin_lock_irq(&ep->lock);
- ep->substream = NULL;
- spin_unlock_irq(&ep->lock);
if (ep->direction == STR_OUT) {
kill_midi_urbs(ep, false);
drain_urb_queue(ep);
free_midi_urbs(ep);
}
- return 0;
}
-/* rawmidi trigger callback */
-static void snd_usb_midi_v2_trigger(struct snd_rawmidi_substream *substream,
+/* ump trigger callback */
+static void snd_usb_midi_v2_trigger(struct snd_ump_endpoint *ump, int dir,
int up)
{
- struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
atomic_set(&ep->running, up);
if (up && ep->direction == STR_OUT && !ep->disconnected)
submit_io_urbs(ep);
}
-/* rawmidi drain callback */
-static void snd_usb_midi_v2_drain(struct snd_rawmidi_substream *substream)
+/* ump drain callback */
+static void snd_usb_midi_v2_drain(struct snd_ump_endpoint *ump, int dir)
{
- struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+ struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
drain_urb_queue(ep);
}
@@ -426,19 +413,13 @@ static int start_input_streams(struct snd_usb_midi2_interface *umidi)
return err;
}
-static const struct snd_rawmidi_ops output_ops = {
+static const struct snd_ump_ops snd_usb_midi_v2_ump_ops = {
.open = snd_usb_midi_v2_open,
.close = snd_usb_midi_v2_close,
.trigger = snd_usb_midi_v2_trigger,
.drain = snd_usb_midi_v2_drain,
};
-static const struct snd_rawmidi_ops input_ops = {
- .open = snd_usb_midi_v2_open,
- .close = snd_usb_midi_v2_close,
- .trigger = snd_usb_midi_v2_trigger,
-};
-
/* create a USB MIDI 2.0 endpoint object */
static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi,
struct usb_host_endpoint *hostep,
@@ -729,23 +710,19 @@ static int create_midi2_ump(struct snd_usb_midi2_interface *umidi,
umidi->chip->num_rawmidis++;
ump->private_data = rmidi;
-
- if (input)
- snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
- &input_ops);
- if (output)
- snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
- &output_ops);
+ ump->ops = &snd_usb_midi_v2_ump_ops;
rmidi->eps[STR_IN] = ep_in;
rmidi->eps[STR_OUT] = ep_out;
if (ep_in) {
ep_in->pair = ep_out;
ep_in->rmidi = rmidi;
+ ep_in->ump = ump;
}
if (ep_out) {
ep_out->pair = ep_in;
ep_out->rmidi = rmidi;
+ ep_out->ump = ump;
}
list_add_tail(&rmidi->list, &umidi->rawmidi_list);