diff options
Diffstat (limited to 'sys/netbt/hci_ioctl.c')
-rw-r--r-- | sys/netbt/hci_ioctl.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/sys/netbt/hci_ioctl.c b/sys/netbt/hci_ioctl.c new file mode 100644 index 00000000000..44a6fbff94d --- /dev/null +++ b/sys/netbt/hci_ioctl.c @@ -0,0 +1,312 @@ +/* $OpenBSD: hci_ioctl.c,v 1.1 2007/06/01 02:46:11 uwe Exp $ */ +/* $NetBSD: hci_ioctl.c,v 1.5 2007/01/04 19:07:03 elad Exp $ */ + +/*- + * Copyright (c) 2005 Iain Hibbert. + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#include <sys/param.h> +#include <sys/domain.h> +#include <sys/ioctl.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/systm.h> + +#include <netbt/bluetooth.h> +#include <netbt/hci.h> +#include <netbt/l2cap.h> +#include <netbt/rfcomm.h> + +#ifdef BLUETOOTH_DEBUG +#define BDADDR(bd) (bd).b[5], (bd).b[4], (bd).b[3], \ + (bd).b[2], (bd).b[1], (bd).b[0] + +static void +hci_dump(void) +{ + struct hci_unit *unit; + struct hci_link *link; + struct l2cap_channel *chan; + struct rfcomm_session *rs; + struct rfcomm_dlc *dlc; + + printf("HCI:\n"); + TAILQ_FOREACH(unit, &hci_unit_list, hci_next) { + printf("UNIT %s: flags 0x%4.4x, " + "num_cmd=%d, num_acl=%d, num_sco=%d\n", + unit->hci_devname, unit->hci_flags, + unit->hci_num_cmd_pkts, + unit->hci_num_acl_pkts, + unit->hci_num_sco_pkts); + TAILQ_FOREACH(link, &unit->hci_links, hl_next) { + printf("+HANDLE #%d: %s " + "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "state %d, refcnt %d\n", + link->hl_handle, + (link->hl_type == HCI_LINK_ACL ? "ACL":"SCO"), + BDADDR(link->hl_bdaddr), + link->hl_state, link->hl_refcnt); + } + } + + printf("L2CAP:\n"); + LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { + printf("CID #%d state %d, psm=0x%4.4x, " + "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + chan->lc_lcid, chan->lc_state, chan->lc_raddr.bt_psm, + BDADDR(chan->lc_laddr.bt_bdaddr), + BDADDR(chan->lc_raddr.bt_bdaddr)); + } + + LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) { + printf("LISTEN psm=0x%4.4x, " + "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + chan->lc_laddr.bt_psm, + BDADDR(chan->lc_laddr.bt_bdaddr)); + } + + printf("RFCOMM:\n"); + LIST_FOREACH(rs, &rfcomm_session_active, rs_next) { + chan = rs->rs_l2cap; + printf("SESSION: state=%d, flags=0x%4.4x, psm 0x%4.4x " + "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + rs->rs_state, rs->rs_flags, chan->lc_raddr.bt_psm, + BDADDR(chan->lc_laddr.bt_bdaddr), + BDADDR(chan->lc_raddr.bt_bdaddr)); + LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) { + printf("+DLC channel=%d, dlci=%d, " + "state=%d, flags=0x%4.4x, rxcred=%d, rxsize=%ld, " + "txcred=%d, pending=%d, txqlen=%d\n", + dlc->rd_raddr.bt_channel, dlc->rd_dlci, + dlc->rd_state, dlc->rd_flags, + dlc->rd_rxcred, (unsigned long)dlc->rd_rxsize, + dlc->rd_txcred, dlc->rd_pending, + (dlc->rd_txbuf ? dlc->rd_txbuf->m_pkthdr.len : 0)); + } + } + + LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) { + chan = rs->rs_l2cap; + printf("LISTEN: psm 0x%4.4x, " + "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + chan->lc_laddr.bt_psm, + BDADDR(chan->lc_laddr.bt_bdaddr)); + LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) + printf("+DLC channel=%d\n", dlc->rd_laddr.bt_channel); + } +} + +#undef BDADDR +#endif + +int +hci_ioctl(unsigned long cmd, void *data, struct proc *p) +{ + struct btreq *btr = data; + struct hci_unit *unit; + int s, err = 0; + + DPRINTFN(1, "cmd %#lx\n", cmd); + + switch(cmd) { +#ifdef BLUETOOTH_DEBUG + case SIOCBTDUMP: + hci_dump(); + return 0; +#endif + /* + * Get unit info based on address rather than name + */ + case SIOCGBTINFOA: + unit = hci_unit_lookup(&btr->btr_bdaddr); + if (unit == NULL) + return ENXIO; + + break; + + /* + * The remaining ioctl's all use the same btreq structure and + * index on the name of the device, so we look that up first. + */ + case SIOCNBTINFO: + /* empty name means give the first unit */ + if (btr->btr_name[0] == '\0') { + unit = NULL; + break; + } + + /* else fall through and look it up */ + case SIOCGBTINFO: + case SIOCSBTFLAGS: + case SIOCSBTPOLICY: + case SIOCSBTPTYPE: + case SIOCGBTSTATS: + case SIOCZBTSTATS: + case SIOCSBTSCOMTU: + TAILQ_FOREACH(unit, &hci_unit_list, hci_next) { + if (strncmp(unit->hci_devname, btr->btr_name, + HCI_DEVNAME_SIZE) == 0) + break; + } + + if (unit == NULL) + return ENXIO; + + break; + + default: /* not one of mine */ + return EPASSTHROUGH; + } + + switch(cmd) { + case SIOCNBTINFO: /* get next info */ + if (unit) + unit = TAILQ_NEXT(unit, hci_next); + else + unit = TAILQ_FIRST(&hci_unit_list); + + if (unit == NULL) { + err = ENXIO; + break; + } + + /* and fall through to */ + case SIOCGBTINFO: /* get unit info */ + case SIOCGBTINFOA: /* get info by address */ + memset(btr, 0, sizeof(struct btreq)); + strlcpy(btr->btr_name, unit->hci_devname, HCI_DEVNAME_SIZE); + bdaddr_copy(&btr->btr_bdaddr, &unit->hci_bdaddr); + + btr->btr_flags = unit->hci_flags; + + btr->btr_num_cmd = unit->hci_num_cmd_pkts; + btr->btr_num_acl = unit->hci_num_acl_pkts; + btr->btr_num_sco = unit->hci_num_sco_pkts; + btr->btr_acl_mtu = unit->hci_max_acl_size; + btr->btr_sco_mtu = unit->hci_max_sco_size; + + btr->btr_packet_type = unit->hci_packet_type; + btr->btr_link_policy = unit->hci_link_policy; + break; + + case SIOCSBTFLAGS: /* set unit flags (privileged) */ + err = suser(p, 0); + if (err) + break; + + if ((unit->hci_flags & BTF_UP) + && (btr->btr_flags & BTF_UP) == 0) { + hci_disable(unit); + unit->hci_flags &= ~BTF_UP; + } + + s = splraiseipl(unit->hci_ipl); + unit->hci_flags |= (btr->btr_flags & BTF_INIT); + splx(s); + + if ((unit->hci_flags & BTF_UP) == 0 + && (btr->btr_flags & BTF_UP)) { + err = hci_enable(unit); + if (err) + break; + + s = splraiseipl(unit->hci_ipl); + unit->hci_flags |= BTF_UP; + splx(s); + } + + btr->btr_flags = unit->hci_flags; + break; + + case SIOCSBTPOLICY: /* set unit link policy (privileged) */ + err = suser(p, 0); + if (err) + break; + + unit->hci_link_policy = btr->btr_link_policy; + unit->hci_link_policy &= unit->hci_lmp_mask; + btr->btr_link_policy = unit->hci_link_policy; + break; + + case SIOCSBTPTYPE: /* set unit packet types (privileged) */ + err = suser(p, 0); + if (err) + break; + + unit->hci_packet_type = btr->btr_packet_type; + unit->hci_packet_type &= unit->hci_acl_mask; + btr->btr_packet_type = unit->hci_packet_type; + break; + + case SIOCGBTSTATS: /* get unit statistics */ + s = splraiseipl(unit->hci_ipl); + memcpy(&btr->btr_stats, &unit->hci_stats, + sizeof(struct bt_stats)); + splx(s); + break; + + case SIOCZBTSTATS: /* get & reset unit statistics */ + err = suser(p, 0); + if (err) + break; + + s = splraiseipl(unit->hci_ipl); + memcpy(&btr->btr_stats, &unit->hci_stats, + sizeof(struct bt_stats)); + memset(&unit->hci_stats, 0, sizeof(struct bt_stats)); + splx(s); + + break; + + case SIOCSBTSCOMTU: /* set sco_mtu value for unit */ + /* + * This is a temporary ioctl and may not be supported + * in the future. The need is that if SCO packets are + * sent to USB bluetooth controllers that are not an + * integer number of frame sizes, the USB bus locks up. + */ + err = suser(p, 0); + if (err) + break; + + unit->hci_max_sco_size = btr->btr_sco_mtu; + break; + + default: + err = EFAULT; + break; + } + + return err; +} |