ng_h4.c revision 109623
1/* 2 * ng_h4.c 3 * 4 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $Id: ng_h4.c,v 1.25 2002/11/03 02:17:31 max Exp $ 29 * $FreeBSD: head/sys/netgraph/bluetooth/drivers/h4/ng_h4.c 109623 2003-01-21 08:56:16Z alfred $ 30 * 31 * Based on: 32 * --------- 33 * 34 * FreeBSD: src/sys/netgraph/ng_tty.c 35 * Author: Archie Cobbs <archie@freebsd.org> 36 * 37 */ 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/conf.h> 43#include <sys/endian.h> 44#include <sys/errno.h> 45#include <sys/fcntl.h> 46#include <sys/ioccom.h> 47#include <sys/malloc.h> 48#include <sys/mbuf.h> 49#include <sys/tty.h> 50#include <sys/ttycom.h> 51#include <netgraph/ng_message.h> 52#include <netgraph/netgraph.h> 53#include <netgraph/ng_parse.h> 54#include <ng_bluetooth.h> 55#include <ng_hci.h> 56#include "ng_h4.h" 57#include "ng_h4_var.h" 58#include "ng_h4_prse.h" 59 60/***************************************************************************** 61 ***************************************************************************** 62 ** This node implements a Bluetooth HCI UART transport layer as per chapter 63 ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line 64 ** discipline that is also a netgraph node. Installing this line discipline 65 ** on a terminal device instantiates a new netgraph node of this type, which 66 ** allows access to the device via the "hook" hook of the node. 67 ** 68 ** Once the line discipline is installed, you can find out the name of the 69 ** corresponding netgraph node via a NGIOCGINFO ioctl(). 70 ***************************************************************************** 71 *****************************************************************************/ 72 73/* MALLOC define */ 74#ifndef NG_SEPARATE_MALLOC 75MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node"); 76#else 77#define M_NETGRAPH_H4 M_NETGRAPH 78#endif /* NG_SEPARATE_MALLOC */ 79 80/* Line discipline methods */ 81static int ng_h4_open (dev_t, struct tty *); 82static int ng_h4_close (struct tty *, int); 83static int ng_h4_read (struct tty *, struct uio *, int); 84static int ng_h4_write (struct tty *, struct uio *, int); 85static int ng_h4_input (int, struct tty *); 86static int ng_h4_start (struct tty *); 87static void ng_h4_start2 (node_p, hook_p, void *, int); 88static int ng_h4_ioctl (struct tty *, u_long, caddr_t, 89 int, struct thread *); 90 91/* Line discipline descriptor */ 92static struct linesw ng_h4_disc = { 93 ng_h4_open, /* open */ 94 ng_h4_close, /* close */ 95 ng_h4_read, /* read */ 96 ng_h4_write, /* write */ 97 ng_h4_ioctl, /* ioctl */ 98 ng_h4_input, /* input */ 99 ng_h4_start, /* start */ 100 ttymodem, /* modem */ 101 0 /* hotchar (don't really care which one) */ 102}; 103 104/* Netgraph methods */ 105static ng_constructor_t ng_h4_constructor; 106static ng_rcvmsg_t ng_h4_rcvmsg; 107static ng_shutdown_t ng_h4_shutdown; 108static ng_newhook_t ng_h4_newhook; 109static ng_connect_t ng_h4_connect; 110static ng_rcvdata_t ng_h4_rcvdata; 111static ng_disconnect_t ng_h4_disconnect; 112 113/* Other stuff */ 114static void ng_h4_timeout (node_p); 115static void ng_h4_untimeout (node_p); 116static void ng_h4_queue_timeout (void *); 117static void ng_h4_process_timeout (node_p, hook_p, void *, int); 118static int ng_h4_mod_event (module_t, int, void *); 119 120/* Netgraph node type descriptor */ 121static struct ng_type typestruct = { 122 NG_ABI_VERSION, 123 NG_H4_NODE_TYPE, /* typename */ 124 ng_h4_mod_event, /* modevent */ 125 ng_h4_constructor, /* constructor */ 126 ng_h4_rcvmsg, /* control message */ 127 ng_h4_shutdown, /* destructor */ 128 ng_h4_newhook, /* new hook */ 129 NULL, /* find hook */ 130 ng_h4_connect, /* connect hook */ 131 ng_h4_rcvdata, /* data */ 132 ng_h4_disconnect, /* disconnect hook */ 133 ng_h4_cmdlist /* node command list */ 134}; 135NETGRAPH_INIT(h4, &typestruct); 136MODULE_VERSION(ng_h4, NG_BLUETOOTH_VERSION); 137 138static int ng_h4_node = 0; 139 140/***************************************************************************** 141 ***************************************************************************** 142 ** Line discipline methods 143 ***************************************************************************** 144 *****************************************************************************/ 145 146/* 147 * Set our line discipline on the tty. 148 */ 149 150static int 151ng_h4_open(dev_t dev, struct tty *tp) 152{ 153 char name[NG_NODELEN + 1]; 154 ng_h4_info_p sc = NULL; 155 int s, error; 156 157 /* Super-user only */ 158 error = suser(curthread); /* XXX */ 159 if (error != 0) 160 return (error); 161 162 s = splnet(); /* XXX */ 163 spltty(); /* XXX */ 164 165 /* Already installed? */ 166 if (tp->t_line == H4DISC) { 167 sc = (ng_h4_info_p) tp->t_sc; 168 if (sc != NULL && sc->tp == tp) 169 goto out; 170 } 171 172 /* Initialize private struct */ 173 MALLOC(sc, ng_h4_info_p, sizeof(*sc), M_NETGRAPH_H4, M_ZERO); 174 if (sc == NULL) { 175 error = ENOMEM; 176 goto out; 177 } 178 179 sc->tp = tp; 180 sc->debug = NG_H4_WARN_LEVEL; 181 182 sc->state = NG_H4_W4_PKT_IND; 183 sc->want = 1; 184 sc->got = 0; 185 186 NG_BT_MBUFQ_INIT(&sc->outq, NG_H4_DEFAULTQLEN); 187 callout_handle_init(&sc->timo); 188 189 /* Setup netgraph node */ 190 error = ng_make_node_common(&typestruct, &sc->node); 191 if (error != 0) { 192 bzero(sc, sizeof(*sc)); 193 FREE(sc, M_NETGRAPH_H4); 194 goto out; 195 } 196 197 /* Assign node its name */ 198 snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++); 199 200 error = ng_name_node(sc->node, name); 201 if (error != 0) { 202 NG_H4_ALERT("%s: %s - node name exists?\n", __func__, name); 203 NG_NODE_UNREF(sc->node); 204 bzero(sc, sizeof(*sc)); 205 FREE(sc, M_NETGRAPH_H4); 206 goto out; 207 } 208 209 /* Set back pointers */ 210 NG_NODE_SET_PRIVATE(sc->node, sc); 211 tp->t_sc = (caddr_t) sc; 212 213 /* The node has to be a WRITER because data can change node status */ 214 NG_NODE_FORCE_WRITER(sc->node); 215 216 /* 217 * Pre-allocate cblocks to the an appropriate amount. 218 * I'm not sure what is appropriate. 219 */ 220 221 ttyflush(tp, FREAD | FWRITE); 222 clist_alloc_cblocks(&tp->t_canq, 0, 0); 223 clist_alloc_cblocks(&tp->t_rawq, 0, 0); 224 clist_alloc_cblocks(&tp->t_outq, 225 MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER); 226out: 227 splx(s); /* XXX */ 228 229 return (error); 230} /* ng_h4_open */ 231 232/* 233 * Line specific close routine, called from device close routine 234 * and from ttioctl. This causes the node to be destroyed as well. 235 */ 236 237static int 238ng_h4_close(struct tty *tp, int flag) 239{ 240 ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; 241 int s; 242 243 s = spltty(); /* XXX */ 244 245 ttyflush(tp, FREAD | FWRITE); 246 clist_free_cblocks(&tp->t_outq); 247 tp->t_line = 0; 248 if (sc != NULL) { 249 tp->t_sc = NULL; 250 251 if (sc->node != NULL) { 252 if (sc->flags & NG_H4_TIMEOUT) 253 ng_h4_untimeout(sc->node); 254 255 NG_NODE_SET_PRIVATE(sc->node, NULL); 256 ng_rmnode_self(sc->node); 257 sc->node = NULL; 258 } 259 260 NG_BT_MBUFQ_DESTROY(&sc->outq); 261 bzero(sc, sizeof(*sc)); 262 FREE(sc, M_NETGRAPH_H4); 263 } 264 265 splx(s); /* XXX */ 266 267 return (0); 268} /* ng_h4_close */ 269 270/* 271 * Once the device has been turned into a node, we don't allow reading. 272 */ 273 274static int 275ng_h4_read(struct tty *tp, struct uio *uio, int flag) 276{ 277 return (EIO); 278} /* ng_h4_read */ 279 280/* 281 * Once the device has been turned into a node, we don't allow writing. 282 */ 283 284static int 285ng_h4_write(struct tty *tp, struct uio *uio, int flag) 286{ 287 return (EIO); 288} /* ng_h4_write */ 289 290/* 291 * We implement the NGIOCGINFO ioctl() defined in ng_message.h. 292 */ 293 294static int 295ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, 296 struct thread *td) 297{ 298 ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; 299 int s, error = 0; 300 301 s = spltty(); /* XXX */ 302 303 switch (cmd) { 304 case NGIOCGINFO: 305#undef NI 306#define NI(x) ((struct nodeinfo *)(x)) 307 308 bzero(data, sizeof(*NI(data))); 309 310 if (NG_NODE_HAS_NAME(sc->node)) 311 strncpy(NI(data)->name, NG_NODE_NAME(sc->node), 312 sizeof(NI(data)->name) - 1); 313 314 strncpy(NI(data)->type, sc->node->nd_type->name, 315 sizeof(NI(data)->type) - 1); 316 317 NI(data)->id = (u_int32_t) ng_node2ID(sc->node); 318 NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node); 319 break; 320 321 default: 322 error = ENOIOCTL; 323 break; 324 } 325 326 splx(s); /* XXX */ 327 328 return (error); 329} /* ng_h4_ioctl */ 330 331/* 332 * Receive data coming from the device. We get one character at a time, which 333 * is kindof silly. 334 */ 335 336static int 337ng_h4_input(int c, struct tty *tp) 338{ 339 ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; 340 341 if (sc == NULL || tp != sc->tp || 342 sc->node == NULL || NG_NODE_NOT_VALID(sc->node)) 343 return (0); 344 345 /* Check for error conditions */ 346 if ((sc->tp->t_state & TS_CONNECTED) == 0) { 347 NG_H4_INFO("%s: %s - no carrier\n", __func__, 348 NG_NODE_NAME(sc->node)); 349 350 sc->state = NG_H4_W4_PKT_IND; 351 sc->want = 1; 352 sc->got = 0; 353 354 return (0); /* XXX Loss of synchronization here! */ 355 } 356 357 /* Check for framing error or overrun on this char */ 358 if (c & TTY_ERRORMASK) { 359 NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__, 360 NG_NODE_NAME(sc->node), c & TTY_ERRORMASK, 361 c & TTY_CHARMASK); 362 363 NG_H4_STAT_IERROR(sc->stat); 364 365 sc->state = NG_H4_W4_PKT_IND; 366 sc->want = 1; 367 sc->got = 0; 368 369 return (0); /* XXX Loss of synchronization here! */ 370 } 371 372 NG_H4_STAT_BYTES_RECV(sc->stat, 1); 373 374 /* Append char to mbuf */ 375 if (sc->got >= sizeof(sc->ibuf)) { 376 NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n", 377 __func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK, 378 sc->got); 379 380 NG_H4_STAT_IERROR(sc->stat); 381 382 sc->state = NG_H4_W4_PKT_IND; 383 sc->want = 1; 384 sc->got = 0; 385 386 return (0); /* XXX Loss of synchronization here! */ 387 } 388 389 sc->ibuf[sc->got ++] = (c & TTY_CHARMASK); 390 391 NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__, 392 NG_NODE_NAME(sc->node), c, sc->want, sc->got); 393 394 if (sc->got < sc->want) 395 return (0); /* Wait for more */ 396 397 switch (sc->state) { 398 /* Got packet indicator */ 399 case NG_H4_W4_PKT_IND: 400 NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__, 401 NG_NODE_NAME(sc->node), sc->ibuf[0]); 402 403 sc->state = NG_H4_W4_PKT_HDR; 404 405 /* 406 * Since packet indicator included in the packet header 407 * just set sc->want to sizeof(packet header). 408 */ 409 410 switch (sc->ibuf[0]) { 411 case NG_HCI_ACL_DATA_PKT: 412 sc->want = sizeof(ng_hci_acldata_pkt_t); 413 break; 414 415 case NG_HCI_SCO_DATA_PKT: 416 sc->want = sizeof(ng_hci_scodata_pkt_t); 417 break; 418 419 case NG_HCI_EVENT_PKT: 420 sc->want = sizeof(ng_hci_event_pkt_t); 421 break; 422 423 default: 424 NG_H4_WARN("%s: %s - ignoring unknown packet " \ 425 "type=%#x\n", __func__, NG_NODE_NAME(sc->node), 426 sc->ibuf[0]); 427 428 NG_H4_STAT_IERROR(sc->stat); 429 430 sc->state = NG_H4_W4_PKT_IND; 431 sc->want = 1; 432 sc->got = 0; 433 break; 434 } 435 break; 436 437 /* Got packet header */ 438 case NG_H4_W4_PKT_HDR: 439 sc->state = NG_H4_W4_PKT_DATA; 440 441 switch (sc->ibuf[0]) { 442 case NG_HCI_ACL_DATA_PKT: 443 c = le16toh(((ng_hci_acldata_pkt_t *) 444 (sc->ibuf))->length); 445 break; 446 447 case NG_HCI_SCO_DATA_PKT: 448 c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length; 449 break; 450 451 case NG_HCI_EVENT_PKT: 452 c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length; 453 break; 454 455 default: 456 KASSERT((0), ("Invalid packet type=%#x\n", 457 sc->ibuf[0])); 458 break; 459 } 460 461 NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \ 462 "packet size=%d, payload size=%d\n", __func__, 463 NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c); 464 465 if (c > 0) { 466 sc->want += c; 467 468 /* 469 * Try to prevent possible buffer overrun 470 * 471 * XXX I'm *really* confused here. It turns out 472 * that Xircom card sends us packets with length 473 * greater then 512 bytes! This is greater then 474 * our old receive buffer (ibuf) size. In the same 475 * time the card demands from us *not* to send 476 * packets greater then 192 bytes. Weird! How the 477 * hell i should know how big *receive* buffer 478 * should be? For now increase receiving buffer 479 * size to 1K and add the following check. 480 */ 481 482 if (sc->want >= sizeof(sc->ibuf)) { 483 int b; 484 485 NG_H4_ALERT("%s: %s - packet too big for " \ 486 "buffer, type=%#x, got=%d, want=%d, " \ 487 "length=%d\n", __func__, 488 NG_NODE_NAME(sc->node), sc->ibuf[0], 489 sc->got, sc->want, c); 490 491 NG_H4_ALERT("Packet header:\n"); 492 for (b = 0; b < sc->got; b++) 493 NG_H4_ALERT("%#x ", sc->ibuf[b]); 494 NG_H4_ALERT("\n"); 495 496 /* Reset state */ 497 NG_H4_STAT_IERROR(sc->stat); 498 499 sc->state = NG_H4_W4_PKT_IND; 500 sc->want = 1; 501 sc->got = 0; 502 } 503 504 break; 505 } 506 507 /* else FALLTHROUGH and deliver frame */ 508 /* XXX Is this true? Should we deliver empty frame? */ 509 510 /* Got packet data */ 511 case NG_H4_W4_PKT_DATA: 512 NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \ 513 "packet size=%d\n", __func__, 514 NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got); 515 516 if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) { 517 struct mbuf *m = NULL; 518 519 MGETHDR(m, M_NOWAIT, MT_DATA); 520 if (m != NULL) { 521 m->m_pkthdr.len = 0; 522 523 /* XXX m_copyback() is stupid */ 524 m->m_len = min(MHLEN, sc->got); 525 526 m_copyback(m, 0, sc->got, sc->ibuf); 527 NG_SEND_DATA_ONLY(c, sc->hook, m); 528 } else { 529 NG_H4_ERR("%s: %s - could not get mbuf\n", 530 __func__, NG_NODE_NAME(sc->node)); 531 532 NG_H4_STAT_IERROR(sc->stat); 533 } 534 } 535 536 sc->state = NG_H4_W4_PKT_IND; 537 sc->want = 1; 538 sc->got = 0; 539 540 NG_H4_STAT_PCKTS_RECV(sc->stat); 541 break; 542 543 default: 544 KASSERT((0), ("Invalid H4 node state=%d", sc->state)); 545 break; 546 } 547 548 return (0); 549} /* ng_h4_input */ 550 551/* 552 * This is called when the device driver is ready for more output. Called from 553 * tty system. 554 */ 555 556static int 557ng_h4_start(struct tty *tp) 558{ 559 ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc; 560 561 if (sc == NULL || tp != sc->tp || 562 sc->node == NULL || NG_NODE_NOT_VALID(sc->node)) 563 return (0); 564 565 return (ng_send_fn(sc->node, NULL, ng_h4_start2, NULL, 0)); 566} /* ng_h4_start */ 567 568/* 569 * Device driver is ready for more output. Part 2. Called (via ng_send_fn) 570 * ng_h4_start() and from ng_h4_rcvdata() when a new mbuf is available for 571 * output. 572 */ 573 574static void 575ng_h4_start2(node_p node, hook_p hook, void *arg1, int arg2) 576{ 577 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 578 struct mbuf *m = NULL; 579 int s, size; 580 581 s = spltty(); /* XXX */ 582 583#if 0 584 while (sc->tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */ 585#else 586 while (1) { 587#endif 588 /* Remove first mbuf from queue */ 589 NG_BT_MBUFQ_DEQUEUE(&sc->outq, m); 590 if (m == NULL) 591 break; 592 593 /* Send as much of it as possible */ 594 while (m != NULL) { 595 size = m->m_len - b_to_q(mtod(m, u_char *), 596 m->m_len, &sc->tp->t_outq); 597 598 NG_H4_STAT_BYTES_SENT(sc->stat, size); 599 600 m->m_data += size; 601 m->m_len -= size; 602 if (m->m_len > 0) 603 break; /* device can't take no more */ 604 605 m = m_free(m); 606 } 607 608 /* Put remainder of mbuf chain (if any) back on queue */ 609 if (m != NULL) { 610 NG_BT_MBUFQ_PREPEND(&sc->outq, m); 611 break; 612 } 613 614 /* Full packet has been sent */ 615 NG_H4_STAT_PCKTS_SENT(sc->stat); 616 } 617 618 /* 619 * Call output process whether or not there is any output. We are 620 * being called in lieu of ttstart and must do what it would. 621 */ 622 623 if (sc->tp->t_oproc != NULL) 624 (*sc->tp->t_oproc)(sc->tp); 625 626 /* 627 * This timeout is needed for operation on a pseudo-tty, because the 628 * pty code doesn't call pppstart after it has drained the t_outq. 629 */ 630 631 if (NG_BT_MBUFQ_LEN(&sc->outq) > 0 && (sc->flags & NG_H4_TIMEOUT) == 0) 632 ng_h4_timeout(node); 633 634 splx(s); /* XXX */ 635} /* ng_h4_start2 */ 636 637/***************************************************************************** 638 ***************************************************************************** 639 ** Netgraph node methods 640 ***************************************************************************** 641 *****************************************************************************/ 642 643/* 644 * Initialize a new node of this type. We only allow nodes to be created as 645 * a result of setting the line discipline on a tty, so always return an error 646 * if not. 647 */ 648 649static int 650ng_h4_constructor(node_p node) 651{ 652 return (EOPNOTSUPP); 653} /* ng_h4_constructor */ 654 655/* 656 * Add a new hook. There can only be one. 657 */ 658 659static int 660ng_h4_newhook(node_p node, hook_p hook, const char *name) 661{ 662 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 663 664 if (strcmp(name, NG_H4_HOOK) != 0) 665 return (EINVAL); 666 667 if (sc->hook != NULL) 668 return (EISCONN); 669 670 sc->hook = hook; 671 672 return (0); 673} /* ng_h4_newhook */ 674 675/* 676 * Connect hook. Just say yes. 677 */ 678 679static int 680ng_h4_connect(hook_p hook) 681{ 682 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 683 684 if (hook != sc->hook) { 685 sc->hook = NULL; 686 return (EINVAL); 687 } 688 689 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 690 691 return (0); 692} /* ng_h4_connect */ 693 694/* 695 * Disconnect the hook 696 */ 697 698static int 699ng_h4_disconnect(hook_p hook) 700{ 701 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 702 703 /* 704 * We need to check for sc != NULL because we can be called from 705 * ng_h4_clsoe() via ng_rmnode_self() 706 */ 707 708 if (sc != NULL) { 709 if (hook != sc->hook) 710 return (EINVAL); 711 712 /* XXX do we have to untimeout and drain out queue? */ 713 if (sc->flags & NG_H4_TIMEOUT) 714 ng_h4_untimeout(NG_HOOK_NODE(hook)); 715 716 NG_BT_MBUFQ_DRAIN(&sc->outq); 717 sc->state = NG_H4_W4_PKT_IND; 718 sc->want = 1; 719 sc->got = 0; 720 721 sc->hook = NULL; 722 } 723 724 return (0); 725} /* ng_h4_disconnect */ 726 727/* 728 * Remove this node. The does the netgraph portion of the shutdown. 729 * This should only be called indirectly from ng_h4_close(). 730 */ 731 732static int 733ng_h4_shutdown(node_p node) 734{ 735 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 736 char name[NG_NODELEN + 1]; 737 738 /* Let old node go */ 739 NG_NODE_SET_PRIVATE(node, NULL); 740 NG_NODE_UNREF(node); 741 742 /* Check if device was closed */ 743 if (sc == NULL) 744 goto out; 745 746 /* Setup new netgraph node */ 747 if (ng_make_node_common(&typestruct, &sc->node) != 0) { 748 printf("%s: Unable to create new node!\n", __func__); 749 sc->node = NULL; 750 goto out; 751 } 752 753 /* Assign node its name */ 754 snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++); 755 756 if (ng_name_node(sc->node, name) != 0) { 757 printf("%s: %s - node name exists?\n", __func__, name); 758 NG_NODE_UNREF(sc->node); 759 sc->node = NULL; 760 goto out; 761 } 762 763 /* The node has to be a WRITER because data can change node status */ 764 NG_NODE_FORCE_WRITER(sc->node); 765 NG_NODE_SET_PRIVATE(sc->node, sc); 766out: 767 return (0); 768} /* ng_h4_shutdown */ 769 770/* 771 * Receive incoming data from Netgraph system. Put it on our 772 * output queue and start output if necessary. 773 */ 774 775static int 776ng_h4_rcvdata(hook_p hook, item_p item) 777{ 778 ng_h4_info_p sc = (ng_h4_info_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 779 int error = 0; 780 struct mbuf *m = NULL; 781 782 if (sc == NULL) { 783 error = EHOSTDOWN; 784 goto out; 785 } 786 787 if (hook != sc->hook) { 788 error = EINVAL; 789 goto out; 790 } 791 792 NGI_GET_M(item, m); 793 794 if (NG_BT_MBUFQ_FULL(&sc->outq)) { 795 NG_H4_ERR("%s: %s - dropping mbuf, len=%d\n", __func__, 796 NG_NODE_NAME(sc->node), m->m_pkthdr.len); 797 798 NG_BT_MBUFQ_DROP(&sc->outq); 799 NG_H4_STAT_OERROR(sc->stat); 800 801 NG_FREE_M(m); 802 error = ENOBUFS; 803 } else { 804 NG_H4_INFO("%s: %s - queue mbuf, len=%d\n", __func__, 805 NG_NODE_NAME(sc->node), m->m_pkthdr.len); 806 807 NG_BT_MBUFQ_ENQUEUE(&sc->outq, m); 808 809 /* 810 * We have lock on the node, so we can call ng_h4_start2() 811 * directly 812 */ 813 814 ng_h4_start2(sc->node, NULL, NULL, 0); 815 } 816out: 817 NG_FREE_ITEM(item); 818 819 return (error); 820} /* ng_h4_rcvdata */ 821 822/* 823 * Receive control message 824 */ 825 826static int 827ng_h4_rcvmsg(node_p node, item_p item, hook_p lasthook) 828{ 829 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 830 struct ng_mesg *msg = NULL, *resp = NULL; 831 int error = 0; 832 833 if (sc == NULL) { 834 error = EHOSTDOWN; 835 goto out; 836 } 837 838 NGI_GET_MSG(item, msg); 839 840 switch (msg->header.typecookie) { 841 case NGM_GENERIC_COOKIE: 842 switch (msg->header.cmd) { 843 case NGM_TEXT_STATUS: 844 NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); 845 if (resp == NULL) 846 error = ENOMEM; 847 else 848 snprintf(resp->data, NG_TEXTRESPONSE, 849 "Hook: %s\n" \ 850 "Flags: %#x\n" \ 851 "Debug: %d\n" \ 852 "State: %d\n" \ 853 "Queue: [have:%d,max:%d]\n" \ 854 "Input: [got:%d,want:%d]", 855 (sc->hook != NULL)? NG_H4_HOOK : "", 856 sc->flags, 857 sc->debug, 858 sc->state, 859 NG_BT_MBUFQ_LEN(&sc->outq), 860 sc->outq.maxlen, 861 sc->got, 862 sc->want); 863 break; 864 865 default: 866 error = EINVAL; 867 break; 868 } 869 break; 870 871 case NGM_H4_COOKIE: 872 switch (msg->header.cmd) { 873 case NGM_H4_NODE_RESET: 874 NG_BT_MBUFQ_DRAIN(&sc->outq); 875 sc->state = NG_H4_W4_PKT_IND; 876 sc->want = 1; 877 sc->got = 0; 878 break; 879 880 case NGM_H4_NODE_GET_STATE: 881 NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_state_ep), 882 M_NOWAIT); 883 if (resp == NULL) 884 error = ENOMEM; 885 else 886 *((ng_h4_node_state_ep *)(resp->data)) = 887 sc->state; 888 break; 889 890 case NGM_H4_NODE_GET_DEBUG: 891 NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_debug_ep), 892 M_NOWAIT); 893 if (resp == NULL) 894 error = ENOMEM; 895 else 896 *((ng_h4_node_debug_ep *)(resp->data)) = 897 sc->debug; 898 break; 899 900 case NGM_H4_NODE_SET_DEBUG: 901 if (msg->header.arglen != sizeof(ng_h4_node_debug_ep)) 902 error = EMSGSIZE; 903 else 904 sc->debug = 905 *((ng_h4_node_debug_ep *)(msg->data)); 906 break; 907 908 case NGM_H4_NODE_GET_QLEN: 909 NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_qlen_ep), 910 M_NOWAIT); 911 if (resp == NULL) 912 error = ENOMEM; 913 else 914 *((ng_h4_node_qlen_ep *)(resp->data)) = 915 sc->outq.maxlen; 916 break; 917 918 case NGM_H4_NODE_SET_QLEN: 919 if (msg->header.arglen != sizeof(ng_h4_node_qlen_ep)) 920 error = EMSGSIZE; 921 else if (*((ng_h4_node_qlen_ep *)(msg->data)) <= 0) 922 error = EINVAL; 923 else 924 sc->outq.maxlen = 925 *((ng_h4_node_qlen_ep *)(msg->data)); 926 break; 927 928 case NGM_H4_NODE_GET_STAT: 929 NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_stat_ep), 930 M_NOWAIT); 931 if (resp == NULL) 932 error = ENOMEM; 933 else 934 bcopy(&sc->stat, resp->data, 935 sizeof(ng_h4_node_stat_ep)); 936 break; 937 938 case NGM_H4_NODE_RESET_STAT: 939 NG_H4_STAT_RESET(sc->stat); 940 break; 941 942 default: 943 error = EINVAL; 944 break; 945 } 946 break; 947 948 default: 949 error = EINVAL; 950 break; 951 } 952out: 953 NG_RESPOND_MSG(error, node, item, resp); 954 NG_FREE_MSG(msg); 955 956 return (error); 957} /* ng_h4_rcvmsg */ 958 959/* 960 * Set timeout 961 */ 962 963static void 964ng_h4_timeout(node_p node) 965{ 966 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 967 968 NG_NODE_REF(node); 969 sc->timo = timeout(ng_h4_queue_timeout, node, 1); 970 sc->flags |= NG_H4_TIMEOUT; 971} /* ng_h4_timeout */ 972 973/* 974 * Unset timeout 975 */ 976 977static void 978ng_h4_untimeout(node_p node) 979{ 980 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 981 982 sc->flags &= ~NG_H4_TIMEOUT; 983 untimeout(ng_h4_queue_timeout, node, sc->timo); 984 NG_NODE_UNREF(node); 985} /* ng_h4_untimeout */ 986 987/* 988 * OK, timeout has happend, so queue function to process it 989 */ 990 991static void 992ng_h4_queue_timeout(void *context) 993{ 994 node_p node = (node_p) context; 995 996 if (NG_NODE_IS_VALID(node)) 997 ng_send_fn(node, NULL, &ng_h4_process_timeout, NULL, 0); 998 999 NG_NODE_UNREF(node); 1000} /* ng_h4_queue_timeout */ 1001 1002/* 1003 * Timeout processing function. 1004 * We still have data to output to the device, so try sending more. 1005 */ 1006 1007static void 1008ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1009{ 1010 ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node); 1011 1012 sc->flags &= ~NG_H4_TIMEOUT; 1013 1014 /* 1015 * We can call ng_h4_start2() directly here because we have lock 1016 * on the node. 1017 */ 1018 1019 ng_h4_start2(node, NULL, NULL, 0); 1020} /* ng_h4_process_timeout */ 1021 1022/* 1023 * Handle loading and unloading for this node type 1024 */ 1025 1026static int 1027ng_h4_mod_event(module_t mod, int event, void *data) 1028{ 1029 static int ng_h4_ldisc; 1030 int s, error = 0; 1031 1032 s = spltty(); /* XXX */ 1033 1034 switch (event) { 1035 case MOD_LOAD: 1036 /* Register line discipline */ 1037 ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc); 1038 if (ng_h4_ldisc < 0) { 1039 printf("%s: can't register H4 line discipline\n", 1040 __func__); 1041 error = EIO; 1042 } 1043 break; 1044 1045 case MOD_UNLOAD: 1046 /* Unregister line discipline */ 1047 ldisc_deregister(ng_h4_ldisc); 1048 break; 1049 1050 default: 1051 error = EOPNOTSUPP; 1052 break; 1053 } 1054 1055 splx(s); /* XXX */ 1056 1057 return (error); 1058} /* ng_h4_mod_event */ 1059 1060