diff options
Diffstat (limited to 'net/bluetooth/l2cap_sock.c')
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 168 | 
1 files changed, 85 insertions, 83 deletions
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 5c406d3136f..9ca5616166f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -3,6 +3,7 @@     Copyright (C) 2000-2001 Qualcomm Incorporated     Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>     Copyright (C) 2010 Google Inc. +   Copyright (C) 2011 ProFUSION Embedded Systems     Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> @@ -122,70 +123,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al  	if (la.l2_cid && la.l2_psm)  		return -EINVAL; -	lock_sock(sk); - -	if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED -			&& !(la.l2_psm || la.l2_cid)) { -		err = -EINVAL; -		goto done; -	} - -	switch (chan->mode) { -	case L2CAP_MODE_BASIC: -		break; -	case L2CAP_MODE_ERTM: -	case L2CAP_MODE_STREAMING: -		if (!disable_ertm) -			break; -		/* fall through */ -	default: -		err = -ENOTSUPP; -		goto done; -	} - -	switch (sk->sk_state) { -	case BT_CONNECT: -	case BT_CONNECT2: -	case BT_CONFIG: -		/* Already connecting */ -		goto wait; - -	case BT_CONNECTED: -		/* Already connected */ -		err = -EISCONN; -		goto done; - -	case BT_OPEN: -	case BT_BOUND: -		/* Can connect */ -		break; - -	default: -		err = -EBADFD; -		goto done; -	} - -	/* PSM must be odd and lsb of upper byte must be 0 */ -	if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid && -					chan->chan_type != L2CAP_CHAN_RAW) { -		err = -EINVAL; -		goto done; -	} - -	/* Set destination address and psm */ -	bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); -	chan->psm = la.l2_psm; -	chan->dcid = la.l2_cid; - -	err = l2cap_chan_connect(l2cap_pi(sk)->chan); +	err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);  	if (err)  		goto done; -wait:  	err = bt_sock_wait_state(sk, BT_CONNECTED,  			sock_sndtimeo(sk, flags & O_NONBLOCK));  done: -	release_sock(sk); +	if (sock_owned_by_user(sk)) +		release_sock(sk);  	return err;  } @@ -334,7 +280,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us  		opts.mode     = chan->mode;  		opts.fcs      = chan->fcs;  		opts.max_tx   = chan->max_tx; -		opts.txwin_size = (__u16)chan->tx_win; +		opts.txwin_size = chan->tx_win;  		len = min_t(unsigned int, len, sizeof(opts));  		if (copy_to_user(optval, (char *) &opts, len)) @@ -359,10 +305,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us  			break;  		} -		if (chan->role_switch) +		if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))  			opt |= L2CAP_LM_MASTER; -		if (chan->force_reliable) +		if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))  			opt |= L2CAP_LM_RELIABLE;  		if (put_user(opt, (u32 __user *) optval)) @@ -449,7 +395,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch  		break;  	case BT_FLUSHABLE: -		if (put_user(chan->flushable, (u32 __user *) optval)) +		if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), +						(u32 __user *) optval))  			err = -EFAULT;  		break; @@ -461,7 +408,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch  			break;  		} -		pwr.force_active = chan->force_active; +		pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);  		len = min_t(unsigned int, len, sizeof(pwr));  		if (copy_to_user(optval, (char *) &pwr, len)) @@ -469,6 +416,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch  		break; +	case BT_CHANNEL_POLICY: +		if (!enable_hs) { +			err = -ENOPROTOOPT; +			break; +		} + +		if (put_user(chan->chan_policy, (u32 __user *) optval)) +			err = -EFAULT; +		break; +  	default:  		err = -ENOPROTOOPT;  		break; @@ -503,7 +460,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us  		opts.mode     = chan->mode;  		opts.fcs      = chan->fcs;  		opts.max_tx   = chan->max_tx; -		opts.txwin_size = (__u16)chan->tx_win; +		opts.txwin_size = chan->tx_win;  		len = min_t(unsigned int, sizeof(opts), optlen);  		if (copy_from_user((char *) &opts, optval, len)) { @@ -511,7 +468,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us  			break;  		} -		if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { +		if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {  			err = -EINVAL;  			break;  		} @@ -535,7 +492,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us  		chan->omtu = opts.omtu;  		chan->fcs  = opts.fcs;  		chan->max_tx = opts.max_tx; -		chan->tx_win = (__u8)opts.txwin_size; +		chan->tx_win = opts.txwin_size;  		break;  	case L2CAP_LM: @@ -551,8 +508,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us  		if (opt & L2CAP_LM_SECURE)  			chan->sec_level = BT_SECURITY_HIGH; -		chan->role_switch    = (opt & L2CAP_LM_MASTER); -		chan->force_reliable = (opt & L2CAP_LM_RELIABLE); +		if (opt & L2CAP_LM_MASTER) +			set_bit(FLAG_ROLE_SWITCH, &chan->flags); +		else +			clear_bit(FLAG_ROLE_SWITCH, &chan->flags); + +		if (opt & L2CAP_LM_RELIABLE) +			set_bit(FLAG_FORCE_RELIABLE, &chan->flags); +		else +			clear_bit(FLAG_FORCE_RELIABLE, &chan->flags);  		break;  	default: @@ -608,8 +572,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch  		chan->sec_level = sec.level; +		if (!chan->conn) +			break; +  		conn = chan->conn; -		if (conn && chan->scid == L2CAP_CID_LE_DATA) { + +		/*change security for LE channels */ +		if (chan->scid == L2CAP_CID_LE_DATA) {  			if (!conn->hcon->out) {  				err = -EINVAL;  				break; @@ -617,9 +586,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch  			if (smp_conn_security(conn, sec.level))  				break; - -			err = 0;  			sk->sk_state = BT_CONFIG; + +		/* or for ACL link, under defer_setup time */ +		} else if (sk->sk_state == BT_CONNECT2 && +					bt_sk(sk)->defer_setup) { +			err = l2cap_chan_check_security(chan); +		} else { +			err = -EINVAL;  		}  		break; @@ -658,7 +632,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch  			}  		} -		chan->flushable = opt; +		if (opt) +			set_bit(FLAG_FLUSHABLE, &chan->flags); +		else +			clear_bit(FLAG_FLUSHABLE, &chan->flags);  		break;  	case BT_POWER: @@ -675,7 +652,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch  			err = -EFAULT;  			break;  		} -		chan->force_active = pwr.force_active; + +		if (pwr.force_active) +			set_bit(FLAG_FORCE_ACTIVE, &chan->flags); +		else +			clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); +		break; + +	case BT_CHANNEL_POLICY: +		if (!enable_hs) { +			err = -ENOPROTOOPT; +			break; +		} + +		if (get_user(opt, (u32 __user *) optval)) { +			err = -EFAULT; +			break; +		} + +		if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { +			err = -EINVAL; +			break; +		} + +		if (chan->mode != L2CAP_MODE_ERTM && +				chan->mode != L2CAP_MODE_STREAMING) { +			err = -EOPNOTSUPP; +			break; +		} + +		chan->chan_policy = (u8) opt;  		break;  	default: @@ -709,7 +715,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms  		return -ENOTCONN;  	} -	err = l2cap_chan_send(chan, msg, len); +	err = l2cap_chan_send(chan, msg, len, sk->sk_priority);  	release_sock(sk);  	return err; @@ -931,11 +937,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)  		chan->fcs  = pchan->fcs;  		chan->max_tx = pchan->max_tx;  		chan->tx_win = pchan->tx_win; +		chan->tx_win_max = pchan->tx_win_max;  		chan->sec_level = pchan->sec_level; -		chan->role_switch = pchan->role_switch; -		chan->force_reliable = pchan->force_reliable; -		chan->flushable = pchan->flushable; -		chan->force_active = pchan->force_active; +		chan->flags = pchan->flags;  		security_sk_clone(parent, sk);  	} else { @@ -964,12 +968,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)  		chan->max_tx = L2CAP_DEFAULT_MAX_TX;  		chan->fcs  = L2CAP_FCS_CRC16;  		chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; +		chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;  		chan->sec_level = BT_SECURITY_LOW; -		chan->role_switch = 0; -		chan->force_reliable = 0; -		chan->flushable = BT_FLUSHABLE_OFF; -		chan->force_active = BT_POWER_FORCE_ACTIVE_ON; - +		chan->flags = 0; +		set_bit(FLAG_FORCE_ACTIVE, &chan->flags);  	}  	/* Default config options */  |