#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <sys/sbuf.h>
#include <machine/bus.h>
#include <vm/vm.h>
#include "../ntb.h"
#define TOOL_BUF_LEN 48
#define DEFAULT_MW_OFF 0
#define DEFAULT_MW_SIZE 1024
MALLOC_DEFINE(M_NTB_TOOL, "ntb_tool", "ntb_tool driver memory allocation");
struct tool_mw {
struct tool_ctx *tc;
int widx;
int pidx;
bus_addr_t dma_base;
caddr_t virt_addr;
bus_dmamap_t dma_map;
bus_dma_tag_t dma_tag;
caddr_t mm_base;
vm_paddr_t phys_addr;
bus_addr_t addr_limit;
size_t phys_size;
size_t xlat_align;
size_t xlat_align_size;
size_t size;
ssize_t mw_buf_size;
ssize_t mw_buf_offset;
ssize_t mw_peer_buf_size;
ssize_t mw_peer_buf_offset;
int mw_cmd_rw;
int mw_peer_cmd_rw;
};
struct tool_spad {
int sidx;
int pidx;
struct tool_ctx *tc;
};
struct tool_peer {
int pidx;
struct tool_ctx *tc;
int inmw_cnt;
struct tool_mw *inmws;
int outspad_cnt;
struct tool_spad *outspads;
unsigned int port_no;
};
struct tool_ctx {
device_t dev;
struct callout link_event_timer;
struct callout db_event_timer;
int peer_cnt;
struct tool_peer *peers;
int inmsg_cnt;
struct tool_msg *inmsgs;
int inspad_cnt;
struct tool_spad *inspads;
unsigned int unsafe;
char link_status;
uint64_t link_bits;
uint64_t link_mask;
uint64_t db_valid_mask;
uint64_t db_mask_val;
uint64_t db_event_val;
uint64_t peer_db_val;
uint64_t peer_db_mask_val;
unsigned int port_no;
};
struct ntb_tool_load_cb_args {
bus_addr_t addr;
int error;
};
static void
tool_link_event(void *ctx)
{
struct tool_ctx *tc = ctx;
enum ntb_speed speed = 0;
enum ntb_width width = 0;
int up = 0;
up = ntb_link_is_up(tc->dev, &speed, &width);
if (up)
tc->link_status = 'Y';
else
tc->link_status = 'N';
device_printf(tc->dev, "link is %s speed %d width %d\n",
up ? "up" : "down", speed, width);
}
static void
tool_db_event(void *ctx, uint32_t vec)
{
struct tool_ctx *tc = ctx;
uint64_t db_bits, db_mask;
db_mask = ntb_db_vector_mask(tc->dev, vec);
db_bits = ntb_db_read(tc->dev);
device_printf(tc->dev, "doorbell vec %d mask %#llx bits %#llx\n",
vec, (unsigned long long)db_mask, (unsigned long long)db_bits);
}
static const struct ntb_ctx_ops tool_ops = {
.link_event = tool_link_event,
.db_event = tool_db_event,
};
static void
tool_link_event_handler(void *arg)
{
struct tool_ctx *tc = (struct tool_ctx *)arg;
uint64_t val;
val = ntb_link_is_up(tc->dev, NULL, NULL) & tc->link_mask;
if (val == tc->link_bits) {
device_printf(tc->dev, "link_event successful for link val="
"0x%jx\n", tc->link_bits);
tc->link_bits = 0x0;
tc->link_mask = 0x0;
} else
callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
}
static void
tool_db_event_handler(void *arg)
{
struct tool_ctx *tc = (struct tool_ctx *)arg;
uint64_t db_bits;
db_bits = ntb_db_read(tc->dev);
if (db_bits == tc->db_event_val) {
device_printf(tc->dev, "db_event successful for db val=0x%jx\n",
tc->db_event_val);
tc->db_event_val = 0x0;
} else
callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
}
static inline int
get_ubuf(struct sysctl_req *req, char *ubuf)
{
int rc;
if (req->newlen >= TOOL_BUF_LEN)
return (EINVAL);
rc = SYSCTL_IN(req, ubuf, req->newlen);
if (rc)
return (rc);
ubuf[req->newlen] = '\0';
return (0);
}
static int
read_out(struct sysctl_req *req, uint64_t val)
{
char ubuf[19];
memset((void *)ubuf, 0, sizeof(ubuf));
snprintf(ubuf, sizeof(ubuf), "0x%jx", val);
return SYSCTL_OUT(req, ubuf, sizeof(ubuf));
}
static int
tool_fn_read(struct tool_ctx *tc, struct sysctl_req *req,
uint64_t (*fn_read)(device_t ), uint64_t val)
{
if (fn_read == NULL)
return read_out(req, val);
else if (fn_read)
return read_out(req, (uint64_t)fn_read(tc->dev));
else
return (EINVAL);
}
static int
tool_fn_write(struct tool_ctx *tc, struct sysctl_oid *oidp,
struct sysctl_req *req, char *ubuf, uint64_t *val, bool db_mask_sflag,
void (*fn_set)(device_t , uint64_t), void (*fn_clear)(device_t , uint64_t))
{
uint64_t db_valid_mask = tc->db_valid_mask;
uint64_t bits;
char cmd;
if (fn_set == NULL && fn_clear == NULL) {
device_printf(tc->dev, "ERR: Set & Clear both are not supported\n");
return (EINVAL);
}
if (tc->db_valid_mask == 0)
db_valid_mask = tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
bits = 0;
sscanf(ubuf, "%c %jx", &cmd, &bits);
if (cmd == 's') {
if ((bits | db_valid_mask) > db_valid_mask) {
device_printf(tc->dev, "0x%jx value is not supported\n", bits);
return (EINVAL);
}
if (fn_set)
fn_set(tc->dev, bits);
else
return (EINVAL);
if (val)
*val |= bits;
} else if (cmd == 'c') {
if ((bits | db_valid_mask) > db_valid_mask) {
device_printf(tc->dev, "0x%jx value is not supported\n", bits);
return (EINVAL);
}
if (fn_clear)
fn_clear(tc->dev, bits);
if (val)
*val &= ~bits;
} else {
device_printf(tc->dev, "Wrong Write\n");
return (EINVAL);
}
return (0);
}
static int
parse_mw_buf(char *buf, char *cmd, ssize_t *offset, ssize_t *buf_size,
uint64_t *pattern, bool *s_pflag)
{
char op1[8], op2[8], op3[8];
uint64_t val1, val2, val3;
bool vs1, vs2, vs3;
int rc = 0;
vs1 = vs2 = vs3 = false;
sscanf(buf, "%c %s %jx %s %jx %s %jx",
cmd, op1, &val1, op2, &val2, op3, &val3);
if (*cmd != 'W' && *cmd != 'R')
return (EINVAL);
if (!strcmp(op1, "offset")) {
*offset = val1 ? val1 : DEFAULT_MW_OFF;
vs1 = true;
} else if (!strcmp(op1, "nbytes")) {
*buf_size = val1 ? val1: DEFAULT_MW_SIZE;
vs2 = true;
} else if (!strcmp(op1, "pattern")) {
*pattern = val1;
vs3 = true;
}
if (!vs1 && !strcmp(op2, "offset")) {
*offset = val2 ? val2 : DEFAULT_MW_OFF;
vs1 = true;
} else if (!vs2 && !strcmp(op2, "nbytes")) {
*buf_size = val2 ? val2: DEFAULT_MW_SIZE;
vs2 = true;
} else if (!vs3 && !strcmp(op2, "pattern")) {
*pattern = val2;
vs3 = true;
}
if (!vs1 && !strcmp(op3, "offset")) {
*offset = val3 ? val3 : DEFAULT_MW_OFF;
} else if (!vs2 && !strcmp(op3, "nbytes")) {
*buf_size = val3 ? val3: DEFAULT_MW_SIZE;
} else if (!vs3 && !strcmp(op3, "pattern")) {
*pattern = val3;
vs3 = true;
}
*s_pflag = vs3;
if (vs3 && *cmd == 'R')
printf("NTB_TOOL_WARN: pattern is not supported with read "
"command\n");
return (rc);
}
static int
tool_mw_read_fn(struct sysctl_req *req, struct tool_mw *inmw, char *read_addr,
int *cmd_op, ssize_t buf_off, ssize_t buf_size, char *type)
{
ssize_t index, size;
struct sbuf *sb;
int i, loop, rc;
char *tmp;
if (*cmd_op == 0)
return (0);
index = buf_off;
tmp = read_addr;
tmp += index;
loop = ((buf_size == 0) || (buf_size > DEFAULT_MW_SIZE)) ?
DEFAULT_MW_SIZE : buf_size;
size = loop + 256;
sb = sbuf_new_for_sysctl(NULL, NULL, size, req);
if (sb == NULL) {
rc = sb->s_error;
return (rc);
}
if (!strcmp(type, "mw"))
sbuf_printf(sb, "\nConfigured MW size\t: %zu\n", inmw->size);
else if (!strcmp(type, "peer_mw"))
sbuf_printf(sb, "\nConfigured Peer MW size\t: %zu\n",
inmw->size);
sbuf_printf(sb, "R/W size\t\t: %zi\nR/W Offset\t\t: %zi\n\nData\n----"
"->", buf_size, buf_off);
for (i = 0 ; i < loop; i++) {
if ((i % 32) == 0) {
sbuf_printf(sb, "\n%08zx:", index);
index += 32;
}
if ((i % 8) == 0)
sbuf_printf(sb, " ");
sbuf_printf(sb, "%02hhx", *(tmp+i));
}
if (buf_size > DEFAULT_MW_SIZE)
sbuf_printf(sb, "\n\nNOTE: Truncating read size %zi->1024 "
"bytes\n", buf_size);
*cmd_op -= 1;
rc = sbuf_finish(sb);
sbuf_delete(sb);
return (rc);
}
static int
tool_mw_write_fn(struct sysctl_oid *oidp, struct sysctl_req *req,
struct tool_mw *inmw, char *ubuf, caddr_t write_buf, int *cmd_op,
ssize_t *buf_offset, ssize_t *buf_size)
{
ssize_t data_buf_size;
uint64_t pattern = 0;
bool s_pflag = false;
void *data_buf;
char cmd;
int rc;
if (!write_buf)
return (ENXIO);
*buf_offset = DEFAULT_MW_OFF;
*buf_size = DEFAULT_MW_SIZE;
rc = parse_mw_buf(ubuf, &cmd, buf_offset, buf_size, &pattern, &s_pflag);
if (rc) {
device_printf(inmw->tc->dev, "Wrong Command \"%c\" provided\n",
cmd);
return (rc);
}
if ((*buf_offset + *buf_size) > inmw->size) {
device_printf(inmw->tc->dev, "%s: configured mw size :%zi and "
"requested size :%zi.\n", __func__, inmw->size,
(*buf_offset + *buf_size));
*buf_offset = DEFAULT_MW_OFF;
*buf_size = DEFAULT_MW_SIZE;
rc = EINVAL;
goto out;
}
if (cmd == 'R')
goto read_out;
else if (cmd == 'W')
goto write;
else
goto out;
write:
data_buf_size = *buf_size;
data_buf = malloc(data_buf_size, M_NTB_TOOL, M_WAITOK | M_ZERO);
if (s_pflag)
memset(data_buf, pattern, data_buf_size);
else
arc4rand(data_buf, data_buf_size, 1);
memcpy(write_buf + *buf_offset, data_buf, data_buf_size);
free(data_buf, M_NTB_TOOL);
read_out:
*cmd_op = 2;
out:
return (rc);
}
static int
sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
int rc, pidx = arg2, peer_port;
peer_port = ntb_peer_port_number(tc->dev, pidx);
rc = sysctl_handle_int(oidp, &peer_port, 0, req);
if (rc)
device_printf(tc->dev, "Peer port sysctl set failed with err="
"(%d).\n", rc);
else
tc->peers[pidx].port_no = peer_port;
return (rc);
}
static int
sysctl_local_port_number(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
int rc, local_port;
local_port = ntb_port_number(tc->dev);
rc = sysctl_handle_int(oidp, &local_port, 0, req);
if (rc)
device_printf(tc->dev, "Local port sysctl set failed with err="
"(%d).\n", rc);
else
tc->port_no = local_port;
return (rc);
}
static void
tool_init_peers(struct tool_ctx *tc)
{
int pidx;
tc->peer_cnt = ntb_peer_port_count(tc->dev);
tc->peers = malloc(tc->peer_cnt * sizeof(*tc->peers), M_NTB_TOOL,
M_WAITOK | M_ZERO);
for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
tc->peers[pidx].pidx = pidx;
tc->peers[pidx].tc = tc;
}
}
static void
tool_clear_peers(struct tool_ctx *tc)
{
free(tc->peers, M_NTB_TOOL);
}
static int
sysctl_link_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL) {
snprintf(buf, 2, "%c", tc->link_status);
return SYSCTL_OUT(req, buf, 2);
}
rc = get_ubuf(req, buf);
if (rc)
return (rc);
if (buf[0] == 'Y')
rc = ntb_link_enable(tc->dev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
else if (buf[0] == 'N')
rc = ntb_link_disable(tc->dev);
else
rc = EINVAL;
sscanf(buf, "%c", &tc->link_status);
return (0);
}
static int
sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
int up = 0, pidx = arg2;
char buf[TOOL_BUF_LEN];
if (req->newptr)
return (0);
up = ntb_link_is_up(tc->dev, NULL, NULL);
memset((void *)buf, 0, TOOL_BUF_LEN);
if (up & (1UL << pidx))
buf[0] = 'Y';
else
buf[0] = 'N';
return SYSCTL_OUT(req, buf, sizeof(buf));
}
static int
sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
int rc, pidx = arg2;
uint64_t bits;
if (req->newptr == NULL)
return read_out(req, tc->link_bits);
rc = get_ubuf(req, buf);
if (rc)
return (rc);
sscanf(buf, "0x%jx", &bits);
tc->link_bits = bits;
tc->link_mask = (1ULL << ((pidx) % 64));
callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
return (0);
}
static void
ntb_tool_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
struct ntb_tool_load_cb_args *cba = (struct ntb_tool_load_cb_args *)arg;
if (!(cba->error = error))
cba->addr = segs[0].ds_addr;
}
static int
sysctl_mw_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_mw *inmw = (struct tool_mw *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL)
return tool_mw_read_fn(req, inmw, (char *)inmw->mm_base,
&inmw->mw_cmd_rw, inmw->mw_buf_offset, inmw->mw_buf_size,
"mw");
rc = get_ubuf(req, buf);
if (!rc)
return tool_mw_write_fn(oidp, req, inmw, buf, inmw->mm_base,
&inmw->mw_cmd_rw, &inmw->mw_buf_offset, &inmw->mw_buf_size);
return (rc);
}
static int
tool_setup_mw(struct tool_ctx *tc, unsigned int pidx, unsigned int widx,
size_t req_size)
{
struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
struct ntb_tool_load_cb_args cba;
int rc;
if (req_size == 0)
inmw->size = roundup(inmw->phys_size, inmw->xlat_align_size);
else
inmw->size = roundup(req_size, inmw->xlat_align_size);
device_printf(tc->dev, "mw_size %zi req_size %zi buff %zi\n",
inmw->phys_size, req_size, inmw->size);
if (bus_dma_tag_create(bus_get_dma_tag(tc->dev), inmw->xlat_align, 0,
inmw->addr_limit, BUS_SPACE_MAXADDR, NULL, NULL, inmw->size, 1,
inmw->size, 0, NULL, NULL, &inmw->dma_tag)) {
device_printf(tc->dev, "Unable to create MW tag of size "
"%zu/%zu\n", inmw->phys_size, inmw->size);
rc = ENOMEM;
goto err_free_dma_var;
}
if (bus_dmamem_alloc(inmw->dma_tag, (void **)&inmw->virt_addr,
BUS_DMA_WAITOK | BUS_DMA_ZERO, &inmw->dma_map)) {
device_printf(tc->dev, "Unable to allocate MW buffer of size "
"%zu/%zu\n", inmw->phys_size, inmw->size);
rc = ENOMEM;
goto err_free_tag_rem;
}
if (bus_dmamap_load(inmw->dma_tag, inmw->dma_map, inmw->virt_addr,
inmw->size, ntb_tool_load_cb, &cba, BUS_DMA_NOWAIT) || cba.error) {
device_printf(tc->dev, "Unable to load MW buffer of size "
"%zu/%zu\n", inmw->phys_size, inmw->size);
rc = ENOMEM;
goto err_free_dma;
}
inmw->dma_base = cba.addr;
rc = ntb_mw_set_trans(tc->dev, widx, inmw->dma_base, inmw->size);
if (rc)
goto err_free_mw;
return (0);
err_free_mw:
bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
err_free_dma:
bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
err_free_tag_rem:
bus_dma_tag_destroy(inmw->dma_tag);
err_free_dma_var:
inmw->size = 0;
inmw->virt_addr = 0;
inmw->dma_base = 0;
inmw->dma_tag = 0;
inmw->dma_map = 0;
return (rc);
}
static void
tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
{
struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
if (inmw->dma_base)
ntb_mw_clear_trans(tc->dev, widx);
if (inmw->virt_addr && inmw->dma_tag) {
bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
bus_dma_tag_destroy(inmw->dma_tag);
}
inmw->virt_addr = 0;
inmw->dma_base = 0;
inmw->dma_tag = 0;
inmw->dma_map = 0;
inmw->mm_base = 0;
inmw->size = 0;
}
static int
tool_mw_trans_read(struct tool_mw *inmw, struct sysctl_req *req)
{
ssize_t buf_size = 512;
struct sbuf *sb;
int rc = 0;
sb = sbuf_new_for_sysctl(NULL, NULL, buf_size, req);
if (sb == NULL) {
rc = sb->s_error;
return (rc);
}
sbuf_printf(sb, "\nInbound MW \t%d\n", inmw->widx);
sbuf_printf(sb, "Port \t%d (%d)\n",
ntb_peer_port_number(inmw->tc->dev, inmw->pidx), inmw->pidx);
sbuf_printf(sb, "Window Address \t%p\n", inmw->mm_base);
sbuf_printf(sb, "DMA Address \t0x%016llx\n", (long long)inmw->dma_base);
sbuf_printf(sb, "Window Size \t0x%016zx[p]\n", inmw->size);
sbuf_printf(sb, "Alignment \t0x%016zx[p]\n", inmw->xlat_align);
sbuf_printf(sb, "Size Alignment \t0x%016zx[p]\n",
inmw->xlat_align_size);
sbuf_printf(sb, "Size Max \t0x%016zx[p]\n", inmw->phys_size);
rc = sbuf_finish(sb);
sbuf_delete(sb);
return (rc);
}
static int
tool_mw_trans_write(struct sysctl_oid *oidp, struct sysctl_req *req,
struct tool_mw *inmw, size_t wsize)
{
struct tool_ctx *tc = inmw->tc;
int rc = 0;
if (wsize == 0)
return (EINVAL);
if (inmw->size == wsize)
return (0);
if (inmw->size)
tool_free_mw(tc, inmw->pidx, inmw->widx);
rc = tool_setup_mw(tc, inmw->pidx, inmw->widx, wsize);
return (rc);
}
static int
sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)
{
struct tool_mw *inmw = (struct tool_mw *)arg1;
char buf[TOOL_BUF_LEN];
ssize_t wsize;
int rc;
if (req->newptr == NULL)
return tool_mw_trans_read(inmw, req);
rc = get_ubuf(req, buf);
if (rc == 0) {
sscanf(buf, "%zi", &wsize);
return tool_mw_trans_write(oidp, req, inmw, wsize);
}
return (rc);
}
static int
sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_mw *inmw = (struct tool_mw *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL)
return tool_mw_read_fn(req, inmw, (char *)inmw->virt_addr,
&inmw->mw_peer_cmd_rw, inmw->mw_peer_buf_offset,
inmw->mw_peer_buf_size, "mw");
rc = get_ubuf(req, buf);
if (rc == 0)
return tool_mw_write_fn(oidp, req, inmw, buf, inmw->virt_addr,
&inmw->mw_peer_cmd_rw, &inmw->mw_peer_buf_offset,
&inmw->mw_peer_buf_size);
return (rc);
}
static void tool_clear_mws(struct tool_ctx *tc)
{
int widx, pidx;
for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++)
tool_free_mw(tc, pidx, widx);
free(tc->peers[pidx].inmws, M_NTB_TOOL);
}
}
static int
tool_init_mws(struct tool_ctx *tc)
{
struct tool_mw *mw;
int widx, pidx, rc;
for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->dev);
tc->peers[pidx].inmws = malloc(tc->peers[pidx].inmw_cnt *
sizeof(*tc->peers[pidx].inmws), M_NTB_TOOL,
M_WAITOK | M_ZERO);
for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
mw = &tc->peers[pidx].inmws[widx];
memset((void *)mw, 0, sizeof(*mw));
mw->tc = tc;
mw->widx = widx;
mw->pidx = pidx;
mw->mw_buf_offset = DEFAULT_MW_OFF;
mw->mw_buf_size = DEFAULT_MW_SIZE;
rc = ntb_mw_get_range(tc->dev, widx, &mw->phys_addr,
&mw->mm_base, &mw->phys_size, &mw->xlat_align,
&mw->xlat_align_size, &mw->addr_limit);
if (rc)
goto free_mws;
}
}
return (0);
free_mws:
tool_clear_mws(tc);
return (rc);
}
static int
sysctl_db_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
uint64_t db_bits;
int rc;
if (req->newptr == NULL) {
db_bits = ntb_db_read(tc->dev);
return read_out(req, db_bits);
}
rc = get_ubuf(req, buf);
if (rc == 0)
return tool_fn_write(tc, oidp, req, buf, NULL, false, NULL,
ntb_db_clear);
return (rc);
}
static int
sysctl_db_valid_mask_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
if (!tc->db_valid_mask) {
device_printf(tc->dev, "Error getting db_valid_mask from "
"hw driver\n");
return (EINVAL);
} else {
return read_out(req, tc->db_valid_mask);
}
}
static int
sysctl_db_mask_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL) {
if (tc->db_mask_val == 0)
ntb_db_valid_mask(tc->dev);
return tool_fn_read(tc, req, NULL, tc->db_mask_val);
}
rc = get_ubuf(req, buf);
if (rc == 0)
return tool_fn_write(tc, oidp, req, buf, &tc->db_mask_val, true,
ntb_db_set_mask, ntb_db_clear_mask);
return (rc);
}
static int
sysctl_peer_db_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL)
return tool_fn_read(tc, req, NULL, tc->peer_db_val);
rc = get_ubuf(req, buf);
if (rc == 0)
return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_val,
false, ntb_peer_db_set, NULL);
return (rc);
}
static int
sysctl_peer_db_mask_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
int rc;
if (req->newptr == NULL){
if (tc->peer_db_mask_val == 0)
ntb_db_valid_mask(tc->dev);
return tool_fn_read(tc, req, NULL, tc->peer_db_mask_val);
}
rc = get_ubuf(req, buf);
if (rc == 0)
return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_mask_val,
true, NULL, NULL);
return (rc);
}
static int
sysctl_db_event_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
char buf[TOOL_BUF_LEN];
uint64_t bits;
int rc;
if (req->newptr == NULL)
return read_out(req, tc->db_event_val);
rc = get_ubuf(req, buf);
if (rc)
return (rc);
sscanf(buf, "%ju", &bits);
tc->db_event_val = bits;
callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
return (0);
}
static int
sysctl_spad_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
unsigned int sidx = arg2;
char buf[TOOL_BUF_LEN];
uint32_t bits;
int rc;
if (req->newptr == NULL) {
rc = ntb_spad_read(tc->dev, sidx, &bits);
if (rc)
return (rc);
else
return read_out(req, (uint64_t )bits);
}
rc = get_ubuf(req, buf);
if (rc == 0) {
sscanf(buf, "%i", &bits);
return ntb_spad_write(tc->dev, sidx, bits);
}
return (rc);
}
static int
sysctl_peer_spad_handle(SYSCTL_HANDLER_ARGS)
{
struct tool_ctx *tc = (struct tool_ctx *)arg1;
unsigned int sidx = arg2;
char buf[TOOL_BUF_LEN];
uint32_t bits;
int rc;
if (req->newptr == NULL) {
rc = ntb_peer_spad_read(tc->dev, sidx, &bits);
if (rc)
return (rc);
else
return read_out(req, (uint64_t )bits);
}
rc = get_ubuf(req, buf);
if (rc == 0) {
sscanf(buf, "%i", &bits);
return ntb_peer_spad_write(tc->dev, sidx, bits);
}
return (rc);
}
static void
tool_init_spads(struct tool_ctx *tc)
{
int sidx, pidx;
tc->inspad_cnt = ntb_spad_count(tc->dev);
tc->inspads = malloc(tc->inspad_cnt * sizeof(*tc->inspads), M_NTB_TOOL,
M_WAITOK | M_ZERO);
for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
tc->inspads[sidx].sidx = sidx;
tc->inspads[sidx].pidx = -1;
tc->inspads[sidx].tc = tc;
}
for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->dev);
tc->peers[pidx].outspads = malloc(tc->peers[pidx].outspad_cnt *
sizeof(*tc->peers[pidx].outspads), M_NTB_TOOL, M_WAITOK |
M_ZERO);
for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
tc->peers[pidx].outspads[sidx].sidx = sidx;
tc->peers[pidx].outspads[sidx].pidx = pidx;
tc->peers[pidx].outspads[sidx].tc = tc;
}
}
}
static void
tool_clear_spads(struct tool_ctx *tc)
{
int pidx;
free(tc->inspads, M_NTB_TOOL);
for (pidx = 0; pidx < tc->peer_cnt; pidx++)
free(tc->peers[pidx].outspads, M_NTB_TOOL);
}
static int
tool_check_ntb(struct tool_ctx *tc)
{
callout_init(&tc->link_event_timer, 1);
callout_init(&tc->db_event_timer, 1);
tc->link_status = 'U';
tc->db_mask_val = 0;
tc->peer_db_val = 0;
tc->peer_db_mask_val = 0;
tc->db_event_val = 0;
tc->link_bits = 0;
return (0);
}
static void
tool_clear_data(struct tool_ctx *tc)
{
callout_drain(&tc->link_event_timer);
callout_drain(&tc->db_event_timer);
}
static int
tool_init_ntb(struct tool_ctx *tc)
{
return ntb_set_ctx(tc->dev, tc, &tool_ops);
}
static void
tool_clear_ntb(struct tool_ctx *tc)
{
ntb_clear_ctx(tc->dev);
ntb_link_disable(tc->dev);
}
static void
tool_setup_sysctl(struct tool_ctx *tc)
{
char buf[TOOL_BUF_LEN], desc[TOOL_BUF_LEN];
struct sysctl_oid_list *top, *peer_top;
struct sysctl_oid *parent, *peer;
struct sysctl_ctx_list *clist;
unsigned int pidx, sidx, widx;
clist = device_get_sysctl_ctx(tc->dev);
parent = device_get_sysctl_tree(tc->dev);
top = SYSCTL_CHILDREN(parent);
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "port", CTLTYPE_UINT |
CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_local_port_number,
"IU", "local port number");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "link", CTLTYPE_STRING |
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_link_handle,
"IU", "link info");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db", CTLTYPE_STRING |
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_handle,
"A", "db info");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_valid_mask", CTLTYPE_STRING |
CTLFLAG_RD | CTLFLAG_MPSAFE, tc, 0, sysctl_db_valid_mask_handle,
"A", "db valid mask");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_mask", CTLTYPE_STRING |
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_mask_handle,
"A", "db mask");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_event", CTLTYPE_STRING |
CTLFLAG_WR | CTLFLAG_MPSAFE, tc, 0, sysctl_db_event_handle,
"A", "db event");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db", CTLTYPE_STRING |
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_handle,
"A", "peer db");
SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db_mask", CTLTYPE_STRING |
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_mask_handle,
"IU", "peer db mask info");
if (tc->inspad_cnt != 0) {
for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
snprintf(buf, sizeof(buf), "spad%d", sidx);
snprintf(desc, sizeof(desc), "spad%d info", sidx);
SYSCTL_ADD_PROC(clist, top, OID_AUTO, buf,
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
tc, sidx, sysctl_spad_handle, "IU", desc);
}
}
for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
snprintf(buf, sizeof(buf), "peer%d", pidx);
peer = SYSCTL_ADD_NODE(clist, top, OID_AUTO, buf,
CTLFLAG_RW | CTLFLAG_MPSAFE, 0, buf);
peer_top = SYSCTL_CHILDREN(peer);
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "port",
CTLTYPE_UINT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, pidx,
sysctl_peer_port_number, "IU", "peer port number");
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link",
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
sysctl_peer_link_handle, "IU", "peer_link info");
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link_event",
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
sysctl_peer_link_event_handle, "IU", "link event");
for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
snprintf(buf, sizeof(buf), "mw_trans%d", widx);
snprintf(desc, sizeof(desc), "mw trans%d info", widx);
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
&tc->peers[pidx].inmws[widx], 0,
sysctl_mw_trans_handler, "IU", desc);
snprintf(buf, sizeof(buf), "mw%d", widx);
snprintf(desc, sizeof(desc), "mw%d info", widx);
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
&tc->peers[pidx].inmws[widx], 0,
sysctl_mw_handle, "IU", desc);
snprintf(buf, sizeof(buf), "peer_mw%d", widx);
snprintf(desc, sizeof(desc), "peer_mw%d info", widx);
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
&tc->peers[pidx].inmws[widx], 0,
sysctl_peer_mw_handle, "IU", desc);
}
for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
snprintf(buf, sizeof(buf), "spad%d", sidx);
snprintf(desc, sizeof(desc), "spad%d info", sidx);
SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
tc, sidx, sysctl_peer_spad_handle, "IU", desc);
}
}
}
static int
ntb_tool_probe(device_t dev)
{
device_set_desc(dev, "NTB TOOL");
return (0);
}
static int
ntb_tool_attach(device_t dev)
{
struct tool_ctx *tc = device_get_softc(dev);
int rc = 0;
tc->dev = dev;
rc = tool_check_ntb(tc);
if (rc)
goto out;
tool_init_peers(tc);
rc = tool_init_mws(tc);
if (rc)
goto err_clear_data;
tool_init_spads(tc);
rc = tool_init_ntb(tc);
if (rc)
goto err_clear_spads;
tool_setup_sysctl(tc);
return (0);
err_clear_spads:
tool_clear_spads(tc);
tool_clear_mws(tc);
tool_clear_peers(tc);
err_clear_data:
tool_clear_data(tc);
out:
device_printf(dev, "ntb_tool attached failed with err=(%d).\n", rc);
return (rc);
}
static int
ntb_tool_detach(device_t dev)
{
struct tool_ctx *tc = device_get_softc(dev);
tool_clear_ntb(tc);
tool_clear_spads(tc);
tool_clear_mws(tc);
tool_clear_peers(tc);
tool_clear_data(tc);
return (0);
}
static device_method_t ntb_tool_methods[] = {
DEVMETHOD(device_probe, ntb_tool_probe),
DEVMETHOD(device_attach, ntb_tool_attach),
DEVMETHOD(device_detach, ntb_tool_detach),
DEVMETHOD_END
};
static DEFINE_CLASS_0(ntb_tool, ntb_tool_driver, ntb_tool_methods,
sizeof(struct tool_ctx));
DRIVER_MODULE(ntb_tool, ntb_hw, ntb_tool_driver, NULL, NULL);
MODULE_DEPEND(ntb_tool, ntb, 1, 1, 1);
MODULE_VERSION(ntb_tool, 1.0);