struct snd_rawmidi_status32 {
s32 stream;
s32 tstamp_sec; /* Timestamp */
s32 tstamp_nsec;
u32 avail; /* available bytes */
u32 xruns; /* count of overruns since last status (in bytes) */ unsignedchar reserved[16]; /* reserved for future use */
};
struct snd_rawmidi_status64 { int stream;
u8 rsvd[4]; /* alignment */
s64 tstamp_sec; /* Timestamp */
s64 tstamp_nsec;
size_t avail; /* available bytes */
size_t xruns; /* count of overruns since last status (in bytes) */ unsignedchar reserved[16]; /* reserved for future use */
};
/* get the current alignment (either 0 or 3) */ staticinlineint get_align(struct snd_rawmidi_runtime *runtime)
{ if (IS_ENABLED(CONFIG_SND_UMP)) return runtime->align; else return 0;
}
/* get the trimmed size with the current alignment */ #define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
if (err != -ERESTARTSYS) { /* we need wait a while to make sure that Tx FIFOs are empty */ if (substream->ops->drain)
substream->ops->drain(substream); else
msleep(50);
snd_rawmidi_drop_output(substream);
}
/* look for an available substream for the given stream direction; * if a specific subdevice is given, try to assign it
*/ staticint assign_substream(struct snd_rawmidi *rmidi, int subdevice, int stream, int mode, struct snd_rawmidi_substream **sub_ret)
{ struct snd_rawmidi_substream *substream; struct snd_rawmidi_str *s = &rmidi->streams[stream]; staticconstunsignedint info_flags[2] = {
[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
};
if (!(rmidi->info_flags & info_flags[stream])) return -ENXIO; if (subdevice >= 0 && subdevice >= s->substream_count) return -ENODEV;
/* open and do ref-counting for the given substream */ staticint open_substream(struct snd_rawmidi *rmidi, struct snd_rawmidi_substream *substream, int mode)
{ int err;
/* called from sound/core/seq/seq_midi.c */ int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, int mode, struct snd_rawmidi_file *rfile)
{ int err;
if (snd_BUG_ON(!rfile)) return -EINVAL; if (!try_module_get(rmidi->card->module)) return -ENXIO;
rfile = file->private_data; if (((cmd >> 8) & 0xff) != 'W') return -ENOTTY; switch (cmd) { case SNDRV_RAWMIDI_IOCTL_PVERSION: return put_user(SNDRV_RAWMIDI_VERSION, (int __user *)argp) ? -EFAULT : 0; case SNDRV_RAWMIDI_IOCTL_INFO:
{ int stream; struct snd_rawmidi_info __user *info = argp;
if (get_user(stream, &info->stream)) return -EFAULT; switch (stream) { case SNDRV_RAWMIDI_STREAM_INPUT: return snd_rawmidi_info_user(rfile->input, info); case SNDRV_RAWMIDI_STREAM_OUTPUT: return snd_rawmidi_info_user(rfile->output, info); default: return -EINVAL;
}
} case SNDRV_RAWMIDI_IOCTL_USER_PVERSION: if (get_user(rfile->user_pversion, (unsignedint __user *)arg)) return -EFAULT; return 0;
case SNDRV_RAWMIDI_IOCTL_PARAMS:
{ struct snd_rawmidi_params params;
if (copy_from_user(¶ms, argp, sizeof(struct snd_rawmidi_params))) return -EFAULT; if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) {
params.mode = 0;
memset(params.reserved, 0, sizeof(params.reserved));
} switch (params.stream) { case SNDRV_RAWMIDI_STREAM_OUTPUT: if (rfile->output == NULL) return -EINVAL; return snd_rawmidi_output_params(rfile->output, ¶ms); case SNDRV_RAWMIDI_STREAM_INPUT: if (rfile->input == NULL) return -EINVAL; return snd_rawmidi_input_params(rfile->input, ¶ms); default: return -EINVAL;
}
} case SNDRV_RAWMIDI_IOCTL_STATUS32: return snd_rawmidi_ioctl_status32(rfile, argp); case SNDRV_RAWMIDI_IOCTL_STATUS64: return snd_rawmidi_ioctl_status64(rfile, argp); case SNDRV_RAWMIDI_IOCTL_DROP:
{ int val;
if (get_user(val, (int __user *) argp)) return -EFAULT; switch (val) { case SNDRV_RAWMIDI_STREAM_OUTPUT: if (rfile->output == NULL) return -EINVAL; return snd_rawmidi_drop_output(rfile->output); default: return -EINVAL;
}
} case SNDRV_RAWMIDI_IOCTL_DRAIN:
{ int val;
if (get_user(val, (int __user *) argp)) return -EFAULT; switch (val) { case SNDRV_RAWMIDI_STREAM_OUTPUT: if (rfile->output == NULL) return -EINVAL; return snd_rawmidi_drain_output(rfile->output); case SNDRV_RAWMIDI_STREAM_INPUT: if (rfile->input == NULL) return -EINVAL; return snd_rawmidi_drain_input(rfile->input); default: return -EINVAL;
}
} default:
rmidi = rfile->rmidi; if (rmidi->ops && rmidi->ops->ioctl) return rmidi->ops->ioctl(rmidi, cmd, argp);
rmidi_dbg(rmidi, "rawmidi: unknown command = 0x%x\n", cmd);
} return -ENOTTY;
}
/* ioctl to find the next device; either legacy or UMP depending on @find_ump */ staticint snd_rawmidi_next_device(struct snd_card *card, int __user *argp, bool find_ump)
{ struct snd_rawmidi *rmidi; int device; bool is_ump;
if (get_user(device, argp)) return -EFAULT; if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
device = SNDRV_RAWMIDI_DEVICES - 1;
scoped_guard(mutex, ®ister_mutex) {
device = device < 0 ? 0 : device + 1; for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
rmidi = snd_rawmidi_search(card, device); if (!rmidi) continue;
is_ump = rawmidi_is_ump(rmidi); if (find_ump == is_ump) break;
} if (device == SNDRV_RAWMIDI_DEVICES)
device = -1;
} if (put_user(device, argp)) return -EFAULT; return 0;
}
#if IS_ENABLED(CONFIG_SND_UMP) /* inquiry of UMP endpoint and block info via control API */ staticint snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd, void __user *argp)
{ struct snd_ump_endpoint_info __user *info = argp; struct snd_rawmidi *rmidi; int device;
switch (substream->clock_type) { case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
ktime_get_raw_ts64(&ts64); break; case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
ktime_get_ts64(&ts64); break; case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
ktime_get_real_ts64(&ts64); break;
} return ts64;
}
/** * snd_rawmidi_receive - receive the input data from the device * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: the data size to read * * Reads the data from the internal buffer. * * Return: The size of read data, or a negative error code on failure.
*/ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, constunsignedchar *buffer, int count)
{ struct timespec64 ts64 = get_framing_tstamp(substream); int result = 0, count1; struct snd_rawmidi_runtime *runtime;
guard(spinlock_irqsave)(&substream->lock); if (!substream->opened) return -EBADFD;
runtime = substream->runtime; if (!runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi, "snd_rawmidi_receive: input is not active!!!\n"); return -EINVAL;
}
count = get_aligned_size(runtime, count); if (!count) return result;
rfile = file->private_data;
substream = rfile->input; if (substream == NULL) return -EIO;
runtime = substream->runtime;
snd_rawmidi_input_trigger(substream, 1);
result = 0; while (count > 0) {
spin_lock_irq(&substream->lock); while (!__snd_rawmidi_ready(runtime)) {
wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
spin_unlock_irq(&substream->lock); return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irq(&substream->lock);
schedule();
remove_wait_queue(&runtime->sleep, &wait); if (rfile->rmidi->card->shutdown) return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS;
spin_lock_irq(&substream->lock); if (!runtime->avail) {
spin_unlock_irq(&substream->lock); return result > 0 ? result : -EIO;
}
}
spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_read1(substream,
(unsignedchar __user *)buf,
NULL/*kernelbuf*/,
count); if (count1 < 0) return result > 0 ? result : count1;
result += count1;
buf += count1;
count -= count1;
} return result;
}
/** * snd_rawmidi_transmit_empty - check whether the output buffer is empty * @substream: the rawmidi substream * * Return: 1 if the internal output buffer is empty, 0 if not.
*/ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{ struct snd_rawmidi_runtime *runtime;
guard(spinlock_irqsave)(&substream->lock);
runtime = substream->runtime; if (!substream->opened || !runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi, "snd_rawmidi_transmit_empty: output is not active!!!\n"); return 1;
} return (runtime->avail >= runtime->buffer_size);
}
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
/* * __snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/ staticint __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsignedchar *buffer, int count)
{ int result, count1; struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
rmidi_dbg(substream->rmidi, "snd_rawmidi_transmit_peek: output is not active!!!\n"); return -EINVAL;
}
result = 0; if (runtime->avail >= runtime->buffer_size) { /* warning: lowlevel layer MUST trigger down the hardware */ goto __skip;
} if (count == 1) { /* special case, faster code */
*buffer = runtime->buffer[runtime->hw_ptr];
result++;
} else {
count1 = runtime->buffer_size - runtime->hw_ptr; if (count1 > count)
count1 = count; if (count1 > (int)(runtime->buffer_size - runtime->avail))
count1 = runtime->buffer_size - runtime->avail;
count1 = get_aligned_size(runtime, count1); if (!count1) goto __skip;
memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
count -= count1;
result += count1; if (count > 0) { if (count > (int)(runtime->buffer_size - runtime->avail - count1))
count = runtime->buffer_size - runtime->avail - count1;
count = get_aligned_size(runtime, count); if (!count) goto __skip;
memcpy(buffer + count1, runtime->buffer, count);
result += count;
}
}
__skip: return result;
}
/** * snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * Copies data from the internal output buffer to the given buffer. * * Call this in the interrupt handler when the midi output is ready, * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * * Return: The size of copied data, or a negative error code on failure.
*/ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsignedchar *buffer, int count)
{
guard(spinlock_irqsave)(&substream->lock); if (!substream->opened || !substream->runtime) return -EBADFD; return __snd_rawmidi_transmit_peek(substream, buffer, count);
}
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
/* * __snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
*/ staticint __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{ struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
rmidi_dbg(substream->rmidi, "snd_rawmidi_transmit_ack: output is not active!!!\n"); return -EINVAL;
}
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
count = get_aligned_size(runtime, count);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count;
substream->bytes += count; if (count > 0) { if (runtime->drain || __snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
} return count;
}
/** * snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * * Return: The advanced size if successful, or a negative error code on failure.
*/ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
guard(spinlock_irqsave)(&substream->lock); if (!substream->opened || !substream->runtime) return -EBADFD; return __snd_rawmidi_transmit_ack(substream, count);
}
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
/** * snd_rawmidi_transmit - copy from the buffer to the device * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: the data size to transfer * * Copies data from the buffer to the device and advances the pointer. * * Return: The copied size if successful, or a negative error code on failure.
*/ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsignedchar *buffer, int count)
{
guard(spinlock_irqsave)(&substream->lock); if (!substream->opened) return -EBADFD;
count = __snd_rawmidi_transmit_peek(substream, buffer, count); if (count <= 0) return count; return __snd_rawmidi_transmit_ack(substream, count);
}
EXPORT_SYMBOL(snd_rawmidi_transmit);
/** * snd_rawmidi_proceed - Discard the all pending bytes and proceed * @substream: rawmidi substream * * Return: the number of discarded bytes
*/ int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
{ struct snd_rawmidi_runtime *runtime; int count = 0;
/* used for both rawmidi and ump */ int snd_rawmidi_init(struct snd_rawmidi *rmidi, struct snd_card *card, char *id, int device, int output_count, int input_count, unsignedint info_flags)
{ int err; staticconststruct snd_device_ops ops = {
.dev_free = snd_rawmidi_dev_free,
.dev_register = snd_rawmidi_dev_register,
.dev_disconnect = snd_rawmidi_dev_disconnect,
};
/** * snd_rawmidi_new - create a rawmidi instance * @card: the card instance * @id: the id string * @device: the device index * @output_count: the number of output streams * @input_count: the number of input streams * @rrawmidi: the pointer to store the new rawmidi instance * * Creates a new rawmidi instance. * Use snd_rawmidi_set_ops() to set the operators to the new instance. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_rawmidi_new(struct snd_card *card, char *id, int device, int output_count, int input_count, struct snd_rawmidi **rrawmidi)
{ struct snd_rawmidi *rmidi; int err;
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); if (!rmidi) return -ENOMEM;
err = snd_rawmidi_init(rmidi, card, id, device,
output_count, input_count, 0); if (err < 0) {
snd_rawmidi_free(rmidi); return err;
} if (rrawmidi)
*rrawmidi = rmidi; return 0;
}
EXPORT_SYMBOL(snd_rawmidi_new);
/** * snd_rawmidi_set_ops - set the rawmidi operators * @rmidi: the rawmidi instance * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX * @ops: the operator table * * Sets the rawmidi operators for the given stream direction.
*/ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, conststruct snd_rawmidi_ops *ops)
{ struct snd_rawmidi_substream *substream;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.