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