| @@ -680,9 +680,12 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, · 679 unmodified lines |
| 680 | 680 | bool is_input) |
| 681 | 681 | { |
| 682 | 682 | char *newbuf, *oldbuf; |
| 683 | + unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK; |
| 683 | 684 | |
| 684 | 685 | if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) |
| 685 | 686 | return -EINVAL; |
| 687 | + if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0) |
| 688 | + return -EINVAL; |
| 686 | 689 | if (params->avail_min < 1 || params->avail_min > params->buffer_size) |
| 687 | 690 | return -EINVAL; |
| 688 | 691 | if (params->buffer_size != runtime->buffer_size) { |
| @@ -720,8 +723,24 @@ EXPORT_SYMBOL(snd_rawmidi_output_params); · 31 unmodified lines |
| 720 | 723 | int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, |
| 721 | 724 | struct snd_rawmidi_params *params) |
| 722 | 725 | { |
| 726 | + unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK; |
| 727 | + unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK; |
| 728 | + int err; |
| 729 | + |
| 730 | + if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE) |
| 731 | + return -EINVAL; |
| 732 | + else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW) |
| 733 | + return -EINVAL; |
| 734 | + if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) |
| 735 | + return -EINVAL; |
| 723 | 736 | snd_rawmidi_drain_input(substream); |
| 724 | | − return resize_runtime_buffer(substream->runtime, params, true); |
| 737 | + err = resize_runtime_buffer(substream->runtime, params, true); |
| 738 | + if (err < 0) |
| 739 | + return err; |
| 740 | + |
| 741 | + substream->framing = framing; |
| 742 | + substream->clock_type = clock_type; |
| 743 | + return 0; |
| 725 | 744 | } |
| 726 | 745 | EXPORT_SYMBOL(snd_rawmidi_input_params); |
| 727 | 746 | |
| @@ -963,6 +982,62 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, · 235 unmodified lines |
| 963 | 982 | return -ENOIOCTLCMD; |
| 964 | 983 | } |
| 965 | 984 | |
| 985 | +static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream, |
| 986 | + const unsigned char *buffer, int src_count, const struct timespec64 *tstamp) |
| 987 | +{ |
| 988 | + struct snd_rawmidi_runtime *runtime = substream->runtime; |
| 989 | + struct snd_rawmidi_framing_tstamp *dest_ptr; |
| 990 | + struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec }; |
| 991 | + int dest_frames = 0; |
| 992 | + int orig_count = src_count; |
| 993 | + int frame_size = sizeof(struct snd_rawmidi_framing_tstamp); |
| 994 | + |
| 995 | + BUILD_BUG_ON(frame_size != 0x20); |
| 996 | + if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0)) |
| 997 | + return -EINVAL; |
| 998 | + |
| 999 | + while (src_count > 0) { |
| 1000 | + if ((int)(runtime->buffer_size - runtime->avail) < frame_size) { |
| 1001 | + runtime->xruns += src_count; |
| 1002 | + break; |
| 1003 | + } |
| 1004 | + if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH) |
| 1005 | + frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH; |
| 1006 | + else { |
| 1007 | + frame.length = src_count; |
| 1008 | + memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH); |
| 1009 | + } |
| 1010 | + memcpy(frame.data, buffer, frame.length); |
| 1011 | + buffer += frame.length; |
| 1012 | + src_count -= frame.length; |
| 1013 | + dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr); |
| 1014 | + *dest_ptr = frame; |
| 1015 | + runtime->avail += frame_size; |
| 1016 | + runtime->hw_ptr += frame_size; |
| 1017 | + runtime->hw_ptr %= runtime->buffer_size; |
| 1018 | + dest_frames++; |
| 1019 | + } |
| 1020 | + return orig_count - src_count; |
| 1021 | +} |
| 1022 | + |
| 1023 | +static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream) |
| 1024 | +{ |
| 1025 | + struct timespec64 ts64 = {0, 0}; |
| 1026 | + |
| 1027 | + switch (substream->clock_type) { |
| 1028 | + case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW: |
| 1029 | + ktime_get_raw_ts64(&ts64); |
| 1030 | + break; |
| 1031 | + case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC: |
| 1032 | + ktime_get_ts64(&ts64); |
| 1033 | + break; |
| 1034 | + case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME: |
| 1035 | + ktime_get_real_ts64(&ts64); |
| 1036 | + break; |
| 1037 | + } |
| 1038 | + return ts64; |
| 1039 | +} |
| 1040 | + |
| 966 | 1041 | /** |
| 967 | 1042 | * snd_rawmidi_receive - receive the input data from the device |
| 968 | 1043 | * @substream: the rawmidi substream |
| @@ -977,6 +1052,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, · 8 unmodified lines |
| 977 | 1052 | const unsigned char *buffer, int count) |
| 978 | 1053 | { |
| 979 | 1054 | unsigned long flags; |
| 1055 | + struct timespec64 ts64 = get_framing_tstamp(substream); |
| 980 | 1056 | int result = 0, count1; |
| 981 | 1057 | struct snd_rawmidi_runtime *runtime = substream->runtime; |
| 982 | 1058 | |
| @@ -987,8 +1063,11 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, · 4 unmodified lines |
| 987 | 1063 | "snd_rawmidi_receive: input is not active!!!\n"); |
| 988 | 1064 | return -EINVAL; |
| 989 | 1065 | } |
| 1066 | + |
| 990 | 1067 | spin_lock_irqsave(&runtime->lock, flags); |
| 991 | | − if (count == 1) { /* special case, faster code */ |
| 1068 | + if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { |
| 1069 | + result = receive_with_tstamp_framing(substream, buffer, count, &ts64); |
| 1070 | + } else if (count == 1) { /* special case, faster code */ |
| 992 | 1071 | substream->bytes++; |
| 993 | 1072 | if (runtime->avail < runtime->buffer_size) { |
| 994 | 1073 | runtime->buffer[runtime->hw_ptr++] = buffer[0]; |
| @@ -1541,6 +1620,8 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, · 546 unmodified lines |
| 1541 | 1620 | struct snd_rawmidi_substream *substream; |
| 1542 | 1621 | struct snd_rawmidi_runtime *runtime; |
| 1543 | 1622 | unsigned long buffer_size, avail, xruns; |
| 1623 | + unsigned int clock_type; |
| 1624 | + static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" }; |
| 1544 | 1625 | |
| 1545 | 1626 | rmidi = entry->private_data; |
| 1546 | 1627 | snd_iprintf(buffer, "%s\n\n", rmidi->name); |
| @@ -1596,6 +1677,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, · 49 unmodified lines |
| 1596 | 1677 | " Avail : %lu\n" |
| 1597 | 1678 | " Overruns : %lu\n", |
| 1598 | 1679 | buffer_size, avail, xruns); |
| 1680 | + if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { |
| 1681 | + clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT; |
| 1682 | + if (!snd_BUG_ON(clock_type >= sizeof(clock_names))) |
| 1683 | + snd_iprintf(buffer, |
| 1684 | + " Framing : tstamp\n" |
| 1685 | + " Clock type : %s\n", |
| 1686 | + clock_names[clock_type]); |
| 1687 | + } |
| 1599 | 1688 | } |
| 1600 | 1689 | } |
| 1601 | 1690 | } |