root/drivers/staging/wusbcore/wa-rpipe.c

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

DEFINITIONS

This source file includes following definitions.
  1. __rpipe_get_descr
  2. __rpipe_set_descr
  3. rpipe_init
  4. rpipe_get_idx
  5. rpipe_put_idx
  6. rpipe_destroy
  7. rpipe_get_idle
  8. __rpipe_reset
  9. rpipe_epc_find
  10. rpipe_aim
  11. rpipe_check_aim
  12. rpipe_get_by_ep
  13. wa_rpipes_create
  14. wa_rpipes_destroy
  15. rpipe_ep_disable
  16. rpipe_clear_feature_stalled

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * WUSB Wire Adapter
   4  * rpipe management
   5  *
   6  * Copyright (C) 2005-2006 Intel Corporation
   7  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
   8  *
   9  * FIXME: docs
  10  *
  11  * RPIPE
  12  *
  13  *   Targeted at different downstream endpoints
  14  *
  15  *   Descriptor: use to config the remote pipe.
  16  *
  17  *   The number of blocks could be dynamic (wBlocks in descriptor is
  18  *   0)--need to schedule them then.
  19  *
  20  * Each bit in wa->rpipe_bm represents if an rpipe is being used or
  21  * not. Rpipes are represented with a 'struct wa_rpipe' that is
  22  * attached to the hcpriv member of a 'struct usb_host_endpoint'.
  23  *
  24  * When you need to xfer data to an endpoint, you get an rpipe for it
  25  * with wa_ep_rpipe_get(), which gives you a reference to the rpipe
  26  * and keeps a single one (the first one) with the endpoint. When you
  27  * are done transferring, you drop that reference. At the end the
  28  * rpipe is always allocated and bound to the endpoint. There it might
  29  * be recycled when not used.
  30  *
  31  * Addresses:
  32  *
  33  *  We use a 1:1 mapping mechanism between port address (0 based
  34  *  index, actually) and the address. The USB stack knows about this.
  35  *
  36  *  USB Stack port number    4 (1 based)
  37  *  WUSB code port index     3 (0 based)
  38  *  USB Address             5 (2 based -- 0 is for default, 1 for root hub)
  39  *
  40  *  Now, because we don't use the concept as default address exactly
  41  *  like the (wired) USB code does, we need to kind of skip it. So we
  42  *  never take addresses from the urb->pipe, but from the
  43  *  urb->dev->devnum, to make sure that we always have the right
  44  *  destination address.
  45  */
  46 #include <linux/atomic.h>
  47 #include <linux/bitmap.h>
  48 #include <linux/slab.h>
  49 #include <linux/export.h>
  50 
  51 #include "wusbhc.h"
  52 #include "wa-hc.h"
  53 
  54 static int __rpipe_get_descr(struct wahc *wa,
  55                              struct usb_rpipe_descriptor *descr, u16 index)
  56 {
  57         ssize_t result;
  58         struct device *dev = &wa->usb_iface->dev;
  59 
  60         /* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor()
  61          * function because the arguments are different.
  62          */
  63         result = usb_control_msg(
  64                 wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
  65                 USB_REQ_GET_DESCRIPTOR,
  66                 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE,
  67                 USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
  68                 USB_CTRL_GET_TIMEOUT);
  69         if (result < 0) {
  70                 dev_err(dev, "rpipe %u: get descriptor failed: %d\n",
  71                         index, (int)result);
  72                 goto error;
  73         }
  74         if (result < sizeof(*descr)) {
  75                 dev_err(dev, "rpipe %u: got short descriptor "
  76                         "(%zd vs %zd bytes needed)\n",
  77                         index, result, sizeof(*descr));
  78                 result = -EINVAL;
  79                 goto error;
  80         }
  81         result = 0;
  82 
  83 error:
  84         return result;
  85 }
  86 
  87 /*
  88  *
  89  * The descriptor is assumed to be properly initialized (ie: you got
  90  * it through __rpipe_get_descr()).
  91  */
  92 static int __rpipe_set_descr(struct wahc *wa,
  93                              struct usb_rpipe_descriptor *descr, u16 index)
  94 {
  95         ssize_t result;
  96         struct device *dev = &wa->usb_iface->dev;
  97 
  98         /* we cannot use the usb_get_descriptor() function because the
  99          * arguments are different.
 100          */
 101         result = usb_control_msg(
 102                 wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
 103                 USB_REQ_SET_DESCRIPTOR,
 104                 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
 105                 USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
 106                 USB_CTRL_SET_TIMEOUT);
 107         if (result < 0) {
 108                 dev_err(dev, "rpipe %u: set descriptor failed: %d\n",
 109                         index, (int)result);
 110                 goto error;
 111         }
 112         if (result < sizeof(*descr)) {
 113                 dev_err(dev, "rpipe %u: sent short descriptor "
 114                         "(%zd vs %zd bytes required)\n",
 115                         index, result, sizeof(*descr));
 116                 result = -EINVAL;
 117                 goto error;
 118         }
 119         result = 0;
 120 
 121 error:
 122         return result;
 123 
 124 }
 125 
 126 static void rpipe_init(struct wa_rpipe *rpipe)
 127 {
 128         kref_init(&rpipe->refcnt);
 129         spin_lock_init(&rpipe->seg_lock);
 130         INIT_LIST_HEAD(&rpipe->seg_list);
 131         INIT_LIST_HEAD(&rpipe->list_node);
 132 }
 133 
 134 static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
 135 {
 136         unsigned long flags;
 137 
 138         spin_lock_irqsave(&wa->rpipe_lock, flags);
 139         rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
 140         if (rpipe_idx < wa->rpipes)
 141                 set_bit(rpipe_idx, wa->rpipe_bm);
 142         spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 143 
 144         return rpipe_idx;
 145 }
 146 
 147 static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
 148 {
 149         unsigned long flags;
 150 
 151         spin_lock_irqsave(&wa->rpipe_lock, flags);
 152         clear_bit(rpipe_idx, wa->rpipe_bm);
 153         spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 154 }
 155 
 156 void rpipe_destroy(struct kref *_rpipe)
 157 {
 158         struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt);
 159         u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
 160 
 161         if (rpipe->ep)
 162                 rpipe->ep->hcpriv = NULL;
 163         rpipe_put_idx(rpipe->wa, index);
 164         wa_put(rpipe->wa);
 165         kfree(rpipe);
 166 }
 167 EXPORT_SYMBOL_GPL(rpipe_destroy);
 168 
 169 /*
 170  * Locate an idle rpipe, create an structure for it and return it
 171  *
 172  * @wa    is referenced and unlocked
 173  * @crs   enum rpipe_attr, required endpoint characteristics
 174  *
 175  * The rpipe can be used only sequentially (not in parallel).
 176  *
 177  * The rpipe is moved into the "ready" state.
 178  */
 179 static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs,
 180                           gfp_t gfp)
 181 {
 182         int result;
 183         unsigned rpipe_idx;
 184         struct wa_rpipe *rpipe;
 185         struct device *dev = &wa->usb_iface->dev;
 186 
 187         rpipe = kzalloc(sizeof(*rpipe), gfp);
 188         if (rpipe == NULL)
 189                 return -ENOMEM;
 190         rpipe_init(rpipe);
 191 
 192         /* Look for an idle pipe */
 193         for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) {
 194                 rpipe_idx = rpipe_get_idx(wa, rpipe_idx);
 195                 if (rpipe_idx >= wa->rpipes)    /* no more pipes :( */
 196                         break;
 197                 result =  __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx);
 198                 if (result < 0)
 199                         dev_err(dev, "Can't get descriptor for rpipe %u: %d\n",
 200                                 rpipe_idx, result);
 201                 else if ((rpipe->descr.bmCharacteristics & crs) != 0)
 202                         goto found;
 203                 rpipe_put_idx(wa, rpipe_idx);
 204         }
 205         *prpipe = NULL;
 206         kfree(rpipe);
 207         return -ENXIO;
 208 
 209 found:
 210         set_bit(rpipe_idx, wa->rpipe_bm);
 211         rpipe->wa = wa_get(wa);
 212         *prpipe = rpipe;
 213         return 0;
 214 }
 215 
 216 static int __rpipe_reset(struct wahc *wa, unsigned index)
 217 {
 218         int result;
 219         struct device *dev = &wa->usb_iface->dev;
 220 
 221         result = usb_control_msg(
 222                 wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
 223                 USB_REQ_RPIPE_RESET,
 224                 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
 225                 0, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
 226         if (result < 0)
 227                 dev_err(dev, "rpipe %u: reset failed: %d\n",
 228                         index, result);
 229         return result;
 230 }
 231 
 232 /*
 233  * Fake companion descriptor for ep0
 234  *
 235  * See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl
 236  */
 237 static struct usb_wireless_ep_comp_descriptor epc0 = {
 238         .bLength = sizeof(epc0),
 239         .bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP,
 240         .bMaxBurst = 1,
 241         .bMaxSequence = 2,
 242 };
 243 
 244 /*
 245  * Look for EP companion descriptor
 246  *
 247  * Get there, look for Inara in the endpoint's extra descriptors
 248  */
 249 static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find(
 250                 struct device *dev, struct usb_host_endpoint *ep)
 251 {
 252         void *itr;
 253         size_t itr_size;
 254         struct usb_descriptor_header *hdr;
 255         struct usb_wireless_ep_comp_descriptor *epcd;
 256 
 257         if (ep->desc.bEndpointAddress == 0) {
 258                 epcd = &epc0;
 259                 goto out;
 260         }
 261         itr = ep->extra;
 262         itr_size = ep->extralen;
 263         epcd = NULL;
 264         while (itr_size > 0) {
 265                 if (itr_size < sizeof(*hdr)) {
 266                         dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors "
 267                                 "at offset %zu: only %zu bytes left\n",
 268                                 ep->desc.bEndpointAddress,
 269                                 itr - (void *) ep->extra, itr_size);
 270                         break;
 271                 }
 272                 hdr = itr;
 273                 if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) {
 274                         epcd = itr;
 275                         break;
 276                 }
 277                 if (hdr->bLength > itr_size) {
 278                         dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor "
 279                                 "at offset %zu (type 0x%02x) "
 280                                 "length %d but only %zu bytes left\n",
 281                                 ep->desc.bEndpointAddress,
 282                                 itr - (void *) ep->extra, hdr->bDescriptorType,
 283                                 hdr->bLength, itr_size);
 284                         break;
 285                 }
 286                 itr += hdr->bLength;
 287                 itr_size -= hdr->bLength;
 288         }
 289 out:
 290         return epcd;
 291 }
 292 
 293 /*
 294  * Aim an rpipe to its device & endpoint destination
 295  *
 296  * Make sure we change the address to unauthenticated if the device
 297  * is WUSB and it is not authenticated.
 298  */
 299 static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
 300                      struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp)
 301 {
 302         int result = -ENOMSG;   /* better code for lack of companion? */
 303         struct device *dev = &wa->usb_iface->dev;
 304         struct usb_device *usb_dev = urb->dev;
 305         struct usb_wireless_ep_comp_descriptor *epcd;
 306         u32 ack_window, epcd_max_sequence;
 307         u8 unauth;
 308 
 309         epcd = rpipe_epc_find(dev, ep);
 310         if (epcd == NULL) {
 311                 dev_err(dev, "ep 0x%02x: can't find companion descriptor\n",
 312                         ep->desc.bEndpointAddress);
 313                 goto error;
 314         }
 315         unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0;
 316         __rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex));
 317         atomic_set(&rpipe->segs_available,
 318                 le16_to_cpu(rpipe->descr.wRequests));
 319         /* FIXME: block allocation system; request with queuing and timeout */
 320         /* FIXME: compute so seg_size > ep->maxpktsize */
 321         rpipe->descr.wBlocks = cpu_to_le16(16);         /* given */
 322         /* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */
 323         if (usb_endpoint_xfer_isoc(&ep->desc))
 324                 rpipe->descr.wMaxPacketSize = epcd->wOverTheAirPacketSize;
 325         else
 326                 rpipe->descr.wMaxPacketSize = ep->desc.wMaxPacketSize;
 327 
 328         rpipe->descr.hwa_bMaxBurst = max(min_t(unsigned int,
 329                                 epcd->bMaxBurst, 16U), 1U);
 330         rpipe->descr.hwa_bDeviceInfoIndex =
 331                         wusb_port_no_to_idx(urb->dev->portnum);
 332         /* FIXME: use maximum speed as supported or recommended by device */
 333         rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0 ?
 334                 UWB_PHY_RATE_53 : UWB_PHY_RATE_200;
 335 
 336         dev_dbg(dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n",
 337                 urb->dev->devnum, urb->dev->devnum | unauth,
 338                 le16_to_cpu(rpipe->descr.wRPipeIndex),
 339                 usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed);
 340 
 341         rpipe->descr.hwa_reserved = 0;
 342 
 343         rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress;
 344         /* FIXME: bDataSequence */
 345         rpipe->descr.bDataSequence = 0;
 346 
 347         /* start with base window of hwa_bMaxBurst bits starting at 0. */
 348         ack_window = 0xFFFFFFFF >> (32 - rpipe->descr.hwa_bMaxBurst);
 349         rpipe->descr.dwCurrentWindow = cpu_to_le32(ack_window);
 350         epcd_max_sequence = max(min_t(unsigned int,
 351                         epcd->bMaxSequence, 32U), 2U);
 352         rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1;
 353         rpipe->descr.bInterval = ep->desc.bInterval;
 354         if (usb_endpoint_xfer_isoc(&ep->desc))
 355                 rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval;
 356         else
 357                 rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
 358         /* FIXME: xmit power & preamble blah blah */
 359         rpipe->descr.bmAttribute = (ep->desc.bmAttributes &
 360                                         USB_ENDPOINT_XFERTYPE_MASK);
 361         /* rpipe->descr.bmCharacteristics RO */
 362         rpipe->descr.bmRetryOptions = (wa->wusb->retry_count & 0xF);
 363         /* FIXME: use for assessing link quality? */
 364         rpipe->descr.wNumTransactionErrors = 0;
 365         result = __rpipe_set_descr(wa, &rpipe->descr,
 366                                    le16_to_cpu(rpipe->descr.wRPipeIndex));
 367         if (result < 0) {
 368                 dev_err(dev, "Cannot aim rpipe: %d\n", result);
 369                 goto error;
 370         }
 371         result = 0;
 372 error:
 373         return result;
 374 }
 375 
 376 /*
 377  * Check an aimed rpipe to make sure it points to where we want
 378  *
 379  * We use bit 19 of the Linux USB pipe bitmap for unauth vs auth
 380  * space; when it is like that, we or 0x80 to make an unauth address.
 381  */
 382 static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa,
 383                            const struct usb_host_endpoint *ep,
 384                            const struct urb *urb, gfp_t gfp)
 385 {
 386         int result = 0;
 387         struct device *dev = &wa->usb_iface->dev;
 388         u8 portnum = wusb_port_no_to_idx(urb->dev->portnum);
 389 
 390 #define AIM_CHECK(rdf, val, text)                                       \
 391         do {                                                            \
 392                 if (rpipe->descr.rdf != (val)) {                        \
 393                         dev_err(dev,                                    \
 394                                 "rpipe aim discrepancy: " #rdf " " text "\n", \
 395                                 rpipe->descr.rdf, (val));               \
 396                         result = -EINVAL;                               \
 397                         WARN_ON(1);                                     \
 398                 }                                                       \
 399         } while (0)
 400         AIM_CHECK(hwa_bDeviceInfoIndex, portnum, "(%u vs %u)");
 401         AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ?
 402                         UWB_PHY_RATE_53 : UWB_PHY_RATE_200,
 403                   "(%u vs %u)");
 404         AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)");
 405         AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)");
 406         AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)");
 407 #undef AIM_CHECK
 408         return result;
 409 }
 410 
 411 #ifndef CONFIG_BUG
 412 #define CONFIG_BUG 0
 413 #endif
 414 
 415 /*
 416  * Make sure there is an rpipe allocated for an endpoint
 417  *
 418  * If already allocated, we just refcount it; if not, we get an
 419  * idle one, aim it to the right location and take it.
 420  *
 421  * Attaches to ep->hcpriv and rpipe->ep to ep.
 422  */
 423 int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep,
 424                     struct urb *urb, gfp_t gfp)
 425 {
 426         int result = 0;
 427         struct device *dev = &wa->usb_iface->dev;
 428         struct wa_rpipe *rpipe;
 429         u8 eptype;
 430 
 431         mutex_lock(&wa->rpipe_mutex);
 432         rpipe = ep->hcpriv;
 433         if (rpipe != NULL) {
 434                 if (CONFIG_BUG == 1) {
 435                         result = rpipe_check_aim(rpipe, wa, ep, urb, gfp);
 436                         if (result < 0)
 437                                 goto error;
 438                 }
 439                 __rpipe_get(rpipe);
 440                 dev_dbg(dev, "ep 0x%02x: reusing rpipe %u\n",
 441                         ep->desc.bEndpointAddress,
 442                         le16_to_cpu(rpipe->descr.wRPipeIndex));
 443         } else {
 444                 /* hmm, assign idle rpipe, aim it */
 445                 result = -ENOBUFS;
 446                 eptype = ep->desc.bmAttributes & 0x03;
 447                 result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp);
 448                 if (result < 0)
 449                         goto error;
 450                 result = rpipe_aim(rpipe, wa, ep, urb, gfp);
 451                 if (result < 0) {
 452                         rpipe_put(rpipe);
 453                         goto error;
 454                 }
 455                 ep->hcpriv = rpipe;
 456                 rpipe->ep = ep;
 457                 __rpipe_get(rpipe);     /* for caching into ep->hcpriv */
 458                 dev_dbg(dev, "ep 0x%02x: using rpipe %u\n",
 459                         ep->desc.bEndpointAddress,
 460                         le16_to_cpu(rpipe->descr.wRPipeIndex));
 461         }
 462 error:
 463         mutex_unlock(&wa->rpipe_mutex);
 464         return result;
 465 }
 466 
 467 /*
 468  * Allocate the bitmap for each rpipe.
 469  */
 470 int wa_rpipes_create(struct wahc *wa)
 471 {
 472         wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
 473         wa->rpipe_bm = bitmap_zalloc(wa->rpipes, GFP_KERNEL);
 474         if (wa->rpipe_bm == NULL)
 475                 return -ENOMEM;
 476         return 0;
 477 }
 478 
 479 void wa_rpipes_destroy(struct wahc *wa)
 480 {
 481         struct device *dev = &wa->usb_iface->dev;
 482 
 483         if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) {
 484                 WARN_ON(1);
 485                 dev_err(dev, "BUG: pipes not released on exit: %*pb\n",
 486                         wa->rpipes, wa->rpipe_bm);
 487         }
 488         bitmap_free(wa->rpipe_bm);
 489 }
 490 
 491 /*
 492  * Release resources allocated for an endpoint
 493  *
 494  * If there is an associated rpipe to this endpoint, Abort any pending
 495  * transfers and put it. If the rpipe ends up being destroyed,
 496  * __rpipe_destroy() will cleanup ep->hcpriv.
 497  *
 498  * This is called before calling hcd->stop(), so you don't need to do
 499  * anything else in there.
 500  */
 501 void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep)
 502 {
 503         struct wa_rpipe *rpipe;
 504 
 505         mutex_lock(&wa->rpipe_mutex);
 506         rpipe = ep->hcpriv;
 507         if (rpipe != NULL) {
 508                 u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
 509 
 510                 usb_control_msg(
 511                         wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
 512                         USB_REQ_RPIPE_ABORT,
 513                         USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
 514                         0, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
 515                 rpipe_put(rpipe);
 516         }
 517         mutex_unlock(&wa->rpipe_mutex);
 518 }
 519 EXPORT_SYMBOL_GPL(rpipe_ep_disable);
 520 
 521 /* Clear the stalled status of an RPIPE. */
 522 void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep)
 523 {
 524         struct wa_rpipe *rpipe;
 525 
 526         mutex_lock(&wa->rpipe_mutex);
 527         rpipe = ep->hcpriv;
 528         if (rpipe != NULL) {
 529                 u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
 530 
 531                 usb_control_msg(
 532                         wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
 533                         USB_REQ_CLEAR_FEATURE,
 534                         USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
 535                         RPIPE_STALL, index, NULL, 0, USB_CTRL_SET_TIMEOUT);
 536         }
 537         mutex_unlock(&wa->rpipe_mutex);
 538 }
 539 EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled);

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