| 1 | /*- |
| 2 | * Copyright (c) 1982, 1986, 1991 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by the University of |
| 16 | * California, Berkeley and its contributors. |
| 17 | * 4. Neither the name of the University nor the names of its contributors |
| 18 | * may be used to endorse or promote products derived from this software |
| 19 | * without specific prior written permission. |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | * SUCH DAMAGE. |
| 32 | * |
| 33 | * from: @(#)tty_tb.c 7.7 (Berkeley) 5/9/91 |
| 34 | * $Id$ |
| 35 | */ |
| 36 | |
| 37 | #include "tb.h" |
| 38 | #if NTB > 0 |
| 39 | |
| 40 | /* |
| 41 | * Line discipline for RS232 tablets; |
| 42 | * supplies binary coordinate data. |
| 43 | */ |
| 44 | #include "param.h" |
| 45 | #include "tablet.h" |
| 46 | #include "tty.h" |
| 47 | |
| 48 | /* |
| 49 | * Tablet configuration table. |
| 50 | */ |
| 51 | struct tbconf { |
| 52 | short tbc_recsize; /* input record size in bytes */ |
| 53 | short tbc_uiosize; /* size of data record returned user */ |
| 54 | int tbc_sync; /* mask for finding sync byte/bit */ |
| 55 | int (*tbc_decode)();/* decoding routine */ |
| 56 | char *tbc_run; /* enter run mode sequence */ |
| 57 | char *tbc_point; /* enter point mode sequence */ |
| 58 | char *tbc_stop; /* stop sequence */ |
| 59 | char *tbc_start; /* start/restart sequence */ |
| 60 | int tbc_flags; |
| 61 | #define TBF_POL 0x1 /* polhemus hack */ |
| 62 | #define TBF_INPROX 0x2 /* tablet has proximity info */ |
| 63 | }; |
| 64 | |
| 65 | static int tbdecode(), gtcodecode(), poldecode(); |
| 66 | static int tblresdecode(), tbhresdecode(); |
| 67 | |
| 68 | struct tbconf tbconf[TBTYPE] = { |
| 69 | { 0 }, |
| 70 | { 5, sizeof (struct tbpos), 0200, tbdecode, "6", "4" }, |
| 71 | { 5, sizeof (struct tbpos), 0200, tbdecode, "\1CN", "\1RT", "\2", "\4" }, |
| 72 | { 8, sizeof (struct gtcopos), 0200, gtcodecode }, |
| 73 | {17, sizeof (struct polpos), 0200, poldecode, 0, 0, "\21", "\5\22\2\23", |
| 74 | TBF_POL }, |
| 75 | { 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CN", "\1PT", "\2", "\4", |
| 76 | TBF_INPROX }, |
| 77 | { 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CN", "\1PT", "\2", "\4", |
| 78 | TBF_INPROX }, |
| 79 | { 5, sizeof (struct tbpos), 0100, tblresdecode, "\1CL\33", "\1PT\33", 0, 0}, |
| 80 | { 6, sizeof (struct tbpos), 0200, tbhresdecode, "\1CL\33", "\1PT\33", 0, 0}, |
| 81 | }; |
| 82 | |
| 83 | /* |
| 84 | * Tablet state |
| 85 | */ |
| 86 | struct tb { |
| 87 | int tbflags; /* mode & type bits */ |
| 88 | #define TBMAXREC 17 /* max input record size */ |
| 89 | char cbuf[TBMAXREC]; /* input buffer */ |
| 90 | union { |
| 91 | struct tbpos tbpos; |
| 92 | struct gtcopos gtcopos; |
| 93 | struct polpos polpos; |
| 94 | } rets; /* processed state */ |
| 95 | #define NTBS 16 |
| 96 | } tb[NTBS]; |
| 97 | |
| 98 | /* |
| 99 | * Open as tablet discipline; called on discipline change. |
| 100 | */ |
| 101 | /*ARGSUSED*/ |
| 102 | tbopen(dev, tp) |
| 103 | dev_t dev; |
| 104 | register struct tty *tp; |
| 105 | { |
| 106 | register struct tb *tbp; |
| 107 | |
| 108 | if (tp->t_line == TABLDISC) |
| 109 | return (ENODEV); |
| 110 | ttywflush(tp); |
| 111 | for (tbp = tb; tbp < &tb[NTBS]; tbp++) |
| 112 | if (tbp->tbflags == 0) |
| 113 | break; |
| 114 | if (tbp >= &tb[NTBS]) |
| 115 | return (EBUSY); |
| 116 | tbp->tbflags = TBTIGER|TBPOINT; /* default */ |
| 117 | tp->t_cp = tbp->cbuf; |
| 118 | tp->t_inbuf = 0; |
| 119 | bzero((caddr_t)&tbp->rets, sizeof (tbp->rets)); |
| 120 | tp->T_LINEP = (caddr_t)tbp; |
| 121 | tp->t_flags = ttcompatgetflags(tp); |
| 122 | tp->t_flags |= LITOUT; |
| 123 | ttcompatsetlflags(tp, &tp->t_termios); |
| 124 | return (0); |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * Line discipline change or last device close. |
| 129 | */ |
| 130 | tbclose(tp) |
| 131 | register struct tty *tp; |
| 132 | { |
| 133 | register int s; |
| 134 | int modebits = TBPOINT|TBSTOP; |
| 135 | |
| 136 | tbioctl(tp, BIOSMODE, &modebits, 0); |
| 137 | s = spltty(); |
| 138 | ((struct tb *)tp->T_LINEP)->tbflags = 0; |
| 139 | tp->t_cp = 0; |
| 140 | tp->t_inbuf = 0; |
| 141 | tp->t_rawq.c_cc = 0; /* clear queues -- paranoid */ |
| 142 | tp->t_canq.c_cc = 0; |
| 143 | tp->t_line = 0; /* paranoid: avoid races */ |
| 144 | splx(s); |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Read from a tablet line. |
| 149 | * Characters have been buffered in a buffer and decoded. |
| 150 | */ |
| 151 | tbread(tp, uio) |
| 152 | register struct tty *tp; |
| 153 | struct uio *uio; |
| 154 | { |
| 155 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 156 | register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 157 | int ret; |
| 158 | |
| 159 | if ((tp->t_state&TS_CARR_ON) == 0) |
| 160 | return (EIO); |
| 161 | ret = uiomove(&tbp->rets, tc->tbc_uiosize, uio); |
| 162 | if (tc->tbc_flags&TBF_POL) |
| 163 | tbp->rets.polpos.p_key = ' '; |
| 164 | return (ret); |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | * Low level character input routine. |
| 169 | * Stuff the character in the buffer, and decode |
| 170 | * if all the chars are there. |
| 171 | * |
| 172 | * This routine could be expanded in-line in the receiver |
| 173 | * interrupt routine to make it run as fast as possible. |
| 174 | */ |
| 175 | tbinput(c, tp) |
| 176 | register int c; |
| 177 | register struct tty *tp; |
| 178 | { |
| 179 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 180 | register struct tbconf *tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 181 | |
| 182 | if (tc->tbc_recsize == 0 || tc->tbc_decode == 0) /* paranoid? */ |
| 183 | return; |
| 184 | /* |
| 185 | * Locate sync bit/byte or reset input buffer. |
| 186 | */ |
| 187 | if (c&tc->tbc_sync || tp->t_inbuf == tc->tbc_recsize) { |
| 188 | tp->t_cp = tbp->cbuf; |
| 189 | tp->t_inbuf = 0; |
| 190 | } |
| 191 | *tp->t_cp++ = c&0177; |
| 192 | /* |
| 193 | * Call decode routine only if a full record has been collected. |
| 194 | */ |
| 195 | if (++tp->t_inbuf == tc->tbc_recsize) |
| 196 | (*tc->tbc_decode)(tc, tbp->cbuf, &tbp->rets); |
| 197 | } |
| 198 | |
| 199 | /* |
| 200 | * Decode GTCO 8 byte format (high res, tilt, and pressure). |
| 201 | */ |
| 202 | static |
| 203 | gtcodecode(tc, cp, tbpos) |
| 204 | struct tbconf *tc; |
| 205 | register char *cp; |
| 206 | register struct gtcopos *tbpos; |
| 207 | { |
| 208 | |
| 209 | tbpos->pressure = *cp >> 2; |
| 210 | tbpos->status = (tbpos->pressure > 16) | TBINPROX; /* half way down */ |
| 211 | tbpos->xpos = (*cp++ & 03) << 14; |
| 212 | tbpos->xpos |= *cp++ << 7; |
| 213 | tbpos->xpos |= *cp++; |
| 214 | tbpos->ypos = (*cp++ & 03) << 14; |
| 215 | tbpos->ypos |= *cp++ << 7; |
| 216 | tbpos->ypos |= *cp++; |
| 217 | tbpos->xtilt = *cp++; |
| 218 | tbpos->ytilt = *cp++; |
| 219 | tbpos->scount++; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | * Decode old Hitachi 5 byte format (low res). |
| 224 | */ |
| 225 | static |
| 226 | tbdecode(tc, cp, tbpos) |
| 227 | struct tbconf *tc; |
| 228 | register char *cp; |
| 229 | register struct tbpos *tbpos; |
| 230 | { |
| 231 | register char byte; |
| 232 | |
| 233 | byte = *cp++; |
| 234 | tbpos->status = (byte&0100) ? TBINPROX : 0; |
| 235 | byte &= ~0100; |
| 236 | if (byte > 036) |
| 237 | tbpos->status |= 1 << ((byte-040)/2); |
| 238 | tbpos->xpos = *cp++ << 7; |
| 239 | tbpos->xpos |= *cp++; |
| 240 | if (tbpos->xpos < 256) /* tablet wraps around at 256 */ |
| 241 | tbpos->status &= ~TBINPROX; /* make it out of proximity */ |
| 242 | tbpos->ypos = *cp++ << 7; |
| 243 | tbpos->ypos |= *cp++; |
| 244 | tbpos->scount++; |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | * Decode new Hitach 5-byte format (low res). |
| 249 | */ |
| 250 | static |
| 251 | tblresdecode(tc, cp, tbpos) |
| 252 | struct tbconf *tc; |
| 253 | register char *cp; |
| 254 | register struct tbpos *tbpos; |
| 255 | { |
| 256 | |
| 257 | *cp &= ~0100; /* mask sync bit */ |
| 258 | tbpos->status = (*cp++ >> 2) | TBINPROX; |
| 259 | if (tc->tbc_flags&TBF_INPROX && tbpos->status&020) |
| 260 | tbpos->status &= ~(020|TBINPROX); |
| 261 | tbpos->xpos = *cp++; |
| 262 | tbpos->xpos |= *cp++ << 6; |
| 263 | tbpos->ypos = *cp++; |
| 264 | tbpos->ypos |= *cp++ << 6; |
| 265 | tbpos->scount++; |
| 266 | } |
| 267 | |
| 268 | /* |
| 269 | * Decode new Hitach 6-byte format (high res). |
| 270 | */ |
| 271 | static |
| 272 | tbhresdecode(tc, cp, tbpos) |
| 273 | struct tbconf *tc; |
| 274 | register char *cp; |
| 275 | register struct tbpos *tbpos; |
| 276 | { |
| 277 | char byte; |
| 278 | |
| 279 | byte = *cp++; |
| 280 | tbpos->xpos = (byte & 03) << 14; |
| 281 | tbpos->xpos |= *cp++ << 7; |
| 282 | tbpos->xpos |= *cp++; |
| 283 | tbpos->ypos = *cp++ << 14; |
| 284 | tbpos->ypos |= *cp++ << 7; |
| 285 | tbpos->ypos |= *cp++; |
| 286 | tbpos->status = (byte >> 2) | TBINPROX; |
| 287 | if (tc->tbc_flags&TBF_INPROX && tbpos->status&020) |
| 288 | tbpos->status &= ~(020|TBINPROX); |
| 289 | tbpos->scount++; |
| 290 | } |
| 291 | |
| 292 | /* |
| 293 | * Polhemus decode. |
| 294 | */ |
| 295 | static |
| 296 | poldecode(tc, cp, polpos) |
| 297 | struct tbconf *tc; |
| 298 | register char *cp; |
| 299 | register struct polpos *polpos; |
| 300 | { |
| 301 | |
| 302 | polpos->p_x = cp[4] | cp[3]<<7 | (cp[9] & 0x03) << 14; |
| 303 | polpos->p_y = cp[6] | cp[5]<<7 | (cp[9] & 0x0c) << 12; |
| 304 | polpos->p_z = cp[8] | cp[7]<<7 | (cp[9] & 0x30) << 10; |
| 305 | polpos->p_azi = cp[11] | cp[10]<<7 | (cp[16] & 0x03) << 14; |
| 306 | polpos->p_pit = cp[13] | cp[12]<<7 | (cp[16] & 0x0c) << 12; |
| 307 | polpos->p_rol = cp[15] | cp[14]<<7 | (cp[16] & 0x30) << 10; |
| 308 | polpos->p_stat = cp[1] | cp[0]<<7; |
| 309 | if (cp[2] != ' ') |
| 310 | polpos->p_key = cp[2]; |
| 311 | } |
| 312 | |
| 313 | /*ARGSUSED*/ |
| 314 | tbioctl(tp, cmd, data, flag) |
| 315 | struct tty *tp; |
| 316 | caddr_t data; |
| 317 | { |
| 318 | register struct tb *tbp = (struct tb *)tp->T_LINEP; |
| 319 | |
| 320 | switch (cmd) { |
| 321 | |
| 322 | case BIOGMODE: |
| 323 | *(int *)data = tbp->tbflags & TBMODE; |
| 324 | break; |
| 325 | |
| 326 | case BIOSTYPE: |
| 327 | if (tbconf[*(int *)data & TBTYPE].tbc_recsize == 0 || |
| 328 | tbconf[*(int *)data & TBTYPE].tbc_decode == 0) |
| 329 | return (EINVAL); |
| 330 | tbp->tbflags &= ~TBTYPE; |
| 331 | tbp->tbflags |= *(int *)data & TBTYPE; |
| 332 | /* fall thru... to set mode bits */ |
| 333 | |
| 334 | case BIOSMODE: { |
| 335 | register struct tbconf *tc; |
| 336 | |
| 337 | tbp->tbflags &= ~TBMODE; |
| 338 | tbp->tbflags |= *(int *)data & TBMODE; |
| 339 | tc = &tbconf[tbp->tbflags & TBTYPE]; |
| 340 | if (tbp->tbflags&TBSTOP) { |
| 341 | if (tc->tbc_stop) |
| 342 | ttyout(tc->tbc_stop, tp); |
| 343 | } else if (tc->tbc_start) |
| 344 | ttyout(tc->tbc_start, tp); |
| 345 | if (tbp->tbflags&TBPOINT) { |
| 346 | if (tc->tbc_point) |
| 347 | ttyout(tc->tbc_point, tp); |
| 348 | } else if (tc->tbc_run) |
| 349 | ttyout(tc->tbc_run, tp); |
| 350 | ttstart(tp); |
| 351 | break; |
| 352 | } |
| 353 | |
| 354 | case BIOGTYPE: |
| 355 | *(int *)data = tbp->tbflags & TBTYPE; |
| 356 | break; |
| 357 | |
| 358 | case TIOCSETD: |
| 359 | case TIOCGETD: |
| 360 | case TIOCGETP: |
| 361 | case TIOCGETC: |
| 362 | return (-1); /* pass thru... */ |
| 363 | |
| 364 | default: |
| 365 | return (ENOTTY); |
| 366 | } |
| 367 | return (0); |
| 368 | } |
| 369 | #endif |