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