]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ntb/test/ntb_tool.c
Merge ^/head r358269 through r358399.
[FreeBSD/FreeBSD.git] / sys / dev / ntb / test / ntb_tool.c
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[16];
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         if (!data_buf) {
623                 rc = ENOMEM;
624                 goto out;
625         }
626
627         if (s_pflag)
628                 memset(data_buf, pattern, data_buf_size);
629         else
630                 arc4rand(data_buf, data_buf_size, 1);
631
632         memcpy(write_buf + *buf_offset, data_buf, data_buf_size);
633
634         free(data_buf, M_NTB_TOOL);
635
636 read_out:
637         /* cmd_op value is set to two as sysctl read call executes twice */
638         *cmd_op = 2;
639 out:
640         return (rc);
641 }
642
643 /*
644  * Port sysctl read/write methods
645  */
646 static int
647 sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)
648 {
649         struct tool_ctx *tc = (struct tool_ctx *)arg1;
650         int rc, pidx = arg2, peer_port;
651
652         peer_port = ntb_peer_port_number(tc->dev, pidx);
653         rc = sysctl_handle_int(oidp, &peer_port, 0, req);
654         if (rc)
655                 device_printf(tc->dev, "Peer port sysctl set failed with err="
656                     "(%d).\n", rc);
657         else
658                 tc->peers[pidx].port_no = peer_port;
659
660         return (rc);
661 }
662
663 static int
664 sysctl_local_port_number(SYSCTL_HANDLER_ARGS)
665 {
666         struct tool_ctx *tc = (struct tool_ctx *)arg1;
667         int rc, local_port;
668
669         local_port = ntb_port_number(tc->dev);
670         rc = sysctl_handle_int(oidp, &local_port, 0, req);
671         if (rc)
672                 device_printf(tc->dev, "Local port sysctl set failed with err="
673                     "(%d).\n", rc);
674         else
675                 tc->port_no = local_port;
676
677         return (rc);
678 }
679
680 static int
681 tool_init_peers(struct tool_ctx *tc)
682 {
683         int pidx;
684
685         tc->peer_cnt = ntb_peer_port_count(tc->dev);
686         tc->peers = malloc(tc->peer_cnt * sizeof(*tc->peers), M_NTB_TOOL,
687             M_WAITOK | M_ZERO);
688         if (tc->peers == NULL)
689                 return (ENOMEM);
690         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
691                 tc->peers[pidx].pidx = pidx;
692                 tc->peers[pidx].tc = tc;
693         }
694
695         return (0);
696 }
697
698 static void
699 tool_clear_peers(struct tool_ctx *tc)
700 {
701
702         free(tc->peers, M_NTB_TOOL);
703 }
704
705 /*
706  * Link state sysctl read/write methods
707  */
708 static int
709 sysctl_link_handle(SYSCTL_HANDLER_ARGS)
710 {
711         struct tool_ctx *tc = (struct tool_ctx *)arg1;
712         char buf[TOOL_BUF_LEN];
713         int rc;
714
715         if (req->newptr == NULL) {
716                 snprintf(buf, 2, "%c", tc->link_status);
717
718                 return SYSCTL_OUT(req, buf, 2);
719         }
720
721         rc = get_ubuf(req, buf);
722         if (rc)
723                 return (rc);
724
725         if (buf[0] == 'Y')
726                 rc = ntb_link_enable(tc->dev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
727         else if (buf[0] == 'N')
728                 rc = ntb_link_disable(tc->dev);
729         else
730                 rc = EINVAL;
731
732         sscanf(buf, "%c", &tc->link_status);
733
734         return (0);
735 }
736
737 static int
738 sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)
739 {
740         struct tool_ctx *tc = (struct tool_ctx *)arg1;
741         int up = 0, pidx = arg2;
742         char buf[TOOL_BUF_LEN];
743
744         if (req->newptr)
745                 return (0);
746
747         up = ntb_link_is_up(tc->dev, NULL, NULL);
748         memset((void *)buf, 0, TOOL_BUF_LEN);
749         if (up & (1UL << pidx))
750                 buf[0] = 'Y';
751         else
752                 buf[0] = 'N';
753
754         return SYSCTL_OUT(req, buf, sizeof(buf));
755 }
756
757 static int
758 sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)
759 {
760         struct tool_ctx *tc = (struct tool_ctx *)arg1;
761         char buf[TOOL_BUF_LEN];
762         int rc, pidx = arg2;
763         uint64_t bits;
764
765         if (req->newptr == NULL)
766                 return read_out(req, tc->link_bits);
767
768         rc = get_ubuf(req, buf);
769         if (rc)
770                 return (rc);
771
772         sscanf(buf, "0x%jx", &bits);
773         tc->link_bits = bits;
774         tc->link_mask = (1ULL << ((pidx) % 64));
775
776         callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
777         return (0);
778 }
779
780 /*
781  * Memory windows read/write/setting methods
782  */
783 static void
784 ntb_tool_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
785 {
786         struct ntb_tool_load_cb_args *cba = (struct ntb_tool_load_cb_args *)arg;
787
788         if (!(cba->error = error))
789                 cba->addr = segs[0].ds_addr;
790 }
791
792 static int
793 sysctl_mw_handle(SYSCTL_HANDLER_ARGS)
794 {
795         struct tool_mw *inmw = (struct tool_mw *)arg1;
796         char buf[TOOL_BUF_LEN];
797         int rc;
798
799         if (req->newptr == NULL)
800                 return tool_mw_read_fn(req, inmw, (char *)inmw->mm_base,
801                     &inmw->mw_cmd_rw, inmw->mw_buf_offset, inmw->mw_buf_size,
802                     "mw");
803
804         rc = get_ubuf(req, buf);
805         if (!rc)
806                 return tool_mw_write_fn(oidp, req, inmw, buf, inmw->mm_base,
807                     &inmw->mw_cmd_rw, &inmw->mw_buf_offset, &inmw->mw_buf_size);
808
809         return (rc);
810 }
811
812 static int
813 tool_setup_mw(struct tool_ctx *tc, unsigned int pidx, unsigned int widx,
814     size_t req_size)
815 {
816         struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
817         struct ntb_tool_load_cb_args cba;
818         int rc;
819
820         if (req_size == 0)
821                 inmw->size = roundup(inmw->phys_size, inmw->xlat_align_size);
822         else
823                 inmw->size = roundup(req_size, inmw->xlat_align_size);
824
825         device_printf(tc->dev, "mw_size %zi req_size %zi buff %zi\n",
826             inmw->phys_size, req_size, inmw->size);
827
828         if (bus_dma_tag_create(bus_get_dma_tag(tc->dev), inmw->xlat_align, 0,
829             inmw->addr_limit, BUS_SPACE_MAXADDR, NULL, NULL, inmw->size, 1,
830             inmw->size, 0, NULL, NULL, &inmw->dma_tag)) {
831                 device_printf(tc->dev, "Unable to create MW tag of size "
832                     "%zu/%zu\n", inmw->phys_size, inmw->size);
833                 rc = ENOMEM;
834                 goto err_free_dma_var;
835         }
836
837         if (bus_dmamem_alloc(inmw->dma_tag, (void **)&inmw->virt_addr,
838             BUS_DMA_WAITOK | BUS_DMA_ZERO, &inmw->dma_map)) {
839                 device_printf(tc->dev, "Unable to allocate MW buffer of size "
840                     "%zu/%zu\n", inmw->phys_size, inmw->size);
841                 rc = ENOMEM;
842                 goto err_free_tag_rem;
843         }
844
845         if (bus_dmamap_load(inmw->dma_tag, inmw->dma_map, inmw->virt_addr,
846             inmw->size, ntb_tool_load_cb, &cba, BUS_DMA_NOWAIT) || cba.error) {
847                 device_printf(tc->dev, "Unable to load MW buffer of size "
848                     "%zu/%zu\n", inmw->phys_size, inmw->size);
849                 rc = ENOMEM;
850                 goto err_free_dma;
851         }
852         inmw->dma_base = cba.addr;
853
854         rc = ntb_mw_set_trans(tc->dev, widx, inmw->dma_base, inmw->size);
855         if (rc)
856                 goto err_free_mw;
857
858         return (0);
859
860 err_free_mw:
861         bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
862
863 err_free_dma:
864         bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
865
866 err_free_tag_rem:
867         bus_dma_tag_destroy(inmw->dma_tag);
868
869 err_free_dma_var:
870         inmw->size = 0;
871         inmw->virt_addr = 0;
872         inmw->dma_base = 0;
873         inmw->dma_tag = 0;
874         inmw->dma_map = 0;
875
876         return (rc);
877 }
878
879 static void
880 tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
881 {
882         struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
883
884         if (inmw->dma_base)
885                 ntb_mw_clear_trans(tc->dev, widx);
886
887         if (inmw->virt_addr && inmw->dma_tag) {
888                 bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
889                 bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
890                 bus_dma_tag_destroy(inmw->dma_tag);
891         }
892
893         inmw->virt_addr = 0;
894         inmw->dma_base = 0;
895         inmw->dma_tag = 0;
896         inmw->dma_map = 0;
897         inmw->mm_base = 0;
898         inmw->size = 0;
899 }
900
901 static int
902 tool_mw_trans_read(struct tool_mw *inmw, struct sysctl_req *req)
903 {
904         ssize_t buf_size = 512;
905         struct sbuf *sb;
906         int rc = 0;
907
908         sb = sbuf_new_for_sysctl(NULL, NULL, buf_size, req);
909         if (sb == NULL) {
910                 rc = sb->s_error;
911                 return (rc);
912         }
913
914         sbuf_printf(sb, "\nInbound MW     \t%d\n", inmw->widx);
915         sbuf_printf(sb, "Port           \t%d (%d)\n",
916             ntb_peer_port_number(inmw->tc->dev, inmw->pidx), inmw->pidx);
917         sbuf_printf(sb, "Window Address \t%p\n", inmw->mm_base);
918         sbuf_printf(sb, "DMA Address    \t0x%016llx\n", (long long)inmw->dma_base);
919         sbuf_printf(sb, "Window Size    \t0x%016zx[p]\n", inmw->size);
920         sbuf_printf(sb, "Alignment      \t0x%016zx[p]\n", inmw->xlat_align);
921         sbuf_printf(sb, "Size Alignment \t0x%016zx[p]\n",
922             inmw->xlat_align_size);
923         sbuf_printf(sb, "Size Max       \t0x%016zx[p]\n", inmw->phys_size);
924
925         rc = sbuf_finish(sb);
926         sbuf_delete(sb);
927
928         return (rc);
929 }
930
931 static int
932 tool_mw_trans_write(struct sysctl_oid *oidp, struct sysctl_req *req,
933     struct tool_mw *inmw, size_t wsize)
934 {
935         struct tool_ctx *tc = inmw->tc;
936         int rc = 0;
937
938         if (wsize == 0)
939                 return (EINVAL);
940
941         /* No need to re-setup mw */
942         if (inmw->size == wsize)
943                 return (0);
944
945         /* free mw dma buffer */
946         if (inmw->size)
947                 tool_free_mw(tc, inmw->pidx, inmw->widx);
948
949         rc = tool_setup_mw(tc, inmw->pidx, inmw->widx, wsize);
950
951         return (rc);
952 }
953
954 static int
955 sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)
956 {
957         struct tool_mw *inmw = (struct tool_mw *)arg1;
958         char buf[TOOL_BUF_LEN];
959         ssize_t wsize;
960         int rc;
961
962         if (req->newptr == NULL)
963                 return tool_mw_trans_read(inmw, req);
964
965         rc = get_ubuf(req, buf);
966         if (rc == 0) {
967                 sscanf(buf, "%zi", &wsize);
968                 return tool_mw_trans_write(oidp, req, inmw, wsize);
969         }
970
971         return (rc);
972 }
973
974 static int
975 sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)
976 {
977         struct tool_mw *inmw = (struct tool_mw *)arg1;
978         char buf[TOOL_BUF_LEN];
979         int rc;
980
981         if (req->newptr == NULL)
982                 return tool_mw_read_fn(req, inmw, (char *)inmw->virt_addr,
983                     &inmw->mw_peer_cmd_rw, inmw->mw_peer_buf_offset,
984                     inmw->mw_peer_buf_size, "mw");
985
986         rc = get_ubuf(req, buf);
987         if (rc == 0)
988                 return tool_mw_write_fn(oidp, req, inmw, buf, inmw->virt_addr,
989                     &inmw->mw_peer_cmd_rw, &inmw->mw_peer_buf_offset,
990                     &inmw->mw_peer_buf_size);
991
992         return (rc);
993 }
994
995 static void tool_clear_mws(struct tool_ctx *tc)
996 {
997         int widx, pidx;
998
999         /* Free outbound memory windows */
1000         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1001                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++)
1002                         tool_free_mw(tc, pidx, widx);
1003                 free(tc->peers[pidx].inmws, M_NTB_TOOL);
1004         }
1005 }
1006
1007 static int
1008 tool_init_mws(struct tool_ctx *tc)
1009 {
1010         struct tool_mw *mw;
1011         int widx, pidx, rc;
1012
1013         /* Initialize inbound memory windows and outbound MWs wrapper */
1014         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1015                 tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->dev);
1016                 tc->peers[pidx].inmws = malloc(tc->peers[pidx].inmw_cnt *
1017                     sizeof(*tc->peers[pidx].inmws), M_NTB_TOOL,
1018                     M_WAITOK | M_ZERO);
1019                 if (tc->peers[pidx].inmws == NULL)
1020                         return (ENOMEM);
1021
1022                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
1023                         mw = &tc->peers[pidx].inmws[widx];
1024                         memset((void *)mw, 0, sizeof(*mw));
1025                         mw->tc = tc;
1026                         mw->widx = widx;
1027                         mw->pidx = pidx;
1028                         mw->mw_buf_offset = DEFAULT_MW_OFF;
1029                         mw->mw_buf_size = DEFAULT_MW_SIZE;
1030                         /* get the tx buff details for each mw attached with each peer */
1031                         rc = ntb_mw_get_range(tc->dev, widx, &mw->phys_addr,
1032                             &mw->mm_base, &mw->phys_size, &mw->xlat_align,
1033                             &mw->xlat_align_size, &mw->addr_limit);
1034                         if (rc)
1035                                 goto free_mws;
1036                 }
1037         }
1038
1039         return (0);
1040
1041 free_mws:
1042         tool_clear_mws(tc);
1043         return (rc);
1044 }
1045
1046 /*
1047  * Doorbell handler for read/write
1048  */
1049 static int
1050 sysctl_db_handle(SYSCTL_HANDLER_ARGS)
1051 {
1052         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1053         char buf[TOOL_BUF_LEN];
1054         uint64_t db_bits;
1055         int rc;
1056
1057         if (req->newptr == NULL) {
1058                 db_bits = ntb_db_read(tc->dev);
1059                 return read_out(req, db_bits);
1060         }
1061
1062         rc = get_ubuf(req, buf);
1063         if (rc == 0)
1064                 return tool_fn_write(tc, oidp, req, buf, NULL, false, NULL,
1065                     ntb_db_clear);
1066
1067         return (rc);
1068 }
1069
1070 static int
1071 sysctl_db_valid_mask_handle(SYSCTL_HANDLER_ARGS)
1072 {
1073         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1074
1075         tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
1076         if (!tc->db_valid_mask) {
1077                 device_printf(tc->dev, "Error getting db_valid_mask from "
1078                     "hw driver\n");
1079                 return (EINVAL);
1080         } else {
1081                 return read_out(req, tc->db_valid_mask);
1082         }
1083 }
1084
1085 static int
1086 sysctl_db_mask_handle(SYSCTL_HANDLER_ARGS)
1087 {
1088         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1089         char buf[TOOL_BUF_LEN];
1090         int rc;
1091
1092         if (req->newptr == NULL) {
1093                 if (tc->db_mask_val == 0)
1094                      ntb_db_valid_mask(tc->dev);
1095                 return tool_fn_read(tc, req, NULL, tc->db_mask_val);
1096         }
1097
1098         rc = get_ubuf(req, buf);
1099         if (rc == 0)
1100                 return tool_fn_write(tc, oidp, req, buf, &tc->db_mask_val, true,
1101                     ntb_db_set_mask, ntb_db_clear_mask);
1102
1103         return (rc);
1104 }
1105
1106 static int
1107 sysctl_peer_db_handle(SYSCTL_HANDLER_ARGS)
1108 {
1109         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1110         char buf[TOOL_BUF_LEN];
1111         int rc;
1112
1113         if (req->newptr == NULL)
1114                 return tool_fn_read(tc, req, NULL, tc->peer_db_val);
1115
1116         rc = get_ubuf(req, buf);
1117         if (rc == 0)
1118                 return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_val,
1119                     false, ntb_peer_db_set, NULL);
1120
1121         return (rc);
1122 }
1123
1124 static int
1125 sysctl_peer_db_mask_handle(SYSCTL_HANDLER_ARGS)
1126 {
1127         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1128         char buf[TOOL_BUF_LEN];
1129         int rc;
1130
1131         if (req->newptr == NULL){
1132                 if (tc->peer_db_mask_val == 0)
1133                         ntb_db_valid_mask(tc->dev);
1134                 return tool_fn_read(tc, req, NULL, tc->peer_db_mask_val);
1135         }
1136
1137         rc = get_ubuf(req, buf);
1138         if (rc == 0)
1139                 return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_mask_val,
1140                     true, NULL, NULL);
1141
1142         return (rc);
1143 }
1144
1145 static int
1146 sysctl_db_event_handle(SYSCTL_HANDLER_ARGS)
1147 {
1148         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1149         char buf[TOOL_BUF_LEN];
1150         uint64_t bits;
1151         int rc;
1152
1153         if (req->newptr == NULL)
1154                 return read_out(req, tc->db_event_val);
1155
1156         rc = get_ubuf(req, buf);
1157         if (rc)
1158                 return (rc);
1159
1160         sscanf(buf, "%ju", &bits);
1161         tc->db_event_val = bits;
1162         callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
1163
1164         return (0);
1165 }
1166
1167 /*
1168  * Scratchpads read/write methods
1169  */
1170 static int
1171 sysctl_spad_handle(SYSCTL_HANDLER_ARGS)
1172 {
1173         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1174         unsigned int sidx = arg2;
1175         char buf[TOOL_BUF_LEN];
1176         uint32_t bits;
1177         int rc;
1178
1179         if (req->newptr == NULL) {
1180                 rc = ntb_spad_read(tc->dev, sidx, &bits);
1181                 if (rc)
1182                         return (rc);
1183                 else
1184                         return read_out(req, (uint64_t )bits);
1185         }
1186
1187         rc = get_ubuf(req, buf);
1188         if (rc == 0) {
1189                 sscanf(buf, "%i", &bits);
1190                 return ntb_spad_write(tc->dev, sidx, bits);
1191         }
1192
1193         return (rc);
1194 }
1195
1196 static int
1197 sysctl_peer_spad_handle(SYSCTL_HANDLER_ARGS)
1198 {
1199         struct tool_ctx *tc = (struct tool_ctx *)arg1;
1200         unsigned int sidx = arg2;
1201         char buf[TOOL_BUF_LEN];
1202         uint32_t bits;
1203         int rc;
1204
1205         if (req->newptr == NULL) {
1206                 rc = ntb_peer_spad_read(tc->dev, sidx, &bits);
1207                 if (rc)
1208                         return (rc);
1209                 else
1210                         return read_out(req, (uint64_t )bits);
1211         }
1212
1213         rc = get_ubuf(req, buf);
1214         if (rc == 0) {
1215                 sscanf(buf, "%i", &bits);
1216                 return ntb_peer_spad_write(tc->dev, sidx, bits);
1217         }
1218
1219         return (rc);
1220 }
1221
1222 static int
1223 tool_init_spads(struct tool_ctx *tc)
1224 {
1225         int sidx, pidx;
1226
1227         /* Initialize inbound scratchpad structures */
1228         tc->inspad_cnt = ntb_spad_count(tc->dev);
1229         tc->inspads = malloc(tc->inspad_cnt * sizeof(*tc->inspads), M_NTB_TOOL,
1230             M_WAITOK | M_ZERO);
1231         if (tc->inspads == NULL)
1232                 return (ENOMEM);
1233
1234         for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
1235                 tc->inspads[sidx].sidx = sidx;
1236                 tc->inspads[sidx].pidx = -1;
1237                 tc->inspads[sidx].tc = tc;
1238         }
1239
1240         /* Initialize outbound scratchpad structures */
1241         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1242                 tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->dev);
1243                 tc->peers[pidx].outspads =  malloc(tc->peers[pidx].outspad_cnt *
1244                     sizeof(*tc->peers[pidx].outspads), M_NTB_TOOL, M_WAITOK |
1245                     M_ZERO);
1246                 if (tc->peers[pidx].outspads == NULL)
1247                         return (ENOMEM);
1248
1249                 for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
1250                         tc->peers[pidx].outspads[sidx].sidx = sidx;
1251                         tc->peers[pidx].outspads[sidx].pidx = pidx;
1252                         tc->peers[pidx].outspads[sidx].tc = tc;
1253                 }
1254         }
1255
1256         return (0);
1257 }
1258
1259 static void
1260 tool_clear_spads(struct tool_ctx *tc)
1261 {
1262         int pidx;
1263
1264         /* Free local inspads. */
1265         free(tc->inspads, M_NTB_TOOL);
1266
1267         /* Free outspads for each peer. */
1268         for (pidx = 0; pidx < tc->peer_cnt; pidx++)
1269                 free(tc->peers[pidx].outspads, M_NTB_TOOL);
1270 }
1271
1272 /*
1273  * Initialization methods
1274  */
1275 static int
1276 tool_check_ntb(struct tool_ctx *tc)
1277 {
1278
1279         /* create and initialize link callout handler */
1280         callout_init(&tc->link_event_timer, 1);
1281
1282         /* create and initialize db callout handler */
1283         callout_init(&tc->db_event_timer, 1);
1284
1285         /* Initialize sysctl read out values to default */
1286         tc->link_status = 'U';
1287         tc->db_mask_val = 0;
1288         tc->peer_db_val = 0;
1289         tc->peer_db_mask_val = 0;
1290         tc->db_event_val = 0;
1291         tc->link_bits = 0;
1292
1293         return (0);
1294 }
1295
1296 static void
1297 tool_clear_data(struct tool_ctx *tc)
1298 {
1299
1300         callout_drain(&tc->link_event_timer);
1301         callout_drain(&tc->db_event_timer);
1302 }
1303
1304 static int
1305 tool_init_ntb(struct tool_ctx *tc)
1306 {
1307
1308         return ntb_set_ctx(tc->dev, tc, &tool_ops);
1309 }
1310
1311 static void
1312 tool_clear_ntb(struct tool_ctx *tc)
1313 {
1314
1315         ntb_clear_ctx(tc->dev);
1316         ntb_link_disable(tc->dev);
1317 }
1318
1319 /*
1320  *  Current sysctl implementation is made such that it gets attached to the
1321  *  device and while detach it gets cleared automatically.
1322  */
1323 static void
1324 tool_setup_sysctl(struct tool_ctx *tc)
1325 {
1326         char buf[TOOL_BUF_LEN], desc[TOOL_BUF_LEN];
1327         struct sysctl_oid_list *top, *peer_top;
1328         struct sysctl_oid *parent, *peer;
1329         struct sysctl_ctx_list *clist;
1330         unsigned int pidx, sidx, widx;
1331
1332         clist = device_get_sysctl_ctx(tc->dev);
1333         parent = device_get_sysctl_tree(tc->dev);
1334         top = SYSCTL_CHILDREN(parent);
1335
1336         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "port", CTLTYPE_UINT |
1337             CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_local_port_number,
1338             "IU", "local port number");
1339
1340         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "link", CTLTYPE_STRING |
1341             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_link_handle,
1342             "IU", "link info");
1343
1344         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db", CTLTYPE_STRING |
1345             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_handle,
1346             "A", "db info");
1347
1348         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_valid_mask", CTLTYPE_STRING |
1349             CTLFLAG_RD | CTLFLAG_MPSAFE, tc, 0, sysctl_db_valid_mask_handle,
1350             "A", "db valid mask");
1351
1352         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_mask", CTLTYPE_STRING |
1353             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_mask_handle,
1354             "A", "db mask");
1355
1356         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_event", CTLTYPE_STRING |
1357             CTLFLAG_WR | CTLFLAG_MPSAFE, tc, 0, sysctl_db_event_handle,
1358             "A", "db event");
1359
1360         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db", CTLTYPE_STRING |
1361             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_handle,
1362             "A", "peer db");
1363
1364         SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db_mask", CTLTYPE_STRING |
1365             CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_mask_handle,
1366             "IU", "peer db mask info");
1367
1368         if (tc->inspad_cnt != 0) {
1369                 for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
1370                         snprintf(buf, sizeof(buf), "spad%d", sidx);
1371                         snprintf(desc, sizeof(desc), "spad%d info", sidx);
1372
1373                         SYSCTL_ADD_PROC(clist, top, OID_AUTO, buf,
1374                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1375                             tc, sidx, sysctl_spad_handle, "IU", desc);
1376                 }
1377         }
1378
1379         for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1380                 snprintf(buf, sizeof(buf), "peer%d", pidx);
1381
1382                 peer = SYSCTL_ADD_NODE(clist, top, OID_AUTO, buf,
1383                     CTLFLAG_RW | CTLFLAG_MPSAFE, 0, buf);
1384                 peer_top = SYSCTL_CHILDREN(peer);
1385
1386                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "port",
1387                     CTLTYPE_UINT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, pidx,
1388                     sysctl_peer_port_number, "IU", "peer port number");
1389
1390                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link",
1391                     CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
1392                     sysctl_peer_link_handle, "IU", "peer_link info");
1393
1394                 SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link_event",
1395                     CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
1396                     sysctl_peer_link_event_handle, "IU", "link event");
1397
1398                 for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
1399                         snprintf(buf, sizeof(buf), "mw_trans%d", widx);
1400                         snprintf(desc, sizeof(desc), "mw trans%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_mw_trans_handler, "IU", desc);
1406
1407                         snprintf(buf, sizeof(buf), "mw%d", widx);
1408                         snprintf(desc, sizeof(desc), "mw%d info", widx);
1409
1410                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1411                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1412                             &tc->peers[pidx].inmws[widx], 0,
1413                             sysctl_mw_handle, "IU", desc);
1414
1415                         snprintf(buf, sizeof(buf), "peer_mw%d", widx);
1416                         snprintf(desc, sizeof(desc), "peer_mw%d info", widx);
1417
1418                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1419                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1420                             &tc->peers[pidx].inmws[widx], 0,
1421                             sysctl_peer_mw_handle, "IU", desc);
1422                 }
1423
1424                 for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
1425                         snprintf(buf, sizeof(buf), "spad%d", sidx);
1426                         snprintf(desc, sizeof(desc), "spad%d info", sidx);
1427
1428                         SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1429                             CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1430                             tc, sidx, sysctl_peer_spad_handle, "IU", desc);
1431                 }
1432         }
1433 }
1434
1435 static int
1436 ntb_tool_probe(device_t dev)
1437 {
1438         device_set_desc(dev, "NTB TOOL");
1439         return (0);
1440 }
1441
1442 static int
1443 ntb_tool_attach(device_t dev)
1444 {
1445         struct tool_ctx *tc = device_get_softc(dev);
1446         int rc = 0;
1447
1448         tc->dev = dev;
1449         rc = tool_check_ntb(tc);
1450         if (rc)
1451                 goto out;
1452
1453         rc = tool_init_peers(tc);
1454         if (rc)
1455                 goto err_clear_data;
1456
1457         rc = tool_init_mws(tc);
1458         if (rc)
1459                 goto err_clear_data;
1460
1461         rc = tool_init_spads(tc);
1462         if (rc)
1463                 goto err_clear_mws;
1464
1465         rc = tool_init_ntb(tc);
1466         if (rc)
1467                 goto err_clear_spads;
1468
1469         tool_setup_sysctl(tc);
1470
1471         return (0);
1472
1473 err_clear_spads:
1474         tool_clear_spads(tc);
1475 err_clear_mws:
1476         tool_clear_mws(tc);
1477         tool_clear_peers(tc);
1478 err_clear_data:
1479         tool_clear_data(tc);
1480 out:
1481         device_printf(dev, "ntb_tool attached failed with err=(%d).\n", rc);
1482         return (rc);
1483 }
1484
1485 static int
1486 ntb_tool_detach(device_t dev)
1487 {
1488         struct tool_ctx *tc = device_get_softc(dev);
1489
1490         tool_clear_ntb(tc);
1491
1492         tool_clear_spads(tc);
1493
1494         tool_clear_mws(tc);
1495
1496         tool_clear_peers(tc);
1497
1498         tool_clear_data(tc);
1499
1500         return (0);
1501 }
1502
1503 static device_method_t ntb_tool_methods[] = {
1504         /* Device interface */
1505         DEVMETHOD(device_probe,     ntb_tool_probe),
1506         DEVMETHOD(device_attach,    ntb_tool_attach),
1507         DEVMETHOD(device_detach,    ntb_tool_detach),
1508         DEVMETHOD_END
1509 };
1510
1511 devclass_t ntb_tool_devclass;
1512 static DEFINE_CLASS_0(ntb_tool, ntb_tool_driver, ntb_tool_methods,
1513     sizeof(struct tool_ctx));
1514 DRIVER_MODULE(ntb_tool, ntb_hw, ntb_tool_driver, ntb_tool_devclass, NULL, NULL);
1515 MODULE_DEPEND(ntb_tool, ntb, 1, 1, 1);
1516 MODULE_VERSION(ntb_tool, 1.0);