xref: /freebsd/sys/dev/ocs_fc/ocs_ioctl.c (revision ab40f58ccfe6c07ebefddc72f4661a52fe746353)
1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #include "ocs.h"
35 #include "ocs_utils.h"
36 
37 #include <sys/conf.h>
38 #include <sys/sysctl.h>
39 #include <sys/ioccom.h>
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/linker.h>
43 #include <sys/firmware.h>
44 
45 static d_open_t		ocs_open;
46 static d_close_t	ocs_close;
47 static d_ioctl_t	ocs_ioctl;
48 
49 static struct cdevsw ocs_cdevsw = {
50 	.d_version =	D_VERSION,
51 	.d_open =	ocs_open,
52 	.d_close =	ocs_close,
53 	.d_ioctl =	ocs_ioctl,
54 	.d_name =	"ocs_fc"
55 };
56 
57 int
58 ocs_firmware_write(ocs_t *ocs, const uint8_t *buf, size_t buf_len, uint8_t *change_status);
59 
60 static int
61 ocs_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
62 {
63 #if 0
64 	struct ocs_softc *ocs = cdev->si_drv1;
65 
66 	device_printf(ocs->dev, "%s\n", __func__);
67 #endif
68 	return 0;
69 }
70 
71 static int
72 ocs_close(struct cdev *cdev, int flag, int fmt, struct thread *td)
73 {
74 #if 0
75 	struct ocs_softc *ocs = cdev->si_drv1;
76 
77 	device_printf(ocs->dev, "%s\n", __func__);
78 #endif
79 	return 0;
80 }
81 
82 static int32_t
83 __ocs_ioctl_mbox_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
84 {
85 	struct ocs_softc *ocs = arg;
86 
87 	/* wait for the ioctl to sleep before calling wakeup */
88 	mtx_lock(&ocs->dbg_lock);
89 
90 	mtx_unlock(&ocs->dbg_lock);
91 
92 	wakeup(arg);
93 
94 	return 0;
95 }
96 
97 static int
98 ocs_process_sli_config (ocs_t *ocs, ocs_ioctl_elxu_mbox_t *mcmd, ocs_dma_t *dma){
99 
100 	sli4_cmd_sli_config_t *sli_config = (sli4_cmd_sli_config_t *)mcmd->payload;
101 
102 	if (sli_config->emb) {
103 		sli4_req_hdr_t	*req = (sli4_req_hdr_t *)sli_config->payload.embed;
104 
105 		switch (req->opcode) {
106 		case SLI4_OPC_COMMON_READ_OBJECT:
107 			if (mcmd->out_bytes) {
108 				sli4_req_common_read_object_t *rdobj =
109 					(sli4_req_common_read_object_t *)sli_config->payload.embed;
110 
111 				if (ocs_dma_alloc(ocs, dma, mcmd->out_bytes, 4096)) {
112 					device_printf(ocs->dev, "%s: COMMON_READ_OBJECT - %lld allocation failed\n",
113 							__func__, (unsigned long long)mcmd->out_bytes);
114 					return ENXIO;
115 				}
116 
117 				memset(dma->virt, 0, mcmd->out_bytes);
118 
119 				rdobj->host_buffer_descriptor[0].bde_type = SLI4_BDE_TYPE_BDE_64;
120 				rdobj->host_buffer_descriptor[0].buffer_length = mcmd->out_bytes;
121 				rdobj->host_buffer_descriptor[0].u.data.buffer_address_low = ocs_addr32_lo(dma->phys);
122 				rdobj->host_buffer_descriptor[0].u.data.buffer_address_high = ocs_addr32_hi(dma->phys);
123 
124 			}
125 			break;
126 		case SLI4_OPC_COMMON_WRITE_OBJECT:
127 		{
128 			sli4_req_common_write_object_t *wrobj =
129 				(sli4_req_common_write_object_t *)sli_config->payload.embed;
130 
131 			if (ocs_dma_alloc(ocs, dma, wrobj->desired_write_length, 4096)) {
132 				device_printf(ocs->dev, "%s: COMMON_WRITE_OBJECT - %d allocation failed\n",
133 						__func__, wrobj->desired_write_length);
134 				return ENXIO;
135 			}
136 			/* setup the descriptor */
137 			wrobj->host_buffer_descriptor[0].bde_type = SLI4_BDE_TYPE_BDE_64;
138 			wrobj->host_buffer_descriptor[0].buffer_length = wrobj->desired_write_length;
139 			wrobj->host_buffer_descriptor[0].u.data.buffer_address_low = ocs_addr32_lo(dma->phys);
140 			wrobj->host_buffer_descriptor[0].u.data.buffer_address_high = ocs_addr32_hi(dma->phys);
141 
142 			/* copy the data into the DMA buffer */
143 			copyin((void *)(uintptr_t)mcmd->in_addr, dma->virt, mcmd->in_bytes);
144 		}
145 			break;
146 		case SLI4_OPC_COMMON_DELETE_OBJECT:
147 			break;
148 		case SLI4_OPC_COMMON_READ_OBJECT_LIST:
149 			if (mcmd->out_bytes) {
150 				sli4_req_common_read_object_list_t *rdobj =
151 					(sli4_req_common_read_object_list_t *)sli_config->payload.embed;
152 
153 				if (ocs_dma_alloc(ocs, dma, mcmd->out_bytes, 4096)) {
154 					device_printf(ocs->dev, "%s: COMMON_READ_OBJECT_LIST - %lld allocation failed\n",
155 							__func__,(unsigned long long) mcmd->out_bytes);
156 					return ENXIO;
157 				}
158 
159 				memset(dma->virt, 0, mcmd->out_bytes);
160 
161 				rdobj->host_buffer_descriptor[0].bde_type = SLI4_BDE_TYPE_BDE_64;
162 				rdobj->host_buffer_descriptor[0].buffer_length = mcmd->out_bytes;
163 				rdobj->host_buffer_descriptor[0].u.data.buffer_address_low = ocs_addr32_lo(dma->phys);
164 				rdobj->host_buffer_descriptor[0].u.data.buffer_address_high = ocs_addr32_hi(dma->phys);
165 
166 			}
167 			break;
168 		case SLI4_OPC_COMMON_READ_TRANSCEIVER_DATA:
169 			break;
170 		default:
171 			device_printf(ocs->dev, "%s: in=%p (%lld) out=%p (%lld)\n", __func__,
172 					(void *)(uintptr_t)mcmd->in_addr, (unsigned long long)mcmd->in_bytes,
173 					(void *)(uintptr_t)mcmd->out_addr, (unsigned long long)mcmd->out_bytes);
174 			device_printf(ocs->dev, "%s: unknown (opc=%#x)\n", __func__,
175 					req->opcode);
176 			hexdump(mcmd, mcmd->size, NULL, 0);
177 			break;
178 		}
179 	} else {
180 		uint32_t max_bytes = max(mcmd->in_bytes, mcmd->out_bytes);
181 		if (ocs_dma_alloc(ocs, dma, max_bytes, 4096)) {
182 			device_printf(ocs->dev, "%s: non-embedded - %u allocation failed\n",
183 					__func__, max_bytes);
184 			return ENXIO;
185 		}
186 
187 		copyin((void *)(uintptr_t)mcmd->in_addr, dma->virt, mcmd->in_bytes);
188 
189 		sli_config->payload.mem.address_low  = ocs_addr32_lo(dma->phys);
190 		sli_config->payload.mem.address_high = ocs_addr32_hi(dma->phys);
191 		sli_config->payload.mem.length       = max_bytes;
192 	}
193 
194 	return 0;
195 }
196 
197 static int
198 ocs_process_mbx_ioctl(ocs_t *ocs, ocs_ioctl_elxu_mbox_t *mcmd)
199 {
200 	ocs_dma_t	dma = { 0 };
201 
202 	if ((ELXU_BSD_MAGIC != mcmd->magic) ||
203 			(sizeof(ocs_ioctl_elxu_mbox_t) != mcmd->size)) {
204 		device_printf(ocs->dev, "%s: malformed command m=%08x s=%08x\n",
205 				__func__, mcmd->magic, mcmd->size);
206 		return EINVAL;
207 	}
208 
209 	switch(((sli4_mbox_command_header_t *)mcmd->payload)->command) {
210 	case SLI4_MBOX_COMMAND_SLI_CONFIG:
211 		if (ENXIO == ocs_process_sli_config(ocs, mcmd, &dma))
212 			return ENXIO;
213 		break;
214 
215 	case SLI4_MBOX_COMMAND_READ_REV:
216 	case SLI4_MBOX_COMMAND_READ_STATUS:
217 	case SLI4_MBOX_COMMAND_READ_LNK_STAT:
218 		break;
219 
220 	default:
221 		device_printf(ocs->dev, "command %d\n",((sli4_mbox_command_header_t *)mcmd->payload)->command);
222 		device_printf(ocs->dev, "%s, command not support\n", __func__);
223 		goto no_support;
224 		break;
225 
226 	}
227 
228 	/*
229 	 * The dbg_lock usage here insures the command completion code
230 	 * (__ocs_ioctl_mbox_cb), which calls wakeup(), does not run until
231 	 * after first calling msleep()
232 	 *
233 	 *  1. ioctl grabs dbg_lock
234 	 *  2. ioctl issues command
235 	 *       if the command completes before msleep(), the
236 	 *       command completion code (__ocs_ioctl_mbox_cb) will spin
237 	 *       on dbg_lock before calling wakeup()
238 	 *  3. ioctl calls msleep which releases dbg_lock before sleeping
239 	 *     and reacquires it before waking
240 	 *  4. command completion handler acquires the dbg_lock, immediately
241 	 *     releases it, and calls wakeup
242 	 *  5. msleep returns, re-acquiring the lock
243 	 *  6. ioctl code releases the lock
244 	 */
245 	mtx_lock(&ocs->dbg_lock);
246 		ocs_hw_command(&ocs->hw, mcmd->payload, OCS_CMD_NOWAIT,
247 				__ocs_ioctl_mbox_cb, ocs);
248 		msleep(ocs, &ocs->dbg_lock, 0, "ocsmbx", 0);
249 	mtx_unlock(&ocs->dbg_lock);
250 
251 	if( SLI4_MBOX_COMMAND_SLI_CONFIG == ((sli4_mbox_command_header_t *)mcmd->payload)->command
252 	  		&& mcmd->out_bytes && dma.virt) {
253 		copyout(dma.virt, (void *)(uintptr_t)mcmd->out_addr, mcmd->out_bytes);
254 	}
255 
256 no_support:
257 	ocs_dma_free(ocs, &dma);
258 
259 	return 0;
260 }
261 
262 /**
263  * @brief perform requested Elx CoreDump helper function
264  *
265  * The Elx CoreDump facility used for BE3 diagnostics uses the OCS_IOCTL_CMD_ECD_HELPER
266  * ioctl function to execute requested "help" functions
267  *
268  * @param ocs pointer to ocs structure
269  * @param req pointer to helper function request
270  *
271  * @return returns 0 for success, a negative error code value for failure.
272  */
273 
274 static int
275 ocs_process_ecd_helper (ocs_t *ocs, ocs_ioctl_ecd_helper_t *req)
276 {
277 	int32_t rc = 0;
278 	uint8_t v8;
279 	uint16_t v16;
280 	uint32_t v32;
281 
282 
283 	/* Check the BAR read/write commands for valid bar */
284 	switch(req->cmd) {
285 	case OCS_ECD_HELPER_BAR_READ8:
286 	case OCS_ECD_HELPER_BAR_READ16:
287 	case OCS_ECD_HELPER_BAR_READ32:
288 	case OCS_ECD_HELPER_BAR_WRITE8:
289 	case OCS_ECD_HELPER_BAR_WRITE16:
290 	case OCS_ECD_HELPER_BAR_WRITE32:
291 		if (req->bar >= PCI_MAX_BAR) {
292 			device_printf(ocs->dev, "Error: bar %d out of range\n", req->bar);
293 			return -EFAULT;
294 		}
295 		if (ocs->reg[req->bar].res == NULL) {
296 			device_printf(ocs->dev, "Error: bar %d not defined\n", req->bar);
297 			return -EFAULT;
298 		}
299 		break;
300 	default:
301 		break;
302 	}
303 
304 	switch(req->cmd) {
305 	case OCS_ECD_HELPER_CFG_READ8:
306 		v8 = ocs_config_read8(ocs, req->offset);
307 		req->data = v8;
308 		break;
309 	case OCS_ECD_HELPER_CFG_READ16:
310 		v16 = ocs_config_read16(ocs, req->offset);
311 		req->data = v16;
312 		break;
313 	case OCS_ECD_HELPER_CFG_READ32:
314 		v32 = ocs_config_read32(ocs, req->offset);
315 		req->data = v32;
316 		break;
317 	case OCS_ECD_HELPER_CFG_WRITE8:
318 		ocs_config_write8(ocs, req->offset, req->data);
319 		break;
320 	case OCS_ECD_HELPER_CFG_WRITE16:
321 		ocs_config_write16(ocs, req->offset, req->data);
322 		break;
323 	case OCS_ECD_HELPER_CFG_WRITE32:
324 		ocs_config_write32(ocs, req->offset, req->data);
325 		break;
326 	case OCS_ECD_HELPER_BAR_READ8:
327 		req->data = ocs_reg_read8(ocs, req->bar, req->offset);
328 		break;
329 	case OCS_ECD_HELPER_BAR_READ16:
330 		req->data = ocs_reg_read16(ocs, req->bar, req->offset);
331 		break;
332 	case OCS_ECD_HELPER_BAR_READ32:
333 		req->data = ocs_reg_read32(ocs, req->bar, req->offset);
334 		break;
335 	case OCS_ECD_HELPER_BAR_WRITE8:
336 		ocs_reg_write8(ocs, req->bar, req->offset, req->data);
337 		break;
338 	case OCS_ECD_HELPER_BAR_WRITE16:
339 		ocs_reg_write16(ocs, req->bar, req->offset, req->data);
340 		break;
341 	case OCS_ECD_HELPER_BAR_WRITE32:
342 		ocs_reg_write32(ocs, req->bar, req->offset, req->data);
343 		break;
344 	default:
345 		device_printf(ocs->dev, "Invalid helper command=%d\n", req->cmd);
346 		break;
347 	}
348 
349 	return rc;
350 }
351 
352 static int
353 ocs_ioctl(struct cdev *cdev, u_long cmd, caddr_t addr, int flag, struct thread *td)
354 {
355 	int status = 0;
356 	struct ocs_softc *ocs = cdev->si_drv1;
357 	device_t dev = ocs->dev;
358 
359 	switch (cmd) {
360 	case OCS_IOCTL_CMD_ELXU_MBOX: {
361 		/* "copyin" done by kernel; thus, just dereference addr */
362 		ocs_ioctl_elxu_mbox_t *mcmd = (void *)addr;
363 		status = ocs_process_mbx_ioctl(ocs, mcmd);
364 		break;
365 	}
366 	case OCS_IOCTL_CMD_ECD_HELPER: {
367 		/* "copyin" done by kernel; thus, just dereference addr */
368 		ocs_ioctl_ecd_helper_t *req = (void *)addr;
369 		status = ocs_process_ecd_helper(ocs, req);
370 		break;
371 	}
372 
373 	case OCS_IOCTL_CMD_VPORT: {
374 		int32_t rc = 0;
375 		ocs_ioctl_vport_t *req = (ocs_ioctl_vport_t*) addr;
376 		ocs_domain_t *domain;
377 
378 		domain = ocs_domain_get_instance(ocs, req->domain_index);
379 		if (domain == NULL) {
380 			device_printf(ocs->dev, "domain [%d] nod found\n",
381 							req->domain_index);
382 			return -EFAULT;
383 		}
384 
385 		if (req->req_create) {
386 			rc = ocs_sport_vport_new(domain, req->wwpn, req->wwnn,
387 						UINT32_MAX, req->enable_ini,
388 					req->enable_tgt, NULL, NULL, TRUE);
389 		} else {
390 			rc = ocs_sport_vport_del(ocs, domain, req->wwpn, req->wwnn);
391 		}
392 
393 		return rc;
394 	}
395 
396 	case OCS_IOCTL_CMD_GET_DDUMP: {
397 		ocs_ioctl_ddump_t *req = (ocs_ioctl_ddump_t*) addr;
398 		ocs_textbuf_t textbuf;
399 		int x;
400 
401 		/* Build a text buffer */
402 		if (ocs_textbuf_alloc(ocs, &textbuf, req->user_buffer_len)) {
403 			device_printf(ocs->dev, "Error: ocs_textbuf_alloc failed\n");
404 			return -EFAULT;
405 		}
406 
407 		switch (req->args.action) {
408 		case OCS_IOCTL_DDUMP_GET:
409 		case OCS_IOCTL_DDUMP_GET_SAVED: {
410 			uint32_t remaining;
411 			uint32_t written;
412 			uint32_t idx;
413 			int32_t n;
414 			ocs_textbuf_t *ptbuf = NULL;
415 			uint32_t flags = 0;
416 
417 			if (req->args.action == OCS_IOCTL_DDUMP_GET_SAVED) {
418 				if (ocs_textbuf_initialized(&ocs->ddump_saved)) {
419 					ptbuf = &ocs->ddump_saved;
420 				}
421 			} else {
422 				if (ocs_textbuf_alloc(ocs, &textbuf, req->user_buffer_len)) {
423 					ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
424 					return -EFAULT;
425 				}
426 
427 				/* translate IOCTL ddump flags to ddump flags */
428 				if (req->args.flags & OCS_IOCTL_DDUMP_FLAGS_WQES) {
429 					flags |= OCS_DDUMP_FLAGS_WQES;
430 				}
431 				if (req->args.flags & OCS_IOCTL_DDUMP_FLAGS_CQES) {
432 					flags |= OCS_DDUMP_FLAGS_CQES;
433 				}
434 				if (req->args.flags & OCS_IOCTL_DDUMP_FLAGS_MQES) {
435 					flags |= OCS_DDUMP_FLAGS_MQES;
436 				}
437 				if (req->args.flags & OCS_IOCTL_DDUMP_FLAGS_RQES) {
438 					flags |= OCS_DDUMP_FLAGS_RQES;
439 				}
440 				if (req->args.flags & OCS_IOCTL_DDUMP_FLAGS_EQES) {
441 					flags |= OCS_DDUMP_FLAGS_EQES;
442 				}
443 
444 				/* Try 3 times to get the dump */
445 				for(x=0; x<3; x++) {
446 					if (ocs_ddump(ocs, &textbuf, flags, req->args.q_entries) != 0) {
447 						ocs_textbuf_reset(&textbuf);
448 					} else {
449 						/* Success */
450 						x = 0;
451 						break;
452 					}
453 				}
454 				if (x != 0 ) {
455 					/* Retries failed */
456 					ocs_log_test(ocs, "ocs_ddump failed\n");
457 				} else {
458 					ptbuf = &textbuf;
459 				}
460 
461 			}
462 			written = 0;
463 			if (ptbuf != NULL) {
464 				/* Process each textbuf segment */
465 				remaining = req->user_buffer_len;
466 				for (idx = 0; remaining; idx++) {
467 					n = ocs_textbuf_ext_get_written(ptbuf, idx);
468 					if (n < 0) {
469 						break;
470 					}
471 					if ((uint32_t)n >= remaining) {
472 						n = (int32_t)remaining;
473 					}
474 					if (ocs_copy_to_user(req->user_buffer + written,
475 						ocs_textbuf_ext_get_buffer(ptbuf, idx), n)) {
476 						ocs_log_test(ocs, "Error: (%d) ocs_copy_to_user failed\n", __LINE__);
477 					}
478 					written += n;
479 					remaining -= (uint32_t)n;
480 				}
481 			}
482 			req->bytes_written = written;
483 			if (ptbuf == &textbuf) {
484 				ocs_textbuf_free(ocs, &textbuf);
485 			}
486 
487 			break;
488 		}
489 		case OCS_IOCTL_DDUMP_CLR_SAVED:
490 			ocs_clear_saved_ddump(ocs);
491 			break;
492 		default:
493 			ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
494 			break;
495 		}
496 		break;
497 	}
498 	case OCS_IOCTL_CMD_DRIVER_INFO: {
499 		ocs_ioctl_driver_info_t *req = (ocs_ioctl_driver_info_t*)addr;
500 
501 		ocs_memset(req, 0, sizeof(*req));
502 
503 		req->pci_vendor = ocs->pci_vendor;
504 		req->pci_device = ocs->pci_device;
505 		ocs_strncpy(req->businfo, ocs->businfo, sizeof(req->businfo));
506 
507 		req->sli_intf = ocs_config_read32(ocs, SLI4_INTF_REG);
508 		ocs_strncpy(req->desc, device_get_desc(dev), sizeof(req->desc));
509 		ocs_strncpy(req->fw_rev, ocs->fwrev, sizeof(req->fw_rev));
510 		if (ocs->domain && ocs->domain->sport) {
511 			*((uint64_t*)req->hw_addr.fc.wwnn) = ocs_htobe64(ocs->domain->sport->wwnn);
512 			*((uint64_t*)req->hw_addr.fc.wwpn) = ocs_htobe64(ocs->domain->sport->wwpn);
513 		}
514 		ocs_strncpy(req->serialnum, ocs->serialnum, sizeof(req->serialnum));
515 		break;
516 	}
517 
518 	case OCS_IOCTL_CMD_MGMT_LIST: {
519 		ocs_ioctl_mgmt_buffer_t* req = (ocs_ioctl_mgmt_buffer_t *)addr;
520 		ocs_textbuf_t textbuf;
521 
522 		/* Build a text buffer */
523 		if (ocs_textbuf_alloc(ocs, &textbuf, req->user_buffer_len)) {
524 			ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
525 			return -EFAULT;
526 		}
527 
528 		ocs_mgmt_get_list(ocs, &textbuf);
529 
530 		if (ocs_textbuf_get_written(&textbuf)) {
531 			if (ocs_copy_to_user(req->user_buffer,
532 				ocs_textbuf_get_buffer(&textbuf),
533 				ocs_textbuf_get_written(&textbuf))) {
534 				ocs_log_test(ocs, "Error: (%d) ocs_copy_to_user failed\n", __LINE__);
535 			}
536 		}
537 		req->bytes_written = ocs_textbuf_get_written(&textbuf);
538 
539 		ocs_textbuf_free(ocs, &textbuf);
540 
541 		break;
542 
543 	}
544 
545 	case OCS_IOCTL_CMD_MGMT_GET_ALL: {
546 		ocs_ioctl_mgmt_buffer_t* req = (ocs_ioctl_mgmt_buffer_t *)addr;
547 		ocs_textbuf_t textbuf;
548 		int32_t n;
549 		uint32_t idx;
550 		uint32_t copied = 0;
551 
552 		/* Build a text buffer */
553 		if (ocs_textbuf_alloc(ocs, &textbuf, req->user_buffer_len)) {
554 			ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
555 			return -EFAULT;
556 		}
557 
558 		ocs_mgmt_get_all(ocs, &textbuf);
559 
560 		for (idx = 0; (n = ocs_textbuf_ext_get_written(&textbuf, idx)) > 0; idx++) {
561 			if(ocs_copy_to_user(req->user_buffer + copied,
562 					ocs_textbuf_ext_get_buffer(&textbuf, idx),
563 					ocs_textbuf_ext_get_written(&textbuf, idx))) {
564 
565 					ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
566 			}
567 			copied += n;
568 		}
569 		req->bytes_written = copied;
570 
571 		ocs_textbuf_free(ocs, &textbuf);
572 
573 		break;
574 	}
575 
576 	case OCS_IOCTL_CMD_MGMT_GET: {
577 		ocs_ioctl_cmd_get_t* req = (ocs_ioctl_cmd_get_t*)addr;
578 		ocs_textbuf_t textbuf;
579 		char name[OCS_MGMT_MAX_NAME];
580 
581 		/* Copy the name value in from user space */
582 		if (ocs_copy_from_user(name, req->name, OCS_MGMT_MAX_NAME)) {
583 			ocs_log_test(ocs, "ocs_copy_from_user failed\n");
584 			ocs_ioctl_free(ocs, req, sizeof(ocs_ioctl_cmd_get_t));
585 			return -EFAULT;
586 		}
587 
588 		/* Build a text buffer */
589 		if (ocs_textbuf_alloc(ocs, &textbuf, req->value_length)) {
590 			ocs_log_err(ocs, "Error: ocs_textbuf_alloc failed\n");
591 			return -EFAULT;
592 		}
593 
594 		ocs_mgmt_get(ocs, name, &textbuf);
595 
596 		if (ocs_textbuf_get_written(&textbuf)) {
597 			if (ocs_copy_to_user(req->value,
598 				ocs_textbuf_get_buffer(&textbuf),
599 				ocs_textbuf_get_written(&textbuf))) {
600 				ocs_log_test(ocs, "Error: (%d) ocs_copy_to_user failed\n", __LINE__);
601 
602 		}
603 		}
604 		req->value_length = ocs_textbuf_get_written(&textbuf);
605 
606 		ocs_textbuf_free(ocs, &textbuf);
607 
608 		break;
609 	}
610 
611 	case OCS_IOCTL_CMD_MGMT_SET: {
612 		char name[OCS_MGMT_MAX_NAME];
613 		char value[OCS_MGMT_MAX_VALUE];
614 		ocs_ioctl_cmd_set_t* req = (ocs_ioctl_cmd_set_t*)addr;
615 
616 		// Copy the name  in from user space
617 		if (ocs_copy_from_user(name, req->name, OCS_MGMT_MAX_NAME)) {
618 			ocs_log_test(ocs, "Error: copy from user failed\n");
619 			ocs_ioctl_free(ocs, req, sizeof(*req));
620 			return -EFAULT;
621 		}
622 
623 		// Copy the  value in from user space
624 		if (ocs_copy_from_user(value, req->value, OCS_MGMT_MAX_VALUE)) {
625 			ocs_log_test(ocs, "Error: copy from user failed\n");
626 			ocs_ioctl_free(ocs, req, sizeof(*req));
627 			return -EFAULT;
628 		}
629 
630 		req->result = ocs_mgmt_set(ocs, req->name, req->value);
631 
632 		break;
633 	}
634 
635 	case OCS_IOCTL_CMD_MGMT_EXEC: {
636 		ocs_ioctl_action_t* req = (ocs_ioctl_action_t*) addr;
637 		char action_name[OCS_MGMT_MAX_NAME];
638 
639 		if (ocs_copy_from_user(action_name, req->name, sizeof(action_name))) {
640 			ocs_log_test(ocs, "Error: copy req.name from user failed\n");
641 			ocs_ioctl_free(ocs, req, sizeof(*req));
642 			return -EFAULT;
643 		}
644 
645 		req->result = ocs_mgmt_exec(ocs, action_name, req->arg_in, req->arg_in_length,
646 				req->arg_out, req->arg_out_length);
647 
648 		break;
649 	}
650 
651 	default:
652 		ocs_log_test(ocs, "Error: unknown cmd %#lx\n", cmd);
653 		status = -ENOTTY;
654 		break;
655 	}
656 	return status;
657 }
658 
659 static void
660 ocs_fw_write_cb(int32_t status, uint32_t actual_write_length,
661 					uint32_t change_status, void *arg)
662 {
663         ocs_mgmt_fw_write_result_t *result = arg;
664 
665         result->status = status;
666         result->actual_xfer = actual_write_length;
667         result->change_status = change_status;
668 
669         ocs_sem_v(&(result->semaphore));
670 }
671 
672 int
673 ocs_firmware_write(ocs_t *ocs, const uint8_t *buf, size_t buf_len,
674 						uint8_t *change_status)
675 {
676         int rc = 0;
677         uint32_t bytes_left;
678         uint32_t xfer_size;
679         uint32_t offset;
680         ocs_dma_t dma;
681         int last = 0;
682         ocs_mgmt_fw_write_result_t result;
683 
684         ocs_sem_init(&(result.semaphore), 0, "fw_write");
685 
686         bytes_left = buf_len;
687         offset = 0;
688 
689         if (ocs_dma_alloc(ocs, &dma, FW_WRITE_BUFSIZE, 4096)) {
690                 ocs_log_err(ocs, "ocs_firmware_write: malloc failed\n");
691                 return -ENOMEM;
692         }
693 
694         while (bytes_left > 0) {
695 
696                 if (bytes_left > FW_WRITE_BUFSIZE) {
697                         xfer_size = FW_WRITE_BUFSIZE;
698                 } else {
699                         xfer_size = bytes_left;
700                 }
701 
702                 ocs_memcpy(dma.virt, buf + offset, xfer_size);
703 
704                 if (bytes_left == xfer_size) {
705                         last = 1;
706                 }
707 
708                 ocs_hw_firmware_write(&ocs->hw, &dma, xfer_size, offset,
709 						last, ocs_fw_write_cb, &result);
710 
711                 if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
712                         rc = -ENXIO;
713                         break;
714                 }
715 
716                 if (result.actual_xfer == 0 || result.status != 0) {
717                         rc = -EFAULT;
718                         break;
719                 }
720 
721                 if (last) {
722                         *change_status = result.change_status;
723                 }
724 
725                 bytes_left -= result.actual_xfer;
726                 offset += result.actual_xfer;
727         }
728 
729         ocs_dma_free(ocs, &dma);
730         return rc;
731 }
732 
733 static int
734 ocs_sys_fwupgrade(SYSCTL_HANDLER_ARGS)
735 {
736 	char file_name[256] = {0};
737 	char fw_change_status;
738 	uint32_t rc = 1;
739         ocs_t *ocs  = (ocs_t *)arg1;
740         const struct firmware *fw;
741 	const struct ocs_hw_grp_hdr *fw_image;
742 
743         rc = sysctl_handle_string(oidp, file_name, sizeof(file_name), req);
744         if (rc || !req->newptr)
745                 return rc;
746 
747         fw = firmware_get(file_name);
748         if (fw == NULL) {
749                 device_printf(ocs->dev, "Unable to get Firmware. "
750                         "Make sure %s is copied to /boot/modules\n", file_name);
751                 return ENOENT;
752         }
753 
754 	fw_image = (const struct ocs_hw_grp_hdr *)fw->data;
755 
756         /* Check if firmware provided is compatible with this particular
757          * Adapter of not*/
758         if ((ocs_be32toh(fw_image->magic_number) != OCS_HW_OBJECT_G5) &&
759                 (ocs_be32toh(fw_image->magic_number) != OCS_HW_OBJECT_G6)) {
760                 device_printf(ocs->dev,
761                         "Invalid FW image found Magic: 0x%x Size: %zu \n",
762                         ocs_be32toh(fw_image->magic_number), fw->datasize);
763                 rc = -1;
764                 goto exit;
765 
766         }
767 
768         if (!strncmp(ocs->fw_version, fw_image->revision,
769 					strnlen(fw_image->revision, 16))) {
770                 device_printf(ocs->dev, "No update req. "
771 				"Firmware is already up to date. \n");
772                 rc = 0;
773                 goto exit;
774         }
775 
776 	device_printf(ocs->dev, "Upgrading Firmware from %s to %s \n",
777 				ocs->fw_version, fw_image->revision);
778 
779 	rc = ocs_firmware_write(ocs, fw->data, fw->datasize, &fw_change_status);
780         if (rc) {
781                 ocs_log_err(ocs, "Firmware update failed with status = %d\n", rc);
782         } else {
783                 ocs_log_info(ocs, "Firmware updated successfully\n");
784                 switch (fw_change_status) {
785                         case 0x00:
786                                 device_printf(ocs->dev,
787 				"No reset needed, new firmware is active.\n");
788                                 break;
789                         case 0x01:
790                                 device_printf(ocs->dev,
791 				"A physical device reset (host reboot) is "
792 				"needed to activate the new firmware\n");
793                                 break;
794                         case 0x02:
795                         case 0x03:
796                                 device_printf(ocs->dev,
797 				"firmware is resetting to activate the new "
798 				"firmware, Host reboot is needed \n");
799                                 break;
800                         default:
801                                 ocs_log_warn(ocs,
802                                         "Unexected value change_status: %d\n",
803                                         fw_change_status);
804                                 break;
805                 }
806 
807         }
808 
809 exit:
810         /* Release Firmware*/
811         firmware_put(fw, FIRMWARE_UNLOAD);
812 
813         return rc;
814 
815 }
816 
817 static int
818 ocs_sysctl_wwnn(SYSCTL_HANDLER_ARGS)
819 {
820 	uint32_t rc = 1;
821 	ocs_t *ocs = oidp->oid_arg1;
822 	char old[64];
823 	char new[64];
824 	uint64_t *wwnn = NULL;
825 	ocs_xport_t *xport = ocs->xport;
826 
827 	if (xport->req_wwnn) {
828 		wwnn = &xport->req_wwnn;
829 		memset(old, 0, sizeof(old));
830 		snprintf(old, sizeof(old), "0x%llx" , (unsigned long long) *wwnn);
831 
832 	} else {
833 		wwnn = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_NODE);
834 
835 		memset(old, 0, sizeof(old));
836 		snprintf(old, sizeof(old), "0x%llx" , (unsigned long long) ocs_htobe64(*wwnn));
837 	}
838 
839 	/*Read wwnn*/
840 	if (!req->newptr) {
841 
842 		return (sysctl_handle_string(oidp, old, sizeof(old), req));
843 	}
844 
845 	/*Configure port wwn*/
846 	rc = sysctl_handle_string(oidp, new, sizeof(new), req);
847 	if (rc)
848 		return (rc);
849 
850 	if (strncmp(old, new, strlen(old)) == 0) {
851 		return 0;
852 	}
853 
854 	return (set_req_wwnn(ocs, NULL, new));
855 }
856 
857 static int
858 ocs_sysctl_wwpn(SYSCTL_HANDLER_ARGS)
859 {
860 	uint32_t rc = 1;
861 	ocs_t *ocs = oidp->oid_arg1;
862 	char old[64];
863 	char new[64];
864 	uint64_t *wwpn = NULL;
865 	ocs_xport_t *xport = ocs->xport;
866 
867 	if (xport->req_wwpn) {
868 		wwpn = &xport->req_wwpn;
869 		memset(old, 0, sizeof(old));
870 		snprintf(old, sizeof(old), "0x%llx",(unsigned long long) *wwpn);
871 	} else {
872 		wwpn = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_PORT);
873 		memset(old, 0, sizeof(old));
874 		snprintf(old, sizeof(old), "0x%llx",(unsigned long long) ocs_htobe64(*wwpn));
875 	}
876 
877 
878 	/*Read wwpn*/
879 	if (!req->newptr) {
880 		return (sysctl_handle_string(oidp, old, sizeof(old), req));
881 	}
882 
883 	/*Configure port wwn*/
884 	rc = sysctl_handle_string(oidp, new, sizeof(new), req);
885 	if (rc)
886 		return (rc);
887 
888 	if (strncmp(old, new, strlen(old)) == 0) {
889 		return 0;
890 	}
891 
892 	return (set_req_wwpn(ocs, NULL, new));
893 }
894 
895 static int
896 ocs_sysctl_current_topology(SYSCTL_HANDLER_ARGS)
897 {
898 	ocs_t *ocs = oidp->oid_arg1;
899 	uint32_t value;
900 
901 	ocs_hw_get(&ocs->hw, OCS_HW_TOPOLOGY, &value);
902 
903 	return (sysctl_handle_int(oidp, &value, 0, req));
904 }
905 
906 static int
907 ocs_sysctl_current_speed(SYSCTL_HANDLER_ARGS)
908 {
909 	ocs_t *ocs = oidp->oid_arg1;
910 	uint32_t value;
911 
912 	ocs_hw_get(&ocs->hw, OCS_HW_LINK_SPEED, &value);
913 
914 	return (sysctl_handle_int(oidp, &value, 0, req));
915 }
916 
917 static int
918 ocs_sysctl_config_topology(SYSCTL_HANDLER_ARGS)
919 {
920 	uint32_t rc = 1;
921 	ocs_t *ocs = oidp->oid_arg1;
922 	uint32_t old_value;
923 	uint32_t new_value;
924 	char buf[64];
925 
926 	ocs_hw_get(&ocs->hw, OCS_HW_CONFIG_TOPOLOGY, &old_value);
927 
928 	/*Read topo*/
929 	if (!req->newptr) {
930 		return (sysctl_handle_int(oidp, &old_value, 0, req));
931 	}
932 
933 	/*Configure port wwn*/
934 	rc = sysctl_handle_int(oidp, &new_value, 0, req);
935 	if (rc)
936 		return (rc);
937 
938 	if (new_value == old_value) {
939 		return 0;
940 	}
941 
942 	snprintf(buf, sizeof(buf), "%d",new_value);
943 	rc = set_configured_topology(ocs, NULL, buf);
944 	return rc;
945 }
946 
947 static int
948 ocs_sysctl_config_speed(SYSCTL_HANDLER_ARGS)
949 {
950 	uint32_t rc = 1;
951 	ocs_t *ocs = oidp->oid_arg1;
952 	uint32_t old_value;
953 	uint32_t new_value;
954 	char buf[64];
955 
956 	ocs_hw_get(&ocs->hw, OCS_HW_LINK_CONFIG_SPEED, &old_value);
957 
958 	/*Read topo*/
959 	if (!req->newptr) {
960 		return (sysctl_handle_int(oidp, &old_value, 0, req));
961 	}
962 
963 	/*Configure port wwn*/
964 	rc = sysctl_handle_int(oidp, &new_value, 0, req);
965 	if (rc)
966 		return (rc);
967 
968 	if (new_value == old_value) {
969 		return 0;
970 	}
971 
972 	snprintf(buf, sizeof(buf), "%d",new_value);
973 	rc = set_configured_speed(ocs, NULL,buf);
974 	return rc;
975 }
976 
977 static int
978 ocs_sysctl_fcid(SYSCTL_HANDLER_ARGS)
979 {
980 	ocs_t *ocs = oidp->oid_arg1;
981 	char buf[64];
982 
983 	memset(buf, 0, sizeof(buf));
984 	if (ocs->domain && ocs->domain->attached) {
985 		snprintf(buf, sizeof(buf), "0x%06x",
986 			ocs->domain->sport->fc_id);
987 	}
988 
989 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
990 }
991 
992 
993 static int
994 ocs_sysctl_port_state(SYSCTL_HANDLER_ARGS)
995 {
996 
997 	char new[256] = {0};
998 	uint32_t rc = 1;
999 	ocs_xport_stats_t old;
1000 	ocs_t *ocs  = (ocs_t *)arg1;
1001 
1002 	ocs_xport_status(ocs->xport, OCS_XPORT_CONFIG_PORT_STATUS, &old);
1003 
1004 	/*Read port state */
1005 	if (!req->newptr) {
1006 		snprintf(new, sizeof(new), "%s",
1007 			(old.value == OCS_XPORT_PORT_OFFLINE) ?
1008 					 "offline" : "online");
1009 		return (sysctl_handle_string(oidp, new, sizeof(new), req));
1010         }
1011 
1012 	/*Configure port state*/
1013 	rc = sysctl_handle_string(oidp, new, sizeof(new), req);
1014 	if (rc)
1015 		return (rc);
1016 
1017 	if (ocs_strcasecmp(new, "offline") == 0) {
1018 		if (old.value == OCS_XPORT_PORT_OFFLINE) {
1019 			return (0);
1020 		}
1021 		ocs_log_debug(ocs, "Setting port to %s\n", new);
1022 		rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
1023 		if (rc != 0) {
1024 			ocs_log_err(ocs, "Setting port to offline failed\n");
1025 		}
1026 	} else if (ocs_strcasecmp(new, "online") == 0) {
1027 		if (old.value == OCS_XPORT_PORT_ONLINE) {
1028 			return (0);
1029 		}
1030 		ocs_log_debug(ocs, "Setting port to %s\n", new);
1031 		rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
1032 		if (rc != 0) {
1033 			ocs_log_err(ocs, "Setting port to online failed\n");
1034 		}
1035 	} else {
1036 		ocs_log_err(ocs, "Unsupported link state %s\n", new);
1037 		rc = 1;
1038 	}
1039 
1040 	return (rc);
1041 
1042 }
1043 
1044 static int
1045 ocs_sysctl_vport_wwpn(SYSCTL_HANDLER_ARGS)
1046 {
1047 	ocs_fcport *fcp = oidp->oid_arg1;
1048 	char str_wwpn[64];
1049 
1050 	memset(str_wwpn, 0, sizeof(str_wwpn));
1051 	snprintf(str_wwpn, sizeof(str_wwpn), "0x%llx", (unsigned long long)fcp->vport->wwpn);
1052 
1053 	return (sysctl_handle_string(oidp, str_wwpn, sizeof(str_wwpn), req));
1054 }
1055 
1056 static int
1057 ocs_sysctl_vport_wwnn(SYSCTL_HANDLER_ARGS)
1058 {
1059 	ocs_fcport *fcp = oidp->oid_arg1;
1060 	char str_wwnn[64];
1061 
1062 	memset(str_wwnn, 0, sizeof(str_wwnn));
1063 	snprintf(str_wwnn, sizeof(str_wwnn), "0x%llx", (unsigned long long)fcp->vport->wwnn);
1064 
1065 	return (sysctl_handle_string(oidp, str_wwnn, sizeof(str_wwnn), req));
1066 }
1067 
1068 /**
1069  * @brief Initialize sysctl
1070  *
1071  * Initialize sysctl so elxsdkutil can query device information.
1072  *
1073  * @param ocs pointer to ocs
1074  * @return void
1075  */
1076 static void
1077 ocs_sysctl_init(ocs_t *ocs)
1078 {
1079 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(ocs->dev);
1080 	struct sysctl_oid *tree = device_get_sysctl_tree(ocs->dev);
1081 	struct sysctl_oid *vtree;
1082 	const char *str = NULL;
1083 	char sli_intf[16], name[16];
1084 	uint32_t rev, if_type, family, i;
1085 	ocs_fcport *fcp = NULL;
1086 
1087 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1088 			"devid", CTLFLAG_RD, NULL,
1089 			pci_get_devid(ocs->dev), "Device ID");
1090 
1091 	memset(ocs->modeldesc, 0, sizeof(ocs->modeldesc));
1092 	if (0 == pci_get_vpd_ident(ocs->dev, &str)) {
1093 		snprintf(ocs->modeldesc, sizeof(ocs->modeldesc), "%s", str);
1094 	}
1095 	SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1096 			"modeldesc", CTLFLAG_RD,
1097 			ocs->modeldesc,
1098 			0, "Model Description");
1099 
1100 	memset(ocs->serialnum, 0, sizeof(ocs->serialnum));
1101 	if (0 == pci_get_vpd_readonly(ocs->dev, "SN", &str)) {
1102 		snprintf(ocs->serialnum, sizeof(ocs->serialnum), "%s", str);
1103 	}
1104 	SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1105 			"sn", CTLFLAG_RD,
1106 			ocs->serialnum,
1107 			0, "Serial Number");
1108 
1109 	ocs_hw_get(&ocs->hw, OCS_HW_SLI_REV, &rev);
1110 	ocs_hw_get(&ocs->hw, OCS_HW_IF_TYPE, &if_type);
1111 	ocs_hw_get(&ocs->hw, OCS_HW_SLI_FAMILY, &family);
1112 
1113 	memset(ocs->fwrev, 0, sizeof(ocs->fwrev));
1114 	snprintf(ocs->fwrev, sizeof(ocs->fwrev), "%s, sli-%d:%d:%x",
1115 			(char *)ocs_hw_get_ptr(&ocs->hw, OCS_HW_FW_REV),
1116 			rev, if_type, family);
1117 	SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1118 			"fwrev", CTLFLAG_RD,
1119 			ocs->fwrev,
1120 			0, "Firmware Revision");
1121 
1122 	memset(ocs->sli_intf, 0, sizeof(ocs->sli_intf));
1123 	snprintf(ocs->sli_intf, sizeof(sli_intf), "%08x",
1124 		 ocs_config_read32(ocs, SLI4_INTF_REG));
1125 	SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1126 			  "sli_intf", CTLFLAG_RD,
1127 			  ocs->sli_intf,
1128 			  0, "SLI Interface");
1129 
1130         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fw_upgrade",
1131                 CTLTYPE_STRING | CTLFLAG_RW, (void *)ocs, 0,
1132                 ocs_sys_fwupgrade, "A", "Firmware grp file");
1133 
1134 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1135 			"wwnn", CTLTYPE_STRING | CTLFLAG_RW,
1136 			ocs, 0, ocs_sysctl_wwnn, "A",
1137 			"World Wide Node Name, wwnn should be in the format 0x<XXXXXXXXXXXXXXXX>");
1138 
1139 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1140 			"wwpn", CTLTYPE_STRING | CTLFLAG_RW,
1141 			ocs, 0, ocs_sysctl_wwpn, "A",
1142 			"World Wide Port Name, wwpn should be in the format 0x<XXXXXXXXXXXXXXXX>");
1143 
1144 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1145 			"current_topology", CTLTYPE_UINT | CTLFLAG_RD,
1146 			ocs, 0, ocs_sysctl_current_topology, "IU",
1147 			"Current Topology, 1-NPort; 2-Loop; 3-None");
1148 
1149 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1150 			"current_speed", CTLTYPE_UINT | CTLFLAG_RD,
1151 			ocs, 0, ocs_sysctl_current_speed, "IU",
1152 			"Current Speed");
1153 
1154 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1155 			"configured_topology", CTLTYPE_UINT | CTLFLAG_RW,
1156 			ocs, 0, ocs_sysctl_config_topology, "IU",
1157 			"Configured Topology, 0-Auto; 1-NPort; 2-Loop");
1158 
1159 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1160 			"configured_speed", CTLTYPE_UINT | CTLFLAG_RW,
1161 			ocs, 0, ocs_sysctl_config_speed, "IU",
1162 			"Configured Speed, 0-Auto, 2000, 4000, 8000, 16000, 32000");
1163 
1164 	SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1165 			"businfo", CTLFLAG_RD,
1166 			ocs->businfo,
1167 			0, "Bus Info");
1168 
1169 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1170 			"fcid", CTLTYPE_STRING | CTLFLAG_RD,
1171 			ocs, 0, ocs_sysctl_fcid, "A",
1172 			"Port FC ID");
1173 
1174 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1175 			"port_state", CTLTYPE_STRING | CTLFLAG_RW,
1176 			ocs, 0, ocs_sysctl_port_state, "A",
1177 			"configured port state");
1178 
1179 	for (i	= 0; i < ocs->num_vports; i++) {
1180 		fcp = FCPORT(ocs, i+1);
1181 
1182 		memset(name, 0, sizeof(name));
1183 		snprintf(name, sizeof(name), "vport%d", i);
1184 		vtree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree),
1185 				OID_AUTO, name, CTLFLAG_RW, 0, "Virtual port");
1186 
1187 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(vtree), OID_AUTO,
1188 			"wwnn", CTLTYPE_STRING | CTLFLAG_RW,
1189 			fcp, 0, ocs_sysctl_vport_wwnn, "A",
1190 			"World Wide Node Name");
1191 
1192 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(vtree), OID_AUTO,
1193 			"wwpn", CTLTYPE_STRING | CTLFLAG_RW,
1194 			fcp, 0, ocs_sysctl_vport_wwpn, "A",
1195 			"World Wide Port Name");
1196 
1197 	}
1198 
1199 }
1200 
1201 /**
1202  * @brief Initialize the debug module
1203  *
1204  * Parse device hints (similar to Linux module parameters) here. To use,
1205  * run the command
1206  *    kenv hint.ocs.U.P=V
1207  * from the command line replacing U with the unit # (0,1,...),
1208  * P with the parameter name (debug_mask), and V with the value
1209  */
1210 void
1211 ocs_debug_attach(void *os)
1212 {
1213 	struct ocs_softc *ocs = os;
1214 	int error = 0;
1215 	char *resname = NULL;
1216 	int32_t	unit = INT32_MAX;
1217 	uint32_t ocs_debug_mask = 0;
1218 
1219 	resname = "debug_mask";
1220 	if (0 == (error = resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
1221 				resname, &ocs_debug_mask))) {
1222 		device_printf(ocs->dev, "setting %s to %010x\n", resname, ocs_debug_mask);
1223 		ocs_debug_enable(ocs_debug_mask);
1224 	}
1225 
1226 	unit = device_get_unit(ocs->dev);
1227 	ocs->cdev = make_dev(&ocs_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
1228 			"ocs%d", unit);
1229 	if (ocs->cdev) {
1230 		ocs->cdev->si_drv1 = ocs;
1231 	}
1232 
1233 	/* initialize sysctl interface */
1234 	ocs_sysctl_init(ocs);
1235 	mtx_init(&ocs->dbg_lock, "ocs_dbg_lock", NULL, MTX_DEF);
1236 }
1237 
1238 /**
1239  * @brief Free the debug module
1240  */
1241 void
1242 ocs_debug_detach(void *os)
1243 {
1244 	struct ocs_softc *ocs = os;
1245 
1246 	mtx_destroy(&ocs->dbg_lock);
1247 
1248 	if (ocs->cdev) {
1249 		ocs->cdev->si_drv1 = NULL;
1250 		destroy_dev(ocs->cdev);
1251 	}
1252 }
1253 
1254