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/oce/oce_hw.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 /*-
    2  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (C) 2013 Emulex
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions are met:
    9  *
   10  * 1. Redistributions of source code must retain the above copyright notice,
   11  *    this list of conditions and the following disclaimer.
   12  *
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * 3. Neither the name of the Emulex Corporation nor the names of its
   18  *    contributors may be used to endorse or promote products derived from
   19  *    this software without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   31  * POSSIBILITY OF SUCH DAMAGE.
   32  *
   33  * Contact Information:
   34  * freebsd-drivers@emulex.com
   35  *
   36  * Emulex
   37  * 3333 Susan Street
   38  * Costa Mesa, CA 92626
   39  */
   40 
   41 /* $FreeBSD$ */
   42 
   43 #include "oce_if.h"
   44 
   45 static int oce_POST(POCE_SOFTC sc);
   46 
   47 /**
   48  * @brief               Function to post status
   49  * @param sc            software handle to the device
   50  */
   51 static int
   52 oce_POST(POCE_SOFTC sc)
   53 {
   54         mpu_ep_semaphore_t post_status;
   55         int tmo = 60000;
   56 
   57         /* read semaphore CSR */
   58         post_status.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc));
   59 
   60         /* if host is ready then wait for fw ready else send POST */
   61         if (post_status.bits.stage <= POST_STAGE_AWAITING_HOST_RDY) {
   62                 post_status.bits.stage = POST_STAGE_CHIP_RESET;
   63                 OCE_WRITE_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc), post_status.dw0);
   64         }
   65 
   66         /* wait for FW ready */
   67         for (;;) {
   68                 if (--tmo == 0)
   69                         break;
   70 
   71                 DELAY(1000);
   72 
   73                 post_status.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc));
   74                 if (post_status.bits.error) {
   75                         device_printf(sc->dev,
   76                                   "POST failed: %x\n", post_status.dw0);
   77                         return ENXIO;
   78                 }
   79                 if (post_status.bits.stage == POST_STAGE_ARMFW_READY)
   80                         return 0;
   81         }
   82 
   83         device_printf(sc->dev, "POST timed out: %x\n", post_status.dw0);
   84 
   85         return ENXIO;
   86 }
   87 
   88 /**
   89  * @brief               Function for hardware initialization
   90  * @param sc            software handle to the device
   91  */
   92 int
   93 oce_hw_init(POCE_SOFTC sc)
   94 {
   95         int rc = 0;
   96 
   97         rc = oce_POST(sc);
   98         if (rc)
   99                 return rc;
  100 
  101         /* create the bootstrap mailbox */
  102         rc = oce_dma_alloc(sc, sizeof(struct oce_bmbx), &sc->bsmbx, 0);
  103         if (rc) {
  104                 device_printf(sc->dev, "Mailbox alloc failed\n");
  105                 return rc;
  106         }
  107 
  108         rc = oce_reset_fun(sc);
  109         if (rc)
  110                 goto error;
  111                 
  112 
  113         rc = oce_mbox_init(sc);
  114         if (rc)
  115                 goto error;
  116 
  117         rc = oce_get_fw_version(sc);
  118         if (rc)
  119                 goto error;
  120 
  121         rc = oce_get_fw_config(sc);
  122         if (rc)
  123                 goto error;
  124 
  125         sc->macaddr.size_of_struct = 6;
  126         rc = oce_read_mac_addr(sc, 0, 1, MAC_ADDRESS_TYPE_NETWORK,
  127                                         &sc->macaddr);
  128         if (rc)
  129                 goto error;
  130 
  131         if ((IS_BE(sc) && (sc->flags & OCE_FLAGS_BE3)) || IS_SH(sc)) {
  132                 rc = oce_mbox_check_native_mode(sc);
  133                 if (rc)
  134                         goto error;
  135         } else
  136                 sc->be3_native = 0;
  137 
  138         return rc;
  139 
  140 error:
  141         oce_dma_free(sc, &sc->bsmbx);
  142         device_printf(sc->dev, "Hardware initialisation failed\n");
  143         return rc;
  144 }
  145 
  146 /**
  147  * @brief               Releases the obtained pci resources
  148  * @param sc            software handle to the device
  149  */
  150 void
  151 oce_hw_pci_free(POCE_SOFTC sc)
  152 {
  153         int pci_cfg_barnum = 0;
  154 
  155         if (IS_BE(sc) && (sc->flags & OCE_FLAGS_BE2))
  156                 pci_cfg_barnum = OCE_DEV_BE2_CFG_BAR;
  157         else
  158                 pci_cfg_barnum = OCE_DEV_CFG_BAR;
  159 
  160         if (sc->devcfg_res != NULL) {
  161                 bus_release_resource(sc->dev,
  162                                      SYS_RES_MEMORY,
  163                                      PCIR_BAR(pci_cfg_barnum), sc->devcfg_res);
  164                 sc->devcfg_res = (struct resource *)NULL;
  165                 sc->devcfg_btag = (bus_space_tag_t) 0;
  166                 sc->devcfg_bhandle = (bus_space_handle_t)0;
  167                 sc->devcfg_vhandle = (void *)NULL;
  168         }
  169 
  170         if (sc->csr_res != NULL) {
  171                 bus_release_resource(sc->dev,
  172                                      SYS_RES_MEMORY,
  173                                      PCIR_BAR(OCE_PCI_CSR_BAR), sc->csr_res);
  174                 sc->csr_res = (struct resource *)NULL;
  175                 sc->csr_btag = (bus_space_tag_t)0;
  176                 sc->csr_bhandle = (bus_space_handle_t)0;
  177                 sc->csr_vhandle = (void *)NULL;
  178         }
  179 
  180         if (sc->db_res != NULL) {
  181                 bus_release_resource(sc->dev,
  182                                      SYS_RES_MEMORY,
  183                                      PCIR_BAR(OCE_PCI_DB_BAR), sc->db_res);
  184                 sc->db_res = (struct resource *)NULL;
  185                 sc->db_btag = (bus_space_tag_t)0;
  186                 sc->db_bhandle = (bus_space_handle_t)0;
  187                 sc->db_vhandle = (void *)NULL;
  188         }
  189 }
  190 
  191 /**
  192  * @brief               Function to get the PCI capabilities
  193  * @param sc            software handle to the device
  194  */
  195 static
  196 void oce_get_pci_capabilities(POCE_SOFTC sc)
  197 {
  198         uint32_t val;
  199 
  200         if (pci_find_cap(sc->dev, PCIY_PCIX, &val) == 0) {
  201                 if (val != 0) 
  202                         sc->flags |= OCE_FLAGS_PCIX;
  203         }
  204 
  205         if (pci_find_cap(sc->dev, PCIY_EXPRESS, &val) == 0) {
  206                 if (val != 0) {
  207                         uint16_t link_status =
  208                             pci_read_config(sc->dev, val + 0x12, 2);
  209 
  210                         sc->flags |= OCE_FLAGS_PCIE;
  211                         sc->pcie_link_speed = link_status & 0xf;
  212                         sc->pcie_link_width = (link_status >> 4) & 0x3f;
  213                 }
  214         }
  215 
  216         if (pci_find_cap(sc->dev, PCIY_MSI, &val) == 0) {
  217                 if (val != 0)
  218                         sc->flags |= OCE_FLAGS_MSI_CAPABLE;
  219         }
  220 
  221         if (pci_find_cap(sc->dev, PCIY_MSIX, &val) == 0) {
  222                 if (val != 0) {
  223                         val = pci_msix_count(sc->dev);
  224                         sc->flags |= OCE_FLAGS_MSIX_CAPABLE;
  225                 }
  226         }
  227 }
  228 
  229 /**
  230  * @brief       Allocate PCI resources.
  231  *
  232  * @param sc            software handle to the device
  233  * @returns             0 if successful, or error
  234  */
  235 int
  236 oce_hw_pci_alloc(POCE_SOFTC sc)
  237 {
  238         int rr, pci_cfg_barnum = 0;
  239         pci_sli_intf_t intf;
  240 
  241         pci_enable_busmaster(sc->dev);
  242 
  243         oce_get_pci_capabilities(sc);
  244 
  245         sc->fn = pci_get_function(sc->dev);
  246 
  247         /* setup the device config region */
  248         if (IS_BE(sc) && (sc->flags & OCE_FLAGS_BE2))
  249                 pci_cfg_barnum = OCE_DEV_BE2_CFG_BAR;
  250         else
  251                 pci_cfg_barnum = OCE_DEV_CFG_BAR;
  252                 
  253         rr = PCIR_BAR(pci_cfg_barnum);
  254 
  255         if (IS_BE(sc) || IS_SH(sc)) 
  256                 sc->devcfg_res = bus_alloc_resource_any(sc->dev,
  257                                 SYS_RES_MEMORY, &rr,
  258                                 RF_ACTIVE|RF_SHAREABLE);
  259         else
  260                 sc->devcfg_res = bus_alloc_resource_anywhere(sc->dev,
  261                                 SYS_RES_MEMORY, &rr, 32768,
  262                                 RF_ACTIVE|RF_SHAREABLE);
  263 
  264         if (!sc->devcfg_res)
  265                 goto error;
  266 
  267         sc->devcfg_btag = rman_get_bustag(sc->devcfg_res);
  268         sc->devcfg_bhandle = rman_get_bushandle(sc->devcfg_res);
  269         sc->devcfg_vhandle = rman_get_virtual(sc->devcfg_res);
  270 
  271         /* Read the SLI_INTF register and determine whether we
  272          * can use this port and its features
  273          */
  274         intf.dw0 = pci_read_config((sc)->dev,OCE_INTF_REG_OFFSET,4);
  275 
  276         if (intf.bits.sli_valid != OCE_INTF_VALID_SIG)
  277                 goto error;
  278 
  279         if (intf.bits.sli_rev != OCE_INTF_SLI_REV4) {
  280                 device_printf(sc->dev, "Adapter doesnt support SLI4\n");
  281                 goto error;
  282         }
  283 
  284         if (intf.bits.sli_if_type == OCE_INTF_IF_TYPE_1)
  285                 sc->flags |= OCE_FLAGS_MBOX_ENDIAN_RQD;
  286 
  287         if (intf.bits.sli_hint1 == OCE_INTF_FUNC_RESET_REQD)
  288                 sc->flags |= OCE_FLAGS_FUNCRESET_RQD;
  289 
  290         if (intf.bits.sli_func_type == OCE_INTF_VIRT_FUNC)
  291                 sc->flags |= OCE_FLAGS_VIRTUAL_PORT;
  292 
  293         /* Lancer has one BAR (CFG) but BE3 has three (CFG, CSR, DB) */
  294         if (IS_BE(sc) || IS_SH(sc)) {
  295                 /* set up CSR region */
  296                 rr = PCIR_BAR(OCE_PCI_CSR_BAR);
  297                 sc->csr_res = bus_alloc_resource_any(sc->dev,
  298                                 SYS_RES_MEMORY, &rr, RF_ACTIVE|RF_SHAREABLE);
  299                 if (!sc->csr_res)
  300                         goto error;
  301                 sc->csr_btag = rman_get_bustag(sc->csr_res);
  302                 sc->csr_bhandle = rman_get_bushandle(sc->csr_res);
  303                 sc->csr_vhandle = rman_get_virtual(sc->csr_res);
  304                 
  305                 /* set up DB doorbell region */
  306                 rr = PCIR_BAR(OCE_PCI_DB_BAR);
  307                 sc->db_res = bus_alloc_resource_any(sc->dev,
  308                                 SYS_RES_MEMORY, &rr, RF_ACTIVE|RF_SHAREABLE);
  309                 if (!sc->db_res)
  310                         goto error;
  311                 sc->db_btag = rman_get_bustag(sc->db_res);
  312                 sc->db_bhandle = rman_get_bushandle(sc->db_res);
  313                 sc->db_vhandle = rman_get_virtual(sc->db_res);
  314         }
  315 
  316         return 0;
  317 
  318 error:  
  319         oce_hw_pci_free(sc);
  320         return ENXIO;
  321 }
  322 
  323 /**
  324  * @brief               Function for device shutdown
  325  * @param sc            software handle to the device
  326  * @returns             0 on success, error otherwise
  327  */
  328 void
  329 oce_hw_shutdown(POCE_SOFTC sc)
  330 {
  331 
  332         oce_stats_free(sc);
  333         /* disable hardware interrupts */
  334         oce_hw_intr_disable(sc);
  335 #if defined(INET6) || defined(INET)
  336         /* Free LRO resources */
  337         oce_free_lro(sc);
  338 #endif
  339         /* Release queue*/
  340         oce_queue_release_all(sc);
  341         /*Delete Network Interface*/
  342         oce_delete_nw_interface(sc);
  343         /* After fw clean we dont send any cmds to fw.*/
  344         oce_fw_clean(sc);
  345         /* release intr resources */
  346         oce_intr_free(sc);
  347         /* release PCI resources */
  348         oce_hw_pci_free(sc);
  349         /* free mbox specific resources */
  350         LOCK_DESTROY(&sc->bmbx_lock);
  351         LOCK_DESTROY(&sc->dev_lock);
  352 
  353         oce_dma_free(sc, &sc->bsmbx);
  354 }
  355 
  356 /**
  357  * @brief               Function for creating nw interface.
  358  * @param sc            software handle to the device
  359  * @returns             0 on success, error otherwise
  360  */
  361 int
  362 oce_create_nw_interface(POCE_SOFTC sc)
  363 {
  364         int rc;
  365         uint32_t capab_flags;
  366         uint32_t capab_en_flags;
  367 
  368         /* interface capabilities to give device when creating interface */
  369         capab_flags = OCE_CAPAB_FLAGS;
  370 
  371         /* capabilities to enable by default (others set dynamically) */
  372         capab_en_flags = OCE_CAPAB_ENABLE;
  373 
  374         if (IS_XE201(sc)) {
  375                 /* LANCER A0 workaround */
  376                 capab_en_flags &= ~MBX_RX_IFACE_FLAGS_PASS_L3L4_ERR;
  377                 capab_flags &= ~MBX_RX_IFACE_FLAGS_PASS_L3L4_ERR;
  378         }
  379 
  380         if (IS_SH(sc) || IS_XE201(sc))
  381                 capab_flags |= MBX_RX_IFACE_FLAGS_MULTICAST;
  382 
  383         if (sc->enable_hwlro) {
  384                 capab_flags |= MBX_RX_IFACE_FLAGS_LRO;
  385                 capab_en_flags |= MBX_RX_IFACE_FLAGS_LRO;
  386         }
  387 
  388         /* enable capabilities controlled via driver startup parameters */
  389         if (is_rss_enabled(sc))
  390                 capab_en_flags |= MBX_RX_IFACE_FLAGS_RSS;
  391         else {
  392                 capab_en_flags &= ~MBX_RX_IFACE_FLAGS_RSS;
  393                 capab_flags &= ~MBX_RX_IFACE_FLAGS_RSS;
  394         }
  395 
  396         rc = oce_if_create(sc,
  397                            capab_flags,
  398                            capab_en_flags,
  399                            0, &sc->macaddr.mac_addr[0], &sc->if_id);
  400         if (rc)
  401                 return rc;
  402 
  403         atomic_inc_32(&sc->nifs);
  404 
  405         sc->if_cap_flags = capab_en_flags;
  406 
  407         /* set default flow control */
  408         rc = oce_set_flow_control(sc, sc->flow_control);
  409         if (rc)
  410                 goto error;
  411 
  412         rc = oce_rxf_set_promiscuous(sc, sc->promisc);
  413         if (rc)
  414                 goto error;
  415 
  416         return rc;
  417 
  418 error:
  419         oce_delete_nw_interface(sc);
  420         return rc;
  421 
  422 }
  423 
  424 /**
  425  * @brief               Function to delete a nw interface.
  426  * @param sc            software handle to the device
  427  */
  428 void
  429 oce_delete_nw_interface(POCE_SOFTC sc)
  430 {
  431         /* currently only single interface is implmeneted */
  432         if (sc->nifs > 0) {
  433                 oce_if_del(sc, sc->if_id);
  434                 atomic_dec_32(&sc->nifs);
  435         }
  436 }
  437 
  438 /**
  439  * @brief Soft reset.
  440  * @param sc            software handle to the device
  441  * @returns             0 on success, error otherwise
  442  */
  443 int
  444 oce_pci_soft_reset(POCE_SOFTC sc)
  445 {
  446         int rc;
  447         mpu_ep_control_t ctrl;
  448 
  449         ctrl.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_CONTROL);
  450         ctrl.bits.cpu_reset = 1;
  451         OCE_WRITE_CSR_MPU(sc, csr, MPU_EP_CONTROL, ctrl.dw0);
  452         DELAY(50);
  453         rc=oce_POST(sc);
  454 
  455         return rc;
  456 }
  457 
  458 /**
  459  * @brief               Function for hardware start
  460  * @param sc            software handle to the device
  461  * @returns             0 on success, error otherwise
  462  */
  463 int
  464 oce_hw_start(POCE_SOFTC sc)
  465 {
  466         struct link_status link = { 0 };
  467         int rc = 0;
  468 
  469         rc = oce_get_link_status(sc, &link);
  470         if (rc) 
  471                 return 1;
  472 
  473         if (link.logical_link_status == NTWK_LOGICAL_LINK_UP) {
  474                 sc->link_status = NTWK_LOGICAL_LINK_UP;
  475                 if_link_state_change(sc->ifp, LINK_STATE_UP);
  476         } else {
  477                 sc->link_status = NTWK_LOGICAL_LINK_DOWN;
  478                 if_link_state_change(sc->ifp, LINK_STATE_DOWN);
  479         }
  480 
  481         sc->link_speed = link.phys_port_speed;
  482         sc->qos_link_speed = (uint32_t )link.qos_link_speed * 10;
  483 
  484         rc = oce_start_mq(sc->mq);
  485 
  486         /* we need to get MCC aync events. So enable intrs and arm
  487            first EQ, Other EQs will be armed after interface is UP 
  488         */
  489         oce_hw_intr_enable(sc);
  490         oce_arm_eq(sc, sc->eq[0]->eq_id, 0, TRUE, FALSE);
  491 
  492         /* Send first mcc cmd and after that we get gracious
  493            MCC notifications from FW
  494         */
  495         oce_first_mcc_cmd(sc);
  496 
  497         return rc;
  498 }
  499 
  500 /**
  501  * @brief               Function for hardware enable interupts.
  502  * @param sc            software handle to the device
  503  */
  504 void
  505 oce_hw_intr_enable(POCE_SOFTC sc)
  506 {
  507         uint32_t reg;
  508 
  509         reg = OCE_READ_REG32(sc, devcfg, PCICFG_INTR_CTRL);
  510         reg |= HOSTINTR_MASK;
  511         OCE_WRITE_REG32(sc, devcfg, PCICFG_INTR_CTRL, reg);
  512 
  513 }
  514 
  515 /**
  516  * @brief               Function for hardware disable interupts
  517  * @param sc            software handle to the device
  518  */
  519 void
  520 oce_hw_intr_disable(POCE_SOFTC sc)
  521 {
  522         uint32_t reg;
  523 
  524         reg = OCE_READ_REG32(sc, devcfg, PCICFG_INTR_CTRL);
  525         reg &= ~HOSTINTR_MASK;
  526         OCE_WRITE_REG32(sc, devcfg, PCICFG_INTR_CTRL, reg);
  527 }
  528 
  529 static u_int
  530 oce_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
  531 {
  532         struct mbx_set_common_iface_multicast *req = arg;
  533 
  534         if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE)
  535                 return (0);
  536 
  537         bcopy(LLADDR(sdl), &req->params.req.mac[req->params.req.num_mac++],
  538             ETHER_ADDR_LEN);
  539 
  540         return (1);
  541 }
  542 
  543 /**
  544  * @brief               Function for hardware update multicast filter
  545  * @param sc            software handle to the device
  546  */
  547 int
  548 oce_hw_update_multicast(POCE_SOFTC sc)
  549 {
  550         struct ifnet    *ifp = sc->ifp;
  551         struct mbx_set_common_iface_multicast *req = NULL;
  552         OCE_DMA_MEM dma;
  553         int rc = 0;
  554 
  555         /* Allocate DMA mem*/
  556         if (oce_dma_alloc(sc, sizeof(struct mbx_set_common_iface_multicast),
  557                                                         &dma, 0))
  558                 return ENOMEM;
  559 
  560         req = OCE_DMAPTR(&dma, struct mbx_set_common_iface_multicast);
  561         bzero(req, sizeof(struct mbx_set_common_iface_multicast));
  562 
  563         if_foreach_llmaddr(ifp, oce_copy_maddr, req);
  564         if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE) {
  565                 /*More multicast addresses than our hardware table
  566                   So Enable multicast promiscus in our hardware to
  567                   accept all multicat packets
  568                 */
  569                 req->params.req.promiscuous = 1;
  570         }
  571 
  572         req->params.req.if_id = sc->if_id;
  573         rc = oce_update_multicast(sc, &dma);
  574         oce_dma_free(sc, &dma);
  575         return rc;
  576 }

Cache object: a31421fc0bd5937f4ae47187557f63cd


[ 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.