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
208MALLOC_DEFINE(M_NTB_TOOL, "ntb_tool", "ntb_tool driver memory allocation");
209
210/*
211 * Memory windows descriptor structure
212 */
213struct 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
244struct tool_spad {
245	int                sidx;
246	int                pidx;
247	struct tool_ctx    *tc;
248};
249
250struct 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
260struct 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 */
285struct ntb_tool_load_cb_args {
286	bus_addr_t addr;
287	int error;
288};
289
290/*
291 * NTB events handlers
292 */
293static void
294tool_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
311static void
312tool_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
324static 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 */
332static void
333tool_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
349static void
350tool_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 */
368static inline int
369get_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
384static int
385read_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
395static int
396tool_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
407static int
408tool_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
454static int
455parse_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
509static int
510tool_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
576static int
577tool_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
619write:
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
632read_out:
633	/* cmd_op value is set to two as sysctl read call executes twice */
634	*cmd_op = 2;
635out:
636	return (rc);
637}
638
639/*
640 * Port sysctl read/write methods
641 */
642static int
643sysctl_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
659static int
660sysctl_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
676static void
677tool_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
690static void
691tool_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 */
700static int
701sysctl_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
729static int
730sysctl_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
749static int
750sysctl_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 */
775static void
776ntb_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
784static int
785sysctl_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
804static int
805tool_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
852err_free_mw:
853	bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
854
855err_free_dma:
856	bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
857
858err_free_tag_rem:
859	bus_dma_tag_destroy(inmw->dma_tag);
860
861err_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
871static void
872tool_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
893static int
894tool_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
923static int
924tool_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
946static int
947sysctl_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
966static int
967sysctl_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
987static 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
999static int
1000tool_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
1031free_mws:
1032	tool_clear_mws(tc);
1033	return (rc);
1034}
1035
1036/*
1037 * Doorbell handler for read/write
1038 */
1039static int
1040sysctl_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
1060static int
1061sysctl_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
1075static int
1076sysctl_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
1096static int
1097sysctl_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
1114static int
1115sysctl_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
1135static int
1136sysctl_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 */
1160static int
1161sysctl_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
1186static int
1187sysctl_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
1212static void
1213tool_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
1243static void
1244tool_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 */
1259static int
1260tool_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
1280static void
1281tool_clear_data(struct tool_ctx *tc)
1282{
1283
1284	callout_drain(&tc->link_event_timer);
1285	callout_drain(&tc->db_event_timer);
1286}
1287
1288static int
1289tool_init_ntb(struct tool_ctx *tc)
1290{
1291
1292	return ntb_set_ctx(tc->dev, tc, &tool_ops);
1293}
1294
1295static void
1296tool_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 */
1307static void
1308tool_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
1419static int
1420ntb_tool_probe(device_t dev)
1421{
1422	device_set_desc(dev, "NTB TOOL");
1423	return (0);
1424}
1425
1426static int
1427ntb_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
1453err_clear_spads:
1454	tool_clear_spads(tc);
1455	tool_clear_mws(tc);
1456	tool_clear_peers(tc);
1457err_clear_data:
1458	tool_clear_data(tc);
1459out:
1460	device_printf(dev, "ntb_tool attached failed with err=(%d).\n", rc);
1461	return (rc);
1462}
1463
1464static int
1465ntb_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
1482static 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
1490devclass_t ntb_tool_devclass;
1491static DEFINE_CLASS_0(ntb_tool, ntb_tool_driver, ntb_tool_methods,
1492    sizeof(struct tool_ctx));
1493DRIVER_MODULE(ntb_tool, ntb_hw, ntb_tool_driver, ntb_tool_devclass, NULL, NULL);
1494MODULE_DEPEND(ntb_tool, ntb, 1, 1, 1);
1495MODULE_VERSION(ntb_tool, 1.0);
1496