| 1 | /*- |
| 2 | * Copyright (c) 1982, 1986, 1991 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * %sccs.include.redist.c% |
| 6 | * |
| 7 | * @(#)tty_tb.c 7.8 (Berkeley) %G% |
| 8 | */ |
| 9 | |
| 10 | #include "tb.h" |
| 11 | #if NTB > 0 |
| 12 | |
| 13 | /* |
| 14 | * Line discipline for RS232 tablets; |
| 15 | * supplies binary coordinate data. |
| 16 | */ |
| 17 | #include <sys/param.h> |
| 18 | #include <sys/tablet.h> |
| 19 | #include <sys/tty.h> |
| 20 | |
| 21 | /* |
| 22 | * Tablet configuration table. |
| 23 | */ |
| 24 | struct tbconf { |
| 25 | short tbc_recsize; /* input record size in bytes */ |
| 26 | short tbc_uiosize; /* size of data record returned user */ |
| 27 | int tbc_sync; /* mask for finding sync byte/bit */ |
| 28 | int (*tbc_decode)();/* decoding routine */ |
| 29 | char *tbc_run; /* enter run mode sequence */ |
| 30 | char *tbc_point; /* enter point mode sequence */ |
| 31 | char *tbc_stop; /* stop sequence */ |
| 32 | char *tbc_start; /* start/restart sequence */ |
| 33 | int tbc_flags; |
| 34 | #define TBF_POL 0x1 /* polhemus hack */ |
| 35 | #define TBF_INPROX 0x2 /* tablet has proximity info */ |
| 36 | }; |
| 37 | |
| 38 | static int tbdecode(), gtcodecode(), poldecode(); |
| 39 | static int tblresdecode(), tbhresdecode(); |
| 40 | |
| 41 | struct tbconf tbconf[TBTYPE] = { |
| 42 | { 0 }, |
| 43 | { 5, sizeof (struct tbpos), 0200, tbdecode, "6", "4" }, |
| 44 | { 5, sizeof (struct tbpos), 0200, tbdecode, "\1CN", "\1RT", "\2", "\4" }, |
| 45 | { 8, sizeof (struct gtcopos), 0200, gtcodecode }, |
| 46 | {17, sizeof (struct polpos), 0200, poldecode, 0, 0, "\21", "\5\22\2\23", |
| 47 | TBF_POL }, |
| 48 | { 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CN", "\1PT", "\2", "\4", |
| 49 | TBF_INPROX }, |
| 50 | { 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CN", "\1PT", "\2", "\4", |
| 51 | TBF_INPROX }, |
| 52 | { 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CL\33", "\1PT\33", 0, 0}, |
| 53 | { 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CL\33", "\1PT\33", 0, 0}, |
| 54 | }; |
| 55 | |
| 56 | /* |
| 57 | * Tablet state |
| 58 | */ |
| 59 | struct tb { |
| 60 | int tbflags; /* mode & type bits */ |
| 61 | #define TBMAXREC 17 /* max input record size */ |
| 62 | char cbuf[TBMAXREC]; /* input buffer */ |
| 63 | union { |
| 64 | struct tbpos tbpos; |
| 65 | struct gtcopos gtcopos; |
| 66 | struct polpos polpos; |
| 67 | } rets; /* processed state */ |
| 68 | #define NTBS 16 |
| 69 | } tb[NTBS]; |
| 70 | |
| 71 | /* |
| 72 | * Open as tablet discipline; called on discipline change. |
| 73 | */ |
| 74 | /*ARGSUSED*/ |
| 75 | tbopen(dev, tp) |
| 76 | dev_t dev; |
| 77 | register struct tty *tp; |
| 78 | { |
| 79 | register struct tb *tbp; |
| 80 | |
| 81 | if (tp->t_line == TABLDISC) |
| 82 | return (ENODEV); |
| 83 | ttywflush(tp); |
| 84 | for (tbp = tb; tbp < &tb[NTBS]; tbp++) |
| 85 | if (tbp->tbflags == 0) |
| 86 | break; |
| 87 | if (tbp >= &tb[NTBS]) |
| 88 | return (EBUSY); |
| 89 | tbp->tbflags = TBTIGER|TBPOINT; /* default */ |
| 90 | tp->t_cp = tbp->cbuf; |
| 91 | tp->t_inbuf = 0; |
| 92 | bzero((caddr_t)&tbp->rets, sizeof (tbp->rets)); |
| 93 | tp->T_LINEP = (caddr_t)tbp; |
| 94 | tp->t_flags |= LITOUT; |
| 95 | return (0); |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | * Line discipline change or last device close. |
| 100 | */ |
| 101 | tbclose(tp) |
| 102 | register struct tty *tp; |
| 103 | { |
| 104 | register int s; |
| 105 | int modebits = TBPOINT|TBSTOP; |
| 106 | |
| 107 | tbioctl(tp, BIOSMODE, &modebits, 0); |
| 108 | s = spltty(); |
| 109 | ((struct tb *)tp->T_LINEP)->tbflags = 0; |
| 110 | tp->t_cp = 0; |
| 111 | tp->t_inbuf = 0; |
| 112 | tp->t_rawq.c_cc = 0; /* clear queues -- paranoid */ |
| 113 | tp->t_canq.c_cc = 0; |
| 114 | tp->t_line = 0; /* paranoid: avoid races */ |
| 115 | splx(s); |
| 116 | } |
| 117 | |
| 118 | /* |
| 119 | * Read from a tablet line. |
| 120 | * Characters have been buffered in a buffer and decoded. |
| 121 | */ |
| 122 | tbread(tp, uio) |
| 123 | register struct tty *tp; |
| 124 | struct uio *uio; |
| 125 | { |
| 126 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 127 | register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 128 | int ret; |
| 129 | |
| 130 | if ((tp->t_state&TS_CARR_ON) == 0) |
| 131 | return (EIO); |
| 132 | ret = uiomove(&tbp->rets, tc->tbc_uiosize, uio); |
| 133 | if (tc->tbc_flags&TBF_POL) |
| 134 | tbp->rets.polpos.p_key = ' '; |
| 135 | return (ret); |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | * Low level character input routine. |
| 140 | * Stuff the character in the buffer, and decode |
| 141 | * if all the chars are there. |
| 142 | * |
| 143 | * This routine could be expanded in-line in the receiver |
| 144 | * interrupt routine to make it run as fast as possible. |
| 145 | */ |
| 146 | tbinput(c, tp) |
| 147 | register int c; |
| 148 | register struct tty *tp; |
| 149 | { |
| 150 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 151 | register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 152 | |
| 153 | if (tc->tbc_recsize == 0 || tc->tbc_decode == 0) /* paranoid? */ |
| 154 | return; |
| 155 | /* |
| 156 | * Locate sync bit/byte or reset input buffer. |
| 157 | */ |
| 158 | if (c&tc->tbc_sync || tp->t_inbuf == tc->tbc_recsize) { |
| 159 | tp->t_cp = tbp->cbuf; |
| 160 | tp->t_inbuf = 0; |
| 161 | } |
| 162 | *tp->t_cp++ = c&0177; |
| 163 | /* |
| 164 | * Call decode routine only if a full record has been collected. |
| 165 | */ |
| 166 | if (++tp->t_inbuf == tc->tbc_recsize) |
| 167 | (*tc->tbc_decode)(tc, tbp->cbuf, &tbp->rets); |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Decode GTCO 8 byte format (high res, tilt, and pressure). |
| 172 | */ |
| 173 | static |
| 174 | gtcodecode(tc, cp, tbpos) |
| 175 | struct tbconf *tc; |
| 176 | register char *cp; |
| 177 | register struct gtcopos *tbpos; |
| 178 | { |
| 179 | |
| 180 | tbpos->pressure = *cp >> 2; |
| 181 | tbpos->status = (tbpos->pressure > 16) | TBINPROX; /* half way down */ |
| 182 | tbpos->xpos = (*cp++ & 03) << 14; |
| 183 | tbpos->xpos |= *cp++ << 7; |
| 184 | tbpos->xpos |= *cp++; |
| 185 | tbpos->ypos = (*cp++ & 03) << 14; |
| 186 | tbpos->ypos |= *cp++ << 7; |
| 187 | tbpos->ypos |= *cp++; |
| 188 | tbpos->xtilt = *cp++; |
| 189 | tbpos->ytilt = *cp++; |
| 190 | tbpos->scount++; |
| 191 | } |
| 192 | |
| 193 | /* |
| 194 | * Decode old Hitachi 5 byte format (low res). |
| 195 | */ |
| 196 | static |
| 197 | tbdecode(tc, cp, tbpos) |
| 198 | struct tbconf *tc; |
| 199 | register char *cp; |
| 200 | register struct tbpos *tbpos; |
| 201 | { |
| 202 | register char byte; |
| 203 | |
| 204 | byte = *cp++; |
| 205 | tbpos->status = (byte&0100) ? TBINPROX : 0; |
| 206 | byte &= ~0100; |
| 207 | if (byte > 036) |
| 208 | tbpos->status |= 1 << ((byte-040)/2); |
| 209 | tbpos->xpos = *cp++ << 7; |
| 210 | tbpos->xpos |= *cp++; |
| 211 | if (tbpos->xpos < 256) /* tablet wraps around at 256 */ |
| 212 | tbpos->status &= ~TBINPROX; /* make it out of proximity */ |
| 213 | tbpos->ypos = *cp++ << 7; |
| 214 | tbpos->ypos |= *cp++; |
| 215 | tbpos->scount++; |
| 216 | } |
| 217 | |
| 218 | /* |
| 219 | * Decode new Hitach 5-byte format (low res). |
| 220 | */ |
| 221 | static |
| 222 | tblresdecode(tc, cp, tbpos) |
| 223 | struct tbconf *tc; |
| 224 | register char *cp; |
| 225 | register struct tbpos *tbpos; |
| 226 | { |
| 227 | |
| 228 | *cp &= ~0100; /* mask sync bit */ |
| 229 | tbpos->status = (*cp++ >> 2) | TBINPROX; |
| 230 | if (tc->tbc_flags&TBF_INPROX && tbpos->status&020) |
| 231 | tbpos->status &= ~(020|TBINPROX); |
| 232 | tbpos->xpos = *cp++; |
| 233 | tbpos->xpos |= *cp++ << 6; |
| 234 | tbpos->ypos = *cp++; |
| 235 | tbpos->ypos |= *cp++ << 6; |
| 236 | tbpos->scount++; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * Decode new Hitach 6-byte format (high res). |
| 241 | */ |
| 242 | static |
| 243 | tbhresdecode(tc, cp, tbpos) |
| 244 | struct tbconf *tc; |
| 245 | register char *cp; |
| 246 | register struct tbpos *tbpos; |
| 247 | { |
| 248 | char byte; |
| 249 | |
| 250 | byte = *cp++; |
| 251 | tbpos->xpos = (byte & 03) << 14; |
| 252 | tbpos->xpos |= *cp++ << 7; |
| 253 | tbpos->xpos |= *cp++; |
| 254 | tbpos->ypos = *cp++ << 14; |
| 255 | tbpos->ypos |= *cp++ << 7; |
| 256 | tbpos->ypos |= *cp++; |
| 257 | tbpos->status = (byte >> 2) | TBINPROX; |
| 258 | if (tc->tbc_flags&TBF_INPROX && tbpos->status&020) |
| 259 | tbpos->status &= ~(020|TBINPROX); |
| 260 | tbpos->scount++; |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | * Polhemus decode. |
| 265 | */ |
| 266 | static |
| 267 | poldecode(tc, cp, polpos) |
| 268 | struct tbconf *tc; |
| 269 | register char *cp; |
| 270 | register struct polpos *polpos; |
| 271 | { |
| 272 | |
| 273 | polpos->p_x = cp[4] | cp[3]<<7 | (cp[9] & 0x03) << 14; |
| 274 | polpos->p_y = cp[6] | cp[5]<<7 | (cp[9] & 0x0c) << 12; |
| 275 | polpos->p_z = cp[8] | cp[7]<<7 | (cp[9] & 0x30) << 10; |
| 276 | polpos->p_azi = cp[11] | cp[10]<<7 | (cp[16] & 0x03) << 14; |
| 277 | polpos->p_pit = cp[13] | cp[12]<<7 | (cp[16] & 0x0c) << 12; |
| 278 | polpos->p_rol = cp[15] | cp[14]<<7 | (cp[16] & 0x30) << 10; |
| 279 | polpos->p_stat = cp[1] | cp[0]<<7; |
| 280 | if (cp[2] != ' ') |
| 281 | polpos->p_key = cp[2]; |
| 282 | } |
| 283 | |
| 284 | /*ARGSUSED*/ |
| 285 | tbioctl(tp, cmd, data, flag) |
| 286 | struct tty *tp; |
| 287 | caddr_t data; |
| 288 | { |
| 289 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 290 | |
| 291 | switch (cmd) { |
| 292 | |
| 293 | case BIOGMODE: |
| 294 | *(int *)data = tbp->tbflags & TBMODE; |
| 295 | break; |
| 296 | |
| 297 | case BIOSTYPE: |
| 298 | if (tbconf[*(int *)data & TBTYPE].tbc_recsize == 0 || |
| 299 | tbconf[*(int *)data & TBTYPE].tbc_decode == 0) |
| 300 | return (EINVAL); |
| 301 | tbp->tbflags &= ~TBTYPE; |
| 302 | tbp->tbflags |= *(int *)data & TBTYPE; |
| 303 | /* fall thru... to set mode bits */ |
| 304 | |
| 305 | case BIOSMODE: { |
| 306 | register struct tbconf *tc; |
| 307 | |
| 308 | tbp->tbflags &= ~TBMODE; |
| 309 | tbp->tbflags |= *(int *)data & TBMODE; |
| 310 | tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 311 | if (tbp->tbflags&TBSTOP) { |
| 312 | if (tc->tbc_stop) |
| 313 | ttyout(tc->tbc_stop, tp); |
| 314 | } else if (tc->tbc_start) |
| 315 | ttyout(tc->tbc_start, tp); |
| 316 | if (tbp->tbflags&TBPOINT) { |
| 317 | if (tc->tbc_point) |
| 318 | ttyout(tc->tbc_point, tp); |
| 319 | } else if (tc->tbc_run) |
| 320 | ttyout(tc->tbc_run, tp); |
| 321 | ttstart(tp); |
| 322 | break; |
| 323 | } |
| 324 | |
| 325 | case BIOGTYPE: |
| 326 | *(int *)data = tbp->tbflags & TBTYPE; |
| 327 | break; |
| 328 | |
| 329 | case TIOCSETD: |
| 330 | case TIOCGETD: |
| 331 | case TIOCGETP: |
| 332 | case TIOCGETC: |
| 333 | return (-1); /* pass thru... */ |
| 334 | |
| 335 | default: |
| 336 | return (ENOTTY); |
| 337 | } |
| 338 | return (0); |
| 339 | } |
| 340 | #endif |