void
ng_hci_unit_clean(ng_hci_unit_p unit, int reason)
{
	int	size;

	/* Drain command queue */
	if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
		ng_hci_command_untimeout(unit);

	NG_BT_MBUFQ_DRAIN(&unit->cmdq);
	NG_HCI_BUFF_CMD_SET(unit->buffer, 1);

	/* Clean up connection list */
	while (!LIST_EMPTY(&unit->con_list)) {
		ng_hci_unit_con_p	con = LIST_FIRST(&unit->con_list);

		/* Remove all timeouts (if any) */
		if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
			ng_hci_con_untimeout(con);

		/*
		 * Notify upper layer protocol and destroy connection 
		 * descriptor. Do not really care about the result.
		 */

		ng_hci_lp_discon_ind(con, reason);
		ng_hci_free_con(con);
	}

	NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
	NG_HCI_BUFF_ACL_FREE(unit->buffer, size);

	NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
	NG_HCI_BUFF_SCO_FREE(unit->buffer, size);

	/* Clean up neighbors list */
	ng_hci_flush_neighbor_cache(unit);
} /* ng_hci_unit_clean */
static int
ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
{
	struct sco_con_req {
		ng_hci_cmd_pkt_t	 hdr;
		ng_hci_add_sco_con_cp	 cp;
	} __attribute__ ((packed))	*req = NULL;
	ng_hci_lp_con_req_ep		*ep = NULL;
	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
	struct mbuf			*m = NULL;
	int				 error = 0;

	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);

	/*
	 * SCO connection without ACL link
	 *
	 * If upper layer requests SCO connection and there is no open ACL 
	 * connection to the desired remote unit, we will reject the request.
	 */

	LIST_FOREACH(acl_con, &unit->con_list, next)
		if (acl_con->link_type == NG_HCI_LINK_ACL &&
		    acl_con->state == NG_HCI_CON_OPEN &&
		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
			break;

	if (acl_con == NULL) {
		NG_HCI_INFO(
"%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
			__func__, NG_NODE_NAME(unit->node),
			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);

		error = ENOENT;
		goto out;
	}

	/*
	 * Multiple SCO connections can exist between the same pair of units.
	 * We assume that multiple SCO connections have to be opened one after 
	 * another. 
	 *
	 * Try to find SCO connection descriptor that matches the following:
	 *
	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
	 * 
	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
	 * 
	 * 3) sco_con->bdaddr == ep->bdaddr
	 *
	 * Two cases:
	 *
	 * 1) We do not have connection descriptor. This is simple. Just 
	 *    create new connection and submit Add_SCO_Connection command.
	 *
	 * 2) We do have connection descriptor. We need to check the state.
	 *
	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
	 *      connection from the remote unit. This is a race condition and
	 *      we will ignore the request.
	 *
	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
	 *      connection or we just accepted it.
	 */

	LIST_FOREACH(sco_con, &unit->con_list, next)
		if (sco_con->link_type == NG_HCI_LINK_SCO &&
		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
			break;

	if (sco_con != NULL) {
		switch (sco_con->state) {
		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
			error = EALREADY;
			break;

		case NG_HCI_CON_W4_CONN_COMPLETE:
			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
			break;

		default:
			panic(
"%s: %s - Inavalid connection state=%d\n",
				__func__, NG_NODE_NAME(unit->node),
				sco_con->state);
			break;
		}

		goto out;
	}

	/*
	 * If we got here then we need to create new SCO connection descriptor
	 * and submit HCI command.
	 */

	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
	if (sco_con == NULL) {
		error = ENOMEM;
		goto out;
	}

	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));

	/* 
	 * Create HCI command 
	 */

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL) {
		ng_hci_free_con(sco_con);
		error = ENOBUFS;
		goto out;
	}

	m->m_pkthdr.len = m->m_len = sizeof(*req);
	req = mtod(m, struct sco_con_req *);
	req->hdr.type = NG_HCI_CMD_PKT;
	req->hdr.length = sizeof(req->cp);
	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
					NG_HCI_OCF_ADD_SCO_CON));

	req->cp.con_handle = htole16(acl_con->con_handle);

	req->cp.pkt_type = NG_HCI_PKT_HV1;
	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
		req->cp.pkt_type |= NG_HCI_PKT_HV2;
	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
		req->cp.pkt_type |= NG_HCI_PKT_HV3;

	req->cp.pkt_type &= unit->packet_mask;
	if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
				 NG_HCI_PKT_HV2|
				 NG_HCI_PKT_HV3)) == 0)
		req->cp.pkt_type = NG_HCI_PKT_HV1;

	req->cp.pkt_type = htole16(req->cp.pkt_type);

	/* 
	 * Adust connection state
	 */

	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;

	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
	ng_hci_con_timeout(sco_con);

	/* 
	 * Queue and send HCI command
	 */

	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
		error = ng_hci_send_command(unit);
