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