1208926Smjacob/*-
2208926Smjacob * Copyright (c) 2010 by Panasas, Inc.
3208926Smjacob * All rights reserved.
4208926Smjacob *
5208926Smjacob * Redistribution and use in source and binary forms, with or without
6208926Smjacob * modification, are permitted provided that the following conditions
7208926Smjacob * are met:
8208926Smjacob * 1. Redistributions of source code must retain the above copyright
9208926Smjacob *    notice immediately at the beginning of the file, without modification,
10208926Smjacob *    this list of conditions, and the following disclaimer.
11208926Smjacob * 2. The name of the author may not be used to endorse or promote products
12208926Smjacob *    derived from this software without specific prior written permission.
13208926Smjacob *
14208926Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15208926Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16208926Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17208926Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18208926Smjacob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19208926Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20208926Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21208926Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22208926Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23208926Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24208926Smjacob * SUCH DAMAGE.
25208926Smjacob */
26208926Smjacob/* $FreeBSD$ */
27208926Smjacob/*
28208926Smjacob * Virtual HBA infrastructure, to be used for testing as well as other cute hacks.
29208926Smjacob */
30208926Smjacob#include "vhba.h"
31208926Smjacobstatic vhba_softc_t *vhba;
32208926Smjacob
33208926Smjacob#ifndef	VHBA_MOD
34208926Smjacob#define	VHBA_MOD	"vhba"
35208926Smjacob#endif
36208926Smjacob
37208926Smjacobstatic void vhba_action(struct cam_sim *, union ccb *);
38208926Smjacobstatic void vhba_poll(struct cam_sim *);
39208926Smjacob
40208926Smjacobstatic int
41208926Smjacobvhba_attach(vhba_softc_t *vhba)
42208926Smjacob{
43208926Smjacob	TAILQ_INIT(&vhba->actv);
44208926Smjacob	TAILQ_INIT(&vhba->done);
45208926Smjacob	vhba->devq = cam_simq_alloc(VHBA_MAXCMDS);
46208926Smjacob	if (vhba->devq == NULL) {
47208926Smjacob		return (ENOMEM);
48208926Smjacob	}
49208926Smjacob	vhba->sim = cam_sim_alloc(vhba_action, vhba_poll, VHBA_MOD, vhba, 0, &vhba->lock, VHBA_MAXCMDS, VHBA_MAXCMDS, vhba->devq);
50208926Smjacob	if (vhba->sim == NULL) {
51208926Smjacob		cam_simq_free(vhba->devq);
52208926Smjacob		return (ENOMEM);
53208926Smjacob	}
54208926Smjacob	vhba_init(vhba);
55208926Smjacob	mtx_lock(&vhba->lock);
56208926Smjacob	if (xpt_bus_register(vhba->sim, 0, 0) != CAM_SUCCESS) {
57208926Smjacob		cam_sim_free(vhba->sim, TRUE);
58208926Smjacob		mtx_unlock(&vhba->lock);
59208926Smjacob		return (EIO);
60208926Smjacob	}
61208926Smjacob	mtx_unlock(&vhba->lock);
62208926Smjacob	return (0);
63208926Smjacob}
64208926Smjacob
65208926Smjacobstatic void
66208926Smjacobvhba_detach(vhba_softc_t *vhba)
67208926Smjacob{
68208926Smjacob	/*
69208926Smjacob	 * We can't be called with anything queued up.
70208926Smjacob	 */
71208926Smjacob	vhba_fini(vhba);
72208926Smjacob	xpt_bus_deregister(cam_sim_path(vhba->sim));
73208926Smjacob	cam_sim_free(vhba->sim, TRUE);
74208926Smjacob}
75208926Smjacob
76208926Smjacobstatic void
77208926Smjacobvhba_poll(struct cam_sim *sim)
78208926Smjacob{
79208926Smjacob	vhba_softc_t *vhba = cam_sim_softc(sim);
80208926Smjacob	vhba_kick(vhba);
81208926Smjacob}
82208926Smjacob
83208926Smjacobstatic void
84208926Smjacobvhba_action(struct cam_sim *sim, union ccb *ccb)
85208926Smjacob{
86208926Smjacob	struct ccb_trans_settings *cts;
87208926Smjacob	vhba_softc_t *vhba;
88208926Smjacob
89208926Smjacob	vhba = cam_sim_softc(sim);
90208926Smjacob	if (vhba->private == NULL) {
91208926Smjacob		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
92208926Smjacob		xpt_done(ccb);
93208926Smjacob		return;
94208926Smjacob	}
95208926Smjacob	switch (ccb->ccb_h.func_code) {
96208926Smjacob	case XPT_SCSI_IO:
97208926Smjacob		ccb->ccb_h.status &= ~CAM_STATUS_MASK;
98208926Smjacob		ccb->ccb_h.status |= CAM_REQ_INPROG;
99208926Smjacob		TAILQ_INSERT_TAIL(&vhba->actv, &ccb->ccb_h, sim_links.tqe);
100208926Smjacob		vhba_kick(vhba);
101208926Smjacob		return;
102208926Smjacob
103208926Smjacob	case XPT_RESET_DEV:
104208926Smjacob		ccb->ccb_h.status = CAM_REQ_CMP;
105208926Smjacob		break;
106208926Smjacob
107208926Smjacob	case XPT_GET_TRAN_SETTINGS:
108208926Smjacob		cts = &ccb->cts;
109211183Smjacob		cts->protocol_version = SCSI_REV_SPC3;
110208926Smjacob		cts->protocol = PROTO_SCSI;
111208926Smjacob		cts->transport_version = 0;
112208926Smjacob		cts->transport = XPORT_PPB;
113208926Smjacob		ccb->ccb_h.status = CAM_REQ_CMP;
114208926Smjacob		break;
115208926Smjacob
116208926Smjacob	case XPT_CALC_GEOMETRY:
117208926Smjacob		cam_calc_geometry(&ccb->ccg, 1);
118208926Smjacob		break;
119208926Smjacob
120208926Smjacob	case XPT_RESET_BUS:		/* Reset the specified bus */
121208926Smjacob		ccb->ccb_h.status = CAM_REQ_CMP;
122208926Smjacob		break;
123208926Smjacob
124208926Smjacob	case XPT_PATH_INQ:		/* Path routing inquiry */
125208926Smjacob	{
126208926Smjacob		struct ccb_pathinq *cpi = &ccb->cpi;
127208926Smjacob
128208926Smjacob		cpi->version_num = 1;
129208926Smjacob		cpi->max_target = VHBA_MAXTGT - 1;
130208926Smjacob		cpi->max_lun = 16383;
131208926Smjacob		cpi->hba_misc = PIM_NOBUSRESET;
132208926Smjacob		cpi->initiator_id = cpi->max_target + 1;
133208926Smjacob		cpi->transport = XPORT_PPB;
134208926Smjacob		cpi->base_transfer_speed = 1000000;
135208926Smjacob		cpi->protocol = PROTO_SCSI;
136211183Smjacob		cpi->protocol_version = SCSI_REV_SPC3;
137208926Smjacob		strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
138208926Smjacob		strlcpy(cpi->hba_vid, "FakeHBA", HBA_IDLEN);
139208926Smjacob		strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
140208926Smjacob		cpi->unit_number = cam_sim_unit(sim);
141208926Smjacob		cpi->ccb_h.status = CAM_REQ_CMP;
142208926Smjacob		break;
143208926Smjacob	}
144208926Smjacob	default:
145208926Smjacob		ccb->ccb_h.status = CAM_REQ_INVALID;
146208926Smjacob		break;
147208926Smjacob	}
148208926Smjacob	xpt_done(ccb);
149208926Smjacob}
150208926Smjacob
151208926Smjacob/*
152208926Smjacob * Common support
153208926Smjacob */
154208926Smjacobvoid
155208926Smjacobvhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq)
156208926Smjacob{
157208926Smjacob	csio->ccb_h.status = CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
158208926Smjacob	csio->scsi_status = SCSI_STATUS_CHECK_COND;
159208926Smjacob	csio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR;
160208926Smjacob	csio->sense_data.flags = key;
161208926Smjacob	csio->sense_data.extra_len = 10;
162208926Smjacob	csio->sense_data.add_sense_code = asc;
163208926Smjacob	csio->sense_data.add_sense_code_qual = ascq;
164208926Smjacob	csio->sense_len = sizeof (csio->sense_data);
165208926Smjacob}
166208926Smjacob
167208926Smjacobint
168208926Smjacobvhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift)
169208926Smjacob{
170208926Smjacob	uint32_t cnt;
171208926Smjacob	uint64_t lba;
172208926Smjacob
173208926Smjacob	switch (cdb[0]) {
174208926Smjacob	case WRITE_16:
175208926Smjacob	case READ_16:
176208926Smjacob		cnt =	(((uint32_t)cdb[10]) <<  24) |
177208926Smjacob			(((uint32_t)cdb[11]) <<  16) |
178208926Smjacob			(((uint32_t)cdb[12]) <<   8) |
179208926Smjacob			((uint32_t)cdb[13]);
180208926Smjacob
181208926Smjacob		lba =	(((uint64_t)cdb[2]) << 56) |
182208926Smjacob			(((uint64_t)cdb[3]) << 48) |
183208926Smjacob			(((uint64_t)cdb[4]) << 40) |
184208926Smjacob			(((uint64_t)cdb[5]) << 32) |
185208926Smjacob			(((uint64_t)cdb[6]) << 24) |
186208926Smjacob			(((uint64_t)cdb[7]) << 16) |
187208926Smjacob			(((uint64_t)cdb[8]) <<  8) |
188208926Smjacob			((uint64_t)cdb[9]);
189208926Smjacob		break;
190208926Smjacob	case WRITE_12:
191208926Smjacob	case READ_12:
192208926Smjacob		cnt =	(((uint32_t)cdb[6]) <<  16) |
193208926Smjacob			(((uint32_t)cdb[7]) <<   8) |
194208926Smjacob			((u_int32_t)cdb[8]);
195208926Smjacob
196208926Smjacob		lba =	(((uint32_t)cdb[2]) << 24) |
197208926Smjacob			(((uint32_t)cdb[3]) << 16) |
198208926Smjacob			(((uint32_t)cdb[4]) <<  8) |
199208926Smjacob			((uint32_t)cdb[5]);
200208926Smjacob		break;
201208926Smjacob	case WRITE_10:
202208926Smjacob	case READ_10:
203208926Smjacob		cnt =	(((uint32_t)cdb[7]) <<  8) |
204208926Smjacob			((u_int32_t)cdb[8]);
205208926Smjacob
206208926Smjacob		lba =	(((uint32_t)cdb[2]) << 24) |
207208926Smjacob			(((uint32_t)cdb[3]) << 16) |
208208926Smjacob			(((uint32_t)cdb[4]) <<  8) |
209208926Smjacob			((uint32_t)cdb[5]);
210208926Smjacob		break;
211208926Smjacob	case WRITE_6:
212208926Smjacob	case READ_6:
213208926Smjacob		cnt = cdb[4];
214208926Smjacob		if (cnt == 0) {
215208926Smjacob			cnt = 256;
216208926Smjacob		}
217208926Smjacob		lba =	(((uint32_t)cdb[1] & 0x1f) << 16) |
218208926Smjacob			(((uint32_t)cdb[2]) << 8) |
219208926Smjacob			((uint32_t)cdb[3]);
220208926Smjacob		break;
221208926Smjacob	default:
222208926Smjacob		return (-1);
223208926Smjacob	}
224208926Smjacob
225208926Smjacob	if (lba + cnt > nblks) {
226208926Smjacob		return (-1);
227208926Smjacob	}
228208926Smjacob	*tl = cnt << blk_shift;
229208926Smjacob	*offset = lba << blk_shift;
230208926Smjacob	return (0);
231208926Smjacob}
232208926Smjacob
233208926Smjacobvoid
234208926Smjacobvhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map)
235208926Smjacob{
236208926Smjacob	char junk[128];
237208926Smjacob	const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = {
238211183Smjacob		0x7f, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32,
239208926Smjacob		'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ',
240208926Smjacob		'N', 'U', 'L', 'L', ' ', 'D', 'E', 'V',
241208926Smjacob		'I', 'C', 'E', ' ', ' ', ' ', ' ', ' ',
242208926Smjacob		'0', '0', '0', '1'
243208926Smjacob	};
244208926Smjacob	const uint8_t iqd[SHORT_INQUIRY_LENGTH] = {
245211183Smjacob		0, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32,
246208926Smjacob		'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ',
247208926Smjacob		'V', 'I', 'R', 'T', ' ', 'M', 'E', 'M',
248208926Smjacob		'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K',
249208926Smjacob		'0', '0', '0', '1'
250208926Smjacob	};
251208926Smjacob	const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 };
252208926Smjacob	const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 };
253208926Smjacob	int i, attached_lun;
254208926Smjacob	uint8_t *cdb, *ptr, status;
255208926Smjacob	uint32_t data_len, nlun;
256208926Smjacob
257208926Smjacob	data_len = 0;
258208926Smjacob	status = SCSI_STATUS_OK;
259208926Smjacob
260208926Smjacob	memset(&csio->sense_data, 0, sizeof (csio->sense_data));
261208926Smjacob	cdb = csio->cdb_io.cdb_bytes;
262208926Smjacob
263208926Smjacob	attached_lun = 1;
264208926Smjacob	if (csio->ccb_h.target_lun >= max_lun) {
265208926Smjacob		attached_lun = 0;
266208926Smjacob	} else if (sparse_lun_map) {
267208926Smjacob		i = csio->ccb_h.target_lun & 0x7;
268208926Smjacob		if ((sparse_lun_map[csio->ccb_h.target_lun >> 3] & (1 << i)) == 0) {
269208926Smjacob			attached_lun = 0;
270208926Smjacob		}
271208926Smjacob	}
272208926Smjacob	if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
273208926Smjacob		vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
274208926Smjacob		return;
275208926Smjacob	}
276208926Smjacob
277208926Smjacob	switch (cdb[0]) {
278208926Smjacob	case REQUEST_SENSE:
279208926Smjacob		data_len = csio->dxfer_len;
280208926Smjacob		if (cdb[4] < csio->dxfer_len)
281208926Smjacob			data_len = cdb[4];
282208926Smjacob		if (data_len) {
283208926Smjacob			memset(junk, 0, sizeof (junk));
284208926Smjacob			junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR;
285208926Smjacob			junk[2] = SSD_KEY_NO_SENSE;
286208926Smjacob			junk[7] = 10;
287208926Smjacob			memcpy(csio->data_ptr, junk,
288208926Smjacob			    (data_len > sizeof junk)? sizeof junk : data_len);
289208926Smjacob		}
290208926Smjacob		csio->resid = csio->dxfer_len - data_len;
291208926Smjacob		break;
292208926Smjacob	case INQUIRY:
293208926Smjacob		i = 0;
294208926Smjacob		if ((cdb[1] & 0x1f) == SI_EVPD) {
295208926Smjacob			if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) {
296208926Smjacob				i = 1;
297208926Smjacob			}
298208926Smjacob		} else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) {
299208926Smjacob			i = 1;
300208926Smjacob		}
301208926Smjacob		if (i) {
302208926Smjacob			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
303208926Smjacob			break;
304208926Smjacob		}
305208926Smjacob		if (attached_lun == 0) {
306208926Smjacob			if (cdb[1] & 0x1f) {
307208926Smjacob				vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
308208926Smjacob				break;
309208926Smjacob			}
310208926Smjacob			memcpy(junk, niliqd, sizeof (niliqd));
311208926Smjacob			data_len = sizeof (niliqd);
312208926Smjacob		} else if (cdb[1] & 0x1f) {
313208926Smjacob			if (cdb[2] == 0) {
314208926Smjacob				memcpy(junk, vp0data, sizeof (vp0data));
315208926Smjacob				data_len = sizeof (vp0data);
316208926Smjacob			} else {
317208926Smjacob				memcpy(junk, vp80data, sizeof (vp80data));
318208926Smjacob				snprintf(&junk[4], sizeof (vp80data) - 4, "TGT%dLUN%d", csio->ccb_h.target_id, csio->ccb_h.target_lun);
319208926Smjacob				for (i = 0; i < sizeof (vp80data); i++) {
320208926Smjacob					if (junk[i] == 0) {
321208926Smjacob						junk[i] = ' ';
322208926Smjacob					}
323208926Smjacob                                }
324208926Smjacob                        }
325208926Smjacob			data_len = sizeof (vp80data);
326208926Smjacob		} else {
327208926Smjacob			memcpy(junk, iqd, sizeof (iqd));
328208926Smjacob			data_len = sizeof (iqd);
329208926Smjacob		}
330208926Smjacob		if (data_len > cdb[4]) {
331208926Smjacob			data_len = cdb[4];
332208926Smjacob		}
333208926Smjacob		if (data_len) {
334208926Smjacob			memcpy(csio->data_ptr, junk, data_len);
335208926Smjacob		}
336208926Smjacob		csio->resid = csio->dxfer_len - data_len;
337208926Smjacob		break;
338208926Smjacob	case TEST_UNIT_READY:
339208926Smjacob	case SYNCHRONIZE_CACHE:
340208926Smjacob	case START_STOP:
341208926Smjacob	case RESERVE:
342208926Smjacob	case RELEASE:
343208926Smjacob		break;
344208926Smjacob
345208926Smjacob	case REPORT_LUNS:
346208926Smjacob		if (csio->dxfer_len) {
347208926Smjacob			memset(csio->data_ptr, 0, csio->dxfer_len);
348208926Smjacob		}
349208926Smjacob		ptr = NULL;
350208926Smjacob		for (nlun = i = 0; i < max_lun; i++) {
351208926Smjacob			if (sparse_lun_map) {
352208926Smjacob				if ((sparse_lun_map[i >> 3] & (1 << (i & 0x7))) == 0) {
353208926Smjacob					continue;
354208926Smjacob				}
355208926Smjacob			}
356208926Smjacob			ptr = &csio->data_ptr[8 + ((nlun++) << 3)];
357208926Smjacob			if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) {
358208926Smjacob				continue;
359208926Smjacob			}
360208926Smjacob			if (i >= 256) {
361208926Smjacob				ptr[0] = 0x40 | ((i >> 8) & 0x3f);
362208926Smjacob			}
363208926Smjacob			ptr[1] = i;
364208926Smjacob		}
365208926Smjacob		junk[0] = (nlun << 3) >> 24;
366208926Smjacob		junk[1] = (nlun << 3) >> 16;
367208926Smjacob		junk[2] = (nlun << 3) >> 8;
368208926Smjacob		junk[3] = (nlun << 3);
369208926Smjacob		memset(junk+4, 0, 4);
370208926Smjacob		if (csio->dxfer_len) {
371208926Smjacob			u_int amt;
372208926Smjacob
373208926Smjacob			amt = MIN(csio->dxfer_len, 8);
374208926Smjacob			memcpy(csio->data_ptr, junk, amt);
375208926Smjacob			amt = MIN((nlun << 3) + 8,  csio->dxfer_len);
376208926Smjacob			csio->resid = csio->dxfer_len - amt;
377208926Smjacob		}
378208926Smjacob		break;
379208926Smjacob
380208926Smjacob	default:
381208926Smjacob		vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0);
382208926Smjacob		break;
383208926Smjacob	}
384208926Smjacob}
385208926Smjacob
386208926Smjacobvoid
387208926Smjacobvhba_set_status(struct ccb_hdr *ccbh, cam_status status)
388208926Smjacob{
389208926Smjacob	ccbh->status &= ~CAM_STATUS_MASK;
390208926Smjacob	ccbh->status |= status;
391208926Smjacob	if (status != CAM_REQ_CMP) {
392208926Smjacob		if ((ccbh->status & CAM_DEV_QFRZN) == 0) {
393208926Smjacob			ccbh->status |= CAM_DEV_QFRZN;
394208926Smjacob			xpt_freeze_devq(ccbh->path, 1);
395208926Smjacob		}
396208926Smjacob	}
397208926Smjacob}
398208926Smjacob
399208926Smjacobint
400208926Smjacobvhba_modprobe(module_t mod, int cmd, void *arg)
401208926Smjacob{
402208926Smjacob	int error = 0;
403208926Smjacob
404208926Smjacob	switch (cmd) {
405208926Smjacob	case MOD_LOAD:
406208926Smjacob		vhba = malloc(sizeof (*vhba), M_DEVBUF, M_WAITOK|M_ZERO);
407208926Smjacob		mtx_init(&vhba->lock, "vhba", NULL, MTX_DEF);
408208926Smjacob		error = vhba_attach(vhba);
409208926Smjacob		if (error) {
410208926Smjacob			mtx_destroy(&vhba->lock);
411208926Smjacob			free(vhba, M_DEVBUF);
412208926Smjacob		}
413208926Smjacob		break;
414208926Smjacob	case MOD_UNLOAD:
415208926Smjacob        	mtx_lock(&vhba->lock);
416208926Smjacob		if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) {
417208926Smjacob			error = EBUSY;
418208926Smjacob			mtx_unlock(&vhba->lock);
419208926Smjacob			break;
420208926Smjacob		}
421208926Smjacob		vhba_detach(vhba);
422208926Smjacob		mtx_unlock(&vhba->lock);
423208926Smjacob		mtx_destroy(&vhba->lock);
424208926Smjacob		free(vhba, M_DEVBUF);
425208926Smjacob		break;
426208926Smjacob	default:
427208926Smjacob		error = EOPNOTSUPP;
428208926Smjacob		break;
429208926Smjacob	}
430208926Smjacob	return (error);
431208926Smjacob}
432