1/*-
2 * Copyright (c) 2015 Netflix, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * derived from ata_xpt.c: Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/cam/nvme/nvme_xpt.c 351754 2019-09-03 16:24:44Z mav $");
31
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/endian.h>
35#include <sys/systm.h>
36#include <sys/types.h>
37#include <sys/malloc.h>
38#include <sys/kernel.h>
39#include <sys/time.h>
40#include <sys/conf.h>
41#include <sys/fcntl.h>
42#include <sys/sbuf.h>
43
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/sysctl.h>
47
48#include <cam/cam.h>
49#include <cam/cam_ccb.h>
50#include <cam/cam_queue.h>
51#include <cam/cam_periph.h>
52#include <cam/cam_sim.h>
53#include <cam/cam_xpt.h>
54#include <cam/cam_xpt_sim.h>
55#include <cam/cam_xpt_periph.h>
56#include <cam/cam_xpt_internal.h>
57#include <cam/cam_debug.h>
58
59#include <cam/scsi/scsi_all.h>
60#include <cam/scsi/scsi_message.h>
61#include <cam/nvme/nvme_all.h>
62#include <machine/stdarg.h>	/* for xpt_print below */
63#include "opt_cam.h"
64
65struct nvme_quirk_entry {
66	u_int quirks;
67#define CAM_QUIRK_MAXTAGS 1
68	u_int mintags;
69	u_int maxtags;
70};
71
72/* Not even sure why we need this */
73static periph_init_t nvme_probe_periph_init;
74
75static struct periph_driver nvme_probe_driver =
76{
77	nvme_probe_periph_init, "nvme_probe",
78	TAILQ_HEAD_INITIALIZER(nvme_probe_driver.units), /* generation */ 0,
79	CAM_PERIPH_DRV_EARLY
80};
81
82PERIPHDRIVER_DECLARE(nvme_probe, nvme_probe_driver);
83
84typedef enum {
85	NVME_PROBE_IDENTIFY,
86	NVME_PROBE_DONE,
87	NVME_PROBE_INVALID,
88	NVME_PROBE_RESET
89} nvme_probe_action;
90
91static char *nvme_probe_action_text[] = {
92	"NVME_PROBE_IDENTIFY",
93	"NVME_PROBE_DONE",
94	"NVME_PROBE_INVALID",
95	"NVME_PROBE_RESET",
96};
97
98#define NVME_PROBE_SET_ACTION(softc, newaction)	\
99do {									\
100	char **text;							\
101	text = nvme_probe_action_text;					\
102	CAM_DEBUG((softc)->periph->path, CAM_DEBUG_PROBE,		\
103	    ("Probe %s to %s\n", text[(softc)->action],			\
104	    text[(newaction)]));					\
105	(softc)->action = (newaction);					\
106} while(0)
107
108typedef enum {
109	NVME_PROBE_NO_ANNOUNCE	= 0x04
110} nvme_probe_flags;
111
112typedef struct {
113	TAILQ_HEAD(, ccb_hdr) request_ccbs;
114	nvme_probe_action	action;
115	nvme_probe_flags	flags;
116	int		restart;
117	struct cam_periph *periph;
118} nvme_probe_softc;
119
120static struct nvme_quirk_entry nvme_quirk_table[] =
121{
122	{
123//		{
124//		  T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
125//		  /*vendor*/"*", /*product*/"*", /*revision*/"*"
126//		},
127		.quirks = 0, .mintags = 0, .maxtags = 0
128	},
129};
130
131static const int nvme_quirk_table_size =
132	sizeof(nvme_quirk_table) / sizeof(*nvme_quirk_table);
133
134static cam_status	nvme_probe_register(struct cam_periph *periph,
135				      void *arg);
136static void	 nvme_probe_schedule(struct cam_periph *nvme_probe_periph);
137static void	 nvme_probe_start(struct cam_periph *periph, union ccb *start_ccb);
138static void	 nvme_probe_cleanup(struct cam_periph *periph);
139//static void	 nvme_find_quirk(struct cam_ed *device);
140static void	 nvme_scan_lun(struct cam_periph *periph,
141			       struct cam_path *path, cam_flags flags,
142			       union ccb *ccb);
143static struct cam_ed *
144		 nvme_alloc_device(struct cam_eb *bus, struct cam_et *target,
145				   lun_id_t lun_id);
146static void	 nvme_device_transport(struct cam_path *path);
147static void	 nvme_dev_async(u_int32_t async_code,
148				struct cam_eb *bus,
149				struct cam_et *target,
150				struct cam_ed *device,
151				void *async_arg);
152static void	 nvme_action(union ccb *start_ccb);
153static void	 nvme_announce_periph(struct cam_periph *periph);
154static void	 nvme_proto_announce(struct cam_ed *device);
155static void	 nvme_proto_denounce(struct cam_ed *device);
156static void	 nvme_proto_debug_out(union ccb *ccb);
157
158static struct xpt_xport_ops nvme_xport_ops = {
159	.alloc_device = nvme_alloc_device,
160	.action = nvme_action,
161	.async = nvme_dev_async,
162	.announce = nvme_announce_periph,
163};
164#define NVME_XPT_XPORT(x, X)			\
165static struct xpt_xport nvme_xport_ ## x = {	\
166	.xport = XPORT_ ## X,			\
167	.name = #x,				\
168	.ops = &nvme_xport_ops,			\
169};						\
170CAM_XPT_XPORT(nvme_xport_ ## x);
171
172NVME_XPT_XPORT(nvme, NVME);
173
174#undef NVME_XPT_XPORT
175
176static struct xpt_proto_ops nvme_proto_ops = {
177	.announce = nvme_proto_announce,
178	.denounce = nvme_proto_denounce,
179	.debug_out = nvme_proto_debug_out,
180};
181static struct xpt_proto nvme_proto = {
182	.proto = PROTO_NVME,
183	.name = "nvme",
184	.ops = &nvme_proto_ops,
185};
186CAM_XPT_PROTO(nvme_proto);
187
188static void
189nvme_probe_periph_init()
190{
191
192}
193
194static cam_status
195nvme_probe_register(struct cam_periph *periph, void *arg)
196{
197	union ccb *request_ccb;	/* CCB representing the probe request */
198	cam_status status;
199	nvme_probe_softc *softc;
200
201	request_ccb = (union ccb *)arg;
202	if (request_ccb == NULL) {
203		printf("nvme_probe_register: no probe CCB, "
204		       "can't register device\n");
205		return(CAM_REQ_CMP_ERR);
206	}
207
208	softc = (nvme_probe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_ZERO | M_NOWAIT);
209
210	if (softc == NULL) {
211		printf("nvme_probe_register: Unable to probe new device. "
212		       "Unable to allocate softc\n");
213		return(CAM_REQ_CMP_ERR);
214	}
215	TAILQ_INIT(&softc->request_ccbs);
216	TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h,
217			  periph_links.tqe);
218	softc->flags = 0;
219	periph->softc = softc;
220	softc->periph = periph;
221	softc->action = NVME_PROBE_INVALID;
222	status = cam_periph_acquire(periph);
223	if (status != CAM_REQ_CMP) {
224		return (status);
225	}
226	CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe started\n"));
227
228//	nvme_device_transport(periph->path);
229	nvme_probe_schedule(periph);
230
231	return(CAM_REQ_CMP);
232}
233
234static void
235nvme_probe_schedule(struct cam_periph *periph)
236{
237	union ccb *ccb;
238	nvme_probe_softc *softc;
239
240	softc = (nvme_probe_softc *)periph->softc;
241	ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs);
242
243	NVME_PROBE_SET_ACTION(softc, NVME_PROBE_IDENTIFY);
244
245	if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE)
246		softc->flags |= NVME_PROBE_NO_ANNOUNCE;
247	else
248		softc->flags &= ~NVME_PROBE_NO_ANNOUNCE;
249
250	xpt_schedule(periph, CAM_PRIORITY_XPT);
251}
252
253static void
254nvme_probe_start(struct cam_periph *periph, union ccb *start_ccb)
255{
256	struct ccb_nvmeio *nvmeio;
257	struct ccb_scsiio *csio;
258	nvme_probe_softc *softc;
259	struct cam_path *path;
260	const struct nvme_namespace_data *nvme_data;
261	lun_id_t lun;
262
263	CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("nvme_probe_start\n"));
264
265	softc = (nvme_probe_softc *)periph->softc;
266	path = start_ccb->ccb_h.path;
267	nvmeio = &start_ccb->nvmeio;
268	csio = &start_ccb->csio;
269	nvme_data = periph->path->device->nvme_data;
270
271	if (softc->restart) {
272		softc->restart = 0;
273		if (periph->path->device->flags & CAM_DEV_UNCONFIGURED)
274			NVME_PROBE_SET_ACTION(softc, NVME_PROBE_RESET);
275		else
276			NVME_PROBE_SET_ACTION(softc, NVME_PROBE_IDENTIFY);
277	}
278
279	/*
280	 * Other transports have to ask their SIM to do a lot of action.
281	 * NVMe doesn't, so don't do the dance. Just do things
282	 * directly.
283	 */
284	switch (softc->action) {
285	case NVME_PROBE_RESET:
286		/* FALLTHROUGH */
287	case NVME_PROBE_IDENTIFY:
288		nvme_device_transport(path);
289		/*
290		 * Test for lun == CAM_LUN_WILDCARD is lame, but
291		 * appears to be necessary here. XXX
292		 */
293		lun = xpt_path_lun_id(periph->path);
294		if (lun == CAM_LUN_WILDCARD ||
295		    periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
296			path->device->flags &= ~CAM_DEV_UNCONFIGURED;
297			xpt_acquire_device(path->device);
298			start_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
299			xpt_action(start_ccb);
300			xpt_async(AC_FOUND_DEVICE, path, start_ccb);
301		}
302		NVME_PROBE_SET_ACTION(softc, NVME_PROBE_DONE);
303		break;
304	default:
305		panic("nvme_probe_start: invalid action state 0x%x\n", softc->action);
306	}
307	/*
308	 * Probing is now done. We need to complete any lingering items
309	 * in the queue, though there shouldn't be any.
310	 */
311	xpt_release_ccb(start_ccb);
312	CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe completed\n"));
313	while ((start_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs))) {
314		TAILQ_REMOVE(&softc->request_ccbs,
315		    &start_ccb->ccb_h, periph_links.tqe);
316		start_ccb->ccb_h.status = CAM_REQ_CMP;
317		xpt_done(start_ccb);
318	}
319	cam_periph_invalidate(periph);
320	/* Can't release periph since we hit a (possibly bogus) assertion */
321//	cam_periph_release_locked(periph);
322}
323
324static void
325nvme_probe_cleanup(struct cam_periph *periph)
326{
327
328	free(periph->softc, M_CAMXPT);
329}
330
331#if 0
332/* XXX should be used, don't delete */
333static void
334nvme_find_quirk(struct cam_ed *device)
335{
336	struct nvme_quirk_entry *quirk;
337	caddr_t	match;
338
339	match = cam_quirkmatch((caddr_t)&device->nvme_data,
340			       (caddr_t)nvme_quirk_table,
341			       nvme_quirk_table_size,
342			       sizeof(*nvme_quirk_table), nvme_identify_match);
343
344	if (match == NULL)
345		panic("xpt_find_quirk: device didn't match wildcard entry!!");
346
347	quirk = (struct nvme_quirk_entry *)match;
348	device->quirk = quirk;
349	if (quirk->quirks & CAM_QUIRK_MAXTAGS) {
350		device->mintags = quirk->mintags;
351		device->maxtags = quirk->maxtags;
352	}
353}
354#endif
355
356static void
357nvme_scan_lun(struct cam_periph *periph, struct cam_path *path,
358	     cam_flags flags, union ccb *request_ccb)
359{
360	struct ccb_pathinq cpi;
361	cam_status status;
362	struct cam_periph *old_periph;
363	int lock;
364
365	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("nvme_scan_lun\n"));
366
367	xpt_path_inq(&cpi, path);
368
369	if (cpi.ccb_h.status != CAM_REQ_CMP) {
370		if (request_ccb != NULL) {
371			request_ccb->ccb_h.status = cpi.ccb_h.status;
372			xpt_done(request_ccb);
373		}
374		return;
375	}
376
377	if (xpt_path_lun_id(path) == CAM_LUN_WILDCARD) {
378		CAM_DEBUG(path, CAM_DEBUG_TRACE, ("nvme_scan_lun ignoring bus\n"));
379		request_ccb->ccb_h.status = CAM_REQ_CMP;	/* XXX signal error ? */
380		xpt_done(request_ccb);
381		return;
382	}
383
384	lock = (xpt_path_owned(path) == 0);
385	if (lock)
386		xpt_path_lock(path);
387	if ((old_periph = cam_periph_find(path, "nvme_probe")) != NULL) {
388		if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) {
389			nvme_probe_softc *softc;
390
391			softc = (nvme_probe_softc *)old_periph->softc;
392			TAILQ_INSERT_TAIL(&softc->request_ccbs,
393				&request_ccb->ccb_h, periph_links.tqe);
394			softc->restart = 1;
395			CAM_DEBUG(path, CAM_DEBUG_TRACE,
396			    ("restarting nvme_probe device\n"));
397		} else {
398			request_ccb->ccb_h.status = CAM_REQ_CMP_ERR;
399			CAM_DEBUG(path, CAM_DEBUG_TRACE,
400			    ("Failing to restart nvme_probe device\n"));
401			xpt_done(request_ccb);
402		}
403	} else {
404		CAM_DEBUG(path, CAM_DEBUG_TRACE,
405		    ("Adding nvme_probe device\n"));
406		status = cam_periph_alloc(nvme_probe_register, NULL, nvme_probe_cleanup,
407					  nvme_probe_start, "nvme_probe",
408					  CAM_PERIPH_BIO,
409					  request_ccb->ccb_h.path, NULL, 0,
410					  request_ccb);
411
412		if (status != CAM_REQ_CMP) {
413			xpt_print(path, "xpt_scan_lun: cam_alloc_periph "
414			    "returned an error, can't continue probe\n");
415			request_ccb->ccb_h.status = status;
416			xpt_done(request_ccb);
417		}
418	}
419	if (lock)
420		xpt_path_unlock(path);
421}
422
423static struct cam_ed *
424nvme_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
425{
426	struct nvme_quirk_entry *quirk;
427	struct cam_ed *device;
428
429	device = xpt_alloc_device(bus, target, lun_id);
430	if (device == NULL)
431		return (NULL);
432
433	/*
434	 * Take the default quirk entry until we have inquiry
435	 * data from nvme and can determine a better quirk to use.
436	 */
437	quirk = &nvme_quirk_table[nvme_quirk_table_size - 1];
438	device->quirk = (void *)quirk;
439	device->mintags = 0;
440	device->maxtags = 0;
441	device->inq_flags = 0;
442	device->queue_flags = 0;
443	device->device_id = NULL;	/* XXX Need to set this somewhere */
444	device->device_id_len = 0;
445	device->serial_num = NULL;	/* XXX Need to set this somewhere */
446	device->serial_num_len = 0;
447	return (device);
448}
449
450static void
451nvme_device_transport(struct cam_path *path)
452{
453	struct ccb_pathinq cpi;
454	struct ccb_trans_settings cts;
455	/* XXX get data from nvme namespace and other info ??? */
456
457	/* Get transport information from the SIM */
458	xpt_path_inq(&cpi, path);
459
460	path->device->transport = cpi.transport;
461	path->device->transport_version = cpi.transport_version;
462
463	path->device->protocol = cpi.protocol;
464	path->device->protocol_version = cpi.protocol_version;
465
466	/* Tell the controller what we think */
467	xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
468	cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
469	cts.type = CTS_TYPE_CURRENT_SETTINGS;
470	cts.transport = path->device->transport;
471	cts.transport_version = path->device->transport_version;
472	cts.protocol = path->device->protocol;
473	cts.protocol_version = path->device->protocol_version;
474	cts.proto_specific.valid = 0;
475	cts.xport_specific.valid = 0;
476	xpt_action((union ccb *)&cts);
477}
478
479static void
480nvme_dev_advinfo(union ccb *start_ccb)
481{
482	struct cam_ed *device;
483	struct ccb_dev_advinfo *cdai;
484	off_t amt;
485
486	xpt_path_assert(start_ccb->ccb_h.path, MA_OWNED);
487	start_ccb->ccb_h.status = CAM_REQ_INVALID;
488	device = start_ccb->ccb_h.path->device;
489	cdai = &start_ccb->cdai;
490	switch(cdai->buftype) {
491	case CDAI_TYPE_SCSI_DEVID:
492		if (cdai->flags & CDAI_FLAG_STORE)
493			return;
494		cdai->provsiz = device->device_id_len;
495		if (device->device_id_len == 0)
496			break;
497		amt = device->device_id_len;
498		if (cdai->provsiz > cdai->bufsiz)
499			amt = cdai->bufsiz;
500		memcpy(cdai->buf, device->device_id, amt);
501		break;
502	case CDAI_TYPE_SERIAL_NUM:
503		if (cdai->flags & CDAI_FLAG_STORE)
504			return;
505		cdai->provsiz = device->serial_num_len;
506		if (device->serial_num_len == 0)
507			break;
508		amt = device->serial_num_len;
509		if (cdai->provsiz > cdai->bufsiz)
510			amt = cdai->bufsiz;
511		memcpy(cdai->buf, device->serial_num, amt);
512		break;
513	case CDAI_TYPE_PHYS_PATH:
514		if (cdai->flags & CDAI_FLAG_STORE) {
515			if (device->physpath != NULL)
516				free(device->physpath, M_CAMXPT);
517			device->physpath_len = cdai->bufsiz;
518			/* Clear existing buffer if zero length */
519			if (cdai->bufsiz == 0)
520				break;
521			device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT);
522			if (device->physpath == NULL) {
523				start_ccb->ccb_h.status = CAM_REQ_ABORTED;
524				return;
525			}
526			memcpy(device->physpath, cdai->buf, cdai->bufsiz);
527		} else {
528			cdai->provsiz = device->physpath_len;
529			if (device->physpath_len == 0)
530				break;
531			amt = device->physpath_len;
532			if (cdai->provsiz > cdai->bufsiz)
533				amt = cdai->bufsiz;
534			memcpy(cdai->buf, device->physpath, amt);
535		}
536		break;
537	case CDAI_TYPE_NVME_CNTRL:
538		if (cdai->flags & CDAI_FLAG_STORE)
539			return;
540		amt = sizeof(struct nvme_controller_data);
541		cdai->provsiz = amt;
542		if (amt > cdai->bufsiz)
543			amt = cdai->bufsiz;
544		memcpy(cdai->buf, device->nvme_cdata, amt);
545		break;
546	case CDAI_TYPE_NVME_NS:
547		if (cdai->flags & CDAI_FLAG_STORE)
548			return;
549		amt = sizeof(struct nvme_namespace_data);
550		cdai->provsiz = amt;
551		if (amt > cdai->bufsiz)
552			amt = cdai->bufsiz;
553		memcpy(cdai->buf, device->nvme_data, amt);
554		break;
555	default:
556		return;
557	}
558	start_ccb->ccb_h.status = CAM_REQ_CMP;
559
560	if (cdai->flags & CDAI_FLAG_STORE) {
561		xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path,
562			  (void *)(uintptr_t)cdai->buftype);
563	}
564}
565
566static void
567nvme_action(union ccb *start_ccb)
568{
569	CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE,
570	    ("nvme_action: func= %#x\n", start_ccb->ccb_h.func_code));
571
572	switch (start_ccb->ccb_h.func_code) {
573	case XPT_SCAN_BUS:
574	case XPT_SCAN_TGT:
575	case XPT_SCAN_LUN:
576		nvme_scan_lun(start_ccb->ccb_h.path->periph,
577			      start_ccb->ccb_h.path, start_ccb->crcn.flags,
578			      start_ccb);
579		break;
580	case XPT_DEV_ADVINFO:
581		nvme_dev_advinfo(start_ccb);
582		break;
583
584	default:
585		xpt_action_default(start_ccb);
586		break;
587	}
588}
589
590/*
591 * Handle any per-device event notifications that require action by the XPT.
592 */
593static void
594nvme_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target,
595	      struct cam_ed *device, void *async_arg)
596{
597
598	/*
599	 * We only need to handle events for real devices.
600	 */
601	if (target->target_id == CAM_TARGET_WILDCARD
602	 || device->lun_id == CAM_LUN_WILDCARD)
603		return;
604
605	if (async_code == AC_LOST_DEVICE &&
606	    (device->flags & CAM_DEV_UNCONFIGURED) == 0) {
607		device->flags |= CAM_DEV_UNCONFIGURED;
608		xpt_release_device(device);
609	}
610}
611
612static void
613nvme_announce_periph(struct cam_periph *periph)
614{
615	struct	ccb_pathinq cpi;
616	struct	ccb_trans_settings cts;
617	struct	cam_path *path = periph->path;
618
619	cam_periph_assert(periph, MA_OWNED);
620
621	xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL);
622	cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
623	cts.type = CTS_TYPE_CURRENT_SETTINGS;
624	xpt_action((union ccb*)&cts);
625	if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
626		return;
627	/* Ask the SIM for its base transfer speed */
628	xpt_path_inq(&cpi, periph->path);
629	/* XXX NVME STUFF HERE */
630	printf("\n");
631}
632
633static void
634nvme_proto_announce(struct cam_ed *device)
635{
636
637	nvme_print_ident(device->nvme_cdata, device->nvme_data);
638}
639
640static void
641nvme_proto_denounce(struct cam_ed *device)
642{
643
644	nvme_print_ident(device->nvme_cdata, device->nvme_data);
645}
646
647static void
648nvme_proto_debug_out(union ccb *ccb)
649{
650	char cdb_str[(sizeof(struct nvme_command) * 3) + 1];
651
652	if (ccb->ccb_h.func_code != XPT_NVME_IO)
653		return;
654
655	CAM_DEBUG(ccb->ccb_h.path,
656	    CAM_DEBUG_CDB,("%s. NCB: %s\n", nvme_op_string(&ccb->nvmeio.cmd),
657		nvme_cmd_string(&ccb->nvmeio.cmd, cdb_str, sizeof(cdb_str))));
658}
659
660