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