7964fb5edb214c3ab360980ae5c3d66a7f5e64b9
[games.git] / sys / dev / misc / snp / snp.c
1 /*
2  * Copyright (c) 1995 Ugen J.S.Antsilevich
3  *
4  * Redistribution and use in source forms, with and without modification,
5  * are permitted provided that this entire comment appears intact.
6  *
7  * Redistribution in binary form may occur without any restrictions.
8  * Obviously, it would be nice if you gave credit where credit is due
9  * but requiring it would be too onerous.
10  *
11  * This software is provided ``AS IS'' without any warranties of any kind.
12  *
13  * Snoop stuff.
14  *
15  * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $
16  * $DragonFly: src/sys/dev/misc/snp/snp.c,v 1.18 2006/12/22 23:26:18 swildner Exp $
17  */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/filio.h>
22 #include <sys/malloc.h>
23 #include <sys/tty.h>
24 #include <sys/conf.h>
25 #include <sys/poll.h>
26 #include <sys/kernel.h>
27 #include <sys/queue.h>
28 #include <sys/snoop.h>
29 #include <sys/thread2.h>
30 #include <sys/vnode.h>
31 #include <sys/device.h>
32
33 static  l_close_t       snplclose;
34 static  l_write_t       snplwrite;
35 static  d_open_t        snpopen;
36 static  d_close_t       snpclose;
37 static  d_read_t        snpread;
38 static  d_write_t       snpwrite;
39 static  d_ioctl_t       snpioctl;
40 static  d_poll_t        snppoll;
41
42 #define CDEV_MAJOR 53
43 static struct dev_ops snp_ops = {
44         { "snp", CDEV_MAJOR, 0 },
45         .d_open =       snpopen,
46         .d_close =      snpclose,
47         .d_read =       snpread,
48         .d_write =      snpwrite,
49         .d_ioctl =      snpioctl,
50         .d_poll =       snppoll,
51 };
52
53 static struct linesw snpdisc = {
54         ttyopen,        snplclose,      ttread,         snplwrite,
55         l_nullioctl,    ttyinput,       ttstart,        ttymodem
56 };
57
58 /*
59  * This is the main snoop per-device structure.
60  */
61 struct snoop {
62         LIST_ENTRY(snoop)       snp_list;       /* List glue. */
63         cdev_t                  snp_target;     /* Target tty device. */
64         struct tty              *snp_tty;       /* Target tty pointer. */
65         u_long                   snp_len;       /* Possible length. */
66         u_long                   snp_base;      /* Data base. */
67         u_long                   snp_blen;      /* Used length. */
68         caddr_t                  snp_buf;       /* Allocation pointer. */
69         int                      snp_flags;     /* Flags. */
70         struct selinfo           snp_sel;       /* Select info. */
71         int                      snp_olddisc;   /* Old line discipline. */
72 };
73
74 /*
75  * Possible flags.
76  */
77 #define SNOOP_ASYNC             0x0002
78 #define SNOOP_OPEN              0x0004
79 #define SNOOP_RWAIT             0x0008
80 #define SNOOP_OFLOW             0x0010
81 #define SNOOP_DOWN              0x0020
82
83 /*
84  * Other constants.
85  */
86 #define SNOOP_MINLEN            (4*1024)        /* This should be power of 2.
87                                                  * 4K tested to be the minimum
88                                                  * for which on normal tty
89                                                  * usage there is no need to
90                                                  * allocate more.
91                                                  */
92 #define SNOOP_MAXLEN            (64*1024)       /* This one also,64K enough
93                                                  * If we grow more,something
94                                                  * really bad in this world..
95                                                  */
96
97 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
98 /*
99  * The number of the "snoop" line discipline.  This gets determined at
100  * module load time.
101  */
102 static int snooplinedisc;
103
104
105 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
106
107 static struct tty       *snpdevtotty (cdev_t dev);
108 static int              snp_detach (struct snoop *snp);
109 static int              snp_down (struct snoop *snp);
110 static int              snp_in (struct snoop *snp, char *buf, int n);
111 static int              snp_modevent (module_t mod, int what, void *arg);
112
113 static int
114 snplclose(struct tty *tp, int flag)
115 {
116         struct snoop *snp;
117         int error;
118
119         snp = tp->t_sc;
120         error = snp_down(snp);
121         if (error != 0)
122                 return (error);
123         error = ttylclose(tp, flag);
124         return (error);
125 }
126
127 static int
128 snplwrite(struct tty *tp, struct uio *uio, int flag)
129 {
130         struct iovec iov;
131         struct uio uio2;
132         struct snoop *snp;
133         int error, ilen;
134         char *ibuf;
135
136         error = 0;
137         ibuf = NULL;
138         snp = tp->t_sc;
139         while (uio->uio_resid > 0) {
140                 ilen = imin(512, uio->uio_resid);
141                 ibuf = kmalloc(ilen, M_SNP, M_WAITOK);
142                 error = uiomove(ibuf, ilen, uio);
143                 if (error != 0)
144                         break;
145                 snp_in(snp, ibuf, ilen);
146                 /* Hackish, but probably the least of all evils. */
147                 iov.iov_base = ibuf;
148                 iov.iov_len = ilen;
149                 uio2.uio_iov = &iov;
150                 uio2.uio_iovcnt = 1;
151                 uio2.uio_offset = 0;
152                 uio2.uio_resid = ilen;
153                 uio2.uio_segflg = UIO_SYSSPACE;
154                 uio2.uio_rw = UIO_WRITE;
155                 uio2.uio_td = uio->uio_td;
156                 error = ttwrite(tp, &uio2, flag);
157                 if (error != 0)
158                         break;
159                 kfree(ibuf, M_SNP);
160                 ibuf = NULL;
161         }
162         if (ibuf != NULL)
163                 kfree(ibuf, M_SNP);
164         return (error);
165 }
166
167 static struct tty *
168 snpdevtotty(cdev_t dev)
169 {
170         if ((dev_dflags(dev) & D_TTY) == 0)
171                 return (NULL);
172         return (dev->si_tty);
173 }
174
175 #define SNP_INPUT_BUF   5       /* This is even too much, the maximal
176                                  * interactive mode write is 3 bytes
177                                  * length for function keys...
178                                  */
179
180 static int
181 snpwrite(struct dev_write_args *ap)
182 {
183         cdev_t dev = ap->a_head.a_dev;
184         struct uio *uio = ap->a_uio;
185         struct snoop *snp;
186         struct tty *tp;
187         int error, i, len;
188         unsigned char c[SNP_INPUT_BUF];
189
190         snp = dev->si_drv1;
191         tp = snp->snp_tty;
192         if (tp == NULL)
193                 return (EIO);
194         if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
195             tp->t_line == snooplinedisc)
196                 goto tty_input;
197
198         kprintf("Snoop: attempt to write to bad tty.\n");
199         return (EIO);
200
201 tty_input:
202         if (!(tp->t_state & TS_ISOPEN))
203                 return (EIO);
204
205         while (uio->uio_resid > 0) {
206                 len = imin(uio->uio_resid, SNP_INPUT_BUF);
207                 if ((error = uiomove(c, len, uio)) != 0)
208                         return (error);
209                 for (i=0; i < len; i++) {
210                         if (ttyinput(c[i], tp))
211                                 return (EIO);
212                 }
213         }
214         return (0);
215 }
216
217
218 static int
219 snpread(struct dev_read_args *ap)
220 {
221         cdev_t dev = ap->a_head.a_dev;
222         struct uio *uio = ap->a_uio;
223         struct snoop *snp;
224         int error, len, n, nblen;
225         caddr_t from;
226         char *nbuf;
227
228         snp = dev->si_drv1;
229         KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
230             ("snoop buffer error"));
231
232         if (snp->snp_tty == NULL)
233                 return (EIO);
234
235         snp->snp_flags &= ~SNOOP_RWAIT;
236
237         do {
238                 if (snp->snp_len == 0) {
239                         if (ap->a_ioflag & IO_NDELAY)
240                                 return (EWOULDBLOCK);
241                         snp->snp_flags |= SNOOP_RWAIT;
242                         error = tsleep((caddr_t)snp, PCATCH, "snprd", 0);
243                         if (error != 0)
244                                 return (error);
245                 }
246         } while (snp->snp_len == 0);
247
248         n = snp->snp_len;
249
250         error = 0;
251         while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
252                 len = min((unsigned)uio->uio_resid, snp->snp_len);
253                 from = (caddr_t)(snp->snp_buf + snp->snp_base);
254                 if (len == 0)
255                         break;
256
257                 error = uiomove(from, len, uio);
258                 snp->snp_base += len;
259                 snp->snp_len -= len;
260         }
261         if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
262                 snp->snp_flags &= ~SNOOP_OFLOW;
263         }
264         crit_enter();
265         nblen = snp->snp_blen;
266         if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
267                 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
268                         nblen = nblen / 2;
269                 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
270                         bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
271                         kfree(snp->snp_buf, M_SNP);
272                         snp->snp_buf = nbuf;
273                         snp->snp_blen = nblen;
274                         snp->snp_base = 0;
275                 }
276         }
277         crit_exit();
278
279         return (error);
280 }
281
282 static int
283 snp_in(struct snoop *snp, char *buf, int n)
284 {
285         int s_free, s_tail;
286         int len, nblen;
287         caddr_t from, to;
288         char *nbuf;
289
290         KASSERT(n >= 0, ("negative snoop char count"));
291
292         if (n == 0)
293                 return (0);
294
295         if (snp->snp_flags & SNOOP_DOWN) {
296                 kprintf("Snoop: more data to down interface.\n");
297                 return (0);
298         }
299
300         if (snp->snp_flags & SNOOP_OFLOW) {
301                 kprintf("Snoop: buffer overflow.\n");
302                 /*
303                  * On overflow we just repeat the standart close
304                  * procedure...yes , this is waste of space but.. Then next
305                  * read from device will fail if one would recall he is
306                  * snooping and retry...
307                  */
308
309                 return (snp_down(snp));
310         }
311         s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
312         s_free = snp->snp_blen - snp->snp_len;
313
314
315         if (n > s_free) {
316                 crit_enter();
317                 nblen = snp->snp_blen;
318                 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
319                         nblen = snp->snp_blen * 2;
320                         s_free = nblen - (snp->snp_len + snp->snp_base);
321                 }
322                 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) {
323                         bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
324                         kfree(snp->snp_buf, M_SNP);
325                         snp->snp_buf = nbuf;
326                         snp->snp_blen = nblen;
327                         snp->snp_base = 0;
328                 } else {
329                         snp->snp_flags |= SNOOP_OFLOW;
330                         if (snp->snp_flags & SNOOP_RWAIT) {
331                                 snp->snp_flags &= ~SNOOP_RWAIT;
332                                 wakeup((caddr_t)snp);
333                         }
334                         crit_exit();
335                         return (0);
336                 }
337                 crit_exit();
338         }
339         if (n > s_tail) {
340                 from = (caddr_t)(snp->snp_buf + snp->snp_base);
341                 to = (caddr_t)(snp->snp_buf);
342                 len = snp->snp_len;
343                 bcopy(from, to, len);
344                 snp->snp_base = 0;
345         }
346         to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
347         bcopy(buf, to, n);
348         snp->snp_len += n;
349
350         if (snp->snp_flags & SNOOP_RWAIT) {
351                 snp->snp_flags &= ~SNOOP_RWAIT;
352                 wakeup((caddr_t)snp);
353         }
354         selwakeup(&snp->snp_sel);
355         snp->snp_sel.si_pid = 0;
356
357         return (n);
358 }
359
360 static int
361 snpopen(struct dev_open_args *ap)
362 {
363         cdev_t dev = ap->a_head.a_dev;
364         struct snoop *snp;
365
366         if (dev->si_drv1 == NULL) {
367                 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL,
368                     0600, "snp%d", minor(dev));
369                 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP,
370                     M_WAITOK | M_ZERO);
371         } else {
372                 return (EBUSY);
373         }
374
375         /*
376          * We intentionally do not OR flags with SNOOP_OPEN, but set them so
377          * all previous settings (especially SNOOP_OFLOW) will be cleared.
378          */
379         snp->snp_flags = SNOOP_OPEN;
380
381         snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
382         snp->snp_blen = SNOOP_MINLEN;
383         snp->snp_base = 0;
384         snp->snp_len = 0;
385
386         /*
387          * snp_tty == NULL  is for inactive snoop devices.
388          */
389         snp->snp_tty = NULL;
390         snp->snp_target = NOCDEV;
391
392         LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
393         return (0);
394 }
395
396
397 static int
398 snp_detach(struct snoop *snp)
399 {
400         struct tty *tp;
401
402         snp->snp_base = 0;
403         snp->snp_len = 0;
404
405         /*
406          * If line disc. changed we do not touch this pointer, SLIP/PPP will
407          * change it anyway.
408          */
409         tp = snp->snp_tty;
410         if (tp == NULL)
411                 goto detach_notty;
412
413         if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
414             tp->t_line == snooplinedisc) {
415                 tp->t_sc = NULL;
416                 tp->t_state &= ~TS_SNOOP;
417                 tp->t_line = snp->snp_olddisc;
418         } else
419                 kprintf("Snoop: bad attached tty data.\n");
420
421         snp->snp_tty = NULL;
422         snp->snp_target = NOCDEV;
423
424 detach_notty:
425         selwakeup(&snp->snp_sel);
426         snp->snp_sel.si_pid = 0;
427         if ((snp->snp_flags & SNOOP_OPEN) == 0) 
428                 kfree(snp, M_SNP);
429
430         return (0);
431 }
432
433 static int
434 snpclose(struct dev_close_args *ap)
435 {
436         cdev_t dev = ap->a_head.a_dev;
437         struct snoop *snp;
438
439         snp = dev->si_drv1;
440         snp->snp_blen = 0;
441         LIST_REMOVE(snp, snp_list);
442         kfree(snp->snp_buf, M_SNP);
443         snp->snp_flags &= ~SNOOP_OPEN;
444         dev->si_drv1 = NULL;
445
446         return (snp_detach(snp));
447 }
448
449 static int
450 snp_down(struct snoop *snp)
451 {
452
453         if (snp->snp_blen != SNOOP_MINLEN) {
454                 kfree(snp->snp_buf, M_SNP);
455                 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
456                 snp->snp_blen = SNOOP_MINLEN;
457         }
458         snp->snp_flags |= SNOOP_DOWN;
459
460         return (snp_detach(snp));
461 }
462
463 static int
464 snpioctl(struct dev_ioctl_args *ap)
465 {
466         cdev_t dev = ap->a_head.a_dev;
467         struct snoop *snp;
468         struct tty *tp, *tpo;
469         cdev_t tdev;
470
471         snp = dev->si_drv1;
472         switch (ap->a_cmd) {
473         case SNPSTTY:
474                 tdev = udev2dev(*((udev_t *)ap->a_data), 0);
475                 if (tdev == NOCDEV)
476                         return (snp_down(snp));
477
478                 tp = snpdevtotty(tdev);
479                 if (!tp)
480                         return (EINVAL);
481                 if (tp->t_state & TS_SNOOP)
482                         return (EBUSY);
483
484                 crit_enter();
485
486                 if (snp->snp_target == NOCDEV) {
487                         tpo = snp->snp_tty;
488                         if (tpo)
489                                 tpo->t_state &= ~TS_SNOOP;
490                 }
491
492                 tp->t_sc = (caddr_t)snp;
493                 tp->t_state |= TS_SNOOP;
494                 snp->snp_olddisc = tp->t_line;
495                 tp->t_line = snooplinedisc;
496                 snp->snp_tty = tp;
497                 snp->snp_target = tdev;
498
499                 /*
500                  * Clean overflow and down flags -
501                  * we'll have a chance to get them in the future :)))
502                  */
503                 snp->snp_flags &= ~SNOOP_OFLOW;
504                 snp->snp_flags &= ~SNOOP_DOWN;
505                 crit_exit();
506                 break;
507
508         case SNPGTTY:
509                 /*
510                  * We keep snp_target field specially to make
511                  * SNPGTTY happy, else we can't know what is device
512                  * major/minor for tty.
513                  */
514                 *((cdev_t *)ap->a_data) = snp->snp_target;
515                 break;
516
517         case FIOASYNC:
518                 if (*(int *)ap->a_data)
519                         snp->snp_flags |= SNOOP_ASYNC;
520                 else
521                         snp->snp_flags &= ~SNOOP_ASYNC;
522                 break;
523
524         case FIONREAD:
525                 crit_enter();
526                 if (snp->snp_tty != NULL)
527                         *(int *)ap->a_data = snp->snp_len;
528                 else
529                         if (snp->snp_flags & SNOOP_DOWN) {
530                                 if (snp->snp_flags & SNOOP_OFLOW)
531                                         *(int *)ap->a_data = SNP_OFLOW;
532                                 else
533                                         *(int *)ap->a_data = SNP_TTYCLOSE;
534                         } else {
535                                 *(int *)ap->a_data = SNP_DETACH;
536                         }
537                 crit_exit();
538                 break;
539
540         default:
541                 return (ENOTTY);
542         }
543         return (0);
544 }
545
546 static int
547 snppoll(struct dev_poll_args *ap)
548 {
549         cdev_t dev = ap->a_head.a_dev;
550         struct snoop *snp;
551         int revents;
552
553         snp = dev->si_drv1;
554         revents = 0;
555         /*
556          * If snoop is down, we don't want to poll() forever so we return 1.
557          * Caller should see if we down via FIONREAD ioctl().  The last should
558          * return -1 to indicate down state.
559          */
560         if (ap->a_events & (POLLIN | POLLRDNORM)) {
561                 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
562                         revents |= ap->a_events & (POLLIN | POLLRDNORM);
563                 else
564                         selrecord(curthread, &snp->snp_sel);
565         }
566         ap->a_events = revents;
567         return (0);
568 }
569
570 static int
571 snp_modevent(module_t mod, int type, void *data)
572 {
573
574         switch (type) {
575         case MOD_LOAD:
576                 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
577                 dev_ops_add(&snp_ops, 0, 0);
578                 break;
579         case MOD_UNLOAD:
580                 if (!LIST_EMPTY(&snp_sclist))
581                         return (EBUSY);
582                 ldisc_deregister(snooplinedisc);
583                 dev_ops_remove(&snp_ops, 0, 0);
584                 break;
585         default:
586                 break;
587         }
588         return (0);
589 }
590
591 static moduledata_t snp_mod = {
592         "snp",
593         snp_modevent,
594         NULL
595 };
596 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR);