1288522Sgrehan/*-
2288522Sgrehan * Copyright (c) 2015  Peter Grehan <grehan@freebsd.org>
3288522Sgrehan * All rights reserved.
4288522Sgrehan *
5288522Sgrehan * Redistribution and use in source and binary forms, with or without
6288522Sgrehan * modification, are permitted provided that the following conditions
7288522Sgrehan * are met:
8288522Sgrehan * 1. Redistributions of source code must retain the above copyright
9288522Sgrehan *    notice, this list of conditions and the following disclaimer.
10288522Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11288522Sgrehan *    notice, this list of conditions and the following disclaimer in the
12288522Sgrehan *    documentation and/or other materials provided with the distribution.
13288522Sgrehan *
14288522Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15288522Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16288522Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17288522Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18288522Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19288522Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20288522Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21288522Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22288522Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23288522Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24288522Sgrehan * SUCH DAMAGE.
25288522Sgrehan *
26288522Sgrehan * $FreeBSD: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $
27288522Sgrehan */
28288522Sgrehan
29288522Sgrehan/*
30288522Sgrehan * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
31288522Sgrehan * but with a request/response messaging protocol.
32288522Sgrehan */
33288522Sgrehan#include <sys/cdefs.h>
34288522Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $");
35288522Sgrehan
36288522Sgrehan#include <sys/param.h>
37288522Sgrehan#include <sys/types.h>
38288522Sgrehan#include <sys/errno.h>
39288522Sgrehan#include <sys/uio.h>
40288522Sgrehan
41288522Sgrehan#include <assert.h>
42288522Sgrehan#include <stdio.h>
43288522Sgrehan#include <stdlib.h>
44288522Sgrehan#include <string.h>
45288522Sgrehan
46288522Sgrehan#include "bhyverun.h"
47288522Sgrehan#include "inout.h"
48288522Sgrehan#include "fwctl.h"
49288522Sgrehan
50288522Sgrehan/*
51288522Sgrehan * Messaging protocol base operations
52288522Sgrehan */
53288522Sgrehan#define	OP_NULL		1
54288522Sgrehan#define	OP_ECHO		2
55288522Sgrehan#define	OP_GET		3
56288522Sgrehan#define	OP_GET_LEN	4
57288522Sgrehan#define	OP_SET		5
58288522Sgrehan#define	OP_MAX		OP_SET
59288522Sgrehan
60288522Sgrehan/* I/O ports */
61288522Sgrehan#define	FWCTL_OUT	0x510
62288522Sgrehan#define	FWCTL_IN	0x511
63288522Sgrehan
64288522Sgrehan/*
65288522Sgrehan * Back-end state-machine
66288522Sgrehan */
67288522Sgrehanenum state {
68288522Sgrehan	DORMANT,
69288522Sgrehan	IDENT_WAIT,
70288522Sgrehan	IDENT_SEND,
71288522Sgrehan	REQ,
72288522Sgrehan	RESP
73288522Sgrehan} be_state = DORMANT;
74288522Sgrehan
75288522Sgrehanstatic uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
76288522Sgrehanstatic u_int ident_idx;
77288522Sgrehan
78288522Sgrehanstruct op_info {
79288522Sgrehan	int op;
80288522Sgrehan	int  (*op_start)(int len);
81288522Sgrehan	void (*op_data)(uint32_t data, int len);
82288522Sgrehan	int  (*op_result)(struct iovec **data);
83288522Sgrehan	void (*op_done)(struct iovec *data);
84288522Sgrehan};
85288522Sgrehanstatic struct op_info *ops[OP_MAX+1];
86288522Sgrehan
87288522Sgrehan/* Return 0-padded uint32_t */
88288522Sgrehanstatic uint32_t
89288522Sgrehanfwctl_send_rest(uint32_t *data, size_t len)
90288522Sgrehan{
91288522Sgrehan	union {
92288522Sgrehan		uint8_t c[4];
93288522Sgrehan		uint32_t w;
94288522Sgrehan	} u;
95288522Sgrehan	uint8_t *cdata;
96288522Sgrehan	int i;
97288522Sgrehan
98288522Sgrehan	cdata = (uint8_t *) data;
99288522Sgrehan	u.w = 0;
100288522Sgrehan
101288522Sgrehan	for (i = 0, u.w = 0; i < len; i++)
102288522Sgrehan		u.c[i] = *cdata++;
103288522Sgrehan
104288522Sgrehan	return (u.w);
105288522Sgrehan}
106288522Sgrehan
107288522Sgrehan/*
108288522Sgrehan * error op dummy proto - drop all data sent and return an error
109288522Sgrehan*/
110288522Sgrehanstatic int errop_code;
111288522Sgrehan
112288522Sgrehanstatic void
113288522Sgrehanerrop_set(int err)
114288522Sgrehan{
115288522Sgrehan
116288522Sgrehan	errop_code = err;
117288522Sgrehan}
118288522Sgrehan
119288522Sgrehanstatic int
120288522Sgrehanerrop_start(int len)
121288522Sgrehan{
122288522Sgrehan	errop_code = ENOENT;
123288522Sgrehan
124288522Sgrehan	/* accept any length */
125288522Sgrehan	return (errop_code);
126288522Sgrehan}
127288522Sgrehan
128288522Sgrehanstatic void
129288522Sgrehanerrop_data(uint32_t data, int len)
130288522Sgrehan{
131288522Sgrehan
132288522Sgrehan	/* ignore */
133288522Sgrehan}
134288522Sgrehan
135288522Sgrehanstatic int
136288522Sgrehanerrop_result(struct iovec **data)
137288522Sgrehan{
138288522Sgrehan
139288522Sgrehan	/* no data to send back; always successful */
140288522Sgrehan	*data = NULL;
141288522Sgrehan	return (errop_code);
142288522Sgrehan}
143288522Sgrehan
144288522Sgrehanstatic void
145288522Sgrehanerrop_done(struct iovec *data)
146288522Sgrehan{
147288522Sgrehan
148288522Sgrehan	/* assert data is NULL */
149288522Sgrehan}
150288522Sgrehan
151288522Sgrehanstatic struct op_info errop_info = {
152288522Sgrehan	.op_start  = errop_start,
153288522Sgrehan	.op_data   = errop_data,
154288522Sgrehan	.op_result = errop_result,
155288522Sgrehan	.op_done   = errop_done
156288522Sgrehan};
157288522Sgrehan
158288522Sgrehan/* OID search */
159288522SgrehanSET_DECLARE(ctl_set, struct ctl);
160288522Sgrehan
161288522SgrehanCTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
162288522Sgrehan
163288522Sgrehanstatic struct ctl *
164288522Sgrehanctl_locate(const char *str, int maxlen)
165288522Sgrehan{
166288522Sgrehan	struct ctl *cp, **cpp;
167288522Sgrehan
168288522Sgrehan	SET_FOREACH(cpp, ctl_set)  {
169288522Sgrehan		cp = *cpp;
170288522Sgrehan		if (!strncmp(str, cp->c_oid, maxlen))
171288522Sgrehan			return (cp);
172288522Sgrehan	}
173288522Sgrehan
174288522Sgrehan	return (NULL);
175288522Sgrehan}
176288522Sgrehan
177288522Sgrehan/* uefi-sysctl get-len */
178288522Sgrehan#define FGET_STRSZ	80
179288522Sgrehanstatic struct iovec fget_biov[2];
180288522Sgrehanstatic char fget_str[FGET_STRSZ];
181288522Sgrehanstatic struct {
182288522Sgrehan	size_t f_sz;
183288522Sgrehan	uint32_t f_data[1024];
184288522Sgrehan} fget_buf;
185288522Sgrehanstatic int fget_cnt;
186288522Sgrehanstatic size_t fget_size;
187288522Sgrehan
188288522Sgrehanstatic int
189288522Sgrehanfget_start(int len)
190288522Sgrehan{
191288522Sgrehan
192288522Sgrehan	if (len > FGET_STRSZ)
193288522Sgrehan		return(E2BIG);
194288522Sgrehan
195288522Sgrehan	fget_cnt = 0;
196288522Sgrehan
197288522Sgrehan	return (0);
198288522Sgrehan}
199288522Sgrehan
200288522Sgrehanstatic void
201288522Sgrehanfget_data(uint32_t data, int len)
202288522Sgrehan{
203288522Sgrehan
204288522Sgrehan	*((uint32_t *) &fget_str[fget_cnt]) = data;
205288522Sgrehan	fget_cnt += sizeof(uint32_t);
206288522Sgrehan}
207288522Sgrehan
208288522Sgrehanstatic int
209288522Sgrehanfget_result(struct iovec **data, int val)
210288522Sgrehan{
211288522Sgrehan	struct ctl *cp;
212288522Sgrehan	int err;
213288522Sgrehan
214288522Sgrehan	err = 0;
215288522Sgrehan
216288522Sgrehan	/* Locate the OID */
217288522Sgrehan	cp = ctl_locate(fget_str, fget_cnt);
218288522Sgrehan	if (cp == NULL) {
219288522Sgrehan		*data = NULL;
220288522Sgrehan		err = ENOENT;
221288522Sgrehan	} else {
222288522Sgrehan		if (val) {
223288522Sgrehan			/* For now, copy the len/data into a buffer */
224288522Sgrehan			memset(&fget_buf, 0, sizeof(fget_buf));
225288522Sgrehan			fget_buf.f_sz = cp->c_len;
226288522Sgrehan			memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
227288522Sgrehan			fget_biov[0].iov_base = (char *)&fget_buf;
228288522Sgrehan			fget_biov[0].iov_len  = sizeof(fget_buf.f_sz) +
229288522Sgrehan				cp->c_len;
230288522Sgrehan		} else {
231288522Sgrehan			fget_size = cp->c_len;
232288522Sgrehan			fget_biov[0].iov_base = (char *)&fget_size;
233288522Sgrehan			fget_biov[0].iov_len  = sizeof(fget_size);
234288522Sgrehan		}
235288522Sgrehan
236288522Sgrehan		fget_biov[1].iov_base = NULL;
237288522Sgrehan		fget_biov[1].iov_len  = 0;
238288522Sgrehan		*data = fget_biov;
239288522Sgrehan	}
240288522Sgrehan
241288522Sgrehan	return (err);
242288522Sgrehan}
243288522Sgrehan
244288522Sgrehanstatic void
245288522Sgrehanfget_done(struct iovec *data)
246288522Sgrehan{
247288522Sgrehan
248288522Sgrehan	/* nothing needs to be freed */
249288522Sgrehan}
250288522Sgrehan
251288522Sgrehanstatic int
252288522Sgrehanfget_len_result(struct iovec **data)
253288522Sgrehan{
254288522Sgrehan	return (fget_result(data, 0));
255288522Sgrehan}
256288522Sgrehan
257288522Sgrehanstatic int
258288522Sgrehanfget_val_result(struct iovec **data)
259288522Sgrehan{
260288522Sgrehan	return (fget_result(data, 1));
261288522Sgrehan}
262288522Sgrehan
263288522Sgrehanstatic struct op_info fgetlen_info = {
264288522Sgrehan	.op_start  = fget_start,
265288522Sgrehan	.op_data   = fget_data,
266288522Sgrehan	.op_result = fget_len_result,
267288522Sgrehan	.op_done   = fget_done
268288522Sgrehan};
269288522Sgrehan
270288522Sgrehanstatic struct op_info fgetval_info = {
271288522Sgrehan	.op_start  = fget_start,
272288522Sgrehan	.op_data   = fget_data,
273288522Sgrehan	.op_result = fget_val_result,
274288522Sgrehan	.op_done   = fget_done
275288522Sgrehan};
276288522Sgrehan
277288522Sgrehanstatic struct req_info {
278288522Sgrehan	int      req_error;
279288522Sgrehan	u_int    req_count;
280288522Sgrehan	uint32_t req_size;
281288522Sgrehan	uint32_t req_type;
282288522Sgrehan	uint32_t req_txid;
283288522Sgrehan	struct op_info *req_op;
284288522Sgrehan	int	 resp_error;
285288522Sgrehan	int	 resp_count;
286288522Sgrehan	int	 resp_size;
287288522Sgrehan	int	 resp_off;
288288522Sgrehan	struct iovec *resp_biov;
289288522Sgrehan} rinfo;
290288522Sgrehan
291288522Sgrehanstatic void
292288522Sgrehanfwctl_response_done(void)
293288522Sgrehan{
294288522Sgrehan
295288522Sgrehan	(*rinfo.req_op->op_done)(rinfo.resp_biov);
296288522Sgrehan
297288522Sgrehan	/* reinit the req data struct */
298288522Sgrehan	memset(&rinfo, 0, sizeof(rinfo));
299288522Sgrehan}
300288522Sgrehan
301288522Sgrehanstatic void
302288522Sgrehanfwctl_request_done(void)
303288522Sgrehan{
304288522Sgrehan
305288522Sgrehan	rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
306288522Sgrehan
307288522Sgrehan	/* XXX only a single vector supported at the moment */
308288522Sgrehan	rinfo.resp_off = 0;
309288522Sgrehan	if (rinfo.resp_biov == NULL) {
310288522Sgrehan		rinfo.resp_size = 0;
311288522Sgrehan	} else {
312288522Sgrehan		rinfo.resp_size = rinfo.resp_biov[0].iov_len;
313288522Sgrehan	}
314288522Sgrehan}
315288522Sgrehan
316288522Sgrehanstatic int
317288522Sgrehanfwctl_request_start(void)
318288522Sgrehan{
319288522Sgrehan	int err;
320288522Sgrehan
321288522Sgrehan	/* Data size doesn't include header */
322288522Sgrehan	rinfo.req_size -= 12;
323288522Sgrehan
324288522Sgrehan	rinfo.req_op = &errop_info;
325288522Sgrehan	if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
326288522Sgrehan		rinfo.req_op = ops[rinfo.req_type];
327288522Sgrehan
328288522Sgrehan	err = (*rinfo.req_op->op_start)(rinfo.req_size);
329288522Sgrehan
330288522Sgrehan	if (err) {
331288522Sgrehan		errop_set(err);
332288522Sgrehan		rinfo.req_op = &errop_info;
333288522Sgrehan	}
334288522Sgrehan
335288522Sgrehan	/* Catch case of zero-length message here */
336288522Sgrehan	if (rinfo.req_size == 0) {
337288522Sgrehan		fwctl_request_done();
338288522Sgrehan		return (1);
339288522Sgrehan	}
340288522Sgrehan
341288522Sgrehan	return (0);
342288522Sgrehan}
343288522Sgrehan
344288522Sgrehanstatic int
345288522Sgrehanfwctl_request_data(uint32_t value)
346288522Sgrehan{
347288522Sgrehan	int remlen;
348288522Sgrehan
349288522Sgrehan	/* Make sure remaining size is >= 0 */
350288522Sgrehan	rinfo.req_size -= sizeof(uint32_t);
351298454Saraujo	remlen = MAX(rinfo.req_size, 0);
352288522Sgrehan
353288522Sgrehan	(*rinfo.req_op->op_data)(value, remlen);
354288522Sgrehan
355288522Sgrehan	if (rinfo.req_size < sizeof(uint32_t)) {
356288522Sgrehan		fwctl_request_done();
357288522Sgrehan		return (1);
358288522Sgrehan	}
359288522Sgrehan
360288522Sgrehan	return (0);
361288522Sgrehan}
362288522Sgrehan
363288522Sgrehanstatic int
364288522Sgrehanfwctl_request(uint32_t value)
365288522Sgrehan{
366288522Sgrehan
367288522Sgrehan	int ret;
368288522Sgrehan
369288522Sgrehan	ret = 0;
370288522Sgrehan
371288522Sgrehan	switch (rinfo.req_count) {
372288522Sgrehan	case 0:
373288522Sgrehan		/* Verify size */
374288522Sgrehan		if (value < 12) {
375288522Sgrehan			printf("msg size error");
376288522Sgrehan			exit(1);
377288522Sgrehan		}
378288522Sgrehan		rinfo.req_size = value;
379288522Sgrehan		rinfo.req_count = 1;
380288522Sgrehan		break;
381288522Sgrehan	case 1:
382288522Sgrehan		rinfo.req_type = value;
383288522Sgrehan		rinfo.req_count++;
384288522Sgrehan		break;
385288522Sgrehan	case 2:
386288522Sgrehan		rinfo.req_txid = value;
387288522Sgrehan		rinfo.req_count++;
388288522Sgrehan		ret = fwctl_request_start();
389288522Sgrehan		break;
390288522Sgrehan	default:
391288522Sgrehan		ret = fwctl_request_data(value);
392288522Sgrehan		break;
393288522Sgrehan	}
394288522Sgrehan
395288522Sgrehan	return (ret);
396288522Sgrehan}
397288522Sgrehan
398288522Sgrehanstatic int
399288522Sgrehanfwctl_response(uint32_t *retval)
400288522Sgrehan{
401288522Sgrehan	uint32_t *dp;
402288522Sgrehan	int remlen;
403288522Sgrehan
404288522Sgrehan	switch(rinfo.resp_count) {
405288522Sgrehan	case 0:
406288522Sgrehan		/* 4 x u32 header len + data */
407288522Sgrehan		*retval = 4*sizeof(uint32_t) +
408288522Sgrehan		    roundup(rinfo.resp_size, sizeof(uint32_t));
409288522Sgrehan		rinfo.resp_count++;
410288522Sgrehan		break;
411288522Sgrehan	case 1:
412288522Sgrehan		*retval = rinfo.req_type;
413288522Sgrehan		rinfo.resp_count++;
414288522Sgrehan		break;
415288522Sgrehan	case 2:
416288522Sgrehan		*retval = rinfo.req_txid;
417288522Sgrehan		rinfo.resp_count++;
418288522Sgrehan		break;
419288522Sgrehan	case 3:
420288522Sgrehan		*retval = rinfo.resp_error;
421288522Sgrehan		rinfo.resp_count++;
422288522Sgrehan		break;
423288522Sgrehan	default:
424288522Sgrehan		remlen = rinfo.resp_size - rinfo.resp_off;
425288522Sgrehan		dp = (uint32_t *)
426288522Sgrehan		    ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
427288522Sgrehan		if (remlen >= sizeof(uint32_t)) {
428288522Sgrehan			*retval = *dp;
429288522Sgrehan		} else if (remlen > 0) {
430288522Sgrehan			*retval = fwctl_send_rest(dp, remlen);
431288522Sgrehan		}
432288522Sgrehan		rinfo.resp_off += sizeof(uint32_t);
433288522Sgrehan		break;
434288522Sgrehan	}
435288522Sgrehan
436288522Sgrehan	if (rinfo.resp_count > 3 &&
437288522Sgrehan	    rinfo.resp_size - rinfo.resp_off <= 0) {
438288522Sgrehan		fwctl_response_done();
439288522Sgrehan		return (1);
440288522Sgrehan	}
441288522Sgrehan
442288522Sgrehan	return (0);
443288522Sgrehan}
444288522Sgrehan
445288522Sgrehan
446288522Sgrehan/*
447288522Sgrehan * i/o port handling.
448288522Sgrehan */
449288522Sgrehanstatic uint8_t
450288522Sgrehanfwctl_inb(void)
451288522Sgrehan{
452288522Sgrehan	uint8_t retval;
453288522Sgrehan
454288522Sgrehan	retval = 0xff;
455288522Sgrehan
456288522Sgrehan	switch (be_state) {
457288522Sgrehan	case IDENT_SEND:
458288522Sgrehan		retval = sig[ident_idx++];
459288522Sgrehan		if (ident_idx >= sizeof(sig))
460288522Sgrehan			be_state = REQ;
461288522Sgrehan		break;
462288522Sgrehan	default:
463288522Sgrehan		break;
464288522Sgrehan	}
465288522Sgrehan
466288522Sgrehan	return (retval);
467288522Sgrehan}
468288522Sgrehan
469288522Sgrehanstatic void
470288522Sgrehanfwctl_outw(uint16_t val)
471288522Sgrehan{
472288522Sgrehan	switch (be_state) {
473288522Sgrehan	case IDENT_WAIT:
474288522Sgrehan		if (val == 0) {
475288522Sgrehan			be_state = IDENT_SEND;
476288522Sgrehan			ident_idx = 0;
477288522Sgrehan		}
478288522Sgrehan		break;
479288522Sgrehan	default:
480288522Sgrehan		/* ignore */
481288522Sgrehan		break;
482288522Sgrehan	}
483288522Sgrehan}
484288522Sgrehan
485288522Sgrehanstatic uint32_t
486288522Sgrehanfwctl_inl(void)
487288522Sgrehan{
488288522Sgrehan	uint32_t retval;
489288522Sgrehan
490288522Sgrehan	switch (be_state) {
491288522Sgrehan	case RESP:
492288522Sgrehan		if (fwctl_response(&retval))
493288522Sgrehan			be_state = REQ;
494288522Sgrehan		break;
495288522Sgrehan	default:
496288522Sgrehan		retval = 0xffffffff;
497288522Sgrehan		break;
498288522Sgrehan	}
499288522Sgrehan
500288522Sgrehan	return (retval);
501288522Sgrehan}
502288522Sgrehan
503288522Sgrehanstatic void
504288522Sgrehanfwctl_outl(uint32_t val)
505288522Sgrehan{
506288522Sgrehan
507288522Sgrehan	switch (be_state) {
508288522Sgrehan	case REQ:
509288522Sgrehan		if (fwctl_request(val))
510288522Sgrehan			be_state = RESP;
511288522Sgrehan	default:
512288522Sgrehan		break;
513288522Sgrehan	}
514288522Sgrehan
515288522Sgrehan}
516288522Sgrehan
517288522Sgrehanstatic int
518288522Sgrehanfwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
519288522Sgrehan    uint32_t *eax, void *arg)
520288522Sgrehan{
521288522Sgrehan
522288522Sgrehan	if (in) {
523288522Sgrehan		if (bytes == 1)
524288522Sgrehan			*eax = fwctl_inb();
525288522Sgrehan		else if (bytes == 4)
526288522Sgrehan			*eax = fwctl_inl();
527288522Sgrehan		else
528288522Sgrehan			*eax = 0xffff;
529288522Sgrehan	} else {
530288522Sgrehan		if (bytes == 2)
531288522Sgrehan			fwctl_outw(*eax);
532288522Sgrehan		else if (bytes == 4)
533288522Sgrehan			fwctl_outl(*eax);
534288522Sgrehan	}
535288522Sgrehan
536288522Sgrehan	return (0);
537288522Sgrehan}
538288522SgrehanINOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
539288524SgrehanINOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
540288522Sgrehan
541288522Sgrehanvoid
542288522Sgrehanfwctl_init(void)
543288522Sgrehan{
544288522Sgrehan
545288522Sgrehan	ops[OP_GET_LEN] = &fgetlen_info;
546288522Sgrehan	ops[OP_GET]     = &fgetval_info;
547288522Sgrehan
548288522Sgrehan	be_state = IDENT_WAIT;
549288522Sgrehan}
550