Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.23
1.23 ! edd 1: /* $OpenBSD: siofile.c,v 1.22 2020/06/28 05:21:39 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/time.h>
18: #include <sys/types.h>
19:
20: #include <poll.h>
21: #include <sndio.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25:
26: #include "abuf.h"
27: #include "defs.h"
28: #include "dev.h"
1.18 ratchov 29: #include "dev_sioctl.h"
1.1 ratchov 30: #include "dsp.h"
1.10 ratchov 31: #include "fdpass.h"
1.1 ratchov 32: #include "file.h"
33: #include "siofile.h"
34: #include "utils.h"
35:
1.9 ratchov 36: #define WATCHDOG_USEC 4000000 /* 4 seconds */
1.2 ratchov 37:
1.3 ratchov 38: void dev_sio_onmove(void *, int);
39: void dev_sio_timeout(void *);
1.1 ratchov 40: int dev_sio_pollfd(void *, struct pollfd *);
41: int dev_sio_revents(void *, struct pollfd *);
42: void dev_sio_run(void *);
43: void dev_sio_hup(void *);
44:
1.19 ratchov 45: extern struct fileops dev_sioctl_ops;
46:
1.1 ratchov 47: struct fileops dev_sio_ops = {
48: "sio",
49: dev_sio_pollfd,
50: dev_sio_revents,
51: dev_sio_run,
52: dev_sio_run,
53: dev_sio_hup
54: };
55:
56: void
57: dev_sio_onmove(void *arg, int delta)
58: {
59: struct dev *d = arg;
60:
61: #ifdef DEBUG
62: if (log_level >= 4) {
63: dev_log(d);
64: log_puts(": tick, delta = ");
65: log_puti(delta);
66: log_puts("\n");
67: }
68: d->sio.sum_utime += file_utime - d->sio.utime;
69: d->sio.sum_wtime += file_wtime - d->sio.wtime;
70: d->sio.wtime = file_wtime;
71: d->sio.utime = file_utime;
72: if (d->mode & MODE_PLAY)
73: d->sio.pused -= delta;
74: if (d->mode & MODE_REC)
75: d->sio.rused += delta;
76: #endif
77: dev_onmove(d, delta);
78: }
79:
1.2 ratchov 80: void
81: dev_sio_timeout(void *arg)
82: {
83: struct dev *d = arg;
84:
85: dev_log(d);
86: log_puts(": watchdog timeout\n");
1.20 ratchov 87: dev_abort(d);
1.2 ratchov 88: }
89:
1.23 ! edd 90: static int
! 91: dev_sio_openalt(struct dev *d, struct dev_alt *n,
! 92: struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
! 93: {
! 94: struct sio_hdl *hdl;
! 95: struct sioctl_hdl *ctlhdl;
! 96: unsigned int mode = d->reqmode & (MODE_PLAY | MODE_REC);
! 97:
! 98: hdl = fdpass_sio_open(d->num, n->idx, mode);
! 99: if (hdl == NULL) {
! 100: if (mode != (SIO_PLAY | SIO_REC))
! 101: return 0;
! 102: hdl = fdpass_sio_open(d->num, n->idx, SIO_PLAY);
! 103: if (hdl != NULL)
! 104: mode = SIO_PLAY;
! 105: else {
! 106: hdl = fdpass_sio_open(d->num, n->idx, SIO_REC);
! 107: if (hdl != NULL)
! 108: mode = SIO_REC;
! 109: else
! 110: return 0;
! 111: }
! 112: if (log_level >= 1) {
! 113: log_puts("warning, device opened in ");
! 114: log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
! 115: log_puts(" mode\n");
! 116: }
! 117: }
! 118:
! 119: ctlhdl = fdpass_sioctl_open(d->num, n->idx, SIOCTL_READ | SIOCTL_WRITE);
! 120: if (ctlhdl == NULL) {
! 121: if (log_level >= 1) {
! 122: dev_log(d);
! 123: log_puts(": no control device\n");
! 124: }
! 125: }
! 126:
! 127: *rhdl = hdl;
! 128: *rctlhdl = ctlhdl;
! 129: *rmode = mode;
! 130: return 1;
! 131: }
! 132:
1.1 ratchov 133: /*
1.17 ratchov 134: * open the device using one of the provided paths
135: */
1.23 ! edd 136: static int
! 137: dev_sio_openlist(struct dev *d,
! 138: struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
1.17 ratchov 139: {
1.21 ratchov 140: struct dev_alt *n;
1.22 ratchov 141: struct ctl *c;
142: int val;
1.17 ratchov 143:
1.21 ratchov 144: for (n = d->alt_list; n != NULL; n = n->next) {
145: if (d->alt_num == n->idx)
146: continue;
1.23 ! edd 147: if (log_level >= 2) {
! 148: dev_log(d);
! 149: log_puts(": trying ");
! 150: log_puts(n->name);
! 151: log_puts("\n");
! 152: }
! 153: if (dev_sio_openalt(d, n, rhdl, rctlhdl, rmode)) {
1.17 ratchov 154: if (log_level >= 2) {
155: dev_log(d);
156: log_puts(": using ");
1.21 ratchov 157: log_puts(n->name);
1.17 ratchov 158: log_puts("\n");
159: }
1.21 ratchov 160: d->alt_num = n->idx;
1.22 ratchov 161: for (c = d->ctl_list; c != NULL; c = c->next) {
162: if (c->addr < CTLADDR_ALT_SEL ||
163: c->addr >= CTLADDR_ALT_SEL + DEV_NMAX)
164: continue;
165: val = (c->addr - CTLADDR_ALT_SEL) == n->idx;
166: if (c->curval == val)
167: continue;
168: c->curval = val;
169: if (val)
170: c->val_mask = ~0U;
171: }
1.23 ! edd 172: return 1;
1.17 ratchov 173: }
174: }
1.23 ! edd 175: return 0;
1.17 ratchov 176: }
177:
178: /*
1.1 ratchov 179: * open the device.
180: */
181: int
182: dev_sio_open(struct dev *d)
183: {
184: struct sio_par par;
185:
1.23 ! edd 186: if (!dev_sio_openlist(d, &d->sio.hdl, &d->sioctl.hdl, &d->mode))
! 187: return 0;
! 188:
1.1 ratchov 189: sio_initpar(&par);
190: par.bits = d->par.bits;
191: par.bps = d->par.bps;
192: par.sig = d->par.sig;
193: par.le = d->par.le;
194: par.msb = d->par.msb;
1.23 ! edd 195: if (d->mode & SIO_PLAY)
1.1 ratchov 196: par.pchan = d->pchan;
1.23 ! edd 197: if (d->mode & SIO_REC)
1.1 ratchov 198: par.rchan = d->rchan;
199: if (d->bufsz)
200: par.appbufsz = d->bufsz;
201: if (d->round)
202: par.round = d->round;
203: if (d->rate)
204: par.rate = d->rate;
205: if (!sio_setpar(d->sio.hdl, &par))
206: goto bad_close;
207: if (!sio_getpar(d->sio.hdl, &par))
208: goto bad_close;
1.5 ratchov 209:
210: #ifdef DEBUG
211: /*
1.13 ratchov 212: * We support any parameter combination exposed by the kernel,
1.5 ratchov 213: * and we have no other choice than trusting the kernel for
214: * returning correct parameters. But let's check parameters
215: * early and nicely report kernel bugs rather than crashing
216: * later in memset(), malloc() or alike.
217: */
218:
219: if (par.bits > BITS_MAX) {
1.15 ratchov 220: dev_log(d);
1.5 ratchov 221: log_puts(": ");
222: log_putu(par.bits);
223: log_puts(": unsupported number of bits\n");
224: goto bad_close;
225: }
226: if (par.bps > SIO_BPS(BITS_MAX)) {
1.15 ratchov 227: dev_log(d);
1.5 ratchov 228: log_puts(": ");
1.14 ratchov 229: log_putu(par.bps);
1.5 ratchov 230: log_puts(": unsupported sample size\n");
231: goto bad_close;
232: }
1.23 ! edd 233: if ((d->mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
1.15 ratchov 234: dev_log(d);
1.5 ratchov 235: log_puts(": ");
236: log_putu(par.pchan);
237: log_puts(": unsupported number of play channels\n");
238: goto bad_close;
1.11 ratchov 239: }
1.23 ! edd 240: if ((d->mode & SIO_REC) && par.rchan > NCHAN_MAX) {
1.15 ratchov 241: dev_log(d);
1.5 ratchov 242: log_puts(": ");
243: log_putu(par.rchan);
244: log_puts(": unsupported number of rec channels\n");
245: goto bad_close;
246: }
247: if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
1.15 ratchov 248: dev_log(d);
1.5 ratchov 249: log_puts(": ");
250: log_putu(par.bufsz);
251: log_puts(": unsupported buffer size\n");
252: goto bad_close;
253: }
254: if (par.round == 0 || par.round > par.bufsz ||
255: par.bufsz % par.round != 0) {
1.15 ratchov 256: dev_log(d);
1.5 ratchov 257: log_puts(": ");
258: log_putu(par.round);
259: log_puts(": unsupported block size\n");
260: goto bad_close;
261: }
262: if (par.rate == 0 || par.rate > RATE_MAX) {
1.15 ratchov 263: dev_log(d);
1.5 ratchov 264: log_puts(": ");
265: log_putu(par.rate);
266: log_puts(": unsupported rate\n");
267: goto bad_close;
268: }
269: #endif
270:
1.1 ratchov 271: d->par.bits = par.bits;
272: d->par.bps = par.bps;
273: d->par.sig = par.sig;
274: d->par.le = par.le;
275: d->par.msb = par.msb;
1.23 ! edd 276: if (d->mode & SIO_PLAY)
1.1 ratchov 277: d->pchan = par.pchan;
1.23 ! edd 278: if (d->mode & SIO_REC)
1.1 ratchov 279: d->rchan = par.rchan;
280: d->bufsz = par.bufsz;
281: d->round = par.round;
282: d->rate = par.rate;
1.23 ! edd 283: if (d->mode & MODE_PLAY)
! 284: d->mode |= MODE_MON;
1.1 ratchov 285: sio_onmove(d->sio.hdl, dev_sio_onmove, d);
1.16 ratchov 286: d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
1.19 ratchov 287: if (d->sioctl.hdl) {
288: d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
289: sioctl_nfds(d->sioctl.hdl));
290: }
1.2 ratchov 291: timo_set(&d->sio.watchdog, dev_sio_timeout, d);
1.18 ratchov 292: dev_sioctl_open(d);
1.1 ratchov 293: return 1;
294: bad_close:
295: sio_close(d->sio.hdl);
1.18 ratchov 296: if (d->sioctl.hdl) {
297: sioctl_close(d->sioctl.hdl);
298: d->sioctl.hdl = NULL;
299: }
1.1 ratchov 300: return 0;
301: }
302:
1.16 ratchov 303: /*
304: * Open an alternate device. Upon success and if the new device is
305: * compatible with the old one, close the old device and continue
306: * using the new one. The new device is not started.
307: */
308: int
309: dev_sio_reopen(struct dev *d)
310: {
311: struct sio_par par;
312: struct sio_hdl *hdl;
1.23 ! edd 313: struct sioctl_hdl *ctlhdl;
! 314: unsigned int mode;
1.16 ratchov 315:
1.23 ! edd 316: if (!dev_sio_openlist(d, &hdl, &ctlhdl, &mode))
1.16 ratchov 317: return 0;
318:
319: sio_initpar(&par);
320: par.bits = d->par.bits;
321: par.bps = d->par.bps;
322: par.sig = d->par.sig;
323: par.le = d->par.le;
324: par.msb = d->par.msb;
1.23 ! edd 325: if (mode & SIO_PLAY)
! 326: par.pchan = d->reqpchan;
! 327: if (mode & SIO_REC)
! 328: par.rchan = d->reqrchan;
1.16 ratchov 329: par.appbufsz = d->bufsz;
330: par.round = d->round;
331: par.rate = d->rate;
332: if (!sio_setpar(hdl, &par))
333: goto bad_close;
334: if (!sio_getpar(hdl, &par))
335: goto bad_close;
336:
337: /* check if new parameters are compatible with old ones */
338: if (par.round != d->round || par.bufsz != d->bufsz ||
339: par.rate != d->rate) {
340: if (log_level >= 1) {
341: dev_log(d);
342: log_puts(": alternate device not compatible\n");
343: }
344: goto bad_close;
345: }
346:
347: /* close unused device */
348: timo_del(&d->sio.watchdog);
349: file_del(d->sio.file);
350: sio_close(d->sio.hdl);
1.18 ratchov 351: if (d->sioctl.hdl) {
1.19 ratchov 352: file_del(d->sioctl.file);
1.18 ratchov 353: sioctl_close(d->sioctl.hdl);
354: d->sioctl.hdl = NULL;
355: }
1.16 ratchov 356:
357: /* update parameters */
1.23 ! edd 358: d->mode = mode;
1.16 ratchov 359: d->par.bits = par.bits;
360: d->par.bps = par.bps;
361: d->par.sig = par.sig;
362: d->par.le = par.le;
363: d->par.msb = par.msb;
364: if (d->mode & SIO_PLAY)
365: d->pchan = par.pchan;
366: if (d->mode & SIO_REC)
367: d->rchan = par.rchan;
368:
369: d->sio.hdl = hdl;
1.18 ratchov 370: d->sioctl.hdl = ctlhdl;
1.16 ratchov 371: d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
1.19 ratchov 372: if (d->sioctl.hdl) {
373: d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
374: sioctl_nfds(ctlhdl));
375: }
1.16 ratchov 376: sio_onmove(hdl, dev_sio_onmove, d);
377: return 1;
378: bad_close:
379: sio_close(hdl);
1.18 ratchov 380: if (ctlhdl)
381: sioctl_close(ctlhdl);
1.16 ratchov 382: return 0;
383: }
384:
1.1 ratchov 385: void
386: dev_sio_close(struct dev *d)
387: {
1.18 ratchov 388: dev_sioctl_close(d);
1.1 ratchov 389: #ifdef DEBUG
390: if (log_level >= 3) {
391: dev_log(d);
392: log_puts(": closed\n");
393: }
394: #endif
1.7 ratchov 395: timo_del(&d->sio.watchdog);
1.1 ratchov 396: file_del(d->sio.file);
1.11 ratchov 397: sio_close(d->sio.hdl);
1.18 ratchov 398: if (d->sioctl.hdl) {
1.19 ratchov 399: file_del(d->sioctl.file);
1.18 ratchov 400: sioctl_close(d->sioctl.hdl);
401: d->sioctl.hdl = NULL;
402: }
1.21 ratchov 403: d->alt_num = -1;
1.1 ratchov 404: }
405:
406: void
407: dev_sio_start(struct dev *d)
408: {
409: if (!sio_start(d->sio.hdl)) {
410: if (log_level >= 1) {
411: dev_log(d);
412: log_puts(": failed to start device\n");
413: }
414: return;
415: }
416: if (d->mode & MODE_PLAY) {
417: d->sio.cstate = DEV_SIO_CYCLE;
418: d->sio.todo = 0;
419: } else {
420: d->sio.cstate = DEV_SIO_READ;
421: d->sio.todo = d->round * d->rchan * d->par.bps;
422: }
423: #ifdef DEBUG
424: d->sio.pused = 0;
425: d->sio.rused = 0;
426: d->sio.sum_utime = 0;
427: d->sio.sum_wtime = 0;
428: d->sio.wtime = file_wtime;
429: d->sio.utime = file_utime;
430: if (log_level >= 3) {
431: dev_log(d);
432: log_puts(": started\n");
433: }
434: #endif
1.2 ratchov 435: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1 ratchov 436: }
437:
438: void
439: dev_sio_stop(struct dev *d)
440: {
441: if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
442: if (log_level >= 1) {
443: dev_log(d);
444: log_puts(": failed to stop device\n");
445: }
446: return;
447: }
448: #ifdef DEBUG
449: if (log_level >= 3) {
450: dev_log(d);
451: log_puts(": stopped, load avg = ");
452: log_puti(d->sio.sum_utime / 1000);
453: log_puts(" / ");
454: log_puti(d->sio.sum_wtime / 1000);
455: log_puts("\n");
456: }
457: #endif
1.2 ratchov 458: timo_del(&d->sio.watchdog);
1.1 ratchov 459: }
460:
461: int
462: dev_sio_pollfd(void *arg, struct pollfd *pfd)
463: {
464: struct dev *d = arg;
465: int events;
1.11 ratchov 466:
1.1 ratchov 467: events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
468: return sio_pollfd(d->sio.hdl, pfd, events);
469: }
470:
471: int
472: dev_sio_revents(void *arg, struct pollfd *pfd)
473: {
474: struct dev *d = arg;
475: int events;
476:
477: events = sio_revents(d->sio.hdl, pfd);
478: #ifdef DEBUG
479: d->sio.events = events;
480: #endif
481: return events;
482: }
483:
484: void
485: dev_sio_run(void *arg)
486: {
487: struct dev *d = arg;
488: unsigned char *data, *base;
489: unsigned int n;
490:
491: /*
492: * sio_read() and sio_write() would block at the end of the
493: * cycle so we *must* return and restart poll()'ing. Otherwise
494: * we may trigger dev_cycle() which would make all clients
495: * underrun (ex, on a play-only device)
496: */
497: for (;;) {
498: if (d->pstate != DEV_RUN)
499: return;
500: switch (d->sio.cstate) {
501: case DEV_SIO_READ:
502: #ifdef DEBUG
503: if (!(d->sio.events & POLLIN)) {
504: dev_log(d);
505: log_puts(": recording, but POLLIN not set\n");
506: panic();
507: }
508: if (d->sio.todo == 0) {
509: dev_log(d);
510: log_puts(": can't read data\n");
511: panic();
512: }
513: if (d->prime > 0) {
514: dev_log(d);
515: log_puts(": unexpected data\n");
516: panic();
517: }
518: #endif
519: base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
520: data = base +
521: d->rchan * d->round * d->par.bps -
522: d->sio.todo;
523: n = sio_read(d->sio.hdl, data, d->sio.todo);
524: d->sio.todo -= n;
525: #ifdef DEBUG
526: if (log_level >= 4) {
527: dev_log(d);
528: log_puts(": read ");
529: log_putu(n);
530: log_puts(": bytes, todo ");
531: log_putu(d->sio.todo);
532: log_puts("/");
533: log_putu(d->round * d->rchan * d->par.bps);
534: log_puts("\n");
535: }
536: #endif
537: if (d->sio.todo > 0)
538: return;
539: #ifdef DEBUG
540: d->sio.rused -= d->round;
541: if (log_level >= 2) {
542: if (d->sio.rused >= d->round) {
543: dev_log(d);
544: log_puts(": rec hw xrun, rused = ");
545: log_puti(d->sio.rused);
546: log_puts("/");
547: log_puti(d->bufsz);
548: log_puts("\n");
549: }
550: if (d->sio.rused < 0 ||
551: d->sio.rused >= d->bufsz) {
552: dev_log(d);
553: log_puts(": out of bounds rused = ");
554: log_puti(d->sio.rused);
555: log_puts("/");
556: log_puti(d->bufsz);
557: log_puts("\n");
558: }
559: }
560: #endif
561: d->sio.cstate = DEV_SIO_CYCLE;
562: break;
563: case DEV_SIO_CYCLE:
1.2 ratchov 564: timo_del(&d->sio.watchdog);
565: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
566:
1.1 ratchov 567: #ifdef DEBUG
568: /*
569: * check that we're called at cycle boundary:
570: * either after a recorded block, or when POLLOUT is
571: * raised
572: */
573: if (!((d->mode & MODE_REC) && d->prime == 0) &&
574: !(d->sio.events & POLLOUT)) {
575: dev_log(d);
576: log_puts(": cycle not at block boundary\n");
577: panic();
578: }
579: #endif
580: dev_cycle(d);
581: if (d->mode & MODE_PLAY) {
582: d->sio.cstate = DEV_SIO_WRITE;
583: d->sio.todo = d->round * d->pchan * d->par.bps;
584: break;
585: } else {
586: d->sio.cstate = DEV_SIO_READ;
587: d->sio.todo = d->round * d->rchan * d->par.bps;
588: return;
589: }
590: case DEV_SIO_WRITE:
591: #ifdef DEBUG
592: if (d->sio.todo == 0) {
593: dev_log(d);
594: log_puts(": can't write data\n");
595: panic();
596: }
597: #endif
598: base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
599: data = base +
600: d->pchan * d->round * d->par.bps -
601: d->sio.todo;
602: n = sio_write(d->sio.hdl, data, d->sio.todo);
603: d->sio.todo -= n;
604: #ifdef DEBUG
605: if (log_level >= 4) {
606: dev_log(d);
607: log_puts(": wrote ");
608: log_putu(n);
609: log_puts(" bytes, todo ");
610: log_putu(d->sio.todo);
611: log_puts("/");
612: log_putu(d->round * d->pchan * d->par.bps);
613: log_puts("\n");
614: }
615: #endif
616: if (d->sio.todo > 0)
617: return;
618: #ifdef DEBUG
619: d->sio.pused += d->round;
620: if (log_level >= 2) {
621: if (d->prime == 0 &&
622: d->sio.pused <= d->bufsz - d->round) {
623: dev_log(d);
624: log_puts(": play hw xrun, pused = ");
625: log_puti(d->sio.pused);
626: log_puts("/");
627: log_puti(d->bufsz);
628: log_puts("\n");
629: }
630: if (d->sio.pused < 0 ||
631: d->sio.pused > d->bufsz) {
632: /* device driver or libsndio bug */
633: dev_log(d);
634: log_puts(": out of bounds pused = ");
635: log_puti(d->sio.pused);
636: log_puts("/");
637: log_puti(d->bufsz);
638: log_puts("\n");
639: }
640: }
641: #endif
642: d->poffs += d->round;
1.4 ratchov 643: if (d->poffs == d->psize)
1.1 ratchov 644: d->poffs = 0;
645: if ((d->mode & MODE_REC) && d->prime == 0) {
646: d->sio.cstate = DEV_SIO_READ;
647: d->sio.todo = d->round * d->rchan * d->par.bps;
648: } else
649: d->sio.cstate = DEV_SIO_CYCLE;
650: return;
651: }
652: }
653: }
654:
655: void
656: dev_sio_hup(void *arg)
657: {
658: struct dev *d = arg;
659:
1.8 ratchov 660: #ifdef DEBUG
661: if (log_level >= 2) {
662: dev_log(d);
663: log_puts(": disconnected\n");
664: }
665: #endif
1.16 ratchov 666: if (!dev_reopen(d))
1.20 ratchov 667: dev_abort(d);
1.1 ratchov 668: }