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