Thomas Gleixner | da607e1 | 2019-05-29 16:57:59 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 2 | /* |
| 3 | * motu-protocol-v2.c - a part of driver for MOTU FireWire series |
| 4 | * |
| 5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 6 | */ |
| 7 | |
| 8 | #include "motu.h" |
| 9 | |
| 10 | #define V2_CLOCK_STATUS_OFFSET 0x0b14 |
| 11 | #define V2_CLOCK_RATE_MASK 0x00000038 |
| 12 | #define V2_CLOCK_RATE_SHIFT 3 |
| 13 | #define V2_CLOCK_SRC_MASK 0x00000007 |
| 14 | #define V2_CLOCK_SRC_SHIFT 0 |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 15 | #define V2_CLOCK_FETCH_ENABLE 0x02000000 |
| 16 | #define V2_CLOCK_MODEL_SPECIFIC 0x04000000 |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 17 | |
| 18 | #define V2_IN_OUT_CONF_OFFSET 0x0c04 |
| 19 | #define V2_OPT_OUT_IFACE_MASK 0x00000c00 |
| 20 | #define V2_OPT_OUT_IFACE_SHIFT 10 |
| 21 | #define V2_OPT_IN_IFACE_MASK 0x00000300 |
| 22 | #define V2_OPT_IN_IFACE_SHIFT 8 |
| 23 | #define V2_OPT_IFACE_MODE_NONE 0 |
| 24 | #define V2_OPT_IFACE_MODE_ADAT 1 |
| 25 | #define V2_OPT_IFACE_MODE_SPDIF 2 |
| 26 | |
Takashi Sakamoto | 4b2079f | 2019-10-30 17:06:42 +0900 | [diff] [blame] | 27 | static int get_clock_rate(u32 data, unsigned int *rate) |
| 28 | { |
| 29 | unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT; |
| 30 | if (index >= ARRAY_SIZE(snd_motu_clock_rates)) |
| 31 | return -EIO; |
| 32 | |
| 33 | *rate = snd_motu_clock_rates[index]; |
| 34 | |
| 35 | return 0; |
| 36 | } |
| 37 | |
Takashi Sakamoto | ff222b7 | 2020-05-19 20:16:31 +0900 | [diff] [blame] | 38 | int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, |
| 39 | unsigned int *rate) |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 40 | { |
| 41 | __be32 reg; |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 42 | int err; |
| 43 | |
| 44 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 45 | sizeof(reg)); |
| 46 | if (err < 0) |
| 47 | return err; |
| 48 | |
Takashi Sakamoto | 4b2079f | 2019-10-30 17:06:42 +0900 | [diff] [blame] | 49 | return get_clock_rate(be32_to_cpu(reg), rate); |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 50 | } |
| 51 | |
Takashi Sakamoto | ff222b7 | 2020-05-19 20:16:31 +0900 | [diff] [blame] | 52 | int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, |
| 53 | unsigned int rate) |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 54 | { |
| 55 | __be32 reg; |
| 56 | u32 data; |
| 57 | int i; |
| 58 | int err; |
| 59 | |
| 60 | for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { |
| 61 | if (snd_motu_clock_rates[i] == rate) |
| 62 | break; |
| 63 | } |
| 64 | if (i == ARRAY_SIZE(snd_motu_clock_rates)) |
| 65 | return -EINVAL; |
| 66 | |
| 67 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 68 | sizeof(reg)); |
| 69 | if (err < 0) |
| 70 | return err; |
| 71 | data = be32_to_cpu(reg); |
| 72 | |
| 73 | data &= ~V2_CLOCK_RATE_MASK; |
| 74 | data |= i << V2_CLOCK_RATE_SHIFT; |
| 75 | |
| 76 | reg = cpu_to_be32(data); |
| 77 | return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 78 | sizeof(reg)); |
| 79 | } |
| 80 | |
Takashi Sakamoto | 2b8f050 | 2020-05-19 20:16:39 +0900 | [diff] [blame^] | 81 | static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data, |
| 82 | enum snd_motu_clock_source *src) |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 83 | { |
Takashi Sakamoto | 2b8f050 | 2020-05-19 20:16:39 +0900 | [diff] [blame^] | 84 | switch (data) { |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 85 | case 0: |
| 86 | *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; |
| 87 | break; |
| 88 | case 1: |
Takashi Sakamoto | 4b2079f | 2019-10-30 17:06:42 +0900 | [diff] [blame] | 89 | { |
| 90 | __be32 reg; |
| 91 | |
| 92 | // To check the configuration of optical interface. |
| 93 | int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, |
| 94 | ®, sizeof(reg)); |
| 95 | if (err < 0) |
| 96 | return err; |
| 97 | |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 98 | if (be32_to_cpu(reg) & 0x00000200) |
| 99 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; |
| 100 | else |
| 101 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; |
| 102 | break; |
Takashi Sakamoto | 4b2079f | 2019-10-30 17:06:42 +0900 | [diff] [blame] | 103 | } |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 104 | case 2: |
| 105 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; |
| 106 | break; |
Takashi Sakamoto | 3f58f00 | 2019-10-30 17:06:41 +0900 | [diff] [blame] | 107 | case 3: |
| 108 | *src = SND_MOTU_CLOCK_SOURCE_SPH; |
| 109 | break; |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 110 | case 4: |
| 111 | *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; |
| 112 | break; |
| 113 | case 5: |
| 114 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; |
| 115 | break; |
| 116 | default: |
Takashi Sakamoto | 2b8f050 | 2020-05-19 20:16:39 +0900 | [diff] [blame^] | 117 | *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; |
| 118 | break; |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | return 0; |
| 122 | } |
| 123 | |
Takashi Sakamoto | 2b8f050 | 2020-05-19 20:16:39 +0900 | [diff] [blame^] | 124 | static int v2_detect_clock_source(struct snd_motu *motu, u32 data, |
| 125 | enum snd_motu_clock_source *src) |
| 126 | { |
| 127 | switch (data) { |
| 128 | case 0: |
| 129 | *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; |
| 130 | break; |
| 131 | case 2: |
| 132 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; |
| 133 | break; |
| 134 | case 3: |
| 135 | *src = SND_MOTU_CLOCK_SOURCE_SPH; |
| 136 | break; |
| 137 | case 4: |
| 138 | *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; |
| 139 | break; |
| 140 | default: |
| 141 | *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; |
| 142 | break; |
| 143 | } |
| 144 | |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | static int get_clock_source(struct snd_motu *motu, u32 data, |
| 149 | enum snd_motu_clock_source *src) |
| 150 | { |
| 151 | data &= V2_CLOCK_SRC_MASK; |
| 152 | if (motu->spec == &snd_motu_spec_828mk2 || |
| 153 | motu->spec == &snd_motu_spec_traveler) |
| 154 | return detect_clock_source_optical_model(motu, data, src); |
| 155 | else |
| 156 | return v2_detect_clock_source(motu, data, src); |
| 157 | } |
| 158 | |
Takashi Sakamoto | ff222b7 | 2020-05-19 20:16:31 +0900 | [diff] [blame] | 159 | int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, |
| 160 | enum snd_motu_clock_source *src) |
Takashi Sakamoto | 4b2079f | 2019-10-30 17:06:42 +0900 | [diff] [blame] | 161 | { |
| 162 | __be32 reg; |
| 163 | int err; |
| 164 | |
| 165 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 166 | sizeof(reg)); |
| 167 | if (err < 0) |
| 168 | return err; |
| 169 | |
| 170 | return get_clock_source(motu, be32_to_cpu(reg), src); |
| 171 | } |
| 172 | |
Takashi Sakamoto | ff222b7 | 2020-05-19 20:16:31 +0900 | [diff] [blame] | 173 | int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, |
| 174 | bool enable) |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 175 | { |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 176 | enum snd_motu_clock_source src; |
Takashi Sakamoto | 6c5e1ac | 2018-06-18 21:07:55 +0900 | [diff] [blame] | 177 | __be32 reg; |
| 178 | u32 data; |
| 179 | int err = 0; |
| 180 | |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 181 | // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. |
| 182 | if (motu->spec == &snd_motu_spec_828mk2) |
| 183 | return 0; |
| 184 | |
| 185 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 186 | sizeof(reg)); |
| 187 | if (err < 0) |
| 188 | return err; |
| 189 | data = be32_to_cpu(reg); |
| 190 | |
| 191 | err = get_clock_source(motu, data, &src); |
| 192 | if (err < 0) |
| 193 | return err; |
| 194 | |
| 195 | data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); |
| 196 | if (enable) |
| 197 | data |= V2_CLOCK_FETCH_ENABLE; |
| 198 | |
Takashi Sakamoto | 28c8d3c | 2020-05-19 20:16:34 +0900 | [diff] [blame] | 199 | if (motu->spec == &snd_motu_spec_traveler) { |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 200 | // Expected for Traveler and 896HD, which implements Altera |
| 201 | // Cyclone EP1C3. |
| 202 | data |= V2_CLOCK_MODEL_SPECIFIC; |
| 203 | } else { |
| 204 | // For UltraLite and 8pre, which implements Xilinx Spartan |
| 205 | // XC3S200. |
| 206 | unsigned int rate; |
| 207 | |
| 208 | err = get_clock_rate(data, &rate); |
Takashi Sakamoto | 6c5e1ac | 2018-06-18 21:07:55 +0900 | [diff] [blame] | 209 | if (err < 0) |
| 210 | return err; |
Takashi Sakamoto | 6c5e1ac | 2018-06-18 21:07:55 +0900 | [diff] [blame] | 211 | |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 212 | if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) |
| 213 | data |= V2_CLOCK_MODEL_SPECIFIC; |
Takashi Sakamoto | 6c5e1ac | 2018-06-18 21:07:55 +0900 | [diff] [blame] | 214 | } |
| 215 | |
Takashi Sakamoto | bd10737 | 2019-10-30 17:06:43 +0900 | [diff] [blame] | 216 | reg = cpu_to_be32(data); |
| 217 | return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, |
| 218 | sizeof(reg)); |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 219 | } |
| 220 | |
Takashi Sakamoto | 28c8d3c | 2020-05-19 20:16:34 +0900 | [diff] [blame] | 221 | static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data) |
| 222 | { |
| 223 | if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == |
| 224 | V2_OPT_IFACE_MODE_ADAT) { |
| 225 | motu->tx_packet_formats.pcm_chunks[0] += 8; |
| 226 | motu->tx_packet_formats.pcm_chunks[1] += 4; |
| 227 | } |
| 228 | |
| 229 | if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == |
| 230 | V2_OPT_IFACE_MODE_ADAT) { |
| 231 | motu->rx_packet_formats.pcm_chunks[0] += 8; |
| 232 | motu->rx_packet_formats.pcm_chunks[1] += 4; |
| 233 | } |
| 234 | |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data) |
| 239 | { |
| 240 | if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == |
| 241 | V2_OPT_IFACE_MODE_ADAT) { |
| 242 | motu->tx_packet_formats.pcm_chunks[0] += 8; |
| 243 | motu->tx_packet_formats.pcm_chunks[1] += 4; |
| 244 | } |
| 245 | |
| 246 | if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == |
| 247 | V2_OPT_IFACE_MODE_ADAT) { |
| 248 | motu->rx_packet_formats.pcm_chunks[0] += 8; |
| 249 | motu->rx_packet_formats.pcm_chunks[1] += 4; |
| 250 | } |
| 251 | |
| 252 | return 0; |
| 253 | } |
| 254 | |
| 255 | static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data) |
| 256 | { |
| 257 | if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == |
| 258 | V2_OPT_IFACE_MODE_ADAT) { |
| 259 | motu->tx_packet_formats.pcm_chunks[0] += 8; |
| 260 | motu->tx_packet_formats.pcm_chunks[1] += 8; |
| 261 | } |
| 262 | |
| 263 | if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == |
| 264 | V2_OPT_IFACE_MODE_ADAT) { |
| 265 | motu->rx_packet_formats.pcm_chunks[0] += 8; |
| 266 | motu->rx_packet_formats.pcm_chunks[1] += 8; |
| 267 | } |
| 268 | |
| 269 | return 0; |
| 270 | } |
| 271 | |
Takashi Sakamoto | ff222b7 | 2020-05-19 20:16:31 +0900 | [diff] [blame] | 272 | int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu) |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 273 | { |
| 274 | __be32 reg; |
| 275 | u32 data; |
| 276 | int err; |
| 277 | |
Takashi Sakamoto | 28c8d3c | 2020-05-19 20:16:34 +0900 | [diff] [blame] | 278 | motu->tx_packet_formats.pcm_byte_offset = 10; |
| 279 | motu->rx_packet_formats.pcm_byte_offset = 10; |
| 280 | |
| 281 | motu->tx_packet_formats.msg_chunks = 2; |
| 282 | motu->rx_packet_formats.msg_chunks = 2; |
| 283 | |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 284 | err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, |
| 285 | sizeof(reg)); |
| 286 | if (err < 0) |
| 287 | return err; |
| 288 | data = be32_to_cpu(reg); |
| 289 | |
Takashi Sakamoto | 0090c1c | 2020-05-19 20:16:35 +0900 | [diff] [blame] | 290 | memcpy(motu->tx_packet_formats.pcm_chunks, |
| 291 | motu->spec->tx_fixed_pcm_chunks, |
| 292 | sizeof(motu->tx_packet_formats.pcm_chunks)); |
| 293 | memcpy(motu->rx_packet_formats.pcm_chunks, |
| 294 | motu->spec->rx_fixed_pcm_chunks, |
| 295 | sizeof(motu->rx_packet_formats.pcm_chunks)); |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 296 | |
Takashi Sakamoto | 28c8d3c | 2020-05-19 20:16:34 +0900 | [diff] [blame] | 297 | if (motu->spec == &snd_motu_spec_828mk2) |
| 298 | return detect_packet_formats_828mk2(motu, data); |
| 299 | else if (motu->spec == &snd_motu_spec_traveler) |
| 300 | return detect_packet_formats_traveler(motu, data); |
| 301 | else if (motu->spec == &snd_motu_spec_8pre) |
| 302 | return detect_packet_formats_8pre(motu, data); |
| 303 | else |
| 304 | return 0; |
Takashi Sakamoto | 949613e | 2017-03-22 21:30:26 +0900 | [diff] [blame] | 305 | } |
| 306 | |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 307 | const struct snd_motu_spec snd_motu_spec_828mk2 = { |
| 308 | .name = "828mk2", |
Takashi Sakamoto | 61d79c7 | 2020-05-19 20:16:30 +0900 | [diff] [blame] | 309 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
Takashi Sakamoto | 739bdba | 2020-05-19 20:16:38 +0900 | [diff] [blame] | 310 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 311 | SND_MOTU_SPEC_TX_MIDI_2ND_Q, |
Takashi Sakamoto | dfbaa4d | 2020-05-19 20:16:33 +0900 | [diff] [blame] | 312 | .tx_fixed_pcm_chunks = {14, 14, 0}, |
| 313 | .rx_fixed_pcm_chunks = {14, 14, 0}, |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 314 | }; |
| 315 | |
| 316 | const struct snd_motu_spec snd_motu_spec_traveler = { |
| 317 | .name = "Traveler", |
Takashi Sakamoto | 61d79c7 | 2020-05-19 20:16:30 +0900 | [diff] [blame] | 318 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
Takashi Sakamoto | 739bdba | 2020-05-19 20:16:38 +0900 | [diff] [blame] | 319 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 320 | SND_MOTU_SPEC_TX_MIDI_2ND_Q, |
Takashi Sakamoto | dfbaa4d | 2020-05-19 20:16:33 +0900 | [diff] [blame] | 321 | .tx_fixed_pcm_chunks = {14, 14, 8}, |
| 322 | .rx_fixed_pcm_chunks = {14, 14, 8}, |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 323 | }; |
| 324 | |
| 325 | const struct snd_motu_spec snd_motu_spec_ultralite = { |
| 326 | .name = "UltraLite", |
Takashi Sakamoto | 61d79c7 | 2020-05-19 20:16:30 +0900 | [diff] [blame] | 327 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
Takashi Sakamoto | 739bdba | 2020-05-19 20:16:38 +0900 | [diff] [blame] | 328 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
| 329 | SND_MOTU_SPEC_TX_MIDI_2ND_Q, |
Takashi Sakamoto | dfbaa4d | 2020-05-19 20:16:33 +0900 | [diff] [blame] | 330 | .tx_fixed_pcm_chunks = {14, 14, 0}, |
| 331 | .rx_fixed_pcm_chunks = {14, 14, 0}, |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 332 | }; |
| 333 | |
| 334 | const struct snd_motu_spec snd_motu_spec_8pre = { |
| 335 | .name = "8pre", |
Takashi Sakamoto | 61d79c7 | 2020-05-19 20:16:30 +0900 | [diff] [blame] | 336 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
Takashi Sakamoto | 739bdba | 2020-05-19 20:16:38 +0900 | [diff] [blame] | 337 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 338 | SND_MOTU_SPEC_TX_MIDI_2ND_Q, |
Takashi Sakamoto | dfbaa4d | 2020-05-19 20:16:33 +0900 | [diff] [blame] | 339 | .tx_fixed_pcm_chunks = {10, 6, 0}, |
| 340 | .rx_fixed_pcm_chunks = {10, 6, 0}, |
Takashi Sakamoto | 0a7c7b4 | 2020-05-19 20:16:28 +0900 | [diff] [blame] | 341 | }; |