diff options
Diffstat (limited to 'net/bluetooth/rfcomm/core.c')
| -rw-r--r-- | net/bluetooth/rfcomm/core.c | 138 | 
1 files changed, 95 insertions, 43 deletions
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ca957d34b0c..754b6fe4f74 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -186,9 +186,9 @@ static void rfcomm_l2state_change(struct sock *sk)  	rfcomm_schedule();  } -static void rfcomm_l2data_ready(struct sock *sk, int bytes) +static void rfcomm_l2data_ready(struct sock *sk)  { -	BT_DBG("%p bytes %d", sk, bytes); +	BT_DBG("%p", sk);  	rfcomm_schedule();  } @@ -216,6 +216,7 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)  	switch (d->sec_level) {  	case BT_SECURITY_HIGH: +	case BT_SECURITY_FIPS:  		auth_type = HCI_AT_GENERAL_BONDING_MITM;  		break;  	case BT_SECURITY_MEDIUM: @@ -306,7 +307,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)  	setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d);  	skb_queue_head_init(&d->tx_queue); -	spin_lock_init(&d->lock); +	mutex_init(&d->lock);  	atomic_set(&d->refcnt, 1);  	rfcomm_dlc_clear_state(d); @@ -359,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)  	return NULL;  } +static int rfcomm_check_channel(u8 channel) +{ +	return channel < 1 || channel > 30; +} +  static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)  {  	struct rfcomm_session *s; @@ -368,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,  	BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",  	       d, d->state, src, dst, channel); -	if (channel < 1 || channel > 30) +	if (rfcomm_check_channel(channel))  		return -EINVAL;  	if (d->state != BT_OPEN && d->state != BT_CLOSED) @@ -425,6 +431,20 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 chann  	return r;  } +static void __rfcomm_dlc_disconn(struct rfcomm_dlc *d) +{ +	struct rfcomm_session *s = d->session; + +	d->state = BT_DISCONN; +	if (skb_queue_empty(&d->tx_queue)) { +		rfcomm_send_disc(s, d->dlci); +		rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); +	} else { +		rfcomm_queue_disc(d); +		rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); +	} +} +  static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)  {  	struct rfcomm_session *s = d->session; @@ -437,32 +457,29 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)  	switch (d->state) {  	case BT_CONNECT:  	case BT_CONFIG: +	case BT_OPEN: +	case BT_CONNECT2:  		if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {  			set_bit(RFCOMM_AUTH_REJECT, &d->flags);  			rfcomm_schedule(); -			break; +			return 0;  		} -		/* Fall through */ +	} +	switch (d->state) { +	case BT_CONNECT:  	case BT_CONNECTED: -		d->state = BT_DISCONN; -		if (skb_queue_empty(&d->tx_queue)) { -			rfcomm_send_disc(s, d->dlci); -			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); -		} else { -			rfcomm_queue_disc(d); -			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); -		} +		__rfcomm_dlc_disconn(d);  		break; -	case BT_OPEN: -	case BT_CONNECT2: -		if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { -			set_bit(RFCOMM_AUTH_REJECT, &d->flags); -			rfcomm_schedule(); +	case BT_CONFIG: +		if (s->state != BT_BOUND) { +			__rfcomm_dlc_disconn(d);  			break;  		} -		/* Fall through */ +		/* if closing a dlc in a session that hasn't been started, +		 * just close and unlink the dlc +		 */  	default:  		rfcomm_dlc_clear_timer(d); @@ -513,6 +530,25 @@ no_session:  	return r;  } +struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel) +{ +	struct rfcomm_session *s; +	struct rfcomm_dlc *dlc = NULL; +	u8 dlci; + +	if (rfcomm_check_channel(channel)) +		return ERR_PTR(-EINVAL); + +	rfcomm_lock(); +	s = rfcomm_session_get(src, dst); +	if (s) { +		dlci = __dlci(!s->initiator, channel); +		dlc = rfcomm_dlc_get(s, dlci); +	} +	rfcomm_unlock(); +	return dlc; +} +  int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)  {  	int len = skb->len; @@ -533,6 +569,20 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)  	return len;  } +void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb) +{ +	int len = skb->len; + +	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); + +	rfcomm_make_uih(skb, d->addr); +	skb_queue_tail(&d->tx_queue, skb); + +	if (d->state == BT_CONNECTED && +	    !test_bit(RFCOMM_TX_THROTTLED, &d->flags)) +		rfcomm_schedule(); +} +  void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)  {  	BT_DBG("dlc %p state %ld", d, d->state); @@ -641,13 +691,13 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)  {  	struct rfcomm_session *s;  	struct list_head *p, *n; -	struct bt_sock *sk; +	struct l2cap_chan *chan;  	list_for_each_safe(p, n, &session_list) {  		s = list_entry(p, struct rfcomm_session, list); -		sk = bt_sk(s->sock->sk); +		chan = l2cap_pi(s->sock->sk)->chan; -		if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && -				!bacmp(&sk->dst, dst)) +		if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) && +		    !bacmp(&chan->dst, dst))  			return s;  	}  	return NULL; @@ -694,6 +744,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,  	addr.l2_family = AF_BLUETOOTH;  	addr.l2_psm    = 0;  	addr.l2_cid    = 0; +	addr.l2_bdaddr_type = BDADDR_BREDR;  	*err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));  	if (*err < 0)  		goto failed; @@ -717,8 +768,9 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,  	bacpy(&addr.l2_bdaddr, dst);  	addr.l2_family = AF_BLUETOOTH; -	addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM); +	addr.l2_psm    = cpu_to_le16(RFCOMM_PSM);  	addr.l2_cid    = 0; +	addr.l2_bdaddr_type = BDADDR_BREDR;  	*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);  	if (*err == 0 || *err == -EINPROGRESS)  		return s; @@ -732,11 +784,11 @@ failed:  void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)  { -	struct sock *sk = s->sock->sk; +	struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;  	if (src) -		bacpy(src, &bt_sk(sk)->src); +		bacpy(src, &chan->src);  	if (dst) -		bacpy(dst, &bt_sk(sk)->dst); +		bacpy(dst, &chan->dst);  }  /* ---- RFCOMM frame sending ---- */ @@ -1941,12 +1993,11 @@ static void rfcomm_process_sessions(void)  			continue;  		} -		if (s->state == BT_LISTEN) { +		switch (s->state) { +		case BT_LISTEN:  			rfcomm_accept_connection(s);  			continue; -		} -		switch (s->state) {  		case BT_BOUND:  			s = rfcomm_check_connection(s);  			break; @@ -1981,8 +2032,9 @@ static int rfcomm_add_listener(bdaddr_t *ba)  	/* Bind socket */  	bacpy(&addr.l2_bdaddr, ba);  	addr.l2_family = AF_BLUETOOTH; -	addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM); +	addr.l2_psm    = cpu_to_le16(RFCOMM_PSM);  	addr.l2_cid    = 0; +	addr.l2_bdaddr_type = BDADDR_BREDR;  	err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));  	if (err < 0) {  		BT_ERR("Bind failed %d", err); @@ -2082,7 +2134,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)  				set_bit(RFCOMM_SEC_PENDING, &d->flags);  				rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);  				continue; -			} else if (d->sec_level == BT_SECURITY_HIGH) { +			} else if (d->sec_level == BT_SECURITY_HIGH || +				   d->sec_level == BT_SECURITY_FIPS) {  				set_bit(RFCOMM_ENC_DROP, &d->flags);  				continue;  			} @@ -2112,12 +2165,11 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)  	rfcomm_lock();  	list_for_each_entry(s, &session_list, list) { +		struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;  		struct rfcomm_dlc *d;  		list_for_each_entry(d, &s->dlcs, list) { -			struct sock *sk = s->sock->sk; -  			seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n", -				   &bt_sk(sk)->src, &bt_sk(sk)->dst, +				   &chan->src, &chan->dst,  				   d->state, d->dlci, d->mtu,  				   d->rx_credits, d->tx_credits);  		} @@ -2155,13 +2207,6 @@ static int __init rfcomm_init(void)  		goto unregister;  	} -	if (bt_debugfs) { -		rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, -				bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops); -		if (!rfcomm_dlc_debugfs) -			BT_ERR("Failed to create RFCOMM debug file"); -	} -  	err = rfcomm_init_ttys();  	if (err < 0)  		goto stop; @@ -2172,6 +2217,13 @@ static int __init rfcomm_init(void)  	BT_INFO("RFCOMM ver %s", VERSION); +	if (IS_ERR_OR_NULL(bt_debugfs)) +		return 0; + +	rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, +						 bt_debugfs, NULL, +						 &rfcomm_dlc_debugfs_fops); +  	return 0;  cleanup:  | 
