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/ntb/test/ntb_tool.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  * This file is provided under a dual BSD/GPLv2 license.  When using or
    3  * redistributing this file, you may do so under either license.
    4  *
    5  * GPL LICENSE SUMMARY
    6  *
    7  * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
    8  *
    9  * This program is free software; you can redistribute it and/or modify
   10  * it under the terms of version 2 of the GNU General Public License as
   11  * published by the Free Software Foundation.
   12  *
   13  * This program is distributed in the hope that it will be useful, but
   14  * WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   16  * General Public License for more details.
   17  *
   18  * BSD LICENSE
   19  *
   20  * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
   21  *
   22  * Redistribution and use in source and binary forms, with or without
   23  * modification, are permitted provided that the following conditions
   24  * are met:
   25  *
   26  *   * Redistributions of source code must retain the above copyright
   27  *     notice, this list of conditions and the following disclaimer.
   28  *   * Redistributions in binary form must reproduce the above copy
   29  *     notice, this list of conditions and the following disclaimer in
   30  *     the documentation and/or other materials provided with the
   31  *     distribution.
   32  *   * Neither the name of Advanced Micro Devices, Inc nor the names of its
   33  *     contributors may be used to endorse or promote products derived
   34  *     from this software without specific prior written permission.
   35  *
   36  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   37  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   38  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   39  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   40  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   43  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   44  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   45  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   46  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   47  *
   48  * PCIe NTB Debugging Tool FreeBSD driver
   49  */
   50 
   51 /*
   52  * How to use this tool, by example.
   53  *
   54  * List of sysctl for ntb_tool driver.
   55  * root@local# sysctl -a | grep ntb_tool
   56  * dev.ntb_tool.0.peer0.spad7: 0x0
   57  * dev.ntb_tool.0.peer0.spad6: 0x0
   58  * dev.ntb_tool.0.peer0.spad5: 0x0
   59  * dev.ntb_tool.0.peer0.spad4: 0x0
   60  * dev.ntb_tool.0.peer0.spad3: 0x0
   61  * dev.ntb_tool.0.peer0.spad2: 0x0
   62  * dev.ntb_tool.0.peer0.spad1: 0x0
   63  * dev.ntb_tool.0.peer0.spad0: 0x0
   64  * dev.ntb_tool.0.peer0.mw_trans2:
   65  * dev.ntb_tool.0.peer0.mw_trans1:
   66  * dev.ntb_tool.0.peer0.mw_trans0:
   67  * dev.ntb_tool.0.peer0.peer_mw2:
   68  * dev.ntb_tool.0.peer0.peer_mw1:
   69  * dev.ntb_tool.0.peer0.peer_mw0:
   70  * dev.ntb_tool.0.peer0.mw2:
   71  * dev.ntb_tool.0.peer0.mw1:
   72  * dev.ntb_tool.0.peer0.mw0:
   73  * dev.ntb_tool.0.peer0.link_event: 0x0
   74  * dev.ntb_tool.0.peer0.link: Y
   75  * dev.ntb_tool.0.peer0.port: 1
   76  * dev.ntb_tool.0.spad7: 0x0
   77  * dev.ntb_tool.0.spad6: 0x0
   78  * dev.ntb_tool.0.spad5: 0x0
   79  * dev.ntb_tool.0.spad4: 0x0
   80  * dev.ntb_tool.0.spad3: 0x0
   81  * dev.ntb_tool.0.spad2: 0x0
   82  * dev.ntb_tool.0.spad1: 0x0
   83  * dev.ntb_tool.0.spad0: 0x0
   84  * dev.ntb_tool.0.db: 0x0
   85  * dev.ntb_tool.0.db_event: 0x0
   86  * dev.ntb_tool.0.db_mask: 0xffff
   87  * dev.ntb_tool.0.db_valid_mask: 0xffff
   88  * dev.ntb_tool.0.peer_db: 0x0
   89  * dev.ntb_tool.0.peer_db_mask: 0xffff
   90  * dev.ntb_tool.0.link: Y
   91  * dev.ntb_tool.0.port: 0
   92  *
   93  * The above example list shows
   94  * 1) three memory windows,
   95  * 1) eight scratchpad registers.
   96  * 3) doorbell config.
   97  * 4) link config.
   98  * 2) One peer.
   99  *
  100  * Based on the underlined ntb_hw driver config & connection topology, these
  101  * things might differ.
  102  *-----------------------------------------------------------------------------
  103  * Eg: check local/peer port information.
  104  *
  105  * # Get local device port number
  106  * root@local# sysctl dev.ntb_tool.0.port
  107  *
  108  * # Check peer device port number
  109  * root@local# sysctl dev.ntb_tool.0.peer0.port
  110  *-----------------------------------------------------------------------------
  111  * Eg: NTB link tests
  112  *
  113  * # Set local link up/down
  114  * root@local# sysctl dev.ntb_tool.0.link=Y
  115  * root@local# sysctl dev.ntb_tool.0.link=N
  116  *
  117  * # Check if link with peer device is up/down:
  118  * root@local# sysctl dev.ntb_tool.0.peer0.link
  119  *
  120  * # Poll until the link specified as up/down. For up, value needs to be set
  121  * depends on peer index, i.e., for peer0 it is 0x1 and for down, value needs
  122  * to be set as 0x0.
  123  * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x1
  124  * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x0
  125  *-----------------------------------------------------------------------------
  126  * Eg: Doorbell registers tests
  127  *
  128  * # clear/get local doorbell
  129  * root@local# sysctl dev.ntb_tool.0.db="c 0x1"
  130  * root@local# sysctl dev.ntb_tool.0.db
  131  *
  132  * # Set/clear/get local doorbell mask
  133  * root@local# sysctl dev.ntb_tool.0.db_mask="s 0x1"
  134  * root@local# sysctl dev.ntb_tool.0.db_mask="c 0x1"
  135  * root@local# sysctl dev.ntb_tool.0.db_mask
  136  *
  137  * # Ring/clear/get peer doorbell
  138  * root@local# sysctl dev.ntb_tool.0.peer_db="s 0x1"
  139  * root@local# sysctl dev.ntb_tool.0.peer_db="c 0x1"
  140  * root@local# sysctl dev.ntb_tool.0.peer_db
  141  *
  142  * # Set/clear/get peer doorbell mask (functionality is absent)
  143  * root@local# sysctl dev.ntb_tool.0.peer_db_mask="s 0x1"
  144  * root@local# sysctl dev.ntb_tool.0.peer_db_mask="c 0x1"
  145  * root@local# sysctl dev.ntb_tool.0.peer_db_mask
  146  *
  147  * # Poll until local doorbell is set with the specified db bits
  148  * root@local# dev.ntb_tool.0.db_event=0x1
  149  *-----------------------------------------------------------------------------
  150  * Eg: Scratchpad registers tests
  151  *
  152  * # Write/read to/from local scratchpad register #0
  153  * root@local# sysctl dev.ntb_tool.0.spad0=0x1023457
  154  * root@local# sysctl dev.ntb_tool.0.spad0
  155  *
  156  * # Write/read to/from peer scratchpad register #0
  157  * root@local# sysctl dev.ntb_tool.0.peer0.spad0=0x01020304
  158  * root@local# sysctl dev.ntb_tool.0.peer0.spad0
  159  *-----------------------------------------------------------------------------
  160  * Eg: Memory windows tests (need to configure local mw_trans on both sides)
  161  *
  162  * # Create inbound memory window buffer of specified size/get its dma address
  163  * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0=16384
  164  * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0
  165  *
  166  * # Write/read data to/from inbound memory window with specific pattern/random
  167  * data.
  168  * root@local# sysctl dev.ntb_tool.0.peer0.mw0="W offset 0 nbytes 100 pattern ab"
  169  * root@local# sysctl dev.ntb_tool.0.peer0.mw0="R offset 0 nbytes 100"
  170  *
  171  * # Write/read data to/from outbound memory window on the local device with
  172  * specific pattern/random (on peer device)
  173  * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="W offset 0 nbytes 100 pattern ab"
  174  * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="R offset 0 nbytes 100"
  175  *-----------------------------------------------------------------------------
  176  * NOTE: *Message registers are not supported*
  177  *-----------------------------------------------------------------------------
  178  *
  179  * contact information:
  180  * Arpan Palit <arpan.palit@amd.com>
  181  *
  182  */
  183 
  184 #include <sys/cdefs.h>
  185 __FBSDID("$FreeBSD$");
  186 
  187 #include <sys/param.h>
  188 #include <sys/bus.h>
  189 #include <sys/kernel.h>
  190 #include <sys/module.h>
  191 #include <sys/mbuf.h>
  192 #include <sys/sysctl.h>
  193 #include <sys/sbuf.h>
  194 
  195 #include <machine/bus.h>
  196 
  197 #include <vm/vm.h>
  198 
  199 #include "../ntb.h"
  200 
  201 /* Buffer length for User input */
  202 #define TOOL_BUF_LEN 48
  203 /* Memory window default command read and write offset. */
  204 #define DEFAULT_MW_OFF  0
  205 /* Memory window default size and also max command read size. */
  206 #define DEFAULT_MW_SIZE 1024
  207 
  208 MALLOC_DEFINE(M_NTB_TOOL, "ntb_tool", "ntb_tool driver memory allocation");
  209 
  210 /*
  211  * Memory windows descriptor structure
  212  */
  213 struct tool_mw {
  214         struct tool_ctx    *tc;
  215         int                widx;
  216         int                pidx;
  217 
  218         /* Rx buff is off virt_addr / dma_base */
  219         bus_addr_t         dma_base;
  220         caddr_t            virt_addr;
  221         bus_dmamap_t       dma_map;
  222         bus_dma_tag_t      dma_tag;
  223 
  224         /* Tx buff is off vbase / phys_addr */
  225         caddr_t            mm_base;
  226         vm_paddr_t         phys_addr;
  227         bus_addr_t         addr_limit;
  228         size_t             phys_size;
  229         size_t             xlat_align;
  230         size_t             xlat_align_size;
  231 
  232         /* Memory window configured size and limits */
  233         size_t             size;
  234         ssize_t            mw_buf_size;
  235         ssize_t            mw_buf_offset;
  236         ssize_t            mw_peer_buf_size;
  237         ssize_t            mw_peer_buf_offset;
  238 
  239         /* options to handle sysctl out */
  240         int                mw_cmd_rw;
  241         int                mw_peer_cmd_rw;
  242 };
  243 
  244 struct tool_spad {
  245         int                sidx;
  246         int                pidx;
  247         struct tool_ctx    *tc;
  248 };
  249 
  250 struct tool_peer {
  251         int                 pidx;
  252         struct tool_ctx     *tc;
  253         int                 inmw_cnt;
  254         struct tool_mw      *inmws;
  255         int                 outspad_cnt;
  256         struct tool_spad    *outspads;
  257         unsigned int        port_no;
  258 };
  259 
  260 struct tool_ctx {
  261         device_t            dev;
  262         struct callout      link_event_timer;
  263         struct callout      db_event_timer;
  264         int                 peer_cnt;
  265         struct tool_peer    *peers;
  266         int                 inmsg_cnt;
  267         struct tool_msg     *inmsgs;
  268         int                 inspad_cnt;
  269         struct tool_spad    *inspads;
  270         unsigned int        unsafe;
  271 
  272         /* sysctl read out variables */
  273         char                link_status;
  274         uint64_t            link_bits;
  275         uint64_t            link_mask;
  276         uint64_t            db_valid_mask;
  277         uint64_t            db_mask_val;
  278         uint64_t            db_event_val;
  279         uint64_t            peer_db_val;
  280         uint64_t            peer_db_mask_val;
  281         unsigned int        port_no;
  282 };
  283 
  284 /* structure to save dma_addr after dma load */
  285 struct ntb_tool_load_cb_args {
  286         bus_addr_t addr;
  287         int error;
  288 };
  289 
  290 /*
  291  * NTB events handlers
  292  */
  293 static void
  294 tool_link_event(void *ctx)
  295 {
  296         struct tool_ctx *tc = ctx;
  297         enum ntb_speed speed = 0;
  298         enum ntb_width width = 0;
  299         int up = 0;
  300 
  301         up = ntb_link_is_up(tc->dev, &speed, &width);
  302         if (up)
  303                 tc->link_status = 'Y';
  304         else
  305                 tc->link_status = 'N';
  306 
  307         device_printf(tc->dev, "link is %s speed %d width %d\n",
  308             up ? "up" : "down", speed, width);
  309 }
  310 
  311 static void
  312 tool_db_event(void *ctx, uint32_t vec)
  313 {
  314         struct tool_ctx *tc = ctx;
  315         uint64_t db_bits, db_mask;
  316 
  317         db_mask = ntb_db_vector_mask(tc->dev, vec);
  318         db_bits = ntb_db_read(tc->dev);
  319 
  320         device_printf(tc->dev, "doorbell vec %d mask %#llx bits %#llx\n",
  321             vec, (unsigned long long)db_mask, (unsigned long long)db_bits);
  322 }
  323 
  324 static const struct ntb_ctx_ops tool_ops = {
  325         .link_event = tool_link_event,
  326         .db_event = tool_db_event,
  327 };
  328 
  329 /*
  330  * Callout event methods
  331  */
  332 static void
  333 tool_link_event_handler(void *arg)
  334 {
  335         struct tool_ctx *tc = (struct tool_ctx *)arg;
  336         uint64_t val;
  337 
  338         val = ntb_link_is_up(tc->dev, NULL, NULL) & tc->link_mask;
  339 
  340         if (val == tc->link_bits) {
  341                 device_printf(tc->dev, "link_event successful for link val="
  342                     "0x%jx\n", tc->link_bits);
  343                 tc->link_bits = 0x0;
  344                 tc->link_mask = 0x0;
  345         } else
  346                 callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
  347 }
  348 
  349 static void
  350 tool_db_event_handler(void *arg)
  351 {
  352         struct tool_ctx *tc = (struct tool_ctx *)arg;
  353         uint64_t db_bits;
  354 
  355         db_bits = ntb_db_read(tc->dev);
  356 
  357         if (db_bits == tc->db_event_val) {
  358                 device_printf(tc->dev, "db_event successful for db val=0x%jx\n",
  359                     tc->db_event_val);
  360                 tc->db_event_val = 0x0;
  361         } else
  362                 callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
  363 }
  364 
  365 /*
  366  * Common read/write methods
  367  */
  368 static inline int
  369 get_ubuf(struct sysctl_req *req, char *ubuf)
  370 {
  371         int rc;
  372 
  373         if (req->newlen >= TOOL_BUF_LEN)
  374                 return (EINVAL);
  375 
  376         rc = SYSCTL_IN(req, ubuf, req->newlen);
  377         if (rc)
  378                 return (rc);
  379         ubuf[req->newlen] = '\0';
  380 
  381         return (0);
  382 }
  383 
  384 static int
  385 read_out(struct sysctl_req *req, uint64_t val)
  386 {
  387         char ubuf[19];
  388 
  389         memset((void *)ubuf, 0, sizeof(ubuf));
  390         snprintf(ubuf, sizeof(ubuf), "0x%jx", val);
  391 
  392         return SYSCTL_OUT(req, ubuf, sizeof(ubuf));
  393 }
  394 
  395 static int
  396 tool_fn_read(struct tool_ctx *tc, struct sysctl_req *req,
  397     uint64_t (*fn_read)(device_t ), uint64_t val)
  398 {
  399         if (fn_read == NULL)
  400                 return read_out(req, val);
  401         else if (fn_read)
  402                 return read_out(req, (uint64_t)fn_read(tc->dev));
  403         else
  404                 return (EINVAL);
  405 }
  406 
  407 static int
  408 tool_fn_write(struct tool_ctx *tc, struct sysctl_oid *oidp,
  409     struct sysctl_req *req, char *ubuf, uint64_t *val, bool db_mask_sflag,
  410     void (*fn_set)(device_t , uint64_t), void (*fn_clear)(device_t , uint64_t))
  411 {
  412         uint64_t db_valid_mask = tc->db_valid_mask;
  413         uint64_t bits;
  414         char cmd;
  415 
  416         if (fn_set == NULL && fn_clear == NULL) {
  417                 device_printf(tc->dev, "ERR: Set & Clear both are not supported\n");
  418                 return (EINVAL);
  419         }
  420 
  421         if (tc->db_valid_mask == 0)
  422                 db_valid_mask = tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
  423 
  424         bits = 0;
  425         sscanf(ubuf, "%c %jx", &cmd, &bits);
  426         if (cmd == 's') {
  427                 if ((bits | db_valid_mask) > db_valid_mask) {
  428                         device_printf(tc->dev, "0x%jx value is not supported\n", bits);
  429                         return (EINVAL);
  430                 }
  431                 if (fn_set)
  432                         fn_set(tc->dev, bits);
  433                 else
  434                         return (EINVAL);
  435                 if (val)
  436                         *val |= bits;
  437         } else if (cmd == 'c') {
  438                 if ((bits | db_valid_mask) > db_valid_mask) {
  439                         device_printf(tc->dev, "0x%jx value is not supported\n", bits);
  440                         return (EINVAL);
  441                 }
  442                 if (fn_clear)
  443                         fn_clear(tc->dev, bits);
  444                 if (val)
  445                         *val &= ~bits;
  446         } else {
  447                 device_printf(tc->dev, "Wrong Write\n");
  448                 return (EINVAL);
  449         }
  450 
  451         return (0);
  452 }
  453 
  454 static int
  455 parse_mw_buf(char *buf, char *cmd, ssize_t *offset, ssize_t *buf_size,
  456     uint64_t *pattern, bool *s_pflag)
  457 {
  458         char op1[8], op2[8], op3[8];
  459         uint64_t val1, val2, val3;
  460         bool vs1, vs2, vs3;
  461         int rc = 0;
  462 
  463         vs1 = vs2 = vs3 = false;
  464         sscanf(buf, "%c %s %jx %s %jx %s %jx",
  465             cmd, op1, &val1, op2, &val2, op3, &val3);
  466 
  467         if (*cmd != 'W' && *cmd != 'R')
  468                 return (EINVAL);
  469 
  470         if (!strcmp(op1, "offset")) {
  471                 *offset = val1 ? val1 : DEFAULT_MW_OFF;
  472                 vs1 = true;
  473         } else if (!strcmp(op1, "nbytes")) {
  474                 *buf_size = val1 ? val1: DEFAULT_MW_SIZE;
  475                 vs2 = true;
  476         } else if (!strcmp(op1, "pattern")) {
  477                 *pattern = val1;
  478                 vs3 = true;
  479         }
  480 
  481         if (!vs1 && !strcmp(op2, "offset")) {
  482                 *offset = val2 ? val2 : DEFAULT_MW_OFF;
  483                 vs1 = true;
  484         } else if (!vs2 && !strcmp(op2, "nbytes")) {
  485                 *buf_size = val2 ? val2: DEFAULT_MW_SIZE;
  486                 vs2 = true;
  487         } else if (!vs3 && !strcmp(op2, "pattern")) {
  488                 *pattern = val2;
  489                 vs3 = true;
  490         }
  491 
  492         if (!vs1 && !strcmp(op3, "offset")) {
  493                 *offset = val3 ? val3 : DEFAULT_MW_OFF;
  494         } else if (!vs2 && !strcmp(op3, "nbytes")) {
  495                 *buf_size = val3 ? val3: DEFAULT_MW_SIZE;
  496         } else if (!vs3 && !strcmp(op3, "pattern")) {
  497                 *pattern = val3;
  498                 vs3 = true;
  499         }
  500 
  501         *s_pflag = vs3;
  502         if (vs3 && *cmd == 'R')
  503                 printf("NTB_TOOL_WARN: pattern is not supported with read "
  504                     "command\n");
  505 
  506         return (rc);
  507 }
  508 
  509 static int
  510 tool_mw_read_fn(struct sysctl_req *req, struct tool_mw *inmw, char *read_addr,
  511     int *cmd_op, ssize_t buf_off, ssize_t buf_size, char *type)
  512 {
  513         ssize_t index, size;
  514         struct sbuf *sb;
  515         int i, loop, rc;
  516         char *tmp;
  517 
  518         /* The below check is made to ignore sysctl read call. */
  519         if (*cmd_op == 0)
  520                 return (0);
  521 
  522         /* Proceeds only when command R/W is requested using sysctl. */
  523         index = buf_off;
  524         tmp = read_addr;
  525         tmp += index;
  526         loop = ((buf_size == 0) || (buf_size > DEFAULT_MW_SIZE)) ?
  527             DEFAULT_MW_SIZE : buf_size;
  528         /*
  529          * 256 bytes of extra buffer has been allocated to print details like
  530          * summary, size, notes, i.e., excluding data part.
  531          */
  532         size = loop + 256;
  533         sb = sbuf_new_for_sysctl(NULL, NULL, size, req);
  534         if (sb == NULL) {
  535                 rc = sb->s_error;
  536                 return (rc);
  537         }
  538 
  539         if (!strcmp(type, "mw"))
  540                 sbuf_printf(sb, "\nConfigured MW size\t: %zu\n", inmw->size);
  541         else if (!strcmp(type, "peer_mw"))
  542                 sbuf_printf(sb, "\nConfigured Peer MW size\t: %zu\n",
  543                     inmw->size);
  544         sbuf_printf(sb, "R/W size\t\t: %zi\nR/W Offset\t\t: %zi\n\nData\n----"
  545             "->", buf_size, buf_off);
  546 
  547         /*
  548          * Data will be read based on MW size provided by the user using nbytes,
  549          * which is limited to 1024 bytes if user req bigger size to read, check
  550          * above loop calculation which is limiting or setting the MW read size.
  551          * Below for loop prints data where in each line contains 32 bytes data
  552          * and after each 8 bytes of data we used four spaces which ensures one
  553          * data block.
  554          */
  555         for (i = 0 ; i < loop; i++) {
  556                 if ((i % 32) == 0) {
  557                         sbuf_printf(sb, "\n%08zx:", index);
  558                         index += 32;
  559                 }
  560                 if ((i % 8) == 0)
  561                         sbuf_printf(sb, "    ");
  562                 sbuf_printf(sb, "%02hhx", *(tmp+i));
  563         }
  564         if (buf_size > DEFAULT_MW_SIZE)
  565                 sbuf_printf(sb, "\n\nNOTE: Truncating read size %zi->1024 "
  566                     "bytes\n", buf_size);
  567 
  568         /* cmd_op is set to zero after completion of each R/W command. */
  569         *cmd_op -= 1;
  570         rc = sbuf_finish(sb);
  571         sbuf_delete(sb);
  572 
  573         return (rc);
  574 }
  575 
  576 static int
  577 tool_mw_write_fn(struct sysctl_oid *oidp, struct sysctl_req *req,
  578     struct tool_mw *inmw, char *ubuf, caddr_t write_buf, int *cmd_op,
  579     ssize_t *buf_offset, ssize_t *buf_size)
  580 {
  581         ssize_t data_buf_size;
  582         uint64_t pattern = 0;
  583         bool s_pflag = false;
  584         void *data_buf;
  585         char cmd;
  586         int rc;
  587 
  588         if (!write_buf)
  589                 return (ENXIO);
  590 
  591         /* buf_offset and buf_size set to default in case user does not req */
  592         *buf_offset = DEFAULT_MW_OFF;
  593         *buf_size = DEFAULT_MW_SIZE;
  594         rc = parse_mw_buf(ubuf, &cmd, buf_offset, buf_size, &pattern, &s_pflag);
  595         if (rc) {
  596                 device_printf(inmw->tc->dev, "Wrong Command \"%c\" provided\n",
  597                     cmd);
  598                 return (rc);
  599         }
  600 
  601         /* Check for req size and buffer limit */
  602         if ((*buf_offset + *buf_size) > inmw->size) {
  603                 device_printf(inmw->tc->dev, "%s: configured mw size :%zi and "
  604                     "requested size :%zi.\n", __func__, inmw->size,
  605                     (*buf_offset + *buf_size));
  606                 *buf_offset = DEFAULT_MW_OFF;
  607                 *buf_size = DEFAULT_MW_SIZE;
  608                 rc = EINVAL;
  609                 goto out;
  610         }
  611 
  612         if (cmd == 'R')
  613                 goto read_out;
  614         else if (cmd == 'W')
  615                 goto write;
  616         else
  617                 goto out;
  618 
  619 write:
  620         data_buf_size = *buf_size;
  621         data_buf = malloc(data_buf_size, M_NTB_TOOL, M_WAITOK | M_ZERO);
  622 
  623         if (s_pflag)
  624                 memset(data_buf, pattern, data_buf_size);
  625         else
  626                 arc4rand(data_buf, data_buf_size, 1);
  627 
  628         memcpy(write_buf + *buf_offset, data_buf, data_buf_size);
  629 
  630         free(data_buf, M_NTB_TOOL);
  631 
  632 read_out:
  633         /* cmd_op value is set to two as sysctl read call executes twice */
  634         *cmd_op = 2;
  635 out:
  636         return (rc);
  637 }
  638 
  639 /*
  640  * Port sysctl read/write methods
  641  */
  642 static int
  643 sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)
  644 {
  645         struct tool_ctx *tc = (struct tool_ctx *)arg1;
  646         int rc, pidx = arg2, peer_port;
  647 
  648         peer_port = ntb_peer_port_number(tc->dev, pidx);
  649         rc = sysctl_handle_int(oidp, &peer_port, 0, req);
  650         if (rc)
  651                 device_printf(tc->dev, "Peer port sysctl set failed with err="
  652                     "(%d).\n", rc);
  653         else
  654                 tc->peers[pidx].port_no = peer_port;
  655 
  656         return (rc);
  657 }
  658 
  659 static int
  660 sysctl_local_port_number(SYSCTL_HANDLER_ARGS)
  661 {
  662         struct tool_ctx *tc = (struct tool_ctx *)arg1;
  663         int rc, local_port;
  664 
  665         local_port = ntb_port_number(tc->dev);
  666         rc = sysctl_handle_int(oidp, &local_port, 0, req);
  667         if (rc)
  668                 device_printf(tc->dev, "Local port sysctl set failed with err="
  669                     "(%d).\n", rc);
  670         else
  671                 tc->port_no = local_port;
  672 
  673         return (rc);
  674 }
  675 
  676 static void
  677 tool_init_peers(struct tool_ctx *tc)
  678 {
  679         int pidx;
  680 
  681         tc->peer_cnt = ntb_peer_port_count(tc->dev);
  682         tc->peers = malloc(tc->peer_cnt * sizeof(*tc->peers), M_NTB_TOOL,
  683             M_WAITOK | M_ZERO);
  684         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
  685                 tc->peers[pidx].pidx = pidx;
  686                 tc->peers[pidx].tc = tc;
  687         }
  688 }
  689 
  690 static void
  691 tool_clear_peers(struct tool_ctx *tc)
  692 {
  693 
  694         free(tc->peers, M_NTB_TOOL);
  695 }
  696 
  697 /*
  698  * Link state sysctl read/write methods
  699  */
  700 static int
  701 sysctl_link_handle(SYSCTL_HANDLER_ARGS)
  702 {
  703         struct tool_ctx *tc = (struct tool_ctx *)arg1;
  704         char buf[TOOL_BUF_LEN];
  705         int rc;
  706 
  707         if (req->newptr == NULL) {
  708                 snprintf(buf, 2, "%c", tc->link_status);
  709 
  710                 return SYSCTL_OUT(req, buf, 2);
  711         }
  712 
  713         rc = get_ubuf(req, buf);
  714         if (rc)
  715                 return (rc);
  716 
  717         if (buf[0] == 'Y')
  718                 rc = ntb_link_enable(tc->dev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
  719         else if (buf[0] == 'N')
  720                 rc = ntb_link_disable(tc->dev);
  721         else
  722                 rc = EINVAL;
  723 
  724         sscanf(buf, "%c", &tc->link_status);
  725 
  726         return (0);
  727 }
  728 
  729 static int
  730 sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)
  731 {
  732         struct tool_ctx *tc = (struct tool_ctx *)arg1;
  733         int up = 0, pidx = arg2;
  734         char buf[TOOL_BUF_LEN];
  735 
  736         if (req->newptr)
  737                 return (0);
  738 
  739         up = ntb_link_is_up(tc->dev, NULL, NULL);
  740         memset((void *)buf, 0, TOOL_BUF_LEN);
  741         if (up & (1UL << pidx))
  742                 buf[0] = 'Y';
  743         else
  744                 buf[0] = 'N';
  745 
  746         return SYSCTL_OUT(req, buf, sizeof(buf));
  747 }
  748 
  749 static int
  750 sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)
  751 {
  752         struct tool_ctx *tc = (struct tool_ctx *)arg1;
  753         char buf[TOOL_BUF_LEN];
  754         int rc, pidx = arg2;
  755         uint64_t bits;
  756 
  757         if (req->newptr == NULL)
  758                 return read_out(req, tc->link_bits);
  759 
  760         rc = get_ubuf(req, buf);
  761         if (rc)
  762                 return (rc);
  763 
  764         sscanf(buf, "0x%jx", &bits);
  765         tc->link_bits = bits;
  766         tc->link_mask = (1ULL << ((pidx) % 64));
  767 
  768         callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
  769         return (0);
  770 }
  771 
  772 /*
  773  * Memory windows read/write/setting methods
  774  */
  775 static void
  776 ntb_tool_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
  777 {
  778         struct ntb_tool_load_cb_args *cba = (struct ntb_tool_load_cb_args *)arg;
  779 
  780         if (!(cba->error = error))
  781                 cba->addr = segs[0].ds_addr;
  782 }
  783 
  784 static int
  785 sysctl_mw_handle(SYSCTL_HANDLER_ARGS)
  786 {
  787         struct tool_mw *inmw = (struct tool_mw *)arg1;
  788         char buf[TOOL_BUF_LEN];
  789         int rc;
  790 
  791         if (req->newptr == NULL)
  792                 return tool_mw_read_fn(req, inmw, (char *)inmw->mm_base,
  793                     &inmw->mw_cmd_rw, inmw->mw_buf_offset, inmw->mw_buf_size,
  794                     "mw");
  795 
  796         rc = get_ubuf(req, buf);
  797         if (!rc)
  798                 return tool_mw_write_fn(oidp, req, inmw, buf, inmw->mm_base,
  799                     &inmw->mw_cmd_rw, &inmw->mw_buf_offset, &inmw->mw_buf_size);
  800 
  801         return (rc);
  802 }
  803 
  804 static int
  805 tool_setup_mw(struct tool_ctx *tc, unsigned int pidx, unsigned int widx,
  806     size_t req_size)
  807 {
  808         struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
  809         struct ntb_tool_load_cb_args cba;
  810         int rc;
  811 
  812         if (req_size == 0)
  813                 inmw->size = roundup(inmw->phys_size, inmw->xlat_align_size);
  814         else
  815                 inmw->size = roundup(req_size, inmw->xlat_align_size);
  816 
  817         device_printf(tc->dev, "mw_size %zi req_size %zi buff %zi\n",
  818             inmw->phys_size, req_size, inmw->size);
  819 
  820         if (bus_dma_tag_create(bus_get_dma_tag(tc->dev), inmw->xlat_align, 0,
  821             inmw->addr_limit, BUS_SPACE_MAXADDR, NULL, NULL, inmw->size, 1,
  822             inmw->size, 0, NULL, NULL, &inmw->dma_tag)) {
  823                 device_printf(tc->dev, "Unable to create MW tag of size "
  824                     "%zu/%zu\n", inmw->phys_size, inmw->size);
  825                 rc = ENOMEM;
  826                 goto err_free_dma_var;
  827         }
  828 
  829         if (bus_dmamem_alloc(inmw->dma_tag, (void **)&inmw->virt_addr,
  830             BUS_DMA_WAITOK | BUS_DMA_ZERO, &inmw->dma_map)) {
  831                 device_printf(tc->dev, "Unable to allocate MW buffer of size "
  832                     "%zu/%zu\n", inmw->phys_size, inmw->size);
  833                 rc = ENOMEM;
  834                 goto err_free_tag_rem;
  835         }
  836 
  837         if (bus_dmamap_load(inmw->dma_tag, inmw->dma_map, inmw->virt_addr,
  838             inmw->size, ntb_tool_load_cb, &cba, BUS_DMA_NOWAIT) || cba.error) {
  839                 device_printf(tc->dev, "Unable to load MW buffer of size "
  840                     "%zu/%zu\n", inmw->phys_size, inmw->size);
  841                 rc = ENOMEM;
  842                 goto err_free_dma;
  843         }
  844         inmw->dma_base = cba.addr;
  845 
  846         rc = ntb_mw_set_trans(tc->dev, widx, inmw->dma_base, inmw->size);
  847         if (rc)
  848                 goto err_free_mw;
  849 
  850         return (0);
  851 
  852 err_free_mw:
  853         bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
  854 
  855 err_free_dma:
  856         bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
  857 
  858 err_free_tag_rem:
  859         bus_dma_tag_destroy(inmw->dma_tag);
  860 
  861 err_free_dma_var:
  862         inmw->size = 0;
  863         inmw->virt_addr = 0;
  864         inmw->dma_base = 0;
  865         inmw->dma_tag = 0;
  866         inmw->dma_map = 0;
  867 
  868         return (rc);
  869 }
  870 
  871 static void
  872 tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
  873 {
  874         struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
  875 
  876         if (inmw->dma_base)
  877                 ntb_mw_clear_trans(tc->dev, widx);
  878 
  879         if (inmw->virt_addr && inmw->dma_tag) {
  880                 bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
  881                 bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
  882                 bus_dma_tag_destroy(inmw->dma_tag);
  883         }
  884 
  885         inmw->virt_addr = 0;
  886         inmw->dma_base = 0;
  887         inmw->dma_tag = 0;
  888         inmw->dma_map = 0;
  889         inmw->mm_base = 0;
  890         inmw->size = 0;
  891 }
  892 
  893 static int
  894 tool_mw_trans_read(struct tool_mw *inmw, struct sysctl_req *req)
  895 {
  896         ssize_t buf_size = 512;
  897         struct sbuf *sb;
  898         int rc = 0;
  899 
  900         sb = sbuf_new_for_sysctl(NULL, NULL, buf_size, req);
  901         if (sb == NULL) {
  902                 rc = sb->s_error;
  903                 return (rc);
  904         }
  905 
  906         sbuf_printf(sb, "\nInbound MW     \t%d\n", inmw->widx);
  907         sbuf_printf(sb, "Port           \t%d (%d)\n",
  908             ntb_peer_port_number(inmw->tc->dev, inmw->pidx), inmw->pidx);
  909         sbuf_printf(sb, "Window Address \t%p\n", inmw->mm_base);
  910         sbuf_printf(sb, "DMA Address    \t0x%016llx\n", (long long)inmw->dma_base);
  911         sbuf_printf(sb, "Window Size    \t0x%016zx[p]\n", inmw->size);
  912         sbuf_printf(sb, "Alignment      \t0x%016zx[p]\n", inmw->xlat_align);
  913         sbuf_printf(sb, "Size Alignment \t0x%016zx[p]\n",
  914             inmw->xlat_align_size);
  915         sbuf_printf(sb, "Size Max       \t0x%016zx[p]\n", inmw->phys_size);
  916 
  917         rc = sbuf_finish(sb);
  918         sbuf_delete(sb);
  919 
  920         return (rc);
  921 }
  922 
  923 static int
  924 tool_mw_trans_write(struct sysctl_oid *oidp, struct sysctl_req *req,
  925     struct tool_mw *inmw, size_t wsize)
  926 {
  927         struct tool_ctx *tc = inmw->tc;
  928         int rc = 0;
  929 
  930         if (wsize == 0)
  931                 return (EINVAL);
  932 
  933         /* No need to re-setup mw */
  934         if (inmw->size == wsize)
  935                 return (0);
  936 
  937         /* free mw dma buffer */
  938         if (inmw->size)
  939                 tool_free_mw(tc, inmw->pidx, inmw->widx);
  940 
  941         rc = tool_setup_mw(tc, inmw->pidx, inmw->widx, wsize);
  942 
  943         return (rc);
  944 }
  945 
  946 static int
  947 sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)
  948 {
  949         struct tool_mw *inmw = (struct tool_mw *)arg1;
  950         char buf[TOOL_BUF_LEN];
  951         ssize_t wsize;
  952         int rc;
  953 
  954         if (req->newptr == NULL)
  955                 return tool_mw_trans_read(inmw, req);
  956 
  957         rc = get_ubuf(req, buf);
  958         if (rc == 0) {
  959                 sscanf(buf, "%zi", &wsize);
  960                 return tool_mw_trans_write(oidp, req, inmw, wsize);
  961         }
  962 
  963         return (rc);
  964 }
  965 
  966 static int
  967 sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)
  968 {
  969         struct tool_mw *inmw = (struct tool_mw *)arg1;
  970         char buf[TOOL_BUF_LEN];
  971         int rc;
  972 
  973         if (req->newptr == NULL)
  974                 return tool_mw_read_fn(req, inmw, (char *)inmw->virt_addr,
  975                     &inmw->mw_peer_cmd_rw, inmw->mw_peer_buf_offset,
  976                     inmw->mw_peer_buf_size, "mw");
  977 
  978         rc = get_ubuf(req, buf);
  979         if (rc == 0)
  980                 return tool_mw_write_fn(oidp, req, inmw, buf, inmw->virt_addr,
  981                     &inmw->mw_peer_cmd_rw, &inmw->mw_peer_buf_offset,
  982                     &inmw->mw_peer_buf_size);
  983 
  984         return (rc);
  985 }
  986 
  987 static void tool_clear_mws(struct tool_ctx *tc)
  988 {
  989         int widx, pidx;
  990 
  991         /* Free outbound memory windows */
  992         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
  993                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++)
  994                         tool_free_mw(tc, pidx, widx);
  995                 free(tc->peers[pidx].inmws, M_NTB_TOOL);
  996         }
  997 }
  998 
  999 static int
 1000 tool_init_mws(struct tool_ctx *tc)
 1001 {
 1002         struct tool_mw *mw;
 1003         int widx, pidx, rc;
 1004 
 1005         /* Initialize inbound memory windows and outbound MWs wrapper */
 1006         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
 1007                 tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->dev);
 1008                 tc->peers[pidx].inmws = malloc(tc->peers[pidx].inmw_cnt *
 1009                     sizeof(*tc->peers[pidx].inmws), M_NTB_TOOL,
 1010                     M_WAITOK | M_ZERO);
 1011 
 1012                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
 1013                         mw = &tc->peers[pidx].inmws[widx];
 1014                         memset((void *)mw, 0, sizeof(*mw));
 1015                         mw->tc = tc;
 1016                         mw->widx = widx;
 1017                         mw->pidx = pidx;
 1018                         mw->mw_buf_offset = DEFAULT_MW_OFF;
 1019                         mw->mw_buf_size = DEFAULT_MW_SIZE;
 1020                         /* get the tx buff details for each mw attached with each peer */
 1021                         rc = ntb_mw_get_range(tc->dev, widx, &mw->phys_addr,
 1022                             &mw->mm_base, &mw->phys_size, &mw->xlat_align,
 1023                             &mw->xlat_align_size, &mw->addr_limit);
 1024                         if (rc)
 1025                                 goto free_mws;
 1026                 }
 1027         }
 1028 
 1029         return (0);
 1030 
 1031 free_mws:
 1032         tool_clear_mws(tc);
 1033         return (rc);
 1034 }
 1035 
 1036 /*
 1037  * Doorbell handler for read/write
 1038  */
 1039 static int
 1040 sysctl_db_handle(SYSCTL_HANDLER_ARGS)
 1041 {
 1042         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1043         char buf[TOOL_BUF_LEN];
 1044         uint64_t db_bits;
 1045         int rc;
 1046 
 1047         if (req->newptr == NULL) {
 1048                 db_bits = ntb_db_read(tc->dev);
 1049                 return read_out(req, db_bits);
 1050         }
 1051 
 1052         rc = get_ubuf(req, buf);
 1053         if (rc == 0)
 1054                 return tool_fn_write(tc, oidp, req, buf, NULL, false, NULL,
 1055                     ntb_db_clear);
 1056 
 1057         return (rc);
 1058 }
 1059 
 1060 static int
 1061 sysctl_db_valid_mask_handle(SYSCTL_HANDLER_ARGS)
 1062 {
 1063         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1064 
 1065         tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
 1066         if (!tc->db_valid_mask) {
 1067                 device_printf(tc->dev, "Error getting db_valid_mask from "
 1068                     "hw driver\n");
 1069                 return (EINVAL);
 1070         } else {
 1071                 return read_out(req, tc->db_valid_mask);
 1072         }
 1073 }
 1074 
 1075 static int
 1076 sysctl_db_mask_handle(SYSCTL_HANDLER_ARGS)
 1077 {
 1078         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1079         char buf[TOOL_BUF_LEN];
 1080         int rc;
 1081 
 1082         if (req->newptr == NULL) {
 1083                 if (tc->db_mask_val == 0)
 1084                      ntb_db_valid_mask(tc->dev);
 1085                 return tool_fn_read(tc, req, NULL, tc->db_mask_val);
 1086         }
 1087 
 1088         rc = get_ubuf(req, buf);
 1089         if (rc == 0)
 1090                 return tool_fn_write(tc, oidp, req, buf, &tc->db_mask_val, true,
 1091                     ntb_db_set_mask, ntb_db_clear_mask);
 1092 
 1093         return (rc);
 1094 }
 1095 
 1096 static int
 1097 sysctl_peer_db_handle(SYSCTL_HANDLER_ARGS)
 1098 {
 1099         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1100         char buf[TOOL_BUF_LEN];
 1101         int rc;
 1102 
 1103         if (req->newptr == NULL)
 1104                 return tool_fn_read(tc, req, NULL, tc->peer_db_val);
 1105 
 1106         rc = get_ubuf(req, buf);
 1107         if (rc == 0)
 1108                 return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_val,
 1109                     false, ntb_peer_db_set, NULL);
 1110 
 1111         return (rc);
 1112 }
 1113 
 1114 static int
 1115 sysctl_peer_db_mask_handle(SYSCTL_HANDLER_ARGS)
 1116 {
 1117         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1118         char buf[TOOL_BUF_LEN];
 1119         int rc;
 1120 
 1121         if (req->newptr == NULL){
 1122                 if (tc->peer_db_mask_val == 0)
 1123                         ntb_db_valid_mask(tc->dev);
 1124                 return tool_fn_read(tc, req, NULL, tc->peer_db_mask_val);
 1125         }
 1126 
 1127         rc = get_ubuf(req, buf);
 1128         if (rc == 0)
 1129                 return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_mask_val,
 1130                     true, NULL, NULL);
 1131 
 1132         return (rc);
 1133 }
 1134 
 1135 static int
 1136 sysctl_db_event_handle(SYSCTL_HANDLER_ARGS)
 1137 {
 1138         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1139         char buf[TOOL_BUF_LEN];
 1140         uint64_t bits;
 1141         int rc;
 1142 
 1143         if (req->newptr == NULL)
 1144                 return read_out(req, tc->db_event_val);
 1145 
 1146         rc = get_ubuf(req, buf);
 1147         if (rc)
 1148                 return (rc);
 1149 
 1150         sscanf(buf, "%ju", &bits);
 1151         tc->db_event_val = bits;
 1152         callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
 1153 
 1154         return (0);
 1155 }
 1156 
 1157 /*
 1158  * Scratchpads read/write methods
 1159  */
 1160 static int
 1161 sysctl_spad_handle(SYSCTL_HANDLER_ARGS)
 1162 {
 1163         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1164         unsigned int sidx = arg2;
 1165         char buf[TOOL_BUF_LEN];
 1166         uint32_t bits;
 1167         int rc;
 1168 
 1169         if (req->newptr == NULL) {
 1170                 rc = ntb_spad_read(tc->dev, sidx, &bits);
 1171                 if (rc)
 1172                         return (rc);
 1173                 else
 1174                         return read_out(req, (uint64_t )bits);
 1175         }
 1176 
 1177         rc = get_ubuf(req, buf);
 1178         if (rc == 0) {
 1179                 sscanf(buf, "%i", &bits);
 1180                 return ntb_spad_write(tc->dev, sidx, bits);
 1181         }
 1182 
 1183         return (rc);
 1184 }
 1185 
 1186 static int
 1187 sysctl_peer_spad_handle(SYSCTL_HANDLER_ARGS)
 1188 {
 1189         struct tool_ctx *tc = (struct tool_ctx *)arg1;
 1190         unsigned int sidx = arg2;
 1191         char buf[TOOL_BUF_LEN];
 1192         uint32_t bits;
 1193         int rc;
 1194 
 1195         if (req->newptr == NULL) {
 1196                 rc = ntb_peer_spad_read(tc->dev, sidx, &bits);
 1197                 if (rc)
 1198                         return (rc);
 1199                 else
 1200                         return read_out(req, (uint64_t )bits);
 1201         }
 1202 
 1203         rc = get_ubuf(req, buf);
 1204         if (rc == 0) {
 1205                 sscanf(buf, "%i", &bits);
 1206                 return ntb_peer_spad_write(tc->dev, sidx, bits);
 1207         }
 1208 
 1209         return (rc);
 1210 }
 1211 
 1212 static void
 1213 tool_init_spads(struct tool_ctx *tc)
 1214 {
 1215         int sidx, pidx;
 1216 
 1217         /* Initialize inbound scratchpad structures */
 1218         tc->inspad_cnt = ntb_spad_count(tc->dev);
 1219         tc->inspads = malloc(tc->inspad_cnt * sizeof(*tc->inspads), M_NTB_TOOL,
 1220             M_WAITOK | M_ZERO);
 1221 
 1222         for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
 1223                 tc->inspads[sidx].sidx = sidx;
 1224                 tc->inspads[sidx].pidx = -1;
 1225                 tc->inspads[sidx].tc = tc;
 1226         }
 1227 
 1228         /* Initialize outbound scratchpad structures */
 1229         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
 1230                 tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->dev);
 1231                 tc->peers[pidx].outspads =  malloc(tc->peers[pidx].outspad_cnt *
 1232                     sizeof(*tc->peers[pidx].outspads), M_NTB_TOOL, M_WAITOK |
 1233                     M_ZERO);
 1234 
 1235                 for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
 1236                         tc->peers[pidx].outspads[sidx].sidx = sidx;
 1237                         tc->peers[pidx].outspads[sidx].pidx = pidx;
 1238                         tc->peers[pidx].outspads[sidx].tc = tc;
 1239                 }
 1240         }
 1241 }
 1242 
 1243 static void
 1244 tool_clear_spads(struct tool_ctx *tc)
 1245 {
 1246         int pidx;
 1247 
 1248         /* Free local inspads. */
 1249         free(tc->inspads, M_NTB_TOOL);
 1250 
 1251         /* Free outspads for each peer. */
 1252         for (pidx = 0; pidx < tc->peer_cnt; pidx++)
 1253                 free(tc->peers[pidx].outspads, M_NTB_TOOL);
 1254 }
 1255 
 1256 /*
 1257  * Initialization methods
 1258  */
 1259 static int
 1260 tool_check_ntb(struct tool_ctx *tc)
 1261 {
 1262 
 1263         /* create and initialize link callout handler */
 1264         callout_init(&tc->link_event_timer, 1);
 1265 
 1266         /* create and initialize db callout handler */
 1267         callout_init(&tc->db_event_timer, 1);
 1268 
 1269         /* Initialize sysctl read out values to default */
 1270         tc->link_status = 'U';
 1271         tc->db_mask_val = 0;
 1272         tc->peer_db_val = 0;
 1273         tc->peer_db_mask_val = 0;
 1274         tc->db_event_val = 0;
 1275         tc->link_bits = 0;
 1276 
 1277         return (0);
 1278 }
 1279 
 1280 static void
 1281 tool_clear_data(struct tool_ctx *tc)
 1282 {
 1283 
 1284         callout_drain(&tc->link_event_timer);
 1285         callout_drain(&tc->db_event_timer);
 1286 }
 1287 
 1288 static int
 1289 tool_init_ntb(struct tool_ctx *tc)
 1290 {
 1291 
 1292         return ntb_set_ctx(tc->dev, tc, &tool_ops);
 1293 }
 1294 
 1295 static void
 1296 tool_clear_ntb(struct tool_ctx *tc)
 1297 {
 1298 
 1299         ntb_clear_ctx(tc->dev);
 1300         ntb_link_disable(tc->dev);
 1301 }
 1302 
 1303 /*
 1304  *  Current sysctl implementation is made such that it gets attached to the
 1305  *  device and while detach it gets cleared automatically.
 1306  */
 1307 static void
 1308 tool_setup_sysctl(struct tool_ctx *tc)
 1309 {
 1310         char buf[TOOL_BUF_LEN], desc[TOOL_BUF_LEN];
 1311         struct sysctl_oid_list *top, *peer_top;
 1312         struct sysctl_oid *parent, *peer;
 1313         struct sysctl_ctx_list *clist;
 1314         unsigned int pidx, sidx, widx;
 1315 
 1316         clist = device_get_sysctl_ctx(tc->dev);
 1317         parent = device_get_sysctl_tree(tc->dev);
 1318         top = SYSCTL_CHILDREN(parent);
 1319 
 1320         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "port", CTLTYPE_UINT |
 1321             CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_local_port_number,
 1322             "IU", "local port number");
 1323 
 1324         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "link", CTLTYPE_STRING |
 1325             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_link_handle,
 1326             "IU", "link info");
 1327 
 1328         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db", CTLTYPE_STRING |
 1329             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_handle,
 1330             "A", "db info");
 1331 
 1332         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_valid_mask", CTLTYPE_STRING |
 1333             CTLFLAG_RD | CTLFLAG_MPSAFE, tc, 0, sysctl_db_valid_mask_handle,
 1334             "A", "db valid mask");
 1335 
 1336         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_mask", CTLTYPE_STRING |
 1337             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_mask_handle,
 1338             "A", "db mask");
 1339 
 1340         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_event", CTLTYPE_STRING |
 1341             CTLFLAG_WR | CTLFLAG_MPSAFE, tc, 0, sysctl_db_event_handle,
 1342             "A", "db event");
 1343 
 1344         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db", CTLTYPE_STRING |
 1345             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_handle,
 1346             "A", "peer db");
 1347 
 1348         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db_mask", CTLTYPE_STRING |
 1349             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_mask_handle,
 1350             "IU", "peer db mask info");
 1351 
 1352         if (tc->inspad_cnt != 0) {
 1353                 for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
 1354                         snprintf(buf, sizeof(buf), "spad%d", sidx);
 1355                         snprintf(desc, sizeof(desc), "spad%d info", sidx);
 1356 
 1357                         SYSCTL_ADD_PROC(clist, top, OID_AUTO, buf,
 1358                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
 1359                             tc, sidx, sysctl_spad_handle, "IU", desc);
 1360                 }
 1361         }
 1362 
 1363         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
 1364                 snprintf(buf, sizeof(buf), "peer%d", pidx);
 1365 
 1366                 peer = SYSCTL_ADD_NODE(clist, top, OID_AUTO, buf,
 1367                     CTLFLAG_RW | CTLFLAG_MPSAFE, 0, buf);
 1368                 peer_top = SYSCTL_CHILDREN(peer);
 1369 
 1370                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "port",
 1371                     CTLTYPE_UINT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, pidx,
 1372                     sysctl_peer_port_number, "IU", "peer port number");
 1373 
 1374                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link",
 1375                     CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
 1376                     sysctl_peer_link_handle, "IU", "peer_link info");
 1377 
 1378                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link_event",
 1379                     CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
 1380                     sysctl_peer_link_event_handle, "IU", "link event");
 1381 
 1382                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
 1383                         snprintf(buf, sizeof(buf), "mw_trans%d", widx);
 1384                         snprintf(desc, sizeof(desc), "mw trans%d info", widx);
 1385 
 1386                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
 1387                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
 1388                             &tc->peers[pidx].inmws[widx], 0,
 1389                             sysctl_mw_trans_handler, "IU", desc);
 1390 
 1391                         snprintf(buf, sizeof(buf), "mw%d", widx);
 1392                         snprintf(desc, sizeof(desc), "mw%d info", widx);
 1393 
 1394                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
 1395                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
 1396                             &tc->peers[pidx].inmws[widx], 0,
 1397                             sysctl_mw_handle, "IU", desc);
 1398 
 1399                         snprintf(buf, sizeof(buf), "peer_mw%d", widx);
 1400                         snprintf(desc, sizeof(desc), "peer_mw%d info", widx);
 1401 
 1402                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
 1403                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
 1404                             &tc->peers[pidx].inmws[widx], 0,
 1405                             sysctl_peer_mw_handle, "IU", desc);
 1406                 }
 1407 
 1408                 for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
 1409                         snprintf(buf, sizeof(buf), "spad%d", sidx);
 1410                         snprintf(desc, sizeof(desc), "spad%d info", sidx);
 1411 
 1412                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
 1413                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
 1414                             tc, sidx, sysctl_peer_spad_handle, "IU", desc);
 1415                 }
 1416         }
 1417 }
 1418 
 1419 static int
 1420 ntb_tool_probe(device_t dev)
 1421 {
 1422         device_set_desc(dev, "NTB TOOL");
 1423         return (0);
 1424 }
 1425 
 1426 static int
 1427 ntb_tool_attach(device_t dev)
 1428 {
 1429         struct tool_ctx *tc = device_get_softc(dev);
 1430         int rc = 0;
 1431 
 1432         tc->dev = dev;
 1433         rc = tool_check_ntb(tc);
 1434         if (rc)
 1435                 goto out;
 1436 
 1437         tool_init_peers(tc);
 1438 
 1439         rc = tool_init_mws(tc);
 1440         if (rc)
 1441                 goto err_clear_data;
 1442 
 1443         tool_init_spads(tc);
 1444 
 1445         rc = tool_init_ntb(tc);
 1446         if (rc)
 1447                 goto err_clear_spads;
 1448 
 1449         tool_setup_sysctl(tc);
 1450 
 1451         return (0);
 1452 
 1453 err_clear_spads:
 1454         tool_clear_spads(tc);
 1455         tool_clear_mws(tc);
 1456         tool_clear_peers(tc);
 1457 err_clear_data:
 1458         tool_clear_data(tc);
 1459 out:
 1460         device_printf(dev, "ntb_tool attached failed with err=(%d).\n", rc);
 1461         return (rc);
 1462 }
 1463 
 1464 static int
 1465 ntb_tool_detach(device_t dev)
 1466 {
 1467         struct tool_ctx *tc = device_get_softc(dev);
 1468 
 1469         tool_clear_ntb(tc);
 1470 
 1471         tool_clear_spads(tc);
 1472 
 1473         tool_clear_mws(tc);
 1474 
 1475         tool_clear_peers(tc);
 1476 
 1477         tool_clear_data(tc);
 1478 
 1479         return (0);
 1480 }
 1481 
 1482 static device_method_t ntb_tool_methods[] = {
 1483         /* Device interface */
 1484         DEVMETHOD(device_probe,     ntb_tool_probe),
 1485         DEVMETHOD(device_attach,    ntb_tool_attach),
 1486         DEVMETHOD(device_detach,    ntb_tool_detach),
 1487         DEVMETHOD_END
 1488 };
 1489 
 1490 static DEFINE_CLASS_0(ntb_tool, ntb_tool_driver, ntb_tool_methods,
 1491     sizeof(struct tool_ctx));
 1492 DRIVER_MODULE(ntb_tool, ntb_hw, ntb_tool_driver, NULL, NULL);
 1493 MODULE_DEPEND(ntb_tool, ntb, 1, 1, 1);
 1494 MODULE_VERSION(ntb_tool, 1.0);

Cache object: 33fe20769cecfc239acc16b62c2ee90b


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