out:
	NG_FREE_ITEM(item);

	return (error);
} /* ng_hci_lp_sco_con_req */
static int
ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
{
	struct acl_con_req {
		ng_hci_cmd_pkt_t	 hdr;
		ng_hci_create_con_cp	 cp;
	} __attribute__ ((packed))	*req = NULL;
	ng_hci_lp_con_req_ep		*ep = NULL;
	ng_hci_unit_con_p		 con = NULL;
	ng_hci_neighbor_t		*n = NULL;
	struct mbuf			*m = NULL;
	int				 error = 0;

	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);

	/*
	 * Only one ACL connection can exist between each pair of units.
	 * So try to find ACL connection descriptor (in any state) that
	 * has requested remote BD_ADDR.
	 *
	 * Two cases:
	 *
	 * 1) We do not have connection to the remote unit. This is simple.
	 *    Just create new connection descriptor and send HCI command to
	 *    create new connection.
	 *
	 * 2) We do have connection descriptor. We need to check connection
	 *    state:
	 * 
	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
	 *      accepting connection from the remote unit. This is a race
	 *      condition. We will ignore this message.
	 *
	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
	 *      requested connection or we just accepted it. In any case
	 *      all we need to do here is set appropriate notification bit
	 *      and wait.
	 *	
	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
	 *      and let upper layer know that we have connection already.
	 */

	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
	if (con != NULL) {
		switch (con->state) {
		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
			error = EALREADY;
			break;

		case NG_HCI_CON_W4_CONN_COMPLETE:
			if (hook == unit->acl)
				con->flags |= NG_HCI_CON_NOTIFY_ACL;
			else
				con->flags |= NG_HCI_CON_NOTIFY_SCO;
			break;

		case NG_HCI_CON_OPEN: {
			struct ng_mesg		*msg = NULL;
			ng_hci_lp_con_cfm_ep	*cfm = NULL;

			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
				NGI_GET_MSG(item, msg);
				NG_FREE_MSG(msg);

				NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 
					NGM_HCI_LP_CON_CFM, sizeof(*cfm), 
					M_NOWAIT);
				if (msg != NULL) {
					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
					cfm->status = 0;
					cfm->link_type = con->link_type;
					cfm->con_handle = con->con_handle;
					bcopy(&con->bdaddr, &cfm->bdaddr, 
						sizeof(cfm->bdaddr));

					/*
					 * This will forward item back to
					 * sender and set item to NULL
					 */

					_NGI_MSG(item) = msg;
					NG_FWD_ITEM_HOOK(error, item, hook);
				} else
					error = ENOMEM;
			} else
				NG_HCI_INFO(
"%s: %s - Source hook is not valid, hook=%p\n",
					__func__, NG_NODE_NAME(unit->node), 
					hook);
			} break;

		default:
			panic(
"%s: %s - Invalid connection state=%d\n",
				__func__, NG_NODE_NAME(unit->node), con->state);
			break;
		}

		goto out;
	}

	/*
	 * If we got here then we need to create new ACL connection descriptor
	 * and submit HCI command. First create new connection desriptor, set
	 * bdaddr and notification flags.
	 */

	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
	if (con == NULL) {
		error = ENOMEM;
		goto out;
	}

	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));

	/* 
	 * Create HCI command 
	 */

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL) {
		ng_hci_free_con(con);
		error = ENOBUFS;
		goto out;
	}

	m->m_pkthdr.len = m->m_len = sizeof(*req);
	req = mtod(m, struct acl_con_req *);
	req->hdr.type = NG_HCI_CMD_PKT;
	req->hdr.length = sizeof(req->cp);
	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
					NG_HCI_OCF_CREATE_CON));

	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));

	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
	if (unit->features[0] & NG_HCI_LMP_3SLOT)
		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
	if (unit->features[0] & NG_HCI_LMP_5SLOT)
		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);

	req->cp.pkt_type &= unit->packet_mask;
	if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
				 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
				 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);

	req->cp.pkt_type = htole16(req->cp.pkt_type);

	if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
		req->cp.accept_role_switch = 1;
	else
		req->cp.accept_role_switch = 0;

	/*
	 * We may speed up connect by specifying valid parameters. 
	 * So check the neighbor cache.
	 */

	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
	if (n == NULL) {
		req->cp.page_scan_rep_mode = 0;
		req->cp.page_scan_mode = 0;
		req->cp.clock_offset = 0;
	} else {
		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
		req->cp.page_scan_mode = n->page_scan_mode;
		req->cp.clock_offset = htole16(n->clock_offset);
	}

	/* 
	 * Adust connection state 
	 */

	if (hook == unit->acl)
		con->flags |= NG_HCI_CON_NOTIFY_ACL;
	else
		con->flags |= NG_HCI_CON_NOTIFY_SCO;

	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
	ng_hci_con_timeout(con);

	/* 
	 * Queue and send HCI command 
	 */

	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
		error = ng_hci_send_command(unit);
