root/dev/usb/uhid.c

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

DEFINITIONS

This source file includes following definitions.
  1. uhid_match
  2. uhid_attach
  3. uhid_activate
  4. uhid_detach
  5. uhid_intr
  6. uhidopen
  7. uhidclose
  8. uhid_do_read
  9. uhidread
  10. uhid_do_write
  11. uhidwrite
  12. uhid_do_ioctl
  13. uhidioctl
  14. uhidpoll
  15. filt_uhidrdetach
  16. filt_uhidread
  17. uhidkqfilter

    1 /*      $OpenBSD: uhid.c,v 1.41 2007/06/14 10:11:15 mbalmer Exp $ */
    2 /*      $NetBSD: uhid.c,v 1.57 2003/03/11 16:44:00 augustss Exp $       */
    3 
    4 /*
    5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by Lennart Augustsson (lennart@augustsson.net) at
   10  * Carlstedt Research & Technology.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. All advertising materials mentioning features or use of this software
   21  *    must display the following acknowledgement:
   22  *        This product includes software developed by the NetBSD
   23  *        Foundation, Inc. and its contributors.
   24  * 4. Neither the name of The NetBSD Foundation nor the names of its
   25  *    contributors may be used to endorse or promote products derived
   26  *    from this software without specific prior written permission.
   27  *
   28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   38  * POSSIBILITY OF SUCH DAMAGE.
   39  */
   40 
   41 /*
   42  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
   43  */
   44 
   45 #include <sys/param.h>
   46 #include <sys/systm.h>
   47 #include <sys/kernel.h>
   48 #include <sys/malloc.h>
   49 #include <sys/signalvar.h>
   50 #include <sys/device.h>
   51 #include <sys/ioctl.h>
   52 #include <sys/conf.h>
   53 #include <sys/tty.h>
   54 #include <sys/file.h>
   55 #include <sys/selinfo.h>
   56 #include <sys/proc.h>
   57 #include <sys/vnode.h>
   58 #include <sys/poll.h>
   59 
   60 #include <dev/usb/usb.h>
   61 #include <dev/usb/usbhid.h>
   62 
   63 #include <dev/usb/usbdevs.h>
   64 #include <dev/usb/usbdi.h>
   65 #include <dev/usb/usbdi_util.h>
   66 #include <dev/usb/hid.h>
   67 #include <dev/usb/usb_quirks.h>
   68 
   69 #include <dev/usb/uhidev.h>
   70 
   71 #ifdef UHID_DEBUG
   72 #define DPRINTF(x)      do { if (uhiddebug) printf x; } while (0)
   73 #define DPRINTFN(n,x)   do { if (uhiddebug>(n)) printf x; } while (0)
   74 int     uhiddebug = 0;
   75 #else
   76 #define DPRINTF(x)
   77 #define DPRINTFN(n,x)
   78 #endif
   79 
   80 struct uhid_softc {
   81         struct uhidev sc_hdev;
   82 
   83         int sc_isize;
   84         int sc_osize;
   85         int sc_fsize;
   86 
   87         u_char *sc_obuf;
   88 
   89         struct clist sc_q;
   90         struct selinfo sc_rsel;
   91         struct proc *sc_async;  /* process that wants SIGIO */
   92         u_char sc_state;        /* driver state */
   93 #define UHID_ASLP       0x01    /* waiting for device data */
   94 #define UHID_IMMED      0x02    /* return read data immediately */
   95 
   96         int sc_refcnt;
   97         u_char sc_dying;
   98 };
   99 
  100 #define UHIDUNIT(dev)   (minor(dev))
  101 #define UHID_CHUNK      128     /* chunk size for read */
  102 #define UHID_BSIZE      1020    /* buffer size */
  103 
  104 void uhid_intr(struct uhidev *, void *, u_int len);
  105 
  106 int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
  107 int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
  108 int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int,
  109                          struct proc *);
  110 
  111 int uhid_match(struct device *, void *, void *); 
  112 void uhid_attach(struct device *, struct device *, void *); 
  113 int uhid_detach(struct device *, int); 
  114 int uhid_activate(struct device *, enum devact); 
  115 
  116 struct cfdriver uhid_cd = { 
  117         NULL, "uhid", DV_DULL 
  118 }; 
  119 
  120 const struct cfattach uhid_ca = { 
  121         sizeof(struct uhid_softc), 
  122         uhid_match, 
  123         uhid_attach, 
  124         uhid_detach, 
  125         uhid_activate, 
  126 };
  127 
  128 int
  129 uhid_match(struct device *parent, void *match, void *aux)
  130 {
  131         struct usb_attach_arg *uaa = aux;
  132         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
  133 
  134         DPRINTF(("uhid_match: report=%d\n", uha->reportid));
  135 
  136         if (uha->matchlvl)
  137                 return (uha->matchlvl);
  138         return (UMATCH_IFACECLASS_GENERIC);
  139 }
  140 
  141 void
  142 uhid_attach(struct device *parent, struct device *self, void *aux)
  143 {
  144         struct uhid_softc *sc = (struct uhid_softc *)self;
  145         struct usb_attach_arg *uaa = aux;
  146         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
  147         int size, repid;
  148         void *desc;
  149 
  150         sc->sc_hdev.sc_intr = uhid_intr;
  151         sc->sc_hdev.sc_parent = uha->parent;
  152         sc->sc_hdev.sc_report_id = uha->reportid;
  153 
  154         uhidev_get_report_desc(uha->parent, &desc, &size);
  155         repid = uha->reportid;
  156         sc->sc_isize = hid_report_size(desc, size, hid_input,   repid);
  157         sc->sc_osize = hid_report_size(desc, size, hid_output,  repid);
  158         sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid);
  159 
  160         printf(": input=%d, output=%d, feature=%d\n",
  161                sc->sc_isize, sc->sc_osize, sc->sc_fsize);
  162 }
  163 
  164 int
  165 uhid_activate(struct device *self, enum devact act)
  166 {
  167         struct uhid_softc *sc = (struct uhid_softc *)self;
  168 
  169         switch (act) {
  170         case DVACT_ACTIVATE:
  171                 break;
  172 
  173         case DVACT_DEACTIVATE:
  174                 sc->sc_dying = 1;
  175                 break;
  176         }
  177         return (0);
  178 }
  179 
  180 int
  181 uhid_detach(struct device *self, int flags)
  182 {
  183         struct uhid_softc *sc = (struct uhid_softc *)self;
  184         int s;
  185         int maj, mn;
  186 
  187         DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
  188 
  189         sc->sc_dying = 1;
  190 
  191         if (sc->sc_hdev.sc_state & UHIDEV_OPEN) {
  192                 s = splusb();
  193                 if (--sc->sc_refcnt >= 0) {
  194                         /* Wake everyone */
  195                         wakeup(&sc->sc_q);
  196                         /* Wait for processes to go away. */
  197                         usb_detach_wait(&sc->sc_hdev.sc_dev);
  198                 }
  199                 splx(s);
  200         }
  201 
  202         /* locate the major number */
  203         for (maj = 0; maj < nchrdev; maj++)
  204                 if (cdevsw[maj].d_open == uhidopen)
  205                         break;
  206 
  207         /* Nuke the vnodes for any open instances (calls close). */
  208         mn = self->dv_unit;
  209         vdevgone(maj, mn, mn, VCHR);
  210 
  211 #if 0
  212         usbd_add_drv_event(USB_EVENT_DRIVER_DETACH,
  213                            sc->sc_hdev.sc_parent->sc_udev,
  214                            &sc->sc_hdev.sc_dev);
  215 #endif
  216 
  217         return (0);
  218 }
  219 
  220 void
  221 uhid_intr(struct uhidev *addr, void *data, u_int len)
  222 {
  223         struct uhid_softc *sc = (struct uhid_softc *)addr;
  224 
  225 #ifdef UHID_DEBUG
  226         if (uhiddebug > 5) {
  227                 u_int32_t i;
  228 
  229                 DPRINTF(("uhid_intr: data ="));
  230                 for (i = 0; i < len; i++)
  231                         DPRINTF((" %02x", ((u_char *)data)[i]));
  232                 DPRINTF(("\n"));
  233         }
  234 #endif
  235 
  236         (void)b_to_q(data, len, &sc->sc_q);
  237 
  238         if (sc->sc_state & UHID_ASLP) {
  239                 sc->sc_state &= ~UHID_ASLP;
  240                 DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
  241                 wakeup(&sc->sc_q);
  242         }
  243         selwakeup(&sc->sc_rsel);
  244         if (sc->sc_async != NULL) {
  245                 DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async));
  246                 psignal(sc->sc_async, SIGIO);
  247         }
  248 }
  249 
  250 int
  251 uhidopen(dev_t dev, int flag, int mode, struct proc *p)
  252 {
  253         struct uhid_softc *sc;
  254         int error;
  255 
  256         if (UHIDUNIT(dev) >= uhid_cd.cd_ndevs)
  257                 return (ENXIO);
  258         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  259         if (sc == NULL)
  260                 return (ENXIO);
  261 
  262         DPRINTF(("uhidopen: sc=%p\n", sc));
  263 
  264         if (sc->sc_dying)
  265                 return (ENXIO);
  266 
  267         error = uhidev_open(&sc->sc_hdev);
  268         if (error)
  269                 return (error);
  270 
  271         if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
  272                 uhidev_close(&sc->sc_hdev);
  273                 return (ENOMEM);
  274         }
  275         sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK);
  276         sc->sc_state &= ~UHID_IMMED;
  277         sc->sc_async = NULL;
  278 
  279         return (0);
  280 }
  281 
  282 int
  283 uhidclose(dev_t dev, int flag, int mode, struct proc *p)
  284 {
  285         struct uhid_softc *sc;
  286 
  287         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  288 
  289         DPRINTF(("uhidclose: sc=%p\n", sc));
  290 
  291         clfree(&sc->sc_q);
  292         free(sc->sc_obuf, M_USBDEV);
  293         sc->sc_async = NULL;
  294         uhidev_close(&sc->sc_hdev);
  295 
  296         return (0);
  297 }
  298 
  299 int
  300 uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
  301 {
  302         int s;
  303         int error = 0;
  304         int extra;
  305         size_t length;
  306         u_char buffer[UHID_CHUNK];
  307         usbd_status err;
  308 
  309         DPRINTFN(1, ("uhidread\n"));
  310         if (sc->sc_state & UHID_IMMED) {
  311                 DPRINTFN(1, ("uhidread immed\n"));
  312                 extra = sc->sc_hdev.sc_report_id != 0;
  313                 err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT,
  314                                         buffer, sc->sc_isize + extra);
  315                 if (err)
  316                         return (EIO);
  317                 return (uiomove(buffer+extra, sc->sc_isize, uio));
  318         }
  319 
  320         s = splusb();
  321         while (sc->sc_q.c_cc == 0) {
  322                 if (flag & IO_NDELAY) {
  323                         splx(s);
  324                         return (EWOULDBLOCK);
  325                 }
  326                 sc->sc_state |= UHID_ASLP;
  327                 DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
  328                 error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
  329                 DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
  330                 if (sc->sc_dying)
  331                         error = EIO;
  332                 if (error) {
  333                         sc->sc_state &= ~UHID_ASLP;
  334                         break;
  335                 }
  336         }
  337         splx(s);
  338 
  339         /* Transfer as many chunks as possible. */
  340         while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
  341                 length = min(sc->sc_q.c_cc, uio->uio_resid);
  342                 if (length > sizeof(buffer))
  343                         length = sizeof(buffer);
  344 
  345                 /* Remove a small chunk from the input queue. */
  346                 (void) q_to_b(&sc->sc_q, buffer, length);
  347                 DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length));
  348 
  349                 /* Copy the data to the user process. */
  350                 if ((error = uiomove(buffer, length, uio)) != 0)
  351                         break;
  352         }
  353 
  354         return (error);
  355 }
  356 
  357 int
  358 uhidread(dev_t dev, struct uio *uio, int flag)
  359 {
  360         struct uhid_softc *sc;
  361         int error;
  362 
  363         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  364 
  365         sc->sc_refcnt++;
  366         error = uhid_do_read(sc, uio, flag);
  367         if (--sc->sc_refcnt < 0)
  368                 usb_detach_wakeup(&sc->sc_hdev.sc_dev);
  369         return (error);
  370 }
  371 
  372 int
  373 uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
  374 {
  375         int error;
  376         int size;
  377         usbd_status err;
  378 
  379         DPRINTFN(1, ("uhidwrite\n"));
  380 
  381         if (sc->sc_dying)
  382                 return (EIO);
  383 
  384         size = sc->sc_osize;
  385         error = 0;
  386         if (uio->uio_resid != size)
  387                 return (EINVAL);
  388         error = uiomove(sc->sc_obuf, size, uio);
  389         if (!error) {
  390                 err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT,
  391                                         sc->sc_obuf, size);
  392                 if (err)
  393                         error = EIO;
  394         }
  395 
  396         return (error);
  397 }
  398 
  399 int
  400 uhidwrite(dev_t dev, struct uio *uio, int flag)
  401 {
  402         struct uhid_softc *sc;
  403         int error;
  404 
  405         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  406 
  407         sc->sc_refcnt++;
  408         error = uhid_do_write(sc, uio, flag);
  409         if (--sc->sc_refcnt < 0)
  410                 usb_detach_wakeup(&sc->sc_hdev.sc_dev);
  411         return (error);
  412 }
  413 
  414 int
  415 uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr,
  416               int flag, struct proc *p)
  417 {
  418         struct usb_ctl_report_desc *rd;
  419         struct usb_ctl_report *re;
  420         u_char buffer[UHID_CHUNK];
  421         int size, extra;
  422         usbd_status err;
  423         void *desc;
  424 
  425         DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
  426 
  427         if (sc->sc_dying)
  428                 return (EIO);
  429 
  430         switch (cmd) {
  431         case FIONBIO:
  432                 /* All handled in the upper FS layer. */
  433                 break;
  434 
  435         case FIOASYNC:
  436                 if (*(int *)addr) {
  437                         if (sc->sc_async != NULL)
  438                                 return (EBUSY);
  439                         sc->sc_async = p;
  440                         DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p));
  441                 } else
  442                         sc->sc_async = NULL;
  443                 break;
  444 
  445         /* XXX this is not the most general solution. */
  446         case TIOCSPGRP:
  447                 if (sc->sc_async == NULL)
  448                         return (EINVAL);
  449                 if (*(int *)addr != sc->sc_async->p_pgid)
  450                         return (EPERM);
  451                 break;
  452 
  453         case USB_GET_REPORT_DESC:
  454                 uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size);
  455                 rd = (struct usb_ctl_report_desc *)addr;
  456                 size = min(size, sizeof rd->ucrd_data);
  457                 rd->ucrd_size = size;
  458                 memcpy(rd->ucrd_data, desc, size);
  459                 break;
  460 
  461         case USB_SET_IMMED:
  462                 if (*(int *)addr) {
  463                         extra = sc->sc_hdev.sc_report_id != 0;
  464                         err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT,
  465                                                 buffer, sc->sc_isize + extra);
  466                         if (err)
  467                                 return (EOPNOTSUPP);
  468 
  469                         sc->sc_state |=  UHID_IMMED;
  470                 } else
  471                         sc->sc_state &= ~UHID_IMMED;
  472                 break;
  473 
  474         case USB_GET_REPORT:
  475                 re = (struct usb_ctl_report *)addr;
  476                 switch (re->ucr_report) {
  477                 case UHID_INPUT_REPORT:
  478                         size = sc->sc_isize;
  479                         break;
  480                 case UHID_OUTPUT_REPORT:
  481                         size = sc->sc_osize;
  482                         break;
  483                 case UHID_FEATURE_REPORT:
  484                         size = sc->sc_fsize;
  485                         break;
  486                 default:
  487                         return (EINVAL);
  488                 }
  489                 extra = sc->sc_hdev.sc_report_id != 0;
  490                 err = uhidev_get_report(&sc->sc_hdev, re->ucr_report,
  491                     re->ucr_data, size + extra);
  492                 if (extra)
  493                         memcpy(re->ucr_data, re->ucr_data+1, size);
  494                 if (err)
  495                         return (EIO);
  496                 break;
  497 
  498         case USB_SET_REPORT:
  499                 re = (struct usb_ctl_report *)addr;
  500                 switch (re->ucr_report) {
  501                 case UHID_INPUT_REPORT:
  502                         size = sc->sc_isize;
  503                         break;
  504                 case UHID_OUTPUT_REPORT:
  505                         size = sc->sc_osize;
  506                         break;
  507                 case UHID_FEATURE_REPORT:
  508                         size = sc->sc_fsize;
  509                         break;
  510                 default:
  511                         return (EINVAL);
  512                 }
  513                 err = uhidev_set_report(&sc->sc_hdev, re->ucr_report,
  514                     re->ucr_data, size);
  515                 if (err)
  516                         return (EIO);
  517                 break;
  518 
  519         case USB_GET_REPORT_ID:
  520                 *(int *)addr = sc->sc_hdev.sc_report_id;
  521                 break;
  522 
  523         default:
  524                 return (EINVAL);
  525         }
  526         return (0);
  527 }
  528 
  529 int
  530 uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
  531 {
  532         struct uhid_softc *sc;
  533         int error;
  534 
  535         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  536 
  537         sc->sc_refcnt++;
  538         error = uhid_do_ioctl(sc, cmd, addr, flag, p);
  539         if (--sc->sc_refcnt < 0)
  540                 usb_detach_wakeup(&sc->sc_hdev.sc_dev);
  541         return (error);
  542 }
  543 
  544 int
  545 uhidpoll(dev_t dev, int events, struct proc *p)
  546 {
  547         struct uhid_softc *sc;
  548         int revents = 0;
  549         int s;
  550 
  551         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  552 
  553         if (sc->sc_dying)
  554                 return (POLLERR);
  555 
  556         s = splusb();
  557         if (events & (POLLOUT | POLLWRNORM))
  558                 revents |= events & (POLLOUT | POLLWRNORM);
  559         if (events & (POLLIN | POLLRDNORM)) {
  560                 if (sc->sc_q.c_cc > 0)
  561                         revents |= events & (POLLIN | POLLRDNORM);
  562                 else
  563                         selrecord(p, &sc->sc_rsel);
  564         }
  565 
  566         splx(s);
  567         return (revents);
  568 }
  569 
  570 void filt_uhidrdetach(struct knote *);
  571 int filt_uhidread(struct knote *, long);
  572 int uhidkqfilter(dev_t, struct knote *);
  573 
  574 void
  575 filt_uhidrdetach(struct knote *kn)
  576 {
  577         struct uhid_softc *sc = (void *)kn->kn_hook;
  578         int s;
  579 
  580         s = splusb();
  581         SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext);
  582         splx(s);
  583 }
  584 
  585 int
  586 filt_uhidread(struct knote *kn, long hint)
  587 {
  588         struct uhid_softc *sc = (void *)kn->kn_hook;
  589 
  590         kn->kn_data = sc->sc_q.c_cc;
  591         return (kn->kn_data > 0);
  592 }
  593 
  594 struct filterops uhidread_filtops =
  595         { 1, NULL, filt_uhidrdetach, filt_uhidread };
  596 
  597 struct filterops uhid_seltrue_filtops =
  598         { 1, NULL, filt_uhidrdetach, filt_seltrue };
  599 
  600 int
  601 uhidkqfilter(dev_t dev, struct knote *kn)
  602 {
  603         struct uhid_softc *sc;
  604         struct klist *klist;
  605         int s;
  606 
  607         sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
  608 
  609         if (sc->sc_dying)
  610                 return (EIO);
  611 
  612         switch (kn->kn_filter) {
  613         case EVFILT_READ:
  614                 klist = &sc->sc_rsel.si_note;
  615                 kn->kn_fop = &uhidread_filtops;
  616                 break;
  617 
  618         case EVFILT_WRITE:
  619                 klist = &sc->sc_rsel.si_note;
  620                 kn->kn_fop = &uhid_seltrue_filtops;
  621                 break;
  622 
  623         default:
  624                 return (1);
  625         }
  626 
  627         kn->kn_hook = (void *)sc;
  628 
  629         s = splusb();
  630         SLIST_INSERT_HEAD(klist, kn, kn_selnext);
  631         splx(s);
  632 
  633         return (0);
  634 }

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