root/sound/soc/soc-generic-dmaengine-pcm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. soc_component_to_pcm
  2. dmaengine_dma_dev
  3. snd_dmaengine_pcm_prepare_slave_config
  4. dmaengine_pcm_hw_params
  5. dmaengine_pcm_set_runtime_hwparams
  6. dmaengine_pcm_open
  7. dmaengine_pcm_compat_request_channel
  8. dmaengine_pcm_can_report_residue
  9. dmaengine_pcm_new
  10. dmaengine_pcm_pointer
  11. dmaengine_copy_user
  12. dmaengine_pcm_request_chan_of
  13. dmaengine_pcm_release_chan
  14. snd_dmaengine_pcm_register
  15. snd_dmaengine_pcm_unregister

   1 // SPDX-License-Identifier: GPL-2.0+
   2 //
   3 //  Copyright (C) 2013, Analog Devices Inc.
   4 //      Author: Lars-Peter Clausen <lars@metafoo.de>
   5 
   6 #include <linux/module.h>
   7 #include <linux/init.h>
   8 #include <linux/dmaengine.h>
   9 #include <linux/slab.h>
  10 #include <sound/pcm.h>
  11 #include <sound/pcm_params.h>
  12 #include <sound/soc.h>
  13 #include <linux/dma-mapping.h>
  14 #include <linux/of.h>
  15 
  16 #include <sound/dmaengine_pcm.h>
  17 
  18 /*
  19  * The platforms dmaengine driver does not support reporting the amount of
  20  * bytes that are still left to transfer.
  21  */
  22 #define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
  23 
  24 struct dmaengine_pcm {
  25         struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
  26         const struct snd_dmaengine_pcm_config *config;
  27         struct snd_soc_component component;
  28         unsigned int flags;
  29 };
  30 
  31 static struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p)
  32 {
  33         return container_of(p, struct dmaengine_pcm, component);
  34 }
  35 
  36 static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
  37         struct snd_pcm_substream *substream)
  38 {
  39         if (!pcm->chan[substream->stream])
  40                 return NULL;
  41 
  42         return pcm->chan[substream->stream]->device->dev;
  43 }
  44 
  45 /**
  46  * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
  47  * @substream: PCM substream
  48  * @params: hw_params
  49  * @slave_config: DMA slave config to prepare
  50  *
  51  * This function can be used as a generic prepare_slave_config callback for
  52  * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
  53  * DAI DMA data. Internally the function will first call
  54  * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
  55  * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
  56  * remaining fields based on the DAI DMA data.
  57  */
  58 int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
  59         struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
  60 {
  61         struct snd_soc_pcm_runtime *rtd = substream->private_data;
  62         struct snd_dmaengine_dai_dma_data *dma_data;
  63         int ret;
  64 
  65         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  66 
  67         ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
  68         if (ret)
  69                 return ret;
  70 
  71         snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
  72                 slave_config);
  73 
  74         return 0;
  75 }
  76 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
  77 
  78 static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
  79         struct snd_pcm_hw_params *params)
  80 {
  81         struct snd_soc_pcm_runtime *rtd = substream->private_data;
  82         struct snd_soc_component *component =
  83                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
  84         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
  85         struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  86         int (*prepare_slave_config)(struct snd_pcm_substream *substream,
  87                         struct snd_pcm_hw_params *params,
  88                         struct dma_slave_config *slave_config);
  89         struct dma_slave_config slave_config;
  90         int ret;
  91 
  92         memset(&slave_config, 0, sizeof(slave_config));
  93 
  94         if (!pcm->config)
  95                 prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
  96         else
  97                 prepare_slave_config = pcm->config->prepare_slave_config;
  98 
  99         if (prepare_slave_config) {
 100                 ret = prepare_slave_config(substream, params, &slave_config);
 101                 if (ret)
 102                         return ret;
 103 
 104                 ret = dmaengine_slave_config(chan, &slave_config);
 105                 if (ret)
 106                         return ret;
 107         }
 108 
 109         return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 110 }
 111 
 112 static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream)
 113 {
 114         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 115         struct snd_soc_component *component =
 116                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 117         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 118         struct device *dma_dev = dmaengine_dma_dev(pcm, substream);
 119         struct dma_chan *chan = pcm->chan[substream->stream];
 120         struct snd_dmaengine_dai_dma_data *dma_data;
 121         struct dma_slave_caps dma_caps;
 122         struct snd_pcm_hardware hw;
 123         u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
 124                           BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
 125                           BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
 126         snd_pcm_format_t i;
 127         int ret;
 128 
 129         if (pcm->config && pcm->config->pcm_hardware)
 130                 return snd_soc_set_runtime_hwparams(substream,
 131                                 pcm->config->pcm_hardware);
 132 
 133         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 134 
 135         memset(&hw, 0, sizeof(hw));
 136         hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 137                         SNDRV_PCM_INFO_INTERLEAVED;
 138         hw.periods_min = 2;
 139         hw.periods_max = UINT_MAX;
 140         hw.period_bytes_min = 256;
 141         hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
 142         hw.buffer_bytes_max = SIZE_MAX;
 143         hw.fifo_size = dma_data->fifo_size;
 144 
 145         if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 146                 hw.info |= SNDRV_PCM_INFO_BATCH;
 147 
 148         ret = dma_get_slave_caps(chan, &dma_caps);
 149         if (ret == 0) {
 150                 if (dma_caps.cmd_pause && dma_caps.cmd_resume)
 151                         hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
 152                 if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
 153                         hw.info |= SNDRV_PCM_INFO_BATCH;
 154 
 155                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 156                         addr_widths = dma_caps.dst_addr_widths;
 157                 else
 158                         addr_widths = dma_caps.src_addr_widths;
 159         }
 160 
 161         /*
 162          * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
 163          * hw.formats set to 0, meaning no restrictions are in place.
 164          * In this case it's the responsibility of the DAI driver to
 165          * provide the supported format information.
 166          */
 167         if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
 168                 /*
 169                  * Prepare formats mask for valid/allowed sample types. If the
 170                  * dma does not have support for the given physical word size,
 171                  * it needs to be masked out so user space can not use the
 172                  * format which produces corrupted audio.
 173                  * In case the dma driver does not implement the slave_caps the
 174                  * default assumption is that it supports 1, 2 and 4 bytes
 175                  * widths.
 176                  */
 177                 for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
 178                         int bits = snd_pcm_format_physical_width(i);
 179 
 180                         /*
 181                          * Enable only samples with DMA supported physical
 182                          * widths
 183                          */
 184                         switch (bits) {
 185                         case 8:
 186                         case 16:
 187                         case 24:
 188                         case 32:
 189                         case 64:
 190                                 if (addr_widths & (1 << (bits / 8)))
 191                                         hw.formats |= pcm_format_to_bits(i);
 192                                 break;
 193                         default:
 194                                 /* Unsupported types */
 195                                 break;
 196                         }
 197                 }
 198 
 199         return snd_soc_set_runtime_hwparams(substream, &hw);
 200 }
 201 
 202 static int dmaengine_pcm_open(struct snd_pcm_substream *substream)
 203 {
 204         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 205         struct snd_soc_component *component =
 206                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 207         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 208         struct dma_chan *chan = pcm->chan[substream->stream];
 209         int ret;
 210 
 211         ret = dmaengine_pcm_set_runtime_hwparams(substream);
 212         if (ret)
 213                 return ret;
 214 
 215         return snd_dmaengine_pcm_open(substream, chan);
 216 }
 217 
 218 static struct dma_chan *dmaengine_pcm_compat_request_channel(
 219         struct snd_soc_pcm_runtime *rtd,
 220         struct snd_pcm_substream *substream)
 221 {
 222         struct snd_soc_component *component =
 223                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 224         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 225         struct snd_dmaengine_dai_dma_data *dma_data;
 226         dma_filter_fn fn = NULL;
 227 
 228         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 229 
 230         if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
 231                 return pcm->chan[0];
 232 
 233         if (pcm->config && pcm->config->compat_request_channel)
 234                 return pcm->config->compat_request_channel(rtd, substream);
 235 
 236         if (pcm->config)
 237                 fn = pcm->config->compat_filter_fn;
 238 
 239         return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
 240 }
 241 
 242 static bool dmaengine_pcm_can_report_residue(struct device *dev,
 243         struct dma_chan *chan)
 244 {
 245         struct dma_slave_caps dma_caps;
 246         int ret;
 247 
 248         ret = dma_get_slave_caps(chan, &dma_caps);
 249         if (ret != 0) {
 250                 dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
 251                          ret);
 252                 return false;
 253         }
 254 
 255         if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
 256                 return false;
 257 
 258         return true;
 259 }
 260 
 261 static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 262 {
 263         struct snd_soc_component *component =
 264                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 265         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 266         const struct snd_dmaengine_pcm_config *config = pcm->config;
 267         struct device *dev = component->dev;
 268         struct snd_pcm_substream *substream;
 269         size_t prealloc_buffer_size;
 270         size_t max_buffer_size;
 271         unsigned int i;
 272 
 273         if (config && config->prealloc_buffer_size) {
 274                 prealloc_buffer_size = config->prealloc_buffer_size;
 275                 max_buffer_size = config->pcm_hardware->buffer_bytes_max;
 276         } else {
 277                 prealloc_buffer_size = 512 * 1024;
 278                 max_buffer_size = SIZE_MAX;
 279         }
 280 
 281         for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
 282                 substream = rtd->pcm->streams[i].substream;
 283                 if (!substream)
 284                         continue;
 285 
 286                 if (!pcm->chan[i] && config && config->chan_names[i])
 287                         pcm->chan[i] = dma_request_slave_channel(dev,
 288                                 config->chan_names[i]);
 289 
 290                 if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
 291                         pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
 292                                 substream);
 293                 }
 294 
 295                 if (!pcm->chan[i]) {
 296                         dev_err(component->dev,
 297                                 "Missing dma channel for stream: %d\n", i);
 298                         return -EINVAL;
 299                 }
 300 
 301                 snd_pcm_lib_preallocate_pages(substream,
 302                                 SNDRV_DMA_TYPE_DEV_IRAM,
 303                                 dmaengine_dma_dev(pcm, substream),
 304                                 prealloc_buffer_size,
 305                                 max_buffer_size);
 306 
 307                 if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
 308                         pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 309 
 310                 if (rtd->pcm->streams[i].pcm->name[0] == '\0') {
 311                         strscpy_pad(rtd->pcm->streams[i].pcm->name,
 312                                     rtd->pcm->streams[i].pcm->id,
 313                                     sizeof(rtd->pcm->streams[i].pcm->name));
 314                 }
 315         }
 316 
 317         return 0;
 318 }
 319 
 320 static snd_pcm_uframes_t dmaengine_pcm_pointer(
 321         struct snd_pcm_substream *substream)
 322 {
 323         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 324         struct snd_soc_component *component =
 325                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 326         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 327 
 328         if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 329                 return snd_dmaengine_pcm_pointer_no_residue(substream);
 330         else
 331                 return snd_dmaengine_pcm_pointer(substream);
 332 }
 333 
 334 static int dmaengine_copy_user(struct snd_pcm_substream *substream,
 335                                int channel, unsigned long hwoff,
 336                                void __user *buf, unsigned long bytes)
 337 {
 338         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 339         struct snd_soc_component *component =
 340                 snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
 341         struct snd_pcm_runtime *runtime = substream->runtime;
 342         struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 343         int (*process)(struct snd_pcm_substream *substream,
 344                        int channel, unsigned long hwoff,
 345                        void *buf, unsigned long bytes) = pcm->config->process;
 346         bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 347         void *dma_ptr = runtime->dma_area + hwoff +
 348                         channel * (runtime->dma_bytes / runtime->channels);
 349         int ret;
 350 
 351         if (is_playback)
 352                 if (copy_from_user(dma_ptr, buf, bytes))
 353                         return -EFAULT;
 354 
 355         if (process) {
 356                 ret = process(substream, channel, hwoff, (__force void *)buf, bytes);
 357                 if (ret < 0)
 358                         return ret;
 359         }
 360 
 361         if (!is_playback)
 362                 if (copy_to_user(buf, dma_ptr, bytes))
 363                         return -EFAULT;
 364 
 365         return 0;
 366 }
 367 
 368 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 369         .open           = dmaengine_pcm_open,
 370         .close          = snd_dmaengine_pcm_close,
 371         .ioctl          = snd_pcm_lib_ioctl,
 372         .hw_params      = dmaengine_pcm_hw_params,
 373         .hw_free        = snd_pcm_lib_free_pages,
 374         .trigger        = snd_dmaengine_pcm_trigger,
 375         .pointer        = dmaengine_pcm_pointer,
 376 };
 377 
 378 static const struct snd_pcm_ops dmaengine_pcm_process_ops = {
 379         .open           = dmaengine_pcm_open,
 380         .close          = snd_dmaengine_pcm_close,
 381         .ioctl          = snd_pcm_lib_ioctl,
 382         .hw_params      = dmaengine_pcm_hw_params,
 383         .hw_free        = snd_pcm_lib_free_pages,
 384         .trigger        = snd_dmaengine_pcm_trigger,
 385         .pointer        = dmaengine_pcm_pointer,
 386         .copy_user      = dmaengine_copy_user,
 387 };
 388 
 389 static const struct snd_soc_component_driver dmaengine_pcm_component = {
 390         .name           = SND_DMAENGINE_PCM_DRV_NAME,
 391         .probe_order    = SND_SOC_COMP_ORDER_LATE,
 392         .ops            = &dmaengine_pcm_ops,
 393         .pcm_new        = dmaengine_pcm_new,
 394 };
 395 
 396 static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
 397         .name           = SND_DMAENGINE_PCM_DRV_NAME,
 398         .probe_order    = SND_SOC_COMP_ORDER_LATE,
 399         .ops            = &dmaengine_pcm_process_ops,
 400         .pcm_new        = dmaengine_pcm_new,
 401 };
 402 
 403 static const char * const dmaengine_pcm_dma_channel_names[] = {
 404         [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 405         [SNDRV_PCM_STREAM_CAPTURE] = "rx",
 406 };
 407 
 408 static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
 409         struct device *dev, const struct snd_dmaengine_pcm_config *config)
 410 {
 411         unsigned int i;
 412         const char *name;
 413         struct dma_chan *chan;
 414 
 415         if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node &&
 416             !(config && config->dma_dev && config->dma_dev->of_node)))
 417                 return 0;
 418 
 419         if (config && config->dma_dev) {
 420                 /*
 421                  * If this warning is seen, it probably means that your Linux
 422                  * device structure does not match your HW device structure.
 423                  * It would be best to refactor the Linux device structure to
 424                  * correctly match the HW structure.
 425                  */
 426                 dev_warn(dev, "DMA channels sourced from device %s",
 427                          dev_name(config->dma_dev));
 428                 dev = config->dma_dev;
 429         }
 430 
 431         for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 432              i++) {
 433                 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 434                         name = "rx-tx";
 435                 else
 436                         name = dmaengine_pcm_dma_channel_names[i];
 437                 if (config && config->chan_names[i])
 438                         name = config->chan_names[i];
 439                 chan = dma_request_slave_channel_reason(dev, name);
 440                 if (IS_ERR(chan)) {
 441                         if (PTR_ERR(chan) == -EPROBE_DEFER)
 442                                 return -EPROBE_DEFER;
 443                         pcm->chan[i] = NULL;
 444                 } else {
 445                         pcm->chan[i] = chan;
 446                 }
 447                 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 448                         break;
 449         }
 450 
 451         if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 452                 pcm->chan[1] = pcm->chan[0];
 453 
 454         return 0;
 455 }
 456 
 457 static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
 458 {
 459         unsigned int i;
 460 
 461         for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 462              i++) {
 463                 if (!pcm->chan[i])
 464                         continue;
 465                 dma_release_channel(pcm->chan[i]);
 466                 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 467                         break;
 468         }
 469 }
 470 
 471 /**
 472  * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
 473  * @dev: The parent device for the PCM device
 474  * @config: Platform specific PCM configuration
 475  * @flags: Platform specific quirks
 476  */
 477 int snd_dmaengine_pcm_register(struct device *dev,
 478         const struct snd_dmaengine_pcm_config *config, unsigned int flags)
 479 {
 480         struct dmaengine_pcm *pcm;
 481         int ret;
 482 
 483         pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 484         if (!pcm)
 485                 return -ENOMEM;
 486 
 487 #ifdef CONFIG_DEBUG_FS
 488         pcm->component.debugfs_prefix = "dma";
 489 #endif
 490         pcm->config = config;
 491         pcm->flags = flags;
 492 
 493         ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
 494         if (ret)
 495                 goto err_free_dma;
 496 
 497         if (config && config->process)
 498                 ret = snd_soc_add_component(dev, &pcm->component,
 499                                             &dmaengine_pcm_component_process,
 500                                             NULL, 0);
 501         else
 502                 ret = snd_soc_add_component(dev, &pcm->component,
 503                                             &dmaengine_pcm_component, NULL, 0);
 504         if (ret)
 505                 goto err_free_dma;
 506 
 507         return 0;
 508 
 509 err_free_dma:
 510         dmaengine_pcm_release_chan(pcm);
 511         kfree(pcm);
 512         return ret;
 513 }
 514 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
 515 
 516 /**
 517  * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
 518  * @dev: Parent device the PCM was register with
 519  *
 520  * Removes a dmaengine based PCM device previously registered with
 521  * snd_dmaengine_pcm_register.
 522  */
 523 void snd_dmaengine_pcm_unregister(struct device *dev)
 524 {
 525         struct snd_soc_component *component;
 526         struct dmaengine_pcm *pcm;
 527 
 528         component = snd_soc_lookup_component(dev, SND_DMAENGINE_PCM_DRV_NAME);
 529         if (!component)
 530                 return;
 531 
 532         pcm = soc_component_to_pcm(component);
 533 
 534         snd_soc_unregister_component(dev);
 535         dmaengine_pcm_release_chan(pcm);
 536         kfree(pcm);
 537 }
 538 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
 539 
 540 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */