udcf.c revision 1.7
1/*	$OpenBSD: udcf.c,v 1.7 2006/05/27 18:22:04 mbalmer Exp $ */
2
3/*
4 * Copyright (c) 2006 Marc Balmer <mbalmer@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21#include <sys/kernel.h>
22#include <sys/conf.h>
23#include <sys/file.h>
24#include <sys/select.h>
25#include <sys/proc.h>
26#include <sys/vnode.h>
27#include <sys/device.h>
28#include <sys/poll.h>
29#include <sys/time.h>
30#include <sys/sensors.h>
31
32#include <dev/clock_subr.h>
33#include <dev/usb/usb.h>
34#include <dev/usb/usbdi.h>
35#include <dev/usb/usbdi_util.h>
36#include <dev/usb/usbdevs.h>
37
38#ifdef UDCF_DEBUG
39#define DPRINTFN(n, x)	do { if (udcfdebug > (n)) printf x; } while (0)
40int udcfdebug = 0;
41#else
42#define DPRINTFN(n, x)
43#endif
44#define DPRINTF(x)	DPRINTFN(0, x)
45
46#define UDCF_READ_REQ	0xc0
47#define UDCF_READ_IDX	0x1f
48
49#define UDCF_CTRL_REQ	0x40
50#define UDCF_CTRL_IDX	0x33
51#define UDCF_CTRL_VAL	0x98
52
53#define DPERIOD		((long) 15 * 60)	/* degrade period, 15 min */
54
55#define CLOCK_DCF77	0
56#define CLOCK_HBG	1
57
58static const char	*clockname[2] = {
59	"DCF77",
60	"HBG" };
61
62struct udcf_softc {
63	USBBASEDEVICE		sc_dev;		/* base device */
64	usbd_device_handle	sc_udev;	/* USB device */
65	usbd_interface_handle	sc_iface;	/* data interface */
66	u_char			sc_dying;	/* disconnecting */
67
68	struct timeout		sc_to;
69	struct usb_task		sc_task;
70
71	struct timeout		sc_bv_to;	/* bit-value detect */
72	struct timeout		sc_db_to;	/* debounce */
73	struct timeout		sc_mg_to;	/* minute-gap detect */
74	struct timeout		sc_sl_to;	/* signal-loss detect */
75	struct timeout		sc_it_to;	/* invalidate time */
76	struct timeout		sc_ct_to;	/* detect clock type */
77	struct usb_task		sc_bv_task;
78	struct usb_task		sc_mg_task;
79	struct usb_task		sc_sl_task;
80	struct usb_task		sc_it_task;
81	struct usb_task		sc_ct_task;
82
83	usb_device_request_t	sc_req;
84
85	int			sc_clocktype;	/* DCF77 or HBG */
86	int			sc_sync;	/* 1 during sync to DCF77 */
87	u_int64_t		sc_mask;	/* 64 bit mask */
88	u_int64_t		sc_tbits;	/* Time bits */
89	int			sc_minute;
90	int			sc_level;
91	time_t			sc_last_mg;
92
93	time_t			sc_current;	/* current time information */
94	time_t			sc_next;	/* time to become valid next */
95
96	struct sensor		sc_sensor;
97};
98
99static int	t1, t2, t3, t4, t5, t6, t7, t8;	/* timeouts in hz */
100static int	t9;
101
102void	udcf_intr(void *);
103void	udcf_probe(void *);
104
105void	udcf_bv_intr(void *);
106void	udcf_mg_intr(void *);
107void	udcf_sl_intr(void *);
108void	udcf_it_intr(void *);
109void	udcf_ct_intr(void *);
110void	udcf_bv_probe(void *);
111void	udcf_mg_probe(void *);
112void	udcf_sl_probe(void *);
113void	udcf_it_probe(void *);
114void	udcf_ct_probe(void *);
115
116USB_DECLARE_DRIVER(udcf);
117
118USB_MATCH(udcf)
119{
120	USB_MATCH_START(udcf, uaa);
121
122	if (uaa->iface != NULL)
123		return (UMATCH_NONE);
124
125	return uaa->vendor == USB_VENDOR_GUDE &&
126	    uaa->product == USB_PRODUCT_GUDE_DCF ?
127	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
128}
129
130USB_ATTACH(udcf)
131{
132	USB_ATTACH_START(udcf, sc, uaa);
133	usbd_device_handle		 dev = uaa->device;
134	usbd_interface_handle		 iface;
135	struct timeval			 t;
136	char				*devinfop;
137	usb_interface_descriptor_t	*id;
138#ifdef UDCF_DEBUG
139	char 				*devname = USBDEVNAME(sc->sc_dev);
140#endif
141	usbd_status			 err;
142	usb_device_request_t		 req;
143	uWord				 result;
144	int				 actlen;
145
146	if ((err = usbd_set_config_index(dev, 0, 1))) {
147		DPRINTF(("\n%s: failed to set configuration, err=%s\n",
148		    devname, usbd_errstr(err)));
149		goto fishy;
150	}
151
152	if ((err = usbd_device2interface_handle(dev, 0, &iface))) {
153		DPRINTF(("\n%s: failed to get interface, err=%s\n",
154		    devname, usbd_errstr(err)));
155		goto fishy;
156	}
157
158	devinfop = usbd_devinfo_alloc(dev, 0);
159	USB_ATTACH_SETUP;
160	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
161	usbd_devinfo_free(devinfop);
162
163	id = usbd_get_interface_descriptor(iface);
164
165	sc->sc_udev = dev;
166	sc->sc_iface = iface;
167
168	sc->sc_clocktype = -1;
169	sc->sc_level = 0;
170	sc->sc_minute = 0;
171	sc->sc_last_mg = 0L;
172
173	sc->sc_sync = 1;
174
175	sc->sc_current = 0L;
176	sc->sc_next = 0L;
177
178	strlcpy(sc->sc_sensor.device, USBDEVNAME(sc->sc_dev),
179	    sizeof(sc->sc_sensor.device));
180	sc->sc_sensor.type = SENSOR_TIMEDELTA;
181	sc->sc_sensor.status = SENSOR_S_UNKNOWN;
182	sc->sc_sensor.flags = SENSOR_FINVALID;
183	sensor_add(&sc->sc_sensor);
184
185	/* Prepare the USB request to probe the value */
186
187	sc->sc_req.bmRequestType = UDCF_READ_REQ;
188	sc->sc_req.bRequest = 1;
189	USETW(sc->sc_req.wValue, 0);
190	USETW(sc->sc_req.wIndex, UDCF_READ_IDX);
191	USETW(sc->sc_req.wLength, 1);
192
193	req.bmRequestType = UDCF_CTRL_REQ;
194	req.bRequest = 0;
195	USETW(req.wValue, 0);
196	USETW(req.wIndex, 0);
197	USETW(req.wLength, 0);
198	if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result,
199	    USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) {
200		DPRINTF(("failed to turn on power for receiver\n"));
201		goto fishy;
202	}
203
204	req.bmRequestType = UDCF_CTRL_REQ;
205	req.bRequest = 0;
206	USETW(req.wValue, UDCF_CTRL_VAL);
207	USETW(req.wIndex, UDCF_CTRL_IDX);
208	USETW(req.wLength, 0);
209	if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result,
210	    USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) {
211		DPRINTF(("failed to turn on receiver\n"));
212		goto fishy;
213	}
214
215	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
216	    USBDEV(sc->sc_dev));
217
218	usb_init_task(&sc->sc_task, udcf_probe, sc);
219	usb_init_task(&sc->sc_bv_task, udcf_bv_probe, sc);
220	usb_init_task(&sc->sc_mg_task, udcf_mg_probe, sc);
221	usb_init_task(&sc->sc_sl_task, udcf_sl_probe, sc);
222	usb_init_task(&sc->sc_it_task, udcf_it_probe, sc);
223	usb_init_task(&sc->sc_ct_task, udcf_ct_probe, sc);
224
225	timeout_set(&sc->sc_to, udcf_intr, sc);
226	timeout_set(&sc->sc_bv_to, udcf_bv_intr, sc);
227	timeout_set(&sc->sc_mg_to, udcf_mg_intr, sc);
228	timeout_set(&sc->sc_sl_to, udcf_sl_intr, sc);
229	timeout_set(&sc->sc_it_to, udcf_it_intr, sc);
230	timeout_set(&sc->sc_ct_to, udcf_ct_intr, sc);
231
232	/* convert timevals to hz */
233
234	t.tv_sec = 0L;
235	t.tv_usec = 150000L;
236	t1 = tvtohz(&t);
237
238	t.tv_usec = 450000L;
239	t4 = tvtohz(&t);
240
241	t.tv_usec = 900000L;
242	t7 = tvtohz(&t);
243
244	t.tv_sec = 1L;
245	t.tv_usec = 500000L;
246	t2 = tvtohz(&t);
247
248	t.tv_sec = 3L;
249	t.tv_usec = 0L;
250	t3 = tvtohz(&t);
251
252	t.tv_sec = 5L;
253	t5 = tvtohz(&t);
254
255	t.tv_sec = 8L;
256	t6 = tvtohz(&t);
257
258	t.tv_sec = DPERIOD;
259	t8 = tvtohz(&t);
260
261	t.tv_sec = 0L;
262	t.tv_usec = 250000L;
263	t9 = tvtohz(&t);
264
265	/* Give the receiver some slack to stabilize */
266	timeout_add(&sc->sc_to, t3);
267
268	/* Detect signal loss in 5 sec */
269	timeout_add(&sc->sc_sl_to, t5);
270
271	DPRINTF(("synchronizing\n"));
272	USB_ATTACH_SUCCESS_RETURN;
273
274fishy:
275	DPRINTF(("udcf_attach failed\n"));
276	sc->sc_dying = 1;
277	USB_ATTACH_ERROR_RETURN;
278}
279
280USB_DETACH(udcf)
281{
282	struct udcf_softc	*sc = (struct udcf_softc *)self;
283
284	sc->sc_dying = 1;
285
286	timeout_del(&sc->sc_to);
287	timeout_del(&sc->sc_bv_to);
288	timeout_del(&sc->sc_mg_to);
289	timeout_del(&sc->sc_sl_to);
290	timeout_del(&sc->sc_it_to);
291	timeout_del(&sc->sc_ct_to);
292
293	/* Unregister the clock with the kernel */
294
295	if (sc->sc_sensor.status != SENSOR_S_UNKNOWN)
296		sensor_del(&sc->sc_sensor);
297
298	usb_rem_task(sc->sc_udev, &sc->sc_task);
299	usb_rem_task(sc->sc_udev, &sc->sc_bv_task);
300	usb_rem_task(sc->sc_udev, &sc->sc_mg_task);
301	usb_rem_task(sc->sc_udev, &sc->sc_sl_task);
302	usb_rem_task(sc->sc_udev, &sc->sc_it_task);
303	usb_rem_task(sc->sc_udev, &sc->sc_ct_task);
304
305	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
306	    USBDEV(sc->sc_dev));
307	return (0);
308}
309
310/* udcf_intr runs in an interrupt context */
311
312void
313udcf_intr(void *xsc)
314{
315	struct udcf_softc *sc = xsc;
316	usb_add_task(sc->sc_udev, &sc->sc_task);
317}
318
319/* bit value detection */
320
321void
322udcf_bv_intr(void *xsc)
323{
324	struct udcf_softc *sc = xsc;
325	usb_add_task(sc->sc_udev, &sc->sc_bv_task);
326}
327
328/* minute gap detection */
329
330void
331udcf_mg_intr(void *xsc)
332{
333	struct udcf_softc *sc = xsc;
334	usb_add_task(sc->sc_udev, &sc->sc_mg_task);
335}
336
337/* signal loss detection */
338
339void
340udcf_sl_intr(void *xsc)
341{
342	struct udcf_softc *sc = xsc;
343	usb_add_task(sc->sc_udev, &sc->sc_sl_task);
344}
345
346/* degrade the sensor if no new time received for >= DPERIOD seconds. */
347
348void
349udcf_it_intr(void *xsc)
350{
351	struct udcf_softc *sc = xsc;
352	usb_add_task(sc->sc_udev, &sc->sc_it_task);
353}
354
355/* detect the cloc type (DCF77 or HBG) */
356
357void
358udcf_ct_intr(void *xsc)
359{
360	struct udcf_softc *sc = xsc;
361	usb_add_task(sc->sc_udev, &sc->sc_ct_task);
362}
363
364/*
365 * udcf_probe runs in a process context.  If Bit 0 is set, the transmitter
366 * emits at full power.  During the low-power emission we decode a zero bit.
367 */
368
369void
370udcf_probe(void *xsc)
371{
372	struct udcf_softc	*sc = xsc;
373	struct timespec		 now;
374	unsigned char		 data;
375	int			 actlen;
376
377	if (sc->sc_dying)
378		return;
379
380	if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data,
381	    USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))
382		/* This happens if we pull the receiver */
383		return;
384
385	if (data & 0x01) {
386		sc->sc_level = 1;
387		timeout_add(&sc->sc_to, 1);
388	} else if (sc->sc_level == 1)	{ /* Begin of a second */
389		sc->sc_level = 0;
390		if (sc->sc_minute == 1) {
391			if (sc->sc_sync) {
392				DPRINTF(("synchronized, collecting bits\n"));
393				sc->sc_sync = 0;
394				if (sc->sc_sensor.status == SENSOR_S_UNKNOWN)
395					sc->sc_clocktype = -1;
396			} else {
397				/* provide the time delta */
398
399				microtime(&sc->sc_sensor.tv);
400				nanotime(&now);
401				sc->sc_current = sc->sc_next;
402				sc->sc_sensor.value =
403				    (now.tv_sec - sc->sc_current)
404				    * 1000000000 + now.tv_nsec;
405
406				/* set the clocktype and make sensor valid */
407
408				if (sc->sc_sensor.status == SENSOR_S_UNKNOWN) {
409					strlcpy(sc->sc_sensor.desc,
410					    sc->sc_clocktype ?
411					    clockname[CLOCK_HBG] :
412					    clockname[CLOCK_DCF77],
413					    sizeof(sc->sc_sensor.desc));
414					DPRINTF(("add timedelta sensor for %s\n",
415						sc->sc_sensor.desc));
416					sc->sc_sensor.flags &=
417					    ~SENSOR_FINVALID;
418				}
419				sc->sc_sensor.status = SENSOR_S_OK;
420
421				timeout_del(&sc->sc_it_to);
422			}
423			sc->sc_tbits = 0LL;
424			sc->sc_mask = 1LL;
425			sc->sc_minute = 0;
426		}
427
428		timeout_add(&sc->sc_to, t7);	/* Begin resync in 900 ms */
429
430		/* No clock and bit detection during sync */
431		if (!sc->sc_sync) {
432			timeout_add(&sc->sc_bv_to, t1);	/* bit in 150 ms */
433
434			/* detect clocktype in 250 ms if not known yet */
435
436			if (sc->sc_clocktype == -1)
437				timeout_add(&sc->sc_ct_to, t9);
438		}
439		timeout_add(&sc->sc_mg_to, t2);	/* minute gap in 1500 ms */
440		timeout_add(&sc->sc_sl_to, t3);	/* signal loss in 3 sec */
441	}
442}
443
444/* detect the bit value */
445
446void
447udcf_bv_probe(void *xsc)
448{
449	struct udcf_softc	*sc = xsc;
450	int			 actlen;
451	unsigned char		 data;
452
453	if (sc->sc_dying)
454		return;
455
456	if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data,
457	    USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) {
458		/* This happens if we pull the receiver */
459		DPRINTF(("bit detection failed\n"));
460		return;
461	}
462
463	DPRINTF((data & 0x01 ? "0" : "1"));
464	if (!(data & 0x01))
465		sc->sc_tbits |= sc->sc_mask;
466	sc->sc_mask <<= 1;
467}
468
469/* detect the minute gap */
470
471void
472udcf_mg_probe(void *xsc)
473{
474	struct udcf_softc	*sc = xsc;
475
476	struct clock_ymdhms	 ymdhm;
477	int			 minute_bits, hour_bits, day_bits;
478	int			 month_bits, year_bits, wday;
479	int			 p1, p2, p3;
480	int			 p1_bit, p2_bit, p3_bit;
481	int			 r_bit, a1_bit, a2_bit, z1_bit, z2_bit;
482	int			 s_bit, m_bit;
483
484	u_int32_t		 parity = 0x6996;
485
486	if (sc->sc_sync) {
487		timeout_add(&sc->sc_to, t4);	/* re-sync in 450 ms */
488		sc->sc_minute = 1;
489		sc->sc_last_mg = time_second;
490	} else {
491		if (time_second - sc->sc_last_mg < 57) {
492			DPRINTF(("unexpected gap, resync\n"));
493			sc->sc_sync = 1;
494			if (sc->sc_sensor.status == SENSOR_S_OK) {
495				sc->sc_sensor.status = SENSOR_S_WARN;
496				timeout_add(&sc->sc_it_to, t8);
497			}
498			timeout_add(&sc->sc_to, t5);
499			timeout_add(&sc->sc_sl_to, t6);
500			sc->sc_last_mg = 0;
501		} else {
502			/* Extract bits w/o parity */
503
504			m_bit = sc->sc_tbits & 1;
505			r_bit = sc->sc_tbits >> 15 & 1;
506			a1_bit = sc->sc_tbits >> 16 & 1;
507			z1_bit = sc->sc_tbits >> 17 & 1;
508			z2_bit = sc->sc_tbits >> 18 & 1;
509			a2_bit = sc->sc_tbits >> 19 & 1;
510			s_bit = sc->sc_tbits >> 20 & 1;
511			p1_bit = sc->sc_tbits >> 28 & 1;
512			p2_bit = sc->sc_tbits >> 35 & 1;
513			p3_bit = sc->sc_tbits >> 58 & 1;
514
515			minute_bits = sc->sc_tbits >> 21 & 0x7f;
516			hour_bits = sc->sc_tbits >> 29 & 0x3f;
517			day_bits = sc->sc_tbits >> 36 & 0x3f;
518			wday = (sc->sc_tbits >> 42) & 0x07;
519			month_bits = sc->sc_tbits >> 45 & 0x1f;
520			year_bits = sc->sc_tbits >> 50 & 0xff;
521
522			/* Validate time information */
523
524			p1 = (parity >> (minute_bits & 0x0f) & 1) ^
525			    (parity >> (minute_bits >> 4) & 1);
526
527			p2 = (parity >> (hour_bits & 0x0f) & 1) ^
528			    (parity >> (hour_bits >> 4) & 1);
529
530			p3 = (parity >> (day_bits & 0x0f) & 1) ^
531			    (parity >> (day_bits >> 4) & 1) ^
532			    ((parity >> wday) & 1) ^
533			    (parity >> (month_bits & 0x0f) & 1) ^
534			    (parity >> (month_bits >> 4) & 1) ^
535			    (parity >> (year_bits & 0x0f) & 1) ^
536			    (parity >> (year_bits >> 4) & 1);
537
538			if (m_bit == 0 && s_bit == 1 &&
539			    p1 == p1_bit && p2 == p2_bit &&
540			    p3 == p3_bit &&
541			    (z1_bit ^ z2_bit)) {
542
543				/* Decode valid time */
544
545				ymdhm.dt_min = FROMBCD(minute_bits);
546				ymdhm.dt_hour = FROMBCD(hour_bits);
547				ymdhm.dt_day = FROMBCD(day_bits);
548				ymdhm.dt_mon = FROMBCD(month_bits);
549				ymdhm.dt_year = 2000 + FROMBCD(year_bits);
550				ymdhm.dt_sec = 0;
551
552				sc->sc_next = clock_ymdhms_to_secs(&ymdhm);
553
554				/* convert to coordinated universal time */
555
556				sc->sc_next -= z1_bit ? 7200 : 3600;
557
558				DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s",
559				    ymdhm.dt_day, ymdhm.dt_mon + 1,
560				    ymdhm.dt_year, ymdhm.dt_hour,
561				    ymdhm.dt_min, z1_bit ? "CEST" : "CET"));
562				DPRINTF((r_bit ? ", reserve antenna" : ""));
563				DPRINTF((a1_bit ? ", dst chg ann." : ""));
564				DPRINTF((a2_bit ? ", leap sec ann." : ""));
565				DPRINTF(("\n"));
566			} else {
567				DPRINTF(("parity error, resync\n"));
568
569				if (sc->sc_sensor.status == SENSOR_S_OK) {
570					sc->sc_sensor.status = SENSOR_S_WARN;
571					timeout_add(&sc->sc_it_to, t8);
572				}
573				sc->sc_sync = 1;
574			}
575			timeout_add(&sc->sc_to, t4);	/* re-sync in 450 ms */
576			sc->sc_minute = 1;
577			sc->sc_last_mg = time_second;
578		}
579	}
580}
581
582/* detect signal loss */
583
584void
585udcf_sl_probe(void *xsc)
586{
587	struct udcf_softc *sc = xsc;
588
589	if (sc->sc_dying)
590		return;
591
592	DPRINTF(("no signal\n"));
593	sc->sc_sync = 1;
594	if (sc->sc_sensor.status == SENSOR_S_OK) {
595		sc->sc_sensor.status = SENSOR_S_WARN;
596		timeout_add(&sc->sc_it_to, t8);
597	}
598	timeout_add(&sc->sc_to, t5);
599	timeout_add(&sc->sc_sl_to, t6);
600}
601
602/* invalidate time delta */
603
604void
605udcf_it_probe(void *xsc)
606{
607	struct udcf_softc *sc = xsc;
608
609	if (sc->sc_dying)
610		return;
611
612	DPRINTF(("\ndegrading sensor to state critical"));
613
614	sc->sc_sensor.status = SENSOR_S_CRIT;
615}
616
617/* detect clock type */
618
619void
620udcf_ct_probe(void *xsc)
621{
622	struct udcf_softc	*sc = xsc;
623	int			 actlen;
624	unsigned char		 data;
625
626	if (sc->sc_dying)
627		return;
628
629	if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data,
630	    USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) {
631		/* This happens if we pull the receiver */
632		DPRINTF(("clocktype detection failed\n"));
633		return;
634	}
635
636	sc->sc_clocktype = data & 0x01 ? 0 : 1;
637	DPRINTF(("\nclocktype is %s\n", sc->sc_clocktype ?
638		clockname[CLOCK_HBG] : clockname[CLOCK_DCF77]));
639}
640
641int
642udcf_activate(device_ptr_t self, enum devact act)
643{
644	struct udcf_softc *sc = (struct udcf_softc *)self;
645
646	switch (act) {
647	case DVACT_ACTIVATE:
648		return (EOPNOTSUPP);
649
650	case DVACT_DEACTIVATE:
651		sc->sc_dying = 1;
652		break;
653	}
654	return (0);
655}
656