The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/midi.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: midi.c,v 1.55 2022/07/02 08:50:41 visa Exp $  */
    2 
    3 /*
    4  * Copyright (c) 2003, 2004 Alexandre Ratchov
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/param.h>
   20 #include <sys/fcntl.h>
   21 #include <sys/systm.h>
   22 #include <sys/ioctl.h>
   23 #include <sys/conf.h>
   24 #include <sys/kernel.h>
   25 #include <sys/timeout.h>
   26 #include <sys/vnode.h>
   27 #include <sys/signalvar.h>
   28 #include <sys/device.h>
   29 
   30 #include <dev/midi_if.h>
   31 #include <dev/audio_if.h>
   32 #include <dev/midivar.h>
   33 
   34 #define IPL_SOFTMIDI            IPL_SOFTNET
   35 #define DEVNAME(sc)             ((sc)->dev.dv_xname)
   36 
   37 int     midiopen(dev_t, int, int, struct proc *);
   38 int     midiclose(dev_t, int, int, struct proc *);
   39 int     midiread(dev_t, struct uio *, int);
   40 int     midiwrite(dev_t, struct uio *, int);
   41 int     midikqfilter(dev_t, struct knote *);
   42 int     midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
   43 int     midiprobe(struct device *, void *, void *);
   44 void    midiattach(struct device *, struct device *, void *);
   45 int     mididetach(struct device *, int);
   46 int     midiprint(void *, const char *);
   47 
   48 void    midi_iintr(void *, int);
   49 void    midi_ointr(void *);
   50 void    midi_timeout(void *);
   51 void    midi_out_start(struct midi_softc *);
   52 void    midi_out_stop(struct midi_softc *);
   53 void    midi_out_do(struct midi_softc *);
   54 void    midi_attach(struct midi_softc *, struct device *);
   55 
   56 
   57 const struct cfattach midi_ca = {
   58         sizeof(struct midi_softc), midiprobe, midiattach, mididetach
   59 };
   60 
   61 struct cfdriver midi_cd = {
   62         NULL, "midi", DV_DULL
   63 };
   64 
   65 
   66 void filt_midiwdetach(struct knote *);
   67 int filt_midiwrite(struct knote *, long);
   68 
   69 const struct filterops midiwrite_filtops = {
   70         .f_flags        = FILTEROP_ISFD,
   71         .f_attach       = NULL,
   72         .f_detach       = filt_midiwdetach,
   73         .f_event        = filt_midiwrite,
   74 };
   75 
   76 void filt_midirdetach(struct knote *);
   77 int filt_midiread(struct knote *, long);
   78 
   79 const struct filterops midiread_filtops = {
   80         .f_flags        = FILTEROP_ISFD,
   81         .f_attach       = NULL,
   82         .f_detach       = filt_midirdetach,
   83         .f_event        = filt_midiread,
   84 };
   85 
   86 void
   87 midi_buf_wakeup(void *addr)
   88 {
   89         struct midi_buffer *buf = addr;
   90 
   91         if (buf->blocking) {
   92                 wakeup(&buf->blocking);
   93                 buf->blocking = 0;
   94         }
   95         /*
   96          * As long as selwakeup() grabs the KERNEL_LOCK() make sure it is
   97          * already held here to avoid lock ordering problems with `audio_lock'
   98          */
   99         KERNEL_ASSERT_LOCKED();
  100         mtx_enter(&audio_lock);
  101         selwakeup(&buf->sel);
  102         mtx_leave(&audio_lock);
  103 }
  104 
  105 void
  106 midi_iintr(void *addr, int data)
  107 {
  108         struct midi_softc  *sc = (struct midi_softc *)addr;
  109         struct midi_buffer *mb = &sc->inbuf;
  110 
  111         MUTEX_ASSERT_LOCKED(&audio_lock);
  112         if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD))
  113                 return;
  114 
  115         if (MIDIBUF_ISFULL(mb))
  116                 return; /* discard data */
  117 
  118         MIDIBUF_WRITE(mb, data);
  119 
  120         /*
  121          * As long as selwakeup() needs to be protected by the
  122          * KERNEL_LOCK() we have to delay the wakeup to another
  123          * context to keep the interrupt context KERNEL_LOCK()
  124          * free.
  125          */
  126         softintr_schedule(sc->inbuf.softintr);
  127 }
  128 
  129 int
  130 midiread(dev_t dev, struct uio *uio, int ioflag)
  131 {
  132         struct midi_softc *sc;
  133         struct midi_buffer *mb;
  134         size_t count;
  135         int error;
  136 
  137         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  138         if (sc == NULL)
  139                 return ENXIO;
  140         if (!(sc->flags & FREAD)) {
  141                 error = ENXIO;
  142                 goto done;
  143         }
  144         mb = &sc->inbuf;
  145 
  146         /* if there is no data then sleep (unless IO_NDELAY flag is set) */
  147         error = 0;
  148         mtx_enter(&audio_lock);
  149         while (MIDIBUF_ISEMPTY(mb)) {
  150                 if (ioflag & IO_NDELAY) {
  151                         error = EWOULDBLOCK;
  152                         goto done_mtx;
  153                 }
  154                 sc->inbuf.blocking = 1;
  155                 error = msleep_nsec(&sc->inbuf.blocking, &audio_lock,
  156                     PWAIT | PCATCH, "mid_rd", INFSLP);
  157                 if (!(sc->dev.dv_flags & DVF_ACTIVE))
  158                         error = EIO;
  159                 if (error)
  160                         goto done_mtx;
  161         }
  162 
  163         /* at this stage, there is at least 1 byte */
  164 
  165         while (uio->uio_resid > 0 && mb->used > 0) {
  166                 count = MIDIBUF_SIZE - mb->start;
  167                 if (count > mb->used)
  168                         count = mb->used;
  169                 if (count > uio->uio_resid)
  170                         count = uio->uio_resid;
  171                 mtx_leave(&audio_lock);
  172                 error = uiomove(mb->data + mb->start, count, uio);
  173                 if (error)
  174                         goto done;
  175                 mtx_enter(&audio_lock);
  176                 MIDIBUF_REMOVE(mb, count);
  177         }
  178 
  179 done_mtx:
  180         mtx_leave(&audio_lock);
  181 done:
  182         device_unref(&sc->dev);
  183         return error;
  184 }
  185 
  186 void
  187 midi_ointr(void *addr)
  188 {
  189         struct midi_softc *sc = (struct midi_softc *)addr;
  190         struct midi_buffer *mb;
  191 
  192         MUTEX_ASSERT_LOCKED(&audio_lock);
  193         if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE))
  194                 return;
  195         
  196         mb = &sc->outbuf;
  197         if (mb->used > 0) {
  198 #ifdef MIDI_DEBUG
  199                 if (!sc->isbusy) {
  200                         printf("midi_ointr: output must be busy\n");
  201                 }
  202 #endif
  203                 midi_out_do(sc);
  204         } else if (sc->isbusy)
  205                 midi_out_stop(sc);
  206 }
  207 
  208 void
  209 midi_timeout(void *addr)
  210 {
  211         mtx_enter(&audio_lock);
  212         midi_ointr(addr);
  213         mtx_leave(&audio_lock);
  214 }
  215 
  216 void
  217 midi_out_start(struct midi_softc *sc)
  218 {
  219         if (!sc->isbusy) {
  220                 sc->isbusy = 1;
  221                 midi_out_do(sc);
  222         }
  223 }
  224 
  225 void
  226 midi_out_stop(struct midi_softc *sc)
  227 {
  228         sc->isbusy = 0;
  229 
  230         /*
  231          * As long as selwakeup() needs to be protected by the
  232          * KERNEL_LOCK() we have to delay the wakeup to another
  233          * context to keep the interrupt context KERNEL_LOCK()
  234          * free.
  235          */
  236         softintr_schedule(sc->outbuf.softintr);
  237 }
  238 
  239 void
  240 midi_out_do(struct midi_softc *sc)
  241 {
  242         struct midi_buffer *mb = &sc->outbuf;
  243 
  244         while (mb->used > 0) {
  245                 if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]))
  246                         break;
  247                 MIDIBUF_REMOVE(mb, 1);
  248                 if (MIDIBUF_ISEMPTY(mb)) {
  249                         if (sc->hw_if->flush != NULL)
  250                                 sc->hw_if->flush(sc->hw_hdl);
  251                         midi_out_stop(sc);
  252                         return;
  253                 }
  254         }
  255 
  256         if (!(sc->props & MIDI_PROP_OUT_INTR)) {
  257                 if (MIDIBUF_ISEMPTY(mb))
  258                         midi_out_stop(sc);
  259                 else
  260                         timeout_add(&sc->timeo, 1);
  261         }
  262 }
  263 
  264 int
  265 midiwrite(dev_t dev, struct uio *uio, int ioflag)
  266 {
  267         struct midi_softc *sc;
  268         struct midi_buffer *mb;
  269         size_t count;
  270         int error;
  271 
  272         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  273         if (sc == NULL)
  274                 return ENXIO;
  275         if (!(sc->flags & FWRITE)) {
  276                 error = ENXIO;
  277                 goto done;
  278         }
  279         mb = &sc->outbuf;
  280 
  281         /*
  282          * If IO_NDELAY flag is set then check if there is enough room
  283          * in the buffer to store at least one byte. If not then dont
  284          * start the write process.
  285          */
  286         error = 0;
  287         mtx_enter(&audio_lock);
  288         if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) {
  289                 error = EWOULDBLOCK;
  290                 goto done_mtx;
  291         }
  292 
  293         while (uio->uio_resid > 0) {
  294                 while (MIDIBUF_ISFULL(mb)) {
  295                         if (ioflag & IO_NDELAY) {
  296                                 /*
  297                                  * At this stage at least one byte is already
  298                                  * moved so we do not return EWOULDBLOCK
  299                                  */
  300                                 goto done_mtx;
  301                         }
  302                         sc->outbuf.blocking = 1;
  303                         error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
  304                             PWAIT | PCATCH, "mid_wr", INFSLP);
  305                         if (!(sc->dev.dv_flags & DVF_ACTIVE))
  306                                 error = EIO;
  307                         if (error)
  308                                 goto done_mtx;
  309                 }
  310 
  311                 count = MIDIBUF_SIZE - MIDIBUF_END(mb);
  312                 if (count > MIDIBUF_AVAIL(mb))
  313                         count = MIDIBUF_AVAIL(mb);
  314                 if (count > uio->uio_resid)
  315                         count = uio->uio_resid;
  316                 mtx_leave(&audio_lock);
  317                 error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
  318                 if (error)
  319                         goto done;
  320                 mtx_enter(&audio_lock);
  321                 mb->used += count;
  322                 midi_out_start(sc);
  323         }
  324 
  325 done_mtx:
  326         mtx_leave(&audio_lock);
  327 done:
  328         device_unref(&sc->dev);
  329         return error;
  330 }
  331 
  332 int
  333 midikqfilter(dev_t dev, struct knote *kn)
  334 {
  335         struct midi_softc *sc;
  336         struct klist      *klist;
  337         int error;
  338 
  339         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  340         if (sc == NULL)
  341                 return ENXIO;
  342         error = 0;
  343         switch (kn->kn_filter) {
  344         case EVFILT_READ:
  345                 klist = &sc->inbuf.sel.si_note;
  346                 kn->kn_fop = &midiread_filtops;
  347                 break;
  348         case EVFILT_WRITE:
  349                 klist = &sc->outbuf.sel.si_note;
  350                 kn->kn_fop = &midiwrite_filtops;
  351                 break;
  352         default:
  353                 error = EINVAL;
  354                 goto done;
  355         }
  356         kn->kn_hook = (void *)sc;
  357 
  358         mtx_enter(&audio_lock);
  359         klist_insert_locked(klist, kn);
  360         mtx_leave(&audio_lock);
  361 done:
  362         device_unref(&sc->dev);
  363         return error;
  364 }
  365 
  366 void
  367 filt_midirdetach(struct knote *kn)
  368 {
  369         struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
  370 
  371         mtx_enter(&audio_lock);
  372         klist_remove_locked(&sc->inbuf.sel.si_note, kn);
  373         mtx_leave(&audio_lock);
  374 }
  375 
  376 int
  377 filt_midiread(struct knote *kn, long hint)
  378 {
  379         struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
  380         int retval;
  381 
  382         if ((hint & NOTE_SUBMIT) == 0)
  383                 mtx_enter(&audio_lock);
  384         retval = !MIDIBUF_ISEMPTY(&sc->inbuf);
  385         if ((hint & NOTE_SUBMIT) == 0)
  386                 mtx_leave(&audio_lock);
  387 
  388         return (retval);
  389 }
  390 
  391 void
  392 filt_midiwdetach(struct knote *kn)
  393 {
  394         struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
  395 
  396         mtx_enter(&audio_lock);
  397         klist_remove_locked(&sc->outbuf.sel.si_note, kn);
  398         mtx_leave(&audio_lock);
  399 }
  400 
  401 int
  402 filt_midiwrite(struct knote *kn, long hint)
  403 {
  404         struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
  405         int                retval;
  406 
  407         if ((hint & NOTE_SUBMIT) == 0)
  408                 mtx_enter(&audio_lock);
  409         retval = !MIDIBUF_ISFULL(&sc->outbuf);
  410         if ((hint & NOTE_SUBMIT) == 0)
  411                 mtx_leave(&audio_lock);
  412 
  413         return (retval);
  414 }
  415 
  416 int
  417 midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
  418 {
  419         struct midi_softc *sc;
  420         int error;
  421 
  422         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  423         if (sc == NULL)
  424                 return ENXIO;
  425         error = 0;
  426         switch(cmd) {
  427         case FIONBIO:
  428                 /* All handled in the upper FS layer */
  429                 break;
  430         default:
  431                 error = ENOTTY;
  432         }
  433         device_unref(&sc->dev);
  434         return error;
  435 }
  436 
  437 int
  438 midiopen(dev_t dev, int flags, int mode, struct proc *p)
  439 {
  440         struct midi_softc *sc;
  441         int error;
  442 
  443         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  444         if (sc == NULL)
  445                 return ENXIO;
  446         error = 0;
  447         if (sc->flags) {
  448                 error = EBUSY;
  449                 goto done;
  450         }
  451         MIDIBUF_INIT(&sc->inbuf);
  452         MIDIBUF_INIT(&sc->outbuf);
  453         sc->isbusy = 0;
  454         sc->inbuf.blocking = sc->outbuf.blocking = 0;
  455         sc->flags = flags;
  456         error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
  457         if (error)
  458                 sc->flags = 0;
  459 done:
  460         device_unref(&sc->dev);
  461         return error;
  462 }
  463 
  464 int
  465 midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
  466 {
  467         struct midi_softc *sc;
  468         struct midi_buffer *mb;
  469         int error;
  470 
  471         sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
  472         if (sc == NULL)
  473                 return ENXIO;
  474 
  475         /* start draining output buffer */
  476         error = 0;
  477         mb = &sc->outbuf;
  478         mtx_enter(&audio_lock);
  479         if (!MIDIBUF_ISEMPTY(mb))
  480                 midi_out_start(sc);
  481         while (sc->isbusy) {
  482                 sc->outbuf.blocking = 1;
  483                 error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
  484                     PWAIT, "mid_dr", SEC_TO_NSEC(5));
  485                 if (!(sc->dev.dv_flags & DVF_ACTIVE))
  486                         error = EIO;
  487                 if (error)
  488                         break;
  489         }
  490         mtx_leave(&audio_lock);
  491 
  492         /*
  493          * some hw_if->close() reset immediately the midi uart
  494          * which flushes the internal buffer of the uart device,
  495          * so we may lose some (important) data. To avoid this,
  496          * sleep 20ms (around 64 bytes) to give the time to the
  497          * uart to drain its internal buffers.
  498          */
  499         tsleep_nsec(&sc->outbuf.blocking, PWAIT, "mid_cl", MSEC_TO_NSEC(20));
  500         sc->hw_if->close(sc->hw_hdl);
  501         sc->flags = 0;
  502         device_unref(&sc->dev);
  503         return 0;
  504 }
  505 
  506 int
  507 midiprobe(struct device *parent, void *match, void *aux)
  508 {
  509         struct audio_attach_args *sa = aux;
  510 
  511         return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
  512 }
  513 
  514 void
  515 midiattach(struct device *parent, struct device *self, void *aux)
  516 {
  517         struct midi_info          mi;
  518         struct midi_softc        *sc = (struct midi_softc *)self;
  519         struct audio_attach_args *sa = (struct audio_attach_args *)aux;
  520         const struct midi_hw_if  *hwif = sa->hwif;
  521         void                     *hdl = sa->hdl;
  522 
  523 #ifdef DIAGNOSTIC
  524         if (hwif == 0 ||
  525             hwif->open == 0 ||
  526             hwif->close == 0 ||
  527             hwif->output == 0 ||
  528             hwif->getinfo == 0) {
  529                 printf("%s: missing method\n", DEVNAME(sc));
  530                 return;
  531         }
  532 #endif
  533 
  534         sc->inbuf.softintr = softintr_establish(IPL_SOFTMIDI,
  535             midi_buf_wakeup, &sc->inbuf);
  536         if (sc->inbuf.softintr == NULL) {
  537                 printf("%s: can't establish input softintr\n", DEVNAME(sc));
  538                 return;
  539         }
  540 
  541         sc->outbuf.softintr = softintr_establish(IPL_SOFTMIDI,
  542             midi_buf_wakeup, &sc->outbuf);
  543         if (sc->outbuf.softintr == NULL) {
  544                 printf("%s: can't establish output softintr\n", DEVNAME(sc));
  545                 softintr_disestablish(sc->inbuf.softintr);
  546                 return;
  547         }
  548 
  549         sc->hw_if = hwif;
  550         sc->hw_hdl = hdl;
  551         sc->hw_if->getinfo(sc->hw_hdl, &mi);
  552         sc->props = mi.props;
  553         sc->flags = 0;
  554         timeout_set(&sc->timeo, midi_timeout, sc);
  555         printf(": <%s>\n", mi.name);
  556 }
  557 
  558 int
  559 mididetach(struct device *self, int flags)
  560 {
  561         struct midi_softc *sc = (struct midi_softc *)self;
  562         int maj, mn;
  563 
  564         /* locate the major number */
  565         for (maj = 0; maj < nchrdev; maj++) {
  566                 if (cdevsw[maj].d_open == midiopen) {
  567                         /* Nuke the vnodes for any open instances (calls close). */
  568                         mn = self->dv_unit;
  569                         vdevgone(maj, mn, mn, VCHR);
  570                 }
  571         }
  572 
  573         /*
  574          * The close() method did nothing (device_lookup() returns
  575          * NULL), so quickly halt transfers (normally parent is already
  576          * gone, and code below is no-op), and wake-up user-land blocked
  577          * in read/write/ioctl, which return EIO.
  578          */
  579         if (sc->flags) {
  580                 KERNEL_ASSERT_LOCKED();
  581                 if (sc->flags & FREAD) {
  582                         wakeup(&sc->inbuf.blocking);
  583                         mtx_enter(&audio_lock);
  584                         selwakeup(&sc->inbuf.sel);
  585                         mtx_leave(&audio_lock);
  586                 }
  587                 if (sc->flags & FWRITE) {
  588                         wakeup(&sc->outbuf.blocking);
  589                         mtx_enter(&audio_lock);
  590                         selwakeup(&sc->outbuf.sel);
  591                         mtx_leave(&audio_lock);
  592                 }
  593                 sc->hw_if->close(sc->hw_hdl);
  594                 sc->flags = 0;
  595         }
  596 
  597         klist_invalidate(&sc->inbuf.sel.si_note);
  598         klist_invalidate(&sc->outbuf.sel.si_note);
  599 
  600         if (sc->inbuf.softintr)
  601                 softintr_disestablish(sc->inbuf.softintr);
  602         if (sc->outbuf.softintr)
  603                 softintr_disestablish(sc->outbuf.softintr);
  604         return 0;
  605 }
  606 
  607 int
  608 midiprint(void *aux, const char *pnp)
  609 {
  610         if (pnp)
  611                 printf("midi at %s", pnp);
  612         return (UNCONF);
  613 }
  614 
  615 struct device *
  616 midi_attach_mi(const struct midi_hw_if *hwif, void *hdl, struct device *dev)
  617 {
  618         struct audio_attach_args arg;
  619 
  620         arg.type = AUDIODEV_TYPE_MIDI;
  621         arg.hwif = hwif;
  622         arg.hdl = hdl;
  623         return config_found(dev, &arg, midiprint);
  624 }

Cache object: 0e4c90a4987f05abe60c4d974b25c552


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.