out:
	if (item != NULL)
		NG_FREE_ITEM(item);

	return (error);
} /* ng_hci_lp_acl_con_req */
Beispiel #4
0
static int
ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type)
{
	struct acl_con_req {
		ng_hci_cmd_pkt_t	 hdr;
		ng_hci_le_create_connection_cp	 cp;
	} __attribute__ ((packed))	*req = NULL;
	ng_hci_lp_con_req_ep		*ep = NULL;
	ng_hci_unit_con_p		 con = NULL;
	struct mbuf			*m = NULL;
	int				 error = 0;

	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
	if((link_type != NG_HCI_LINK_LE_PUBLIC)&&
	   (link_type != NG_HCI_LINK_LE_RANDOM)){
		printf("%s: Link type %d Cannot be here \n", __func__, 
		       link_type);
	}
	/*
	 * Only one ACL connection can exist between each pair of units.
	 * So try to find ACL connection descriptor (in any state) that
	 * has requested remote BD_ADDR.
	 *
	 * Two cases:
	 *
	 * 1) We do not have connection to the remote unit. This is simple.
	 *    Just create new connection descriptor and send HCI command to
	 *    create new connection.
	 *
	 * 2) We do have connection descriptor. We need to check connection
	 *    state:
	 * 
	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
	 *      accepting connection from the remote unit. This is a race
	 *      condition. We will ignore this message.
	 *
	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
	 *      requested connection or we just accepted it. In any case
	 *      all we need to do here is set appropriate notification bit
	 *      and wait.
	 *	
	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
	 *      and let upper layer know that we have connection already.
	 */

	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type);
	if (con != NULL) {
		switch (con->state) {
		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
			error = EALREADY;
			break;

		case NG_HCI_CON_W4_CONN_COMPLETE:
			if (hook != unit->sco)
				con->flags |= NG_HCI_CON_NOTIFY_ACL;
			else
				con->flags |= NG_HCI_CON_NOTIFY_SCO;
			break;

		case NG_HCI_CON_OPEN: {
			struct ng_mesg		*msg = NULL;
			ng_hci_lp_con_cfm_ep	*cfm = NULL;

			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
				NGI_GET_MSG(item, msg);
				NG_FREE_MSG(msg);

				NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 
					NGM_HCI_LP_CON_CFM, sizeof(*cfm), 
					M_NOWAIT);
				if (msg != NULL) {
					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
					cfm->status = 0;
					cfm->link_type = con->link_type;
					cfm->con_handle = con->con_handle;
					bcopy(&con->bdaddr, &cfm->bdaddr, 
						sizeof(cfm->bdaddr));

					/*
					 * This will forward item back to
					 * sender and set item to NULL
					 */

					_NGI_MSG(item) = msg;
					NG_FWD_ITEM_HOOK(error, item, hook);
				} else
					error = ENOMEM;
			} else
				NG_HCI_INFO(
"%s: %s - Source hook is not valid, hook=%p\n",
					__func__, NG_NODE_NAME(unit->node), 
					hook);
			} break;

		default:
			panic(
"%s: %s - Invalid connection state=%d\n",
				__func__, NG_NODE_NAME(unit->node), con->state);
			break;
		}

		goto out;
	}

	/*
	 * If we got here then we need to create new ACL connection descriptor
	 * and submit HCI command. First create new connection desriptor, set
	 * bdaddr and notification flags.
	 */

	con = ng_hci_new_con(unit, link_type);
	if (con == NULL) {
		error = ENOMEM;
		goto out;
	}

	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));

	/* 
	 * Create HCI command 
	 */

	MGETHDR(m, M_NOWAIT, MT_DATA);
	if (m == NULL) {
		ng_hci_free_con(con);
		error = ENOBUFS;
		goto out;
	}

	m->m_pkthdr.len = m->m_len = sizeof(*req);
	req = mtod(m, struct acl_con_req *);
	req->hdr.type = NG_HCI_CMD_PKT;
	req->hdr.length = sizeof(req->cp);
	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE,
					NG_HCI_OCF_LE_CREATE_CONNECTION));
	
	bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr));
	req->cp.own_address_type = 0;
	req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0;
	req->cp.scan_interval = htole16(4);
	req->cp.scan_window = htole16(4);
	req->cp.filter_policy = 0;
	req->cp.conn_interval_min = htole16(0xf);
	req->cp.conn_interval_max = htole16(0xf);
	req->cp.conn_latency = htole16(0);
	req->cp.supervision_timeout = htole16(0xc80);
	req->cp.min_ce_length = htole16(1);
	req->cp.max_ce_length = htole16(1);
	/* 
	 * Adust connection state 
	 */

	if (hook != unit->sco)
		con->flags |= NG_HCI_CON_NOTIFY_ACL;
	else
		con->flags |= NG_HCI_CON_NOTIFY_SCO;

	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
	ng_hci_con_timeout(con);

	/* 
	 * Queue and send HCI command 
	 */

	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
		error = ng_hci_send_command(unit);
out:
	if (item != NULL)
		NG_FREE_ITEM(item);

	return (error);
} /* ng_hci_lp_acl_con_req */