[BACK]Return to sco_upper.c CVS log [TXT][DIR] Up to [local] / sys / netbt

Annotation of sys/netbt/sco_upper.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: sco_upper.c,v 1.1 2007/06/01 02:46:12 uwe Exp $       */
        !             2: /*     $NetBSD: sco_upper.c,v 1.6 2007/03/30 20:47:03 plunky Exp $     */
        !             3:
        !             4: /*-
        !             5:  * Copyright (c) 2006 Itronix Inc.
        !             6:  * All rights reserved.
        !             7:  *
        !             8:  * Written by Iain Hibbert for Itronix Inc.
        !             9:  *
        !            10:  * Redistribution and use in source and binary forms, with or without
        !            11:  * modification, are permitted provided that the following conditions
        !            12:  * are met:
        !            13:  * 1. Redistributions of source code must retain the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer.
        !            15:  * 2. Redistributions in binary form must reproduce the above copyright
        !            16:  *    notice, this list of conditions and the following disclaimer in the
        !            17:  *    documentation and/or other materials provided with the distribution.
        !            18:  * 3. The name of Itronix Inc. may not be used to endorse
        !            19:  *    or promote products derived from this software without specific
        !            20:  *    prior written permission.
        !            21:  *
        !            22:  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
        !            23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
        !            24:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            25:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
        !            26:  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            27:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
        !            28:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
        !            29:  * ON ANY THEORY OF LIABILITY, WHETHER IN
        !            30:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        !            31:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            32:  * POSSIBILITY OF SUCH DAMAGE.
        !            33:  */
        !            34:
        !            35: #include <sys/cdefs.h>
        !            36:
        !            37: #include <sys/param.h>
        !            38: #include <sys/kernel.h>
        !            39: #include <sys/mbuf.h>
        !            40: #include <sys/proc.h>
        !            41: #include <sys/systm.h>
        !            42:
        !            43: #include <netbt/bluetooth.h>
        !            44: #include <netbt/hci.h>
        !            45: #include <netbt/sco.h>
        !            46:
        !            47: /****************************************************************************
        !            48:  *
        !            49:  *     SCO - Upper Protocol API
        !            50:  */
        !            51:
        !            52: struct sco_pcb_list sco_pcb = LIST_HEAD_INITIALIZER(sco_pcb);
        !            53:
        !            54: /*
        !            55:  * sco_attach(handle, proto, upper)
        !            56:  *
        !            57:  *     Attach a new instance of SCO pcb to handle
        !            58:  */
        !            59: int
        !            60: sco_attach(struct sco_pcb **handle,
        !            61:                const struct btproto *proto, void *upper)
        !            62: {
        !            63:        struct sco_pcb *pcb;
        !            64:
        !            65:        KASSERT(handle != NULL);
        !            66:        KASSERT(proto != NULL);
        !            67:        KASSERT(upper != NULL);
        !            68:
        !            69:        pcb = malloc(sizeof(struct sco_pcb), M_BLUETOOTH, M_NOWAIT);
        !            70:        if (pcb == NULL)
        !            71:                return ENOMEM;
        !            72:        bzero(pcb, sizeof *pcb);
        !            73:
        !            74:        pcb->sp_proto = proto;
        !            75:        pcb->sp_upper = upper;
        !            76:
        !            77:        LIST_INSERT_HEAD(&sco_pcb, pcb, sp_next);
        !            78:
        !            79:        *handle = pcb;
        !            80:        return 0;
        !            81: }
        !            82:
        !            83: /*
        !            84:  * sco_bind(pcb, sockaddr)
        !            85:  *
        !            86:  *     Bind SCO pcb to local address
        !            87:  */
        !            88: int
        !            89: sco_bind(struct sco_pcb *pcb, struct sockaddr_bt *addr)
        !            90: {
        !            91:
        !            92:        bdaddr_copy(&pcb->sp_laddr, &addr->bt_bdaddr);
        !            93:        return 0;
        !            94: }
        !            95:
        !            96: /*
        !            97:  * sco_sockaddr(pcb, sockaddr)
        !            98:  *
        !            99:  *     Copy local address of PCB to sockaddr
        !           100:  */
        !           101: int
        !           102: sco_sockaddr(struct sco_pcb *pcb, struct sockaddr_bt *addr)
        !           103: {
        !           104:
        !           105:        memset(addr, 0, sizeof(struct sockaddr_bt));
        !           106:        addr->bt_len = sizeof(struct sockaddr_bt);
        !           107:        addr->bt_family = AF_BLUETOOTH;
        !           108:        bdaddr_copy(&addr->bt_bdaddr, &pcb->sp_laddr);
        !           109:        return 0;
        !           110: }
        !           111:
        !           112: /*
        !           113:  * sco_connect(pcb, sockaddr)
        !           114:  *
        !           115:  *     Initiate a SCO connection to the destination address.
        !           116:  */
        !           117: int
        !           118: sco_connect(struct sco_pcb *pcb, struct sockaddr_bt *dest)
        !           119: {
        !           120:        hci_add_sco_con_cp cp;
        !           121:        struct hci_unit *unit;
        !           122:        struct hci_link *acl, *sco;
        !           123:        int err;
        !           124:
        !           125:        if (pcb->sp_flags & SP_LISTENING)
        !           126:                return EINVAL;
        !           127:
        !           128:        bdaddr_copy(&pcb->sp_raddr, &dest->bt_bdaddr);
        !           129:
        !           130:        if (bdaddr_any(&pcb->sp_raddr))
        !           131:                return EDESTADDRREQ;
        !           132:
        !           133:        if (bdaddr_any(&pcb->sp_laddr)) {
        !           134:                err = hci_route_lookup(&pcb->sp_laddr, &pcb->sp_raddr);
        !           135:                if (err)
        !           136:                        return err;
        !           137:        }
        !           138:
        !           139:        unit = hci_unit_lookup(&pcb->sp_laddr);
        !           140:        if (unit == NULL)
        !           141:                return ENETDOWN;
        !           142:
        !           143:        /*
        !           144:         * We must have an already open ACL connection before we open the SCO
        !           145:         * connection, and since SCO connections dont happen on their own we
        !           146:         * will not open one, the application wanting this should have opened
        !           147:         * it previously.
        !           148:         */
        !           149:        acl = hci_link_lookup_bdaddr(unit, &pcb->sp_raddr, HCI_LINK_ACL);
        !           150:        if (acl == NULL || acl->hl_state != HCI_LINK_OPEN)
        !           151:                return EHOSTUNREACH;
        !           152:
        !           153:        sco = hci_link_alloc(unit);
        !           154:        if (sco == NULL)
        !           155:                return ENOMEM;
        !           156:
        !           157:        sco->hl_type = HCI_LINK_SCO;
        !           158:        bdaddr_copy(&sco->hl_bdaddr, &pcb->sp_raddr);
        !           159:
        !           160:        sco->hl_link = hci_acl_open(unit, &pcb->sp_raddr);
        !           161:        KASSERT(sco->hl_link == acl);
        !           162:
        !           163:        cp.con_handle = htole16(acl->hl_handle);
        !           164:        cp.pkt_type = htole16(0x00e0);          /* HV1, HV2, HV3 */
        !           165:        err = hci_send_cmd(unit, HCI_CMD_ADD_SCO_CON, &cp, sizeof(cp));
        !           166:        if (err) {
        !           167:                hci_link_free(sco, err);
        !           168:                return err;
        !           169:        }
        !           170:
        !           171:        sco->hl_sco = pcb;
        !           172:        pcb->sp_link = sco;
        !           173:
        !           174:        pcb->sp_mtu = unit->hci_max_sco_size;
        !           175:        return 0;
        !           176: }
        !           177:
        !           178: /*
        !           179:  * sco_peeraddr(pcb, sockaddr)
        !           180:  *
        !           181:  *     Copy remote address of SCO pcb to sockaddr
        !           182:  */
        !           183: int
        !           184: sco_peeraddr(struct sco_pcb *pcb, struct sockaddr_bt *addr)
        !           185: {
        !           186:
        !           187:        memset(addr, 0, sizeof(struct sockaddr_bt));
        !           188:        addr->bt_len = sizeof(struct sockaddr_bt);
        !           189:        addr->bt_family = AF_BLUETOOTH;
        !           190:        bdaddr_copy(&addr->bt_bdaddr, &pcb->sp_raddr);
        !           191:        return 0;
        !           192: }
        !           193:
        !           194: /*
        !           195:  * sco_disconnect(pcb, linger)
        !           196:  *
        !           197:  *     Initiate disconnection of connected SCO pcb
        !           198:  */
        !           199: int
        !           200: sco_disconnect(struct sco_pcb *pcb, int linger)
        !           201: {
        !           202:        hci_discon_cp cp;
        !           203:        struct hci_link *sco;
        !           204:        int err;
        !           205:
        !           206:        sco = pcb->sp_link;
        !           207:        if (sco == NULL)
        !           208:                return EINVAL;
        !           209:
        !           210:        cp.con_handle = htole16(sco->hl_handle);
        !           211:        cp.reason = 0x13;       /* "Remote User Terminated Connection" */
        !           212:
        !           213:        err = hci_send_cmd(sco->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp));
        !           214:        if (err || linger == 0) {
        !           215:                sco->hl_sco = NULL;
        !           216:                pcb->sp_link = NULL;
        !           217:                hci_link_free(sco, err);
        !           218:        }
        !           219:
        !           220:        return err;
        !           221: }
        !           222:
        !           223: /*
        !           224:  * sco_detach(handle)
        !           225:  *
        !           226:  *     Detach SCO pcb from handle and clear up
        !           227:  */
        !           228: int
        !           229: sco_detach(struct sco_pcb **handle)
        !           230: {
        !           231:        struct sco_pcb *pcb;
        !           232:
        !           233:        KASSERT(handle != NULL);
        !           234:        pcb = *handle;
        !           235:        *handle = NULL;
        !           236:
        !           237:        if (pcb == NULL)
        !           238:                return EINVAL;
        !           239:
        !           240:        if (pcb->sp_link != NULL) {
        !           241:                sco_disconnect(pcb, 0);
        !           242:                pcb->sp_link = NULL;
        !           243:        }
        !           244:
        !           245:        LIST_REMOVE(pcb, sp_next);
        !           246:        free(pcb, M_BLUETOOTH);
        !           247:        return 0;
        !           248: }
        !           249:
        !           250: /*
        !           251:  * sco_listen(pcb)
        !           252:  *
        !           253:  *     Mark pcb as a listener.
        !           254:  */
        !           255: int
        !           256: sco_listen(struct sco_pcb *pcb)
        !           257: {
        !           258:
        !           259:        if (pcb->sp_link != NULL)
        !           260:                return EINVAL;
        !           261:
        !           262:        pcb->sp_flags |= SP_LISTENING;
        !           263:        return 0;
        !           264: }
        !           265:
        !           266: /*
        !           267:  * sco_send(pcb, mbuf)
        !           268:  *
        !           269:  *     Send data on SCO pcb.
        !           270:  *
        !           271:  * Gross hackage, we just output the packet directly onto the unit queue.
        !           272:  * This will work fine for one channel per unit, but for more channels it
        !           273:  * really needs fixing. We set the context so that when the packet is sent,
        !           274:  * we can drop a record from the socket buffer.
        !           275:  */
        !           276: int
        !           277: sco_send(struct sco_pcb *pcb, struct mbuf *m)
        !           278: {
        !           279:        hci_scodata_hdr_t *hdr;
        !           280:        int plen;
        !           281:
        !           282:        if (pcb->sp_link == NULL) {
        !           283:                m_freem(m);
        !           284:                return EINVAL;
        !           285:        }
        !           286:
        !           287:        plen = m->m_pkthdr.len;
        !           288:        DPRINTFN(10, "%d bytes\n", plen);
        !           289:
        !           290:        /*
        !           291:         * This is a temporary limitation, as USB devices cannot
        !           292:         * handle SCO packet sizes that are not an integer number
        !           293:         * of Isochronous frames. See ubt(4)
        !           294:         */
        !           295:        if (plen != pcb->sp_mtu) {
        !           296:                m_freem(m);
        !           297:                return EMSGSIZE;
        !           298:        }
        !           299:
        !           300:        M_PREPEND(m, sizeof(hci_scodata_hdr_t), M_DONTWAIT);
        !           301:        if (m == NULL)
        !           302:                return ENOMEM;
        !           303:
        !           304:        hdr = mtod(m, hci_scodata_hdr_t *);
        !           305:        hdr->type = HCI_SCO_DATA_PKT;
        !           306:        hdr->con_handle = htole16(pcb->sp_link->hl_handle);
        !           307:        hdr->length = plen;
        !           308:
        !           309:        pcb->sp_pending++;
        !           310:        M_SETCTX(m, pcb->sp_link);
        !           311:        hci_output_sco(pcb->sp_link->hl_unit, m);
        !           312:
        !           313:        return 0;
        !           314: }
        !           315:
        !           316: /*
        !           317:  * sco_setopt(pcb, option, addr)
        !           318:  *
        !           319:  *     Set SCO pcb options
        !           320:  */
        !           321: int
        !           322: sco_setopt(struct sco_pcb *pcb, int opt, void *addr)
        !           323: {
        !           324:        int err = 0;
        !           325:
        !           326:        switch (opt) {
        !           327:        default:
        !           328:                err = ENOPROTOOPT;
        !           329:                break;
        !           330:        }
        !           331:
        !           332:        return err;
        !           333: }
        !           334:
        !           335: /*
        !           336:  * sco_getopt(pcb, option, addr)
        !           337:  *
        !           338:  *     Get SCO pcb options
        !           339:  */
        !           340: int
        !           341: sco_getopt(struct sco_pcb *pcb, int opt, void *addr)
        !           342: {
        !           343:
        !           344:        switch (opt) {
        !           345:        case SO_SCO_MTU:
        !           346:                *(uint16_t *)addr = pcb->sp_mtu;
        !           347:                return sizeof(uint16_t);
        !           348:
        !           349:        case SO_SCO_HANDLE:
        !           350:                if (pcb->sp_link) {
        !           351:                        *(uint16_t *)addr = pcb->sp_link->hl_handle;
        !           352:                        return sizeof(uint16_t);
        !           353:                }
        !           354:                break;
        !           355:
        !           356:        default:
        !           357:                break;
        !           358:        }
        !           359:        return 0;
        !           360: }

CVSweb