e0cc03a09b
kqueue provides a stateful and efficient event notification framework currently supported events include socket, file, directory, fifo, pipe, tty and device changes, and monitoring of processes and signals kqueue is supported by all writable filesystems in NetBSD tree (with exception of Coda) and all device drivers supporting poll(2) based on work done by Jonathan Lemon for FreeBSD initial NetBSD port done by Luke Mewburn and Jason Thorpe
1090 lines
26 KiB
C
1090 lines
26 KiB
C
/* $NetBSD: irframe_tty.c,v 1.22 2002/10/23 09:13:21 jdolecek Exp $ */
|
|
|
|
/*
|
|
* TODO
|
|
* Test dongle code.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Lennart Augustsson (lennart@augustsson.net) and Tommy Bohlin
|
|
* (tommy@gatespace.com).
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Loosely based on ppp_tty.c.
|
|
* Framing and dongle handling written by Tommy Bohlin.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/file.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/poll.h>
|
|
|
|
#include <dev/ir/ir.h>
|
|
#include <dev/ir/sir.h>
|
|
#include <dev/ir/irdaio.h>
|
|
#include <dev/ir/irframevar.h>
|
|
|
|
/* Macros to clear/set/test flags. */
|
|
#define SET(t, f) (t) |= (f)
|
|
#define CLR(t, f) (t) &= ~(f)
|
|
#define ISSET(t, f) ((t) & (f))
|
|
|
|
#ifdef IRFRAMET_DEBUG
|
|
#define DPRINTF(x) if (irframetdebug) printf x
|
|
#define Static
|
|
int irframetdebug = 0;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#define Static static
|
|
#endif
|
|
|
|
/*****/
|
|
|
|
/* Max size with framing. */
|
|
#define MAX_IRDA_FRAME (2*IRDA_MAX_FRAME_SIZE + IRDA_MAX_EBOFS + 4)
|
|
|
|
struct frame {
|
|
u_char *buf;
|
|
u_int len;
|
|
};
|
|
#define MAXFRAMES 8
|
|
|
|
struct irframet_softc {
|
|
struct irframe_softc sc_irp;
|
|
struct tty *sc_tp;
|
|
|
|
int sc_dongle;
|
|
int sc_dongle_private;
|
|
|
|
int sc_state;
|
|
#define IRT_RSLP 0x01 /* waiting for data (read) */
|
|
#if 0
|
|
#define IRT_WSLP 0x02 /* waiting for data (write) */
|
|
#define IRT_CLOSING 0x04 /* waiting for output to drain */
|
|
#endif
|
|
struct lock sc_wr_lk;
|
|
|
|
struct irda_params sc_params;
|
|
|
|
u_char* sc_inbuf;
|
|
int sc_framestate;
|
|
#define FRAME_OUTSIDE 0
|
|
#define FRAME_INSIDE 1
|
|
#define FRAME_ESCAPE 2
|
|
int sc_inchars;
|
|
int sc_inFCS;
|
|
struct callout sc_timeout;
|
|
|
|
u_int sc_nframes;
|
|
u_int sc_framei;
|
|
u_int sc_frameo;
|
|
struct frame sc_frames[MAXFRAMES];
|
|
struct selinfo sc_rsel;
|
|
/* XXXJRT Nothing selnotify's sc_wsel */
|
|
struct selinfo sc_wsel;
|
|
};
|
|
|
|
/* line discipline methods */
|
|
int irframetopen(dev_t dev, struct tty *tp);
|
|
int irframetclose(struct tty *tp, int flag);
|
|
int irframetioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
|
|
struct proc *);
|
|
int irframetinput(int c, struct tty *tp);
|
|
int irframetstart(struct tty *tp);
|
|
|
|
/* pseudo device init */
|
|
void irframettyattach(int);
|
|
|
|
/* irframe methods */
|
|
Static int irframet_open(void *h, int flag, int mode, struct proc *p);
|
|
Static int irframet_close(void *h, int flag, int mode, struct proc *p);
|
|
Static int irframet_read(void *h, struct uio *uio, int flag);
|
|
Static int irframet_write(void *h, struct uio *uio, int flag);
|
|
Static int irframet_poll(void *h, int events, struct proc *p);
|
|
Static int irframet_kqfilter(void *h, struct knote *kn);
|
|
Static int irframet_set_params(void *h, struct irda_params *params);
|
|
Static int irframet_get_speeds(void *h, int *speeds);
|
|
Static int irframet_get_turnarounds(void *h, int *times);
|
|
|
|
/* internal */
|
|
Static int irt_write_frame(struct tty *tp, u_int8_t *buf, size_t len);
|
|
Static int irt_putc(struct tty *tp, int c);
|
|
Static void irt_frame(struct irframet_softc *sc, u_char *buf, u_int len);
|
|
Static void irt_timeout(void *v);
|
|
Static void irt_ioctl(struct tty *tp, u_long cmd, void *arg);
|
|
Static void irt_setspeed(struct tty *tp, u_int speed);
|
|
Static void irt_setline(struct tty *tp, u_int line);
|
|
Static void irt_delay(struct tty *tp, u_int delay);
|
|
|
|
Static const struct irframe_methods irframet_methods = {
|
|
irframet_open, irframet_close, irframet_read, irframet_write,
|
|
irframet_poll, irframet_kqfilter, irframet_set_params,
|
|
irframet_get_speeds, irframet_get_turnarounds
|
|
};
|
|
|
|
Static void irts_none(struct tty *tp, u_int speed);
|
|
Static void irts_tekram(struct tty *tp, u_int speed);
|
|
Static void irts_jeteye(struct tty *tp, u_int speed);
|
|
Static void irts_actisys(struct tty *tp, u_int speed);
|
|
Static void irts_litelink(struct tty *tp, u_int speed);
|
|
Static void irts_girbil(struct tty *tp, u_int speed);
|
|
|
|
#define NORMAL_SPEEDS (IRDA_SPEEDS_SIR & ~IRDA_SPEED_2400)
|
|
#define TURNT_POS (IRDA_TURNT_10000 | IRDA_TURNT_5000 | IRDA_TURNT_1000 | \
|
|
IRDA_TURNT_500 | IRDA_TURNT_100 | IRDA_TURNT_50 | IRDA_TURNT_10)
|
|
Static const struct dongle {
|
|
void (*setspeed)(struct tty *tp, u_int speed);
|
|
u_int speedmask;
|
|
u_int turnmask;
|
|
} irt_dongles[DONGLE_MAX] = {
|
|
/* Indexed by dongle number from irdaio.h */
|
|
{ irts_none, IRDA_SPEEDS_SIR, IRDA_TURNT_10000 },
|
|
{ irts_tekram, IRDA_SPEEDS_SIR, IRDA_TURNT_10000 },
|
|
{ irts_jeteye, IRDA_SPEED_9600|IRDA_SPEED_19200|IRDA_SPEED_115200,
|
|
IRDA_TURNT_10000 },
|
|
{ irts_actisys, NORMAL_SPEEDS & ~IRDA_SPEED_38400, TURNT_POS },
|
|
{ irts_actisys, NORMAL_SPEEDS, TURNT_POS },
|
|
{ irts_litelink, NORMAL_SPEEDS, TURNT_POS },
|
|
{ irts_girbil, IRDA_SPEEDS_SIR, IRDA_TURNT_10000 | IRDA_TURNT_5000 },
|
|
};
|
|
|
|
void
|
|
irframettyattach(int n)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Line specific open routine for async tty devices.
|
|
* Attach the given tty to the first available irframe unit.
|
|
* Called from device open routine or ttioctl.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
irframetopen(dev_t dev, struct tty *tp)
|
|
{
|
|
struct proc *p = curproc; /* XXX */
|
|
struct irframet_softc *sc;
|
|
int error, s;
|
|
|
|
DPRINTF(("%s\n", __FUNCTION__));
|
|
|
|
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
|
|
return (error);
|
|
|
|
s = spltty();
|
|
|
|
DPRINTF(("%s: linesw=%p disc=%s\n", __FUNCTION__, tp->t_linesw,
|
|
tp->t_linesw->l_name));
|
|
if (strcmp(tp->t_linesw->l_name, "irframe") == 0) { /* XXX */
|
|
sc = (struct irframet_softc *)tp->t_sc;
|
|
DPRINTF(("%s: sc=%p sc_tp=%p\n", __FUNCTION__, sc, sc->sc_tp));
|
|
if (sc != NULL) {
|
|
splx(s);
|
|
return (EBUSY);
|
|
}
|
|
}
|
|
|
|
tp->t_sc = irframe_alloc(sizeof (struct irframet_softc),
|
|
&irframet_methods, tp);
|
|
sc = (struct irframet_softc *)tp->t_sc;
|
|
sc->sc_tp = tp;
|
|
printf("%s attached at tty%02d\n", sc->sc_irp.sc_dev.dv_xname,
|
|
minor(tp->t_dev));
|
|
|
|
DPRINTF(("%s: set sc=%p\n", __FUNCTION__, sc));
|
|
|
|
ttyflush(tp, FREAD | FWRITE);
|
|
|
|
sc->sc_dongle = DONGLE_NONE;
|
|
sc->sc_dongle_private = 0;
|
|
|
|
splx(s);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Line specific close routine, called from device close routine
|
|
* and from ttioctl.
|
|
* Detach the tty from the irframe unit.
|
|
* Mimics part of ttyclose().
|
|
*/
|
|
int
|
|
irframetclose(struct tty *tp, int flag)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int s;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
s = spltty();
|
|
ttyflush(tp, FREAD | FWRITE);
|
|
tp->t_linesw = linesw[0]; /* default line discipline */
|
|
if (sc != NULL) {
|
|
tp->t_sc = NULL;
|
|
printf("%s detached from tty%02d\n", sc->sc_irp.sc_dev.dv_xname,
|
|
minor(tp->t_dev));
|
|
|
|
if (sc->sc_tp == tp)
|
|
irframe_dealloc(&sc->sc_irp.sc_dev);
|
|
}
|
|
splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Line specific (tty) ioctl routine.
|
|
* This discipline requires that tty device drivers call
|
|
* the line specific l_ioctl routine from their ioctl routines.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
irframetioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
|
|
struct proc *p)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int error;
|
|
int d;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
if (sc == NULL || tp != sc->sc_tp)
|
|
return (EPASSTHROUGH);
|
|
|
|
error = 0;
|
|
switch (cmd) {
|
|
case IRFRAMETTY_GET_DEVICE:
|
|
*(int *)data = sc->sc_irp.sc_dev.dv_unit;
|
|
break;
|
|
case IRFRAMETTY_GET_DONGLE:
|
|
*(int *)data = sc->sc_dongle;
|
|
break;
|
|
case IRFRAMETTY_SET_DONGLE:
|
|
d = *(int *)data;
|
|
if (d < 0 || d >= DONGLE_MAX)
|
|
return (EINVAL);
|
|
sc->sc_dongle = d;
|
|
break;
|
|
default:
|
|
error = EPASSTHROUGH;
|
|
break;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Start output on async tty interface.
|
|
*/
|
|
int
|
|
irframetstart(struct tty *tp)
|
|
{
|
|
/*struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;*/
|
|
int s;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
s = spltty();
|
|
if (tp->t_oproc != NULL)
|
|
(*tp->t_oproc)(tp);
|
|
splx(s);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
irt_frame(struct irframet_softc *sc, u_char *buf, u_int len)
|
|
{
|
|
DPRINTF(("%s: nframe=%d framei=%d frameo=%d\n",
|
|
__FUNCTION__, sc->sc_nframes, sc->sc_framei, sc->sc_frameo));
|
|
|
|
if (sc->sc_inbuf == NULL) /* XXX happens if device is closed? */
|
|
return;
|
|
if (sc->sc_nframes >= MAXFRAMES) {
|
|
#ifdef IRFRAMET_DEBUG
|
|
printf("%s: dropped frame\n", __FUNCTION__);
|
|
#endif
|
|
return;
|
|
}
|
|
if (sc->sc_frames[sc->sc_framei].buf == NULL)
|
|
return;
|
|
memcpy(sc->sc_frames[sc->sc_framei].buf, buf, len);
|
|
sc->sc_frames[sc->sc_framei].len = len;
|
|
sc->sc_framei = (sc->sc_framei+1) % MAXFRAMES;
|
|
sc->sc_nframes++;
|
|
if (sc->sc_state & IRT_RSLP) {
|
|
sc->sc_state &= ~IRT_RSLP;
|
|
DPRINTF(("%s: waking up reader\n", __FUNCTION__));
|
|
wakeup(sc->sc_frames);
|
|
}
|
|
selnotify(&sc->sc_rsel, 0);
|
|
}
|
|
|
|
void
|
|
irt_timeout(void *v)
|
|
{
|
|
struct irframet_softc *sc = v;
|
|
|
|
#ifdef IRFRAMET_DEBUG
|
|
if (sc->sc_framestate != FRAME_OUTSIDE)
|
|
printf("%s: input frame timeout\n", __FUNCTION__);
|
|
#endif
|
|
sc->sc_framestate = FRAME_OUTSIDE;
|
|
}
|
|
|
|
int
|
|
irframetinput(int c, struct tty *tp)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
|
|
c &= 0xff;
|
|
|
|
#if IRFRAMET_DEBUG
|
|
if (irframetdebug > 1)
|
|
DPRINTF(("%s: tp=%p c=0x%02x\n", __FUNCTION__, tp, c));
|
|
#endif
|
|
|
|
if (sc == NULL || tp != (struct tty *)sc->sc_tp)
|
|
return (0);
|
|
|
|
if (sc->sc_inbuf == NULL)
|
|
return (0);
|
|
|
|
switch (c) {
|
|
case SIR_BOF:
|
|
DPRINTF(("%s: BOF\n", __FUNCTION__));
|
|
sc->sc_framestate = FRAME_INSIDE;
|
|
sc->sc_inchars = 0;
|
|
sc->sc_inFCS = INITFCS;
|
|
break;
|
|
case SIR_EOF:
|
|
DPRINTF(("%s: EOF state=%d inchars=%d fcs=0x%04x\n",
|
|
__FUNCTION__,
|
|
sc->sc_framestate, sc->sc_inchars, sc->sc_inFCS));
|
|
if (sc->sc_framestate == FRAME_INSIDE &&
|
|
sc->sc_inchars >= 4 && sc->sc_inFCS == GOODFCS) {
|
|
irt_frame(sc, sc->sc_inbuf, sc->sc_inchars - 2);
|
|
} else if (sc->sc_framestate != FRAME_OUTSIDE) {
|
|
#ifdef IRFRAMET_DEBUG
|
|
printf("%s: malformed input frame\n", __FUNCTION__);
|
|
#endif
|
|
}
|
|
sc->sc_framestate = FRAME_OUTSIDE;
|
|
break;
|
|
case SIR_CE:
|
|
DPRINTF(("%s: CE\n", __FUNCTION__));
|
|
if (sc->sc_framestate == FRAME_INSIDE)
|
|
sc->sc_framestate = FRAME_ESCAPE;
|
|
break;
|
|
default:
|
|
#if IRFRAMET_DEBUG
|
|
if (irframetdebug > 1)
|
|
DPRINTF(("%s: c=0x%02x, inchar=%d state=%d\n", __FUNCTION__, c,
|
|
sc->sc_inchars, sc->sc_state));
|
|
#endif
|
|
if (sc->sc_framestate != FRAME_OUTSIDE) {
|
|
if (sc->sc_framestate == FRAME_ESCAPE) {
|
|
sc->sc_framestate = FRAME_INSIDE;
|
|
c ^= SIR_ESC_BIT;
|
|
}
|
|
if (sc->sc_inchars < sc->sc_params.maxsize + 2) {
|
|
sc->sc_inbuf[sc->sc_inchars++] = c;
|
|
sc->sc_inFCS = updateFCS(sc->sc_inFCS, c);
|
|
} else {
|
|
sc->sc_framestate = FRAME_OUTSIDE;
|
|
#ifdef IRFRAMET_DEBUG
|
|
printf("%s: input frame overrun\n",
|
|
__FUNCTION__);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if 1
|
|
if (sc->sc_framestate != FRAME_OUTSIDE) {
|
|
callout_reset(&sc->sc_timeout, hz/20, irt_timeout, sc);
|
|
}
|
|
#endif
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*** irframe methods ***/
|
|
|
|
int
|
|
irframet_open(void *h, int flag, int mode, struct proc *p)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
sc->sc_params.speed = 0;
|
|
sc->sc_params.ebofs = IRDA_DEFAULT_EBOFS;
|
|
sc->sc_params.maxsize = 0;
|
|
sc->sc_framestate = FRAME_OUTSIDE;
|
|
sc->sc_nframes = 0;
|
|
sc->sc_framei = 0;
|
|
sc->sc_frameo = 0;
|
|
callout_init(&sc->sc_timeout);
|
|
lockinit(&sc->sc_wr_lk, PZERO, "irfrtl", 0, 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_close(void *h, int flag, int mode, struct proc *p)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int i, s;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
callout_stop(&sc->sc_timeout);
|
|
s = splir();
|
|
if (sc->sc_inbuf != NULL) {
|
|
free(sc->sc_inbuf, M_DEVBUF);
|
|
sc->sc_inbuf = NULL;
|
|
}
|
|
for (i = 0; i < MAXFRAMES; i++) {
|
|
if (sc->sc_frames[i].buf != NULL) {
|
|
free(sc->sc_frames[i].buf, M_DEVBUF);
|
|
sc->sc_frames[i].buf = NULL;
|
|
}
|
|
}
|
|
splx(s);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_read(void *h, struct uio *uio, int flag)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int error = 0;
|
|
int s;
|
|
|
|
DPRINTF(("%s: resid=%d, iovcnt=%d, offset=%ld\n",
|
|
__FUNCTION__, uio->uio_resid, uio->uio_iovcnt,
|
|
(long)uio->uio_offset));
|
|
DPRINTF(("%s: nframe=%d framei=%d frameo=%d\n",
|
|
__FUNCTION__, sc->sc_nframes, sc->sc_framei, sc->sc_frameo));
|
|
|
|
|
|
s = splir();
|
|
while (sc->sc_nframes == 0) {
|
|
if (flag & IO_NDELAY) {
|
|
splx(s);
|
|
return (EWOULDBLOCK);
|
|
}
|
|
sc->sc_state |= IRT_RSLP;
|
|
DPRINTF(("%s: sleep\n", __FUNCTION__));
|
|
error = tsleep(sc->sc_frames, PZERO | PCATCH, "irtrd", 0);
|
|
DPRINTF(("%s: woke, error=%d\n", __FUNCTION__, error));
|
|
if (error) {
|
|
sc->sc_state &= ~IRT_RSLP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Do just one frame transfer per read */
|
|
if (!error) {
|
|
if (uio->uio_resid < sc->sc_frames[sc->sc_frameo].len) {
|
|
DPRINTF(("%s: uio buffer smaller than frame size "
|
|
"(%d < %d)\n", __FUNCTION__, uio->uio_resid,
|
|
sc->sc_frames[sc->sc_frameo].len));
|
|
error = EINVAL;
|
|
} else {
|
|
DPRINTF(("%s: moving %d bytes\n", __FUNCTION__,
|
|
sc->sc_frames[sc->sc_frameo].len));
|
|
error = uiomove(sc->sc_frames[sc->sc_frameo].buf,
|
|
sc->sc_frames[sc->sc_frameo].len, uio);
|
|
DPRINTF(("%s: error=%d\n", __FUNCTION__, error));
|
|
}
|
|
sc->sc_frameo = (sc->sc_frameo+1) % MAXFRAMES;
|
|
sc->sc_nframes--;
|
|
}
|
|
splx(s);
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
irt_putc(struct tty *tp, int c)
|
|
{
|
|
int s;
|
|
int error;
|
|
|
|
#if IRFRAMET_DEBUG
|
|
if (irframetdebug > 3)
|
|
DPRINTF(("%s: tp=%p c=0x%02x cc=%d\n", __FUNCTION__, tp, c,
|
|
tp->t_outq.c_cc));
|
|
#endif
|
|
if (tp->t_outq.c_cc > tp->t_hiwat) {
|
|
irframetstart(tp);
|
|
s = spltty();
|
|
/*
|
|
* This can only occur if FLUSHO is set in t_lflag,
|
|
* or if ttstart/oproc is synchronous (or very fast).
|
|
*/
|
|
if (tp->t_outq.c_cc <= tp->t_hiwat) {
|
|
splx(s);
|
|
goto go;
|
|
}
|
|
SET(tp->t_state, TS_ASLEEP);
|
|
error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout, 0);
|
|
splx(s);
|
|
if (error)
|
|
return (error);
|
|
}
|
|
go:
|
|
if (putc(c, &tp->t_outq) < 0) {
|
|
printf("irframe: putc failed\n");
|
|
return (EIO);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_write(void *h, struct uio *uio, int flag)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
u_int8_t buf[MAX_IRDA_FRAME];
|
|
int n;
|
|
|
|
DPRINTF(("%s: resid=%d, iovcnt=%d, offset=%ld\n",
|
|
__FUNCTION__, uio->uio_resid, uio->uio_iovcnt,
|
|
(long)uio->uio_offset));
|
|
|
|
n = irda_sir_frame(buf, MAX_IRDA_FRAME, uio, sc->sc_params.ebofs);
|
|
if (n < 0) {
|
|
#ifdef IRFRAMET_DEBUG
|
|
printf("%s: irda_sir_frame() error=%d\n", __FUNCTION__, -n);
|
|
#endif
|
|
return (-n);
|
|
}
|
|
return (irt_write_frame(tp, buf, n));
|
|
}
|
|
|
|
int
|
|
irt_write_frame(struct tty *tp, u_int8_t *buf, size_t len)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int error, i;
|
|
|
|
DPRINTF(("%s: tp=%p len=%d\n", __FUNCTION__, tp, len));
|
|
|
|
lockmgr(&sc->sc_wr_lk, LK_EXCLUSIVE, NULL);
|
|
error = 0;
|
|
for (i = 0; !error && i < len; i++)
|
|
error = irt_putc(tp, buf[i]);
|
|
lockmgr(&sc->sc_wr_lk, LK_RELEASE, NULL);
|
|
|
|
irframetstart(tp);
|
|
|
|
DPRINTF(("%s: done, error=%d\n", __FUNCTION__, error));
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
irframet_poll(void *h, int events, struct proc *p)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int revents = 0;
|
|
int s;
|
|
|
|
DPRINTF(("%s: sc=%p\n", __FUNCTION__, sc));
|
|
|
|
s = splir();
|
|
/* XXX is this a good check? */
|
|
if (events & (POLLOUT | POLLWRNORM))
|
|
if (tp->t_outq.c_cc <= tp->t_lowat)
|
|
revents |= events & (POLLOUT | POLLWRNORM);
|
|
|
|
if (events & (POLLIN | POLLRDNORM)) {
|
|
if (sc->sc_nframes > 0) {
|
|
DPRINTF(("%s: have data\n", __FUNCTION__));
|
|
revents |= events & (POLLIN | POLLRDNORM);
|
|
} else {
|
|
DPRINTF(("%s: recording select\n", __FUNCTION__));
|
|
selrecord(p, &sc->sc_rsel);
|
|
}
|
|
}
|
|
splx(s);
|
|
|
|
return (revents);
|
|
}
|
|
|
|
static void
|
|
filt_irframetrdetach(struct knote *kn)
|
|
{
|
|
struct tty *tp = kn->kn_hook;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int s;
|
|
|
|
s = splir();
|
|
SLIST_REMOVE(&sc->sc_rsel.si_klist, kn, knote, kn_selnext);
|
|
splx(s);
|
|
}
|
|
|
|
static int
|
|
filt_irframetread(struct knote *kn, long hint)
|
|
{
|
|
struct tty *tp = kn->kn_hook;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
|
|
kn->kn_data = sc->sc_nframes;
|
|
return (kn->kn_data > 0);
|
|
}
|
|
|
|
static void
|
|
filt_irframetwdetach(struct knote *kn)
|
|
{
|
|
struct tty *tp = kn->kn_hook;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int s;
|
|
|
|
s = splir();
|
|
SLIST_REMOVE(&sc->sc_wsel.si_klist, kn, knote, kn_selnext);
|
|
splx(s);
|
|
}
|
|
|
|
static int
|
|
filt_irframetwrite(struct knote *kn, long hint)
|
|
{
|
|
struct tty *tp = kn->kn_hook;
|
|
|
|
/* XXX double-check this */
|
|
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
kn->kn_data = tp->t_lowat - tp->t_outq.c_cc;
|
|
return (1);
|
|
}
|
|
|
|
kn->kn_data = 0;
|
|
return (0);
|
|
}
|
|
|
|
static const struct filterops irframetread_filtops =
|
|
{ 1, NULL, filt_irframetrdetach, filt_irframetread };
|
|
static const struct filterops irframetwrite_filtops =
|
|
{ 1, NULL, filt_irframetwdetach, filt_irframetwrite };
|
|
|
|
int
|
|
irframet_kqfilter(void *h, struct knote *kn)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
struct klist *klist;
|
|
int s;
|
|
|
|
switch (kn->kn_filter) {
|
|
case EVFILT_READ:
|
|
klist = &sc->sc_rsel.si_klist;
|
|
kn->kn_fop = &irframetread_filtops;
|
|
break;
|
|
case EVFILT_WRITE:
|
|
klist = &sc->sc_wsel.si_klist;
|
|
kn->kn_fop = &irframetwrite_filtops;
|
|
break;
|
|
default:
|
|
return (1);
|
|
}
|
|
|
|
kn->kn_hook = tp;
|
|
|
|
s = splir();
|
|
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
|
|
splx(s);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_set_params(void *h, struct irda_params *p)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int i;
|
|
|
|
DPRINTF(("%s: tp=%p speed=%d ebofs=%d maxsize=%d\n",
|
|
__FUNCTION__, tp, p->speed, p->ebofs, p->maxsize));
|
|
|
|
if (p->speed != sc->sc_params.speed) {
|
|
/* Checked in irframe.c */
|
|
lockmgr(&sc->sc_wr_lk, LK_EXCLUSIVE, NULL);
|
|
irt_dongles[sc->sc_dongle].setspeed(tp, p->speed);
|
|
lockmgr(&sc->sc_wr_lk, LK_RELEASE, NULL);
|
|
sc->sc_params.speed = p->speed;
|
|
}
|
|
|
|
/* Max size checked in irframe.c */
|
|
sc->sc_params.ebofs = p->ebofs;
|
|
/* Max size checked in irframe.c */
|
|
if (sc->sc_params.maxsize != p->maxsize) {
|
|
sc->sc_params.maxsize = p->maxsize;
|
|
if (sc->sc_inbuf != NULL)
|
|
free(sc->sc_inbuf, M_DEVBUF);
|
|
for (i = 0; i < MAXFRAMES; i++)
|
|
if (sc->sc_frames[i].buf != NULL)
|
|
free(sc->sc_frames[i].buf, M_DEVBUF);
|
|
if (sc->sc_params.maxsize != 0) {
|
|
sc->sc_inbuf = malloc(sc->sc_params.maxsize+2,
|
|
M_DEVBUF, M_WAITOK);
|
|
for (i = 0; i < MAXFRAMES; i++)
|
|
sc->sc_frames[i].buf =
|
|
malloc(sc->sc_params.maxsize,
|
|
M_DEVBUF, M_WAITOK);
|
|
} else {
|
|
sc->sc_inbuf = NULL;
|
|
for (i = 0; i < MAXFRAMES; i++)
|
|
sc->sc_frames[i].buf = NULL;
|
|
}
|
|
}
|
|
sc->sc_framestate = FRAME_OUTSIDE;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_get_speeds(void *h, int *speeds)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
if (sc == NULL) /* during attach */
|
|
*speeds = IRDA_SPEEDS_SIR;
|
|
else
|
|
*speeds = irt_dongles[sc->sc_dongle].speedmask;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
irframet_get_turnarounds(void *h, int *turnarounds)
|
|
{
|
|
struct tty *tp = h;
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
|
|
DPRINTF(("%s: tp=%p\n", __FUNCTION__, tp));
|
|
|
|
*turnarounds = irt_dongles[sc->sc_dongle].turnmask;
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
irt_ioctl(struct tty *tp, u_long cmd, void *arg)
|
|
{
|
|
const struct cdevsw *cdev;
|
|
int error;
|
|
dev_t dev;
|
|
|
|
dev = tp->t_dev;
|
|
cdev = cdevsw_lookup(dev);
|
|
if (cdev != NULL)
|
|
error = (*cdev->d_ioctl)(dev, cmd, arg, 0, curproc);
|
|
else
|
|
error = ENXIO;
|
|
#ifdef DIAGNOSTIC
|
|
if (error)
|
|
printf("irt_ioctl: cmd=0x%08lx error=%d\n", cmd, error);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
irt_setspeed(struct tty *tp, u_int speed)
|
|
{
|
|
struct termios tt;
|
|
|
|
irt_ioctl(tp, TIOCGETA, &tt);
|
|
tt.c_ispeed = tt.c_ospeed = speed;
|
|
tt.c_cflag &= ~HUPCL;
|
|
tt.c_cflag |= CLOCAL;
|
|
irt_ioctl(tp, TIOCSETAF, &tt);
|
|
}
|
|
|
|
void
|
|
irt_setline(struct tty *tp, u_int line)
|
|
{
|
|
int mline;
|
|
|
|
irt_ioctl(tp, TIOCMGET, &mline);
|
|
mline &= ~(TIOCM_DTR | TIOCM_RTS);
|
|
mline |= line;
|
|
irt_ioctl(tp, TIOCMSET, (caddr_t)&mline);
|
|
}
|
|
|
|
void
|
|
irt_delay(struct tty *tp, u_int ms)
|
|
{
|
|
if (cold)
|
|
delay(ms * 1000);
|
|
else
|
|
tsleep(&irt_delay, PZERO, "irtdly", ms * hz / 1000 + 1);
|
|
|
|
}
|
|
|
|
/**********************************************************************
|
|
* No dongle
|
|
**********************************************************************/
|
|
void
|
|
irts_none(struct tty *tp, u_int speed)
|
|
{
|
|
irt_setspeed(tp, speed);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Tekram
|
|
**********************************************************************/
|
|
#define TEKRAM_PW 0x10
|
|
|
|
#define TEKRAM_115200 (TEKRAM_PW|0x00)
|
|
#define TEKRAM_57600 (TEKRAM_PW|0x01)
|
|
#define TEKRAM_38400 (TEKRAM_PW|0x02)
|
|
#define TEKRAM_19200 (TEKRAM_PW|0x03)
|
|
#define TEKRAM_9600 (TEKRAM_PW|0x04)
|
|
#define TEKRAM_2400 (TEKRAM_PW|0x08)
|
|
|
|
#define TEKRAM_TV (TEKRAM_PW|0x05)
|
|
|
|
void
|
|
irts_tekram(struct tty *tp, u_int speed)
|
|
{
|
|
int s;
|
|
|
|
irt_setspeed(tp, 9600);
|
|
irt_setline(tp, 0);
|
|
irt_delay(tp, 50);
|
|
|
|
irt_setline(tp, TIOCM_RTS);
|
|
irt_delay(tp, 1);
|
|
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
irt_delay(tp, 1); /* 50 us */
|
|
|
|
irt_setline(tp, TIOCM_DTR);
|
|
irt_delay(tp, 1); /* 7 us */
|
|
|
|
switch(speed) {
|
|
case 115200: s = TEKRAM_115200; break;
|
|
case 57600: s = TEKRAM_57600; break;
|
|
case 38400: s = TEKRAM_38400; break;
|
|
case 19200: s = TEKRAM_19200; break;
|
|
case 2400: s = TEKRAM_2400; break;
|
|
default: s = TEKRAM_9600; break;
|
|
}
|
|
irt_putc(tp, s);
|
|
irframetstart(tp);
|
|
|
|
irt_delay(tp, 100);
|
|
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
if (speed != 9600)
|
|
irt_setspeed(tp, speed);
|
|
irt_delay(tp, 1); /* 50 us */
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Jeteye
|
|
**********************************************************************/
|
|
void
|
|
irts_jeteye(struct tty *tp, u_int speed)
|
|
{
|
|
switch (speed) {
|
|
case 19200:
|
|
irt_setline(tp, TIOCM_DTR);
|
|
break;
|
|
case 115200:
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
break;
|
|
default: /*9600*/
|
|
irt_setline(tp, TIOCM_RTS);
|
|
break;
|
|
}
|
|
irt_setspeed(tp, speed);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Actisys
|
|
**********************************************************************/
|
|
void
|
|
irts_actisys(struct tty *tp, u_int speed)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int pulses;
|
|
|
|
irt_setspeed(tp, speed);
|
|
|
|
switch(speed) {
|
|
case 19200: pulses=1; break;
|
|
case 57600: pulses=2; break;
|
|
case 115200: pulses=3; break;
|
|
case 38400: pulses=4; break;
|
|
default: /* 9600 */ pulses=0; break;
|
|
}
|
|
|
|
if (sc->sc_dongle_private == 0) {
|
|
sc->sc_dongle_private = 1;
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
/*
|
|
* Must wait at least 50ms after initial
|
|
* power on to charge internal capacitor
|
|
*/
|
|
irt_delay(tp, 50);
|
|
}
|
|
irt_setline(tp, TIOCM_RTS);
|
|
delay(2);
|
|
for (;;) {
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
delay(2);
|
|
if (--pulses <= 0)
|
|
break;
|
|
irt_setline(tp, TIOCM_DTR);
|
|
delay(2);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Litelink
|
|
**********************************************************************/
|
|
void
|
|
irts_litelink(struct tty *tp, u_int speed)
|
|
{
|
|
struct irframet_softc *sc = (struct irframet_softc *)tp->t_sc;
|
|
int pulses;
|
|
|
|
irt_setspeed(tp, speed);
|
|
|
|
switch(speed) {
|
|
case 57600: pulses=1; break;
|
|
case 38400: pulses=2; break;
|
|
case 19200: pulses=3; break;
|
|
case 9600: pulses=4; break;
|
|
default: /* 115200 */ pulses=0; break;
|
|
}
|
|
|
|
if (sc->sc_dongle_private == 0) {
|
|
sc->sc_dongle_private = 1;
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
}
|
|
irt_setline(tp, TIOCM_RTS);
|
|
irt_delay(tp, 1); /* 15 us */;
|
|
for (;;) {
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
irt_delay(tp, 1); /* 15 us */;
|
|
if (--pulses <= 0)
|
|
break;
|
|
irt_setline(tp, TIOCM_DTR);
|
|
irt_delay(tp, 1); /* 15 us */;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Girbil
|
|
**********************************************************************/
|
|
/* Control register 1 */
|
|
#define GIRBIL_TXEN 0x01 /* Enable transmitter */
|
|
#define GIRBIL_RXEN 0x02 /* Enable receiver */
|
|
#define GIRBIL_ECAN 0x04 /* Cancel self emmited data */
|
|
#define GIRBIL_ECHO 0x08 /* Echo control characters */
|
|
|
|
/* LED Current Register */
|
|
#define GIRBIL_HIGH 0x20
|
|
#define GIRBIL_MEDIUM 0x21
|
|
#define GIRBIL_LOW 0x22
|
|
|
|
/* Baud register */
|
|
#define GIRBIL_2400 0x30
|
|
#define GIRBIL_4800 0x31
|
|
#define GIRBIL_9600 0x32
|
|
#define GIRBIL_19200 0x33
|
|
#define GIRBIL_38400 0x34
|
|
#define GIRBIL_57600 0x35
|
|
#define GIRBIL_115200 0x36
|
|
|
|
/* Mode register */
|
|
#define GIRBIL_IRDA 0x40
|
|
#define GIRBIL_ASK 0x41
|
|
|
|
/* Control register 2 */
|
|
#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */
|
|
|
|
void
|
|
irts_girbil(struct tty *tp, u_int speed)
|
|
{
|
|
int s;
|
|
|
|
irt_setspeed(tp, 9600);
|
|
irt_setline(tp, TIOCM_DTR);
|
|
irt_delay(tp, 5);
|
|
irt_setline(tp, TIOCM_RTS);
|
|
irt_delay(tp, 20);
|
|
switch(speed) {
|
|
case 115200: s = GIRBIL_115200; break;
|
|
case 57600: s = GIRBIL_57600; break;
|
|
case 38400: s = GIRBIL_38400; break;
|
|
case 19200: s = GIRBIL_19200; break;
|
|
case 4800: s = GIRBIL_4800; break;
|
|
case 2400: s = GIRBIL_2400; break;
|
|
default: s = GIRBIL_9600; break;
|
|
}
|
|
irt_putc(tp, GIRBIL_TXEN|GIRBIL_RXEN);
|
|
irt_putc(tp, s);
|
|
irt_putc(tp, GIRBIL_LOAD);
|
|
irframetstart(tp);
|
|
irt_delay(tp, 100);
|
|
irt_setline(tp, TIOCM_DTR | TIOCM_RTS);
|
|
if (speed != 9600)
|
|
irt_setspeed(tp, speed);
|
|
}
|