| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    RFCOMM implementation for Linux Bluetooth stack (BlueZ). | 
					
						
							|  |  |  |    Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> | 
					
						
							|  |  |  |    Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |    it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |    published by the Free Software Foundation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 
					
						
							|  |  |  |    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | 
					
						
							|  |  |  |    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  |    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | 
					
						
							|  |  |  |    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
					
						
							|  |  |  |    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  |    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | 
					
						
							|  |  |  |    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    SOFTWARE IS DISCLAIMED. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Bluetooth RFCOMM core. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2010-03-21 05:27:45 +01:00
										 |  |  | #include <linux/debugfs.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | #include <linux/kthread.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/unaligned.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <net/bluetooth/bluetooth.h>
 | 
					
						
							|  |  |  | #include <net/bluetooth/hci_core.h>
 | 
					
						
							|  |  |  | #include <net/bluetooth/l2cap.h>
 | 
					
						
							|  |  |  | #include <net/bluetooth/rfcomm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-16 10:09:50 +01:00
										 |  |  | #define VERSION "1.11"
 | 
					
						
							| 
									
										
										
										
											2006-02-13 11:39:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-19 14:08:01 +00:00
										 |  |  | static bool disable_cfc; | 
					
						
							|  |  |  | static bool l2cap_ertm; | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | static int channel_mtu = -1; | 
					
						
							| 
									
										
										
										
											2006-02-13 11:39:57 +01:00
										 |  |  | static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static struct task_struct *rfcomm_thread; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-20 22:33:17 -08:00
										 |  |  | static DEFINE_MUTEX(rfcomm_mutex); | 
					
						
							|  |  |  | #define rfcomm_lock()	mutex_lock(&rfcomm_mutex)
 | 
					
						
							|  |  |  | #define rfcomm_unlock()	mutex_unlock(&rfcomm_mutex)
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static LIST_HEAD(session_list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); | 
					
						
							|  |  |  | static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); | 
					
						
							|  |  |  | static int rfcomm_queue_disc(struct rfcomm_dlc *d); | 
					
						
							|  |  |  | static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); | 
					
						
							|  |  |  | static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); | 
					
						
							|  |  |  | static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); | 
					
						
							|  |  |  | static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); | 
					
						
							|  |  |  | static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); | 
					
						
							|  |  |  | static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_process_connect(struct rfcomm_session *s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-19 14:06:10 +03:00
										 |  |  | static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, | 
					
						
							|  |  |  | 							bdaddr_t *dst, | 
					
						
							|  |  |  | 							u8 sec_level, | 
					
						
							|  |  |  | 							int *err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* ---- RFCOMM frame parsing macros ---- */ | 
					
						
							|  |  |  | #define __get_dlci(b)     ((b & 0xfc) >> 2)
 | 
					
						
							|  |  |  | #define __get_channel(b)  ((b & 0xf8) >> 3)
 | 
					
						
							|  |  |  | #define __get_dir(b)      ((b & 0x04) >> 2)
 | 
					
						
							|  |  |  | #define __get_type(b)     ((b & 0xef))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __test_ea(b)      ((b & 0x01))
 | 
					
						
							|  |  |  | #define __test_cr(b)      ((b & 0x02))
 | 
					
						
							|  |  |  | #define __test_pf(b)      ((b & 0x10))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __addr(cr, dlci)       (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
 | 
					
						
							|  |  |  | #define __ctrl(type, pf)       (((type & 0xef) | (pf << 4)))
 | 
					
						
							|  |  |  | #define __dlci(dir, chn)       (((chn & 0x1f) << 1) | dir)
 | 
					
						
							|  |  |  | #define __srv_channel(dlci)    (dlci >> 1)
 | 
					
						
							|  |  |  | #define __dir(dlci)            (dlci & 0x01)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __len8(len)       (((len) << 1) | 1)
 | 
					
						
							|  |  |  | #define __len16(len)      ((len) << 1)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* MCC macros */ | 
					
						
							|  |  |  | #define __mcc_type(cr, type)   (((type << 2) | (cr << 1) | 0x01))
 | 
					
						
							|  |  |  | #define __get_mcc_type(b) ((b & 0xfc) >> 2)
 | 
					
						
							|  |  |  | #define __get_mcc_len(b)  ((b & 0xfe) >> 1)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* RPN macros */ | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | #define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3))
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define __get_rpn_data_bits(line) ((line) & 0x3)
 | 
					
						
							|  |  |  | #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
 | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | #define __get_rpn_parity(line)    (((line) >> 3) & 0x7)
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static void rfcomm_schedule(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!rfcomm_thread) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	wake_up_process(rfcomm_thread); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- RFCOMM FCS computation ---- */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-28 19:20:36 +02:00
										 |  |  | /* reversed, 8-bit, poly=0x07 */ | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | static unsigned char rfcomm_crc_table[256] = { | 
					
						
							| 
									
										
										
										
											2005-10-28 19:20:36 +02:00
										 |  |  | 	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, | 
					
						
							|  |  |  | 	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, | 
					
						
							|  |  |  | 	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, | 
					
						
							|  |  |  | 	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, | 
					
						
							|  |  |  | 	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, | 
					
						
							|  |  |  | 	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, | 
					
						
							|  |  |  | 	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, | 
					
						
							|  |  |  | 	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, | 
					
						
							|  |  |  | 	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, | 
					
						
							|  |  |  | 	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, | 
					
						
							|  |  |  | 	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, | 
					
						
							|  |  |  | 	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, | 
					
						
							|  |  |  | 	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, | 
					
						
							|  |  |  | 	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, | 
					
						
							|  |  |  | 	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, | 
					
						
							|  |  |  | 	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, | 
					
						
							|  |  |  | 	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, | 
					
						
							|  |  |  | 	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, | 
					
						
							|  |  |  | 	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, | 
					
						
							|  |  |  | 	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, | 
					
						
							|  |  |  | 	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, | 
					
						
							|  |  |  | 	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, | 
					
						
							|  |  |  | 	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, | 
					
						
							|  |  |  | 	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, | 
					
						
							|  |  |  | 	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /* CRC on 2 bytes */ | 
					
						
							|  |  |  | #define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | /* FCS on 2 bytes */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline u8 __fcs(u8 *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-09-22 20:43:57 +00:00
										 |  |  | 	return 0xff - __crc(data); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | /* FCS on 3 bytes */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static inline u8 __fcs2(u8 *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-09-22 20:43:57 +00:00
										 |  |  | 	return 0xff - rfcomm_crc_table[__crc(data) ^ data[2]]; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Check FCS */ | 
					
						
							|  |  |  | static inline int __check_fcs(u8 *data, int type, u8 fcs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 f = __crc(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (type != RFCOMM_UIH) | 
					
						
							|  |  |  | 		f = rfcomm_crc_table[f ^ data[2]]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rfcomm_crc_table[f ^ fcs] != 0xcf; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- L2CAP callbacks ---- */ | 
					
						
							|  |  |  | static void rfcomm_l2state_change(struct sock *sk) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("%p state %d", sk, sk->sk_state); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_l2data_ready(struct sock *sk, int bytes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("%p bytes %d", sk, bytes); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_l2sock_create(struct socket **sock) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); | 
					
						
							|  |  |  | 	if (!err) { | 
					
						
							|  |  |  | 		struct sock *sk = (*sock)->sk; | 
					
						
							|  |  |  | 		sk->sk_data_ready   = rfcomm_l2data_ready; | 
					
						
							|  |  |  | 		sk->sk_state_change = rfcomm_l2state_change; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static int rfcomm_check_security(struct rfcomm_dlc *d) | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sock *sk = d->session->sock->sk; | 
					
						
							| 
									
										
										
										
											2011-04-13 20:23:55 -03:00
										 |  |  | 	struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-09 02:48:38 +01:00
										 |  |  | 	__u8 auth_type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (d->sec_level) { | 
					
						
							|  |  |  | 	case BT_SECURITY_HIGH: | 
					
						
							|  |  |  | 		auth_type = HCI_AT_GENERAL_BONDING_MITM; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case BT_SECURITY_MEDIUM: | 
					
						
							|  |  |  | 		auth_type = HCI_AT_GENERAL_BONDING; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		auth_type = HCI_AT_NO_BONDING; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-13 20:23:55 -03:00
										 |  |  | 	return hci_conn_security(conn->hcon, d->sec_level, auth_type); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | static void rfcomm_session_timeout(unsigned long arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s = (void *) arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	set_bit(RFCOMM_TIMED_OUT, &s->flags); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:56 +00:00
										 |  |  | 	mod_timer(&s->timer, jiffies + timeout); | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_session_clear_timer(struct rfcomm_session *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:56 +00:00
										 |  |  | 	del_timer_sync(&s->timer); | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /* ---- RFCOMM DLCs ---- */ | 
					
						
							|  |  |  | static void rfcomm_dlc_timeout(unsigned long arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d = (void *) arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld", d, d->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	set_bit(RFCOMM_TIMED_OUT, &d->flags); | 
					
						
							|  |  |  | 	rfcomm_dlc_put(d); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!mod_timer(&d->timer, jiffies + timeout)) | 
					
						
							|  |  |  | 		rfcomm_dlc_hold(d); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld", d, d->state); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-03 20:32:57 +00:00
										 |  |  | 	if (del_timer(&d->timer)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rfcomm_dlc_put(d); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("%p", d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d->state      = BT_OPEN; | 
					
						
							|  |  |  | 	d->flags      = 0; | 
					
						
							|  |  |  | 	d->mscex      = 0; | 
					
						
							| 
									
										
										
										
											2010-12-06 15:56:17 +02:00
										 |  |  | 	d->sec_level  = BT_SECURITY_LOW; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	d->mtu        = RFCOMM_DEFAULT_MTU; | 
					
						
							|  |  |  | 	d->v24_sig    = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d->cfc        = RFCOMM_CFC_DISABLED; | 
					
						
							|  |  |  | 	d->rx_credits = RFCOMM_DEFAULT_CREDITS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-07 07:46:04 +01:00
										 |  |  | struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-06 15:40:09 +02:00
										 |  |  | 	struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!d) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-23 21:20:07 -08:00
										 |  |  | 	setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	skb_queue_head_init(&d->tx_queue); | 
					
						
							|  |  |  | 	spin_lock_init(&d->lock); | 
					
						
							|  |  |  | 	atomic_set(&d->refcnt, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_dlc_clear_state(d); | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	BT_DBG("%p", d); | 
					
						
							| 
									
										
										
										
											2006-07-06 15:40:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return d; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void rfcomm_dlc_free(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("%p", d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_queue_purge(&d->tx_queue); | 
					
						
							|  |  |  | 	kfree(d); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("dlc %p session %p", d, s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 	rfcomm_session_clear_timer(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_dlc_hold(d); | 
					
						
							|  |  |  | 	list_add(&d->list, &s->dlcs); | 
					
						
							|  |  |  | 	d->session = s; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s = d->session; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_del(&d->list); | 
					
						
							|  |  |  | 	d->session = NULL; | 
					
						
							|  |  |  | 	rfcomm_dlc_put(d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 	if (list_empty(&s->dlcs)) | 
					
						
							|  |  |  | 		rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:56 +02:00
										 |  |  | 	list_for_each_entry(d, &s->dlcs, list) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (d->dlci == dlci) | 
					
						
							|  |  |  | 			return d; | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 	u8 dlci; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-25 12:49:43 +03:00
										 |  |  | 	BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d", | 
					
						
							|  |  |  | 	       d, d->state, src, dst, channel); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (channel < 1 || channel > 30) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (d->state != BT_OPEN && d->state != BT_CLOSED) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s = rfcomm_session_get(src, dst); | 
					
						
							|  |  |  | 	if (!s) { | 
					
						
							| 
									
										
										
										
											2010-08-19 14:06:10 +03:00
										 |  |  | 		s = rfcomm_session_create(src, dst, d->sec_level, &err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (!s) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dlci = __dlci(!s->initiator, channel); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check if DLCI already exists */ | 
					
						
							|  |  |  | 	if (rfcomm_dlc_get(s, dlci)) | 
					
						
							|  |  |  | 		return -EBUSY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_dlc_clear_state(d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d->dlci     = dlci; | 
					
						
							|  |  |  | 	d->addr     = __addr(s->initiator, dlci); | 
					
						
							|  |  |  | 	d->priority = 7; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 	d->state = BT_CONFIG; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_dlc_link(s, d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 	d->out = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	d->mtu = s->mtu; | 
					
						
							|  |  |  | 	d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 	if (s->state == BT_CONNECTED) { | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:40 +01:00
										 |  |  | 		if (rfcomm_check_security(d)) | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 			rfcomm_send_pn(s, 1, d); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | 		else | 
					
						
							|  |  |  | 			set_bit(RFCOMM_AUTH_PENDING, &d->flags); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int r; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r = __rfcomm_dlc_open(d, src, dst, channel); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_unlock(); | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s = d->session; | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld dlci %d err %d session %p", | 
					
						
							|  |  |  | 			d, d->state, d->dlci, err, s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (d->state) { | 
					
						
							|  |  |  | 	case BT_CONNECT: | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 	case BT_CONFIG: | 
					
						
							|  |  |  | 		if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { | 
					
						
							|  |  |  | 			set_bit(RFCOMM_AUTH_REJECT, &d->flags); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 			rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* Fall through */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case BT_CONNECTED: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		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); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 	case BT_OPEN: | 
					
						
							| 
									
										
										
										
											2009-02-16 02:59:49 +01:00
										 |  |  | 	case BT_CONNECT2: | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 		if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { | 
					
						
							|  |  |  | 			set_bit(RFCOMM_AUTH_REJECT, &d->flags); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 			rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* Fall through */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		rfcomm_dlc_clear_timer(d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 		d->state = BT_CLOSED; | 
					
						
							| 
									
										
										
										
											2008-04-01 23:59:06 -07:00
										 |  |  | 		d->state_change(d, err); | 
					
						
							| 
									
										
											  
											
												bluetooth: fix locking bug in the rfcomm socket cleanup handling
in net/bluetooth/rfcomm/sock.c, rfcomm_sk_state_change() does the
following operation:
        if (parent && sock_flag(sk, SOCK_ZAPPED)) {
                /* We have to drop DLC lock here, otherwise
                 * rfcomm_sock_destruct() will dead lock. */
                rfcomm_dlc_unlock(d);
                rfcomm_sock_kill(sk);
                rfcomm_dlc_lock(d);
        }
}
which is fine, since rfcomm_sock_kill() will call sk_free() which will call
rfcomm_sock_destruct() which takes the rfcomm_dlc_lock()... so far so good.
HOWEVER, this assumes that the rfcomm_sk_state_change() function always gets
called with the rfcomm_dlc_lock() taken. This is the case for all but one
case, and in that case where we don't have the lock, we do a double unlock
followed by an attempt to take the lock, which due to underflow isn't
going anywhere fast.
This patch fixes this by moving the stragling case inside the lock, like
the other usages of the same call are doing in this code.
This was found with the help of the www.kerneloops.org project, where this
deadlock was observed 51 times at this point in time:
http://www.kerneloops.org/search.php?search=rfcomm_sock_destruct
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
											
										 
											2008-05-29 01:32:47 -07:00
										 |  |  | 		rfcomm_dlc_unlock(d); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		skb_queue_purge(&d->tx_queue); | 
					
						
							|  |  |  | 		rfcomm_dlc_unlink(d); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:54 +00:00
										 |  |  | 	int r = 0; | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d_list; | 
					
						
							|  |  |  | 	struct rfcomm_session *s, *s_list; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld dlci %d err %d", d, d->state, d->dlci, err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:54 +00:00
										 |  |  | 	s = d->session; | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		goto no_session; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* after waiting on the mutex check the session still exists
 | 
					
						
							|  |  |  | 	 * then check the dlc still exists | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	list_for_each_entry(s_list, &session_list, list) { | 
					
						
							|  |  |  | 		if (s_list == s) { | 
					
						
							|  |  |  | 			list_for_each_entry(d_list, &s->dlcs, list) { | 
					
						
							|  |  |  | 				if (d_list == d) { | 
					
						
							|  |  |  | 					r = __rfcomm_dlc_close(d, err); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:54 +00:00
										 |  |  | no_session: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_unlock(); | 
					
						
							|  |  |  | 	return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int len = skb->len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (d->state != BT_CONNECTED) | 
					
						
							|  |  |  | 		return -ENOTCONN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (len > d->mtu) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_make_uih(skb, d->addr); | 
					
						
							|  |  |  | 	skb_queue_tail(&d->tx_queue, skb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 		rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 15:03:16 -08:00
										 |  |  | void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld", d, d->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!d->cfc) { | 
					
						
							|  |  |  | 		d->v24_sig |= RFCOMM_V24_FC; | 
					
						
							|  |  |  | 		set_bit(RFCOMM_MSC_PENDING, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 15:03:16 -08:00
										 |  |  | void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("dlc %p state %ld", d, d->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!d->cfc) { | 
					
						
							|  |  |  | 		d->v24_sig &= ~RFCOMM_V24_FC; | 
					
						
							|  |  |  | 		set_bit(RFCOMM_MSC_PENDING, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |    Set/get modem status functions use _local_ status i.e. what we report | 
					
						
							|  |  |  |    to the other side. | 
					
						
							|  |  |  |    Remote status is provided by dlc->modem_status() callback. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 	BT_DBG("dlc %p state %ld v24_sig 0x%x", | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			d, d->state, v24_sig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) | 
					
						
							|  |  |  | 		v24_sig |= RFCOMM_V24_FC; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		v24_sig &= ~RFCOMM_V24_FC; | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	d->v24_sig = v24_sig; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 		rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 	BT_DBG("dlc %p state %ld v24_sig 0x%x", | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			d, d->state, d->v24_sig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*v24_sig = d->v24_sig; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- RFCOMM sessions ---- */ | 
					
						
							|  |  |  | static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-06 15:40:09 +02:00
										 |  |  | 	struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!s) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p sock %p", s, sock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 	setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	INIT_LIST_HEAD(&s->dlcs); | 
					
						
							|  |  |  | 	s->state = state; | 
					
						
							|  |  |  | 	s->sock  = sock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->mtu = RFCOMM_DEFAULT_MTU; | 
					
						
							| 
									
										
										
										
											2006-07-03 10:02:51 +02:00
										 |  |  | 	s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Do not increment module usage count for listening sessions.
 | 
					
						
							|  |  |  | 	 * Otherwise we won't be able to unload the module. */ | 
					
						
							|  |  |  | 	if (state != BT_LISTEN) | 
					
						
							|  |  |  | 		if (!try_module_get(THIS_MODULE)) { | 
					
						
							|  |  |  | 			kfree(s); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_add(&s->list, &session_list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int state = s->state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_del(&s->list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 	rfcomm_session_clear_timer(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	sock_release(s->sock); | 
					
						
							|  |  |  | 	kfree(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (state != BT_LISTEN) | 
					
						
							|  |  |  | 		module_put(THIS_MODULE); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 	struct l2cap_chan *chan; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	list_for_each_safe(p, n, &session_list) { | 
					
						
							|  |  |  | 		s = list_entry(p, struct rfcomm_session, list); | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 		chan = l2cap_pi(s->sock->sk)->chan; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 		if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) && | 
					
						
							|  |  |  | 		    !bacmp(&chan->dst, dst)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return s; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, | 
					
						
							|  |  |  | 						   int err) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->state = BT_CLOSED; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:58 +00:00
										 |  |  | 	BT_DBG("session %p state %ld err %d", s, s->state, err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/* Close all dlcs */ | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &s->dlcs) { | 
					
						
							|  |  |  | 		d = list_entry(p, struct rfcomm_dlc, list); | 
					
						
							|  |  |  | 		d->state = BT_CLOSED; | 
					
						
							|  |  |  | 		__rfcomm_dlc_close(d, err); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 	rfcomm_session_clear_timer(s); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:56 +00:00
										 |  |  | 	return rfcomm_session_del(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-19 14:06:10 +03:00
										 |  |  | static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, | 
					
						
							|  |  |  | 							bdaddr_t *dst, | 
					
						
							|  |  |  | 							u8 sec_level, | 
					
						
							|  |  |  | 							int *err) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s = NULL; | 
					
						
							|  |  |  | 	struct sockaddr_l2 addr; | 
					
						
							|  |  |  | 	struct socket *sock; | 
					
						
							|  |  |  | 	struct sock *sk; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-25 12:49:43 +03:00
										 |  |  | 	BT_DBG("%pMR -> %pMR", src, dst); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*err = rfcomm_l2sock_create(&sock); | 
					
						
							|  |  |  | 	if (*err < 0) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bacpy(&addr.l2_bdaddr, src); | 
					
						
							|  |  |  | 	addr.l2_family = AF_BLUETOOTH; | 
					
						
							|  |  |  | 	addr.l2_psm    = 0; | 
					
						
							| 
									
										
										
										
											2009-02-17 21:49:33 +01:00
										 |  |  | 	addr.l2_cid    = 0; | 
					
						
							| 
									
										
										
										
											2013-11-05 16:02:24 +09:00
										 |  |  | 	addr.l2_bdaddr_type = BDADDR_BREDR; | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:06 +02:00
										 |  |  | 	*err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (*err < 0) | 
					
						
							|  |  |  | 		goto failed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set L2CAP options */ | 
					
						
							|  |  |  | 	sk = sock->sk; | 
					
						
							|  |  |  | 	lock_sock(sk); | 
					
						
							| 
									
										
										
										
											2011-04-13 17:20:49 -03:00
										 |  |  | 	l2cap_pi(sk)->chan->imtu = l2cap_mtu; | 
					
						
							| 
									
										
										
										
											2011-04-13 15:57:03 -03:00
										 |  |  | 	l2cap_pi(sk)->chan->sec_level = sec_level; | 
					
						
							| 
									
										
										
										
											2009-10-05 12:23:48 +02:00
										 |  |  | 	if (l2cap_ertm) | 
					
						
							| 
									
										
										
										
											2011-04-13 17:20:49 -03:00
										 |  |  | 		l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	release_sock(sk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s = rfcomm_session_add(sock, BT_BOUND); | 
					
						
							|  |  |  | 	if (!s) { | 
					
						
							|  |  |  | 		*err = -ENOMEM; | 
					
						
							|  |  |  | 		goto failed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s->initiator = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bacpy(&addr.l2_bdaddr, dst); | 
					
						
							|  |  |  | 	addr.l2_family = AF_BLUETOOTH; | 
					
						
							| 
									
										
										
										
											2012-10-10 22:09:29 +05:30
										 |  |  | 	addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM); | 
					
						
							| 
									
										
										
										
											2009-02-17 21:49:33 +01:00
										 |  |  | 	addr.l2_cid    = 0; | 
					
						
							| 
									
										
										
										
											2013-11-05 17:15:42 +09:00
										 |  |  | 	addr.l2_bdaddr_type = BDADDR_BREDR; | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:06 +02:00
										 |  |  | 	*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); | 
					
						
							| 
									
										
										
										
											2006-09-23 09:54:38 +02:00
										 |  |  | 	if (*err == 0 || *err == -EINPROGRESS) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return s; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return rfcomm_session_del(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | failed: | 
					
						
							|  |  |  | 	sock_release(sock); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 	struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (src) | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 		bacpy(src, &chan->src); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (dst) | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 		bacpy(dst, &chan->dst); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- RFCOMM frame sending ---- */ | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct kvec iv = { data, len }; | 
					
						
							|  |  |  | 	struct msghdr msg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	BT_DBG("session %p len %d", s, len); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	memset(&msg, 0, sizeof(msg)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return kernel_sendmsg(s->sock, &msg, &iv, 1, len); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("%p cmd %u", s, cmd->ctrl); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd)); | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_cmd cmd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p dlci %d", s, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd.addr = __addr(s->initiator, dlci); | 
					
						
							|  |  |  | 	cmd.ctrl = __ctrl(RFCOMM_SABM, 1); | 
					
						
							|  |  |  | 	cmd.len  = __len8(0); | 
					
						
							|  |  |  | 	cmd.fcs  = __fcs2((u8 *) &cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | 	return rfcomm_send_cmd(s, &cmd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_cmd cmd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p dlci %d", s, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd.addr = __addr(!s->initiator, dlci); | 
					
						
							|  |  |  | 	cmd.ctrl = __ctrl(RFCOMM_UA, 1); | 
					
						
							|  |  |  | 	cmd.len  = __len8(0); | 
					
						
							|  |  |  | 	cmd.fcs  = __fcs2((u8 *) &cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | 	return rfcomm_send_cmd(s, &cmd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_cmd cmd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p dlci %d", s, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd.addr = __addr(s->initiator, dlci); | 
					
						
							|  |  |  | 	cmd.ctrl = __ctrl(RFCOMM_DISC, 1); | 
					
						
							|  |  |  | 	cmd.len  = __len8(0); | 
					
						
							|  |  |  | 	cmd.fcs  = __fcs2((u8 *) &cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | 	return rfcomm_send_cmd(s, &cmd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_queue_disc(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_cmd *cmd; | 
					
						
							|  |  |  | 	struct sk_buff *skb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlc %p dlci %d", d, d->dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!skb) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd = (void *) __skb_put(skb, sizeof(*cmd)); | 
					
						
							|  |  |  | 	cmd->addr = d->addr; | 
					
						
							|  |  |  | 	cmd->ctrl = __ctrl(RFCOMM_DISC, 1); | 
					
						
							|  |  |  | 	cmd->len  = __len8(0); | 
					
						
							|  |  |  | 	cmd->fcs  = __fcs2((u8 *) cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_queue_tail(&d->tx_queue, skb); | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_cmd cmd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p dlci %d", s, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd.addr = __addr(!s->initiator, dlci); | 
					
						
							|  |  |  | 	cmd.ctrl = __ctrl(RFCOMM_DM, 1); | 
					
						
							|  |  |  | 	cmd.len  = __len8(0); | 
					
						
							|  |  |  | 	cmd.fcs  = __fcs2((u8 *) &cmd); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:58 +02:00
										 |  |  | 	return rfcomm_send_cmd(s, &cmd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d type %d", s, cr, type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc) + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_NSC); | 
					
						
							|  |  |  | 	mcc->len  = __len8(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Type that we didn't like */ | 
					
						
							|  |  |  | 	*ptr = __mcc_type(cr, type); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	struct rfcomm_pn  *pn; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc) + sizeof(*pn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_PN); | 
					
						
							|  |  |  | 	mcc->len  = __len8(sizeof(*pn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pn = (void *) ptr; ptr += sizeof(*pn); | 
					
						
							|  |  |  | 	pn->dlci        = d->dlci; | 
					
						
							|  |  |  | 	pn->priority    = d->priority; | 
					
						
							|  |  |  | 	pn->ack_timer   = 0; | 
					
						
							|  |  |  | 	pn->max_retrans = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (s->cfc) { | 
					
						
							|  |  |  | 		pn->flow_ctrl = cr ? 0xf0 : 0xe0; | 
					
						
							|  |  |  | 		pn->credits = RFCOMM_DEFAULT_CREDITS; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pn->flow_ctrl = 0; | 
					
						
							|  |  |  | 		pn->credits   = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | 	if (cr && channel_mtu >= 0) | 
					
						
							| 
									
										
										
										
											2009-06-07 18:06:51 +02:00
										 |  |  | 		pn->mtu = cpu_to_le16(channel_mtu); | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2009-06-07 18:06:51 +02:00
										 |  |  | 		pn->mtu = cpu_to_le16(d->mtu); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, | 
					
						
							|  |  |  | 			u8 bit_rate, u8 data_bits, u8 stop_bits, | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 			u8 parity, u8 flow_ctrl_settings, | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 			u8 xon_char, u8 xoff_char, u16 param_mask) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	struct rfcomm_rpn *rpn; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 			" flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", | 
					
						
							|  |  |  | 		s, cr, dlci, bit_rate, data_bits, stop_bits, parity, | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 		flow_ctrl_settings, xon_char, xoff_char, param_mask); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc) + sizeof(*rpn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_RPN); | 
					
						
							|  |  |  | 	mcc->len  = __len8(sizeof(*rpn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rpn = (void *) ptr; ptr += sizeof(*rpn); | 
					
						
							|  |  |  | 	rpn->dlci          = __addr(1, dlci); | 
					
						
							|  |  |  | 	rpn->bit_rate      = bit_rate; | 
					
						
							|  |  |  | 	rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); | 
					
						
							|  |  |  | 	rpn->flow_ctrl     = flow_ctrl_settings; | 
					
						
							|  |  |  | 	rpn->xon_char      = xon_char; | 
					
						
							|  |  |  | 	rpn->xoff_char     = xoff_char; | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	rpn->param_mask    = cpu_to_le16(param_mask); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	struct rfcomm_rls *rls; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d status 0x%x", s, cr, status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc) + sizeof(*rls)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_RLS); | 
					
						
							|  |  |  | 	mcc->len  = __len8(sizeof(*rls)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rls = (void *) ptr; ptr += sizeof(*rls); | 
					
						
							|  |  |  | 	rls->dlci   = __addr(1, dlci); | 
					
						
							|  |  |  | 	rls->status = status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	struct rfcomm_msc *msc; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc) + sizeof(*msc)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_MSC); | 
					
						
							|  |  |  | 	mcc->len  = __len8(sizeof(*msc)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msc = (void *) ptr; ptr += sizeof(*msc); | 
					
						
							|  |  |  | 	msc->dlci    = __addr(1, dlci); | 
					
						
							|  |  |  | 	msc->v24_sig = v24_sig | 0x01; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d", s, cr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_FCOFF); | 
					
						
							|  |  |  | 	mcc->len  = __len8(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d", s, cr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr->len  = __len8(sizeof(*mcc)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mcc = (void *) ptr; ptr += sizeof(*mcc); | 
					
						
							|  |  |  | 	mcc->type = __mcc_type(cr, RFCOMM_FCON); | 
					
						
							|  |  |  | 	mcc->len  = __len8(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct socket *sock = s->sock; | 
					
						
							|  |  |  | 	struct kvec iv[3]; | 
					
						
							|  |  |  | 	struct msghdr msg; | 
					
						
							|  |  |  | 	unsigned char hdr[5], crc[1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (len > 125) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p cr %d", s, cr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr[0] = __addr(s->initiator, 0); | 
					
						
							|  |  |  | 	hdr[1] = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 	hdr[2] = 0x01 | ((len + 2) << 1); | 
					
						
							|  |  |  | 	hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); | 
					
						
							|  |  |  | 	hdr[4] = 0x01 | (len << 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	crc[0] = __fcs(hdr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iv[0].iov_base = hdr; | 
					
						
							|  |  |  | 	iv[0].iov_len  = 5; | 
					
						
							|  |  |  | 	iv[1].iov_base = pattern; | 
					
						
							|  |  |  | 	iv[1].iov_len  = len; | 
					
						
							|  |  |  | 	iv[2].iov_base = crc; | 
					
						
							|  |  |  | 	iv[2].iov_len  = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(&msg, 0, sizeof(msg)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return kernel_sendmsg(sock, &msg, iv, 3, 6 + len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	u8 buf[16], *ptr = buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p addr %d credits %d", s, addr, credits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hdr = (void *) ptr; ptr += sizeof(*hdr); | 
					
						
							|  |  |  | 	hdr->addr = addr; | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 1); | 
					
						
							|  |  |  | 	hdr->len  = __len8(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = credits; ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ptr = __fcs(buf); ptr++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 	return rfcomm_send_frame(s, buf, ptr - buf); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr; | 
					
						
							|  |  |  | 	int len = skb->len; | 
					
						
							|  |  |  | 	u8 *crc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (len > 127) { | 
					
						
							|  |  |  | 		hdr = (void *) skb_push(skb, 4); | 
					
						
							| 
									
										
										
										
											2009-06-07 18:06:51 +02:00
										 |  |  | 		put_unaligned(cpu_to_le16(__len16(len)), (__le16 *) &hdr->len); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		hdr = (void *) skb_push(skb, 3); | 
					
						
							|  |  |  | 		hdr->len = __len8(len); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hdr->addr = addr; | 
					
						
							|  |  |  | 	hdr->ctrl = __ctrl(RFCOMM_UIH, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	crc = skb_put(skb, 1); | 
					
						
							|  |  |  | 	*crc = __fcs((void *) hdr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- RFCOMM frame reception ---- */ | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dlci) { | 
					
						
							|  |  |  | 		/* Data channel */ | 
					
						
							|  |  |  | 		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 		if (!d) { | 
					
						
							|  |  |  | 			rfcomm_send_dm(s, dlci); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (d->state) { | 
					
						
							|  |  |  | 		case BT_CONNECT: | 
					
						
							|  |  |  | 			rfcomm_dlc_clear_timer(d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 			d->state = BT_CONNECTED; | 
					
						
							|  |  |  | 			d->state_change(d, 0); | 
					
						
							|  |  |  | 			rfcomm_dlc_unlock(d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rfcomm_send_msc(s, 1, dlci, d->v24_sig); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case BT_DISCONN: | 
					
						
							|  |  |  | 			d->state = BT_CLOSED; | 
					
						
							|  |  |  | 			__rfcomm_dlc_close(d, 0); | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (list_empty(&s->dlcs)) { | 
					
						
							|  |  |  | 				s->state = BT_DISCONN; | 
					
						
							|  |  |  | 				rfcomm_send_disc(s, 0); | 
					
						
							| 
									
										
										
										
											2011-12-06 16:23:26 -08:00
										 |  |  | 				rfcomm_session_clear_timer(s); | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:13 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Control channel */ | 
					
						
							|  |  |  | 		switch (s->state) { | 
					
						
							|  |  |  | 		case BT_CONNECT: | 
					
						
							|  |  |  | 			s->state = BT_CONNECTED; | 
					
						
							|  |  |  | 			rfcomm_process_connect(s); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		case BT_DISCONN: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_session_close(s, ECONNRESET); | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:13 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dlci) { | 
					
						
							|  |  |  | 		/* Data DLC */ | 
					
						
							|  |  |  | 		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 		if (d) { | 
					
						
							|  |  |  | 			if (d->state == BT_CONNECT || d->state == BT_CONFIG) | 
					
						
							|  |  |  | 				err = ECONNREFUSED; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				err = ECONNRESET; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			d->state = BT_CLOSED; | 
					
						
							|  |  |  | 			__rfcomm_dlc_close(d, err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (s->state == BT_CONNECT) | 
					
						
							|  |  |  | 			err = ECONNREFUSED; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			err = ECONNRESET; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		s = rfcomm_session_close(s, err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s, | 
					
						
							|  |  |  | 					       u8 dlci) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dlci) { | 
					
						
							|  |  |  | 		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 		if (d) { | 
					
						
							|  |  |  | 			rfcomm_send_ua(s, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (d->state == BT_CONNECT || d->state == BT_CONFIG) | 
					
						
							|  |  |  | 				err = ECONNREFUSED; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				err = ECONNRESET; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			d->state = BT_CLOSED; | 
					
						
							|  |  |  | 			__rfcomm_dlc_close(d, err); | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			rfcomm_send_dm(s, dlci); | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		rfcomm_send_ua(s, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (s->state == BT_CONNECT) | 
					
						
							|  |  |  | 			err = ECONNREFUSED; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			err = ECONNRESET; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		s = rfcomm_session_close(s, err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | void rfcomm_dlc_accept(struct rfcomm_dlc *d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-03 10:37:55 +02:00
										 |  |  | 	struct sock *sk = d->session->sock->sk; | 
					
						
							| 
									
										
										
										
											2011-04-13 20:23:55 -03:00
										 |  |  | 	struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; | 
					
						
							| 
									
										
										
										
											2006-07-03 10:37:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	BT_DBG("dlc %p", d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_send_ua(d->session, d->dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-26 16:41:56 +02:00
										 |  |  | 	rfcomm_dlc_clear_timer(d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 	d->state = BT_CONNECTED; | 
					
						
							|  |  |  | 	d->state_change(d, 0); | 
					
						
							|  |  |  | 	rfcomm_dlc_unlock(d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:40 +01:00
										 |  |  | 	if (d->role_switch) | 
					
						
							| 
									
										
										
										
											2011-04-13 20:23:55 -03:00
										 |  |  | 		hci_conn_switch_role(conn->hcon, 0x00); | 
					
						
							| 
									
										
										
										
											2006-07-03 10:37:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | static void rfcomm_check_accept(struct rfcomm_dlc *d) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:40 +01:00
										 |  |  | 	if (rfcomm_check_security(d)) { | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 		if (d->defer_setup) { | 
					
						
							|  |  |  | 			set_bit(RFCOMM_DEFER_SETUP, &d->flags); | 
					
						
							|  |  |  | 			rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2009-02-16 02:59:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 			d->state = BT_CONNECT2; | 
					
						
							|  |  |  | 			d->state_change(d, 0); | 
					
						
							|  |  |  | 			rfcomm_dlc_unlock(d); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 		} else | 
					
						
							|  |  |  | 			rfcomm_dlc_accept(d); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		set_bit(RFCOMM_AUTH_PENDING, &d->flags); | 
					
						
							|  |  |  | 		rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	u8 channel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!dlci) { | 
					
						
							|  |  |  | 		rfcomm_send_ua(s, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (s->state == BT_OPEN) { | 
					
						
							|  |  |  | 			s->state = BT_CONNECTED; | 
					
						
							|  |  |  | 			rfcomm_process_connect(s); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check if DLC exists */ | 
					
						
							|  |  |  | 	d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 	if (d) { | 
					
						
							|  |  |  | 		if (d->state == BT_OPEN) { | 
					
						
							|  |  |  | 			/* DLC was previously opened by PN request */ | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 			rfcomm_check_accept(d); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Notify socket layer about incoming connection */ | 
					
						
							|  |  |  | 	channel = __srv_channel(dlci); | 
					
						
							|  |  |  | 	if (rfcomm_connect_ind(s, channel, &d)) { | 
					
						
							|  |  |  | 		d->dlci = dlci; | 
					
						
							|  |  |  | 		d->addr = __addr(s->initiator, dlci); | 
					
						
							|  |  |  | 		rfcomm_dlc_link(s, d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 		rfcomm_check_accept(d); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		rfcomm_send_dm(s, dlci); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s = d->session; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 	BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-03 10:02:51 +02:00
										 |  |  | 	if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || | 
					
						
							|  |  |  | 						pn->flow_ctrl == 0xe0) { | 
					
						
							|  |  |  | 		d->cfc = RFCOMM_CFC_ENABLED; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		d->tx_credits = pn->credits; | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2006-07-03 10:02:51 +02:00
										 |  |  | 		d->cfc = RFCOMM_CFC_DISABLED; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		set_bit(RFCOMM_TX_THROTTLED, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-03 10:02:51 +02:00
										 |  |  | 	if (s->cfc == RFCOMM_CFC_UNKNOWN) | 
					
						
							|  |  |  | 		s->cfc = d->cfc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	d->priority = pn->priority; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-07 18:06:51 +02:00
										 |  |  | 	d->mtu = __le16_to_cpu(pn->mtu); | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (cr && d->mtu > s->mtu) | 
					
						
							|  |  |  | 		d->mtu = s->mtu; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_pn *pn = (void *) skb->data; | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	u8 dlci = pn->dlci; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!dlci) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 	if (d) { | 
					
						
							|  |  |  | 		if (cr) { | 
					
						
							|  |  |  | 			/* PN request */ | 
					
						
							|  |  |  | 			rfcomm_apply_pn(d, cr, pn); | 
					
						
							|  |  |  | 			rfcomm_send_pn(s, 0, d); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* PN response */ | 
					
						
							|  |  |  | 			switch (d->state) { | 
					
						
							|  |  |  | 			case BT_CONFIG: | 
					
						
							|  |  |  | 				rfcomm_apply_pn(d, cr, pn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				d->state = BT_CONNECT; | 
					
						
							|  |  |  | 				rfcomm_send_sabm(s, d->dlci); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		u8 channel = __srv_channel(dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!cr) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* PN request for non existing DLC.
 | 
					
						
							|  |  |  | 		 * Assume incoming connection. */ | 
					
						
							|  |  |  | 		if (rfcomm_connect_ind(s, channel, &d)) { | 
					
						
							|  |  |  | 			d->dlci = dlci; | 
					
						
							|  |  |  | 			d->addr = __addr(s->initiator, dlci); | 
					
						
							|  |  |  | 			rfcomm_dlc_link(s, d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rfcomm_apply_pn(d, cr, pn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			d->state = BT_OPEN; | 
					
						
							|  |  |  | 			rfcomm_send_pn(s, 0, d); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			rfcomm_send_dm(s, dlci); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_rpn *rpn = (void *) skb->data; | 
					
						
							|  |  |  | 	u8 dlci = __get_dlci(rpn->dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u8 bit_rate  = 0; | 
					
						
							|  |  |  | 	u8 data_bits = 0; | 
					
						
							|  |  |  | 	u8 stop_bits = 0; | 
					
						
							|  |  |  | 	u8 parity    = 0; | 
					
						
							|  |  |  | 	u8 flow_ctrl = 0; | 
					
						
							|  |  |  | 	u8 xon_char  = 0; | 
					
						
							|  |  |  | 	u8 xoff_char = 0; | 
					
						
							|  |  |  | 	u16 rpn_mask = RFCOMM_RPN_PM_ALL; | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", | 
					
						
							|  |  |  | 		dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, | 
					
						
							|  |  |  | 		rpn->xon_char, rpn->xoff_char, rpn->param_mask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cr) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (len == 1) { | 
					
						
							| 
									
										
										
										
											2010-07-23 13:57:12 +04:00
										 |  |  | 		/* This is a request, return default (according to ETSI TS 07.10) settings */ | 
					
						
							|  |  |  | 		bit_rate  = RFCOMM_RPN_BR_9600; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		data_bits = RFCOMM_RPN_DATA_8; | 
					
						
							|  |  |  | 		stop_bits = RFCOMM_RPN_STOP_1; | 
					
						
							|  |  |  | 		parity    = RFCOMM_RPN_PARITY_NONE; | 
					
						
							|  |  |  | 		flow_ctrl = RFCOMM_RPN_FLOW_NONE; | 
					
						
							|  |  |  | 		xon_char  = RFCOMM_RPN_XON_CHAR; | 
					
						
							|  |  |  | 		xoff_char = RFCOMM_RPN_XOFF_CHAR; | 
					
						
							|  |  |  | 		goto rpn_out; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
 | 
					
						
							|  |  |  | 	 * no parity, no flow control lines, normal XON/XOFF chars */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		bit_rate = rpn->bit_rate; | 
					
						
							| 
									
										
										
										
											2010-07-23 13:57:12 +04:00
										 |  |  | 		if (bit_rate > RFCOMM_RPN_BR_230400) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); | 
					
						
							| 
									
										
										
										
											2010-07-23 13:57:12 +04:00
										 |  |  | 			bit_rate = RFCOMM_RPN_BR_9600; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_BITRATE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		data_bits = __get_rpn_data_bits(rpn->line_settings); | 
					
						
							|  |  |  | 		if (data_bits != RFCOMM_RPN_DATA_8) { | 
					
						
							|  |  |  | 			BT_DBG("RPN data bits mismatch 0x%x", data_bits); | 
					
						
							|  |  |  | 			data_bits = RFCOMM_RPN_DATA_8; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_DATA; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		stop_bits = __get_rpn_stop_bits(rpn->line_settings); | 
					
						
							|  |  |  | 		if (stop_bits != RFCOMM_RPN_STOP_1) { | 
					
						
							|  |  |  | 			BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); | 
					
						
							|  |  |  | 			stop_bits = RFCOMM_RPN_STOP_1; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_STOP; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		parity = __get_rpn_parity(rpn->line_settings); | 
					
						
							|  |  |  | 		if (parity != RFCOMM_RPN_PARITY_NONE) { | 
					
						
							|  |  |  | 			BT_DBG("RPN parity mismatch 0x%x", parity); | 
					
						
							|  |  |  | 			parity = RFCOMM_RPN_PARITY_NONE; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_PARITY; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		flow_ctrl = rpn->flow_ctrl; | 
					
						
							|  |  |  | 		if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { | 
					
						
							|  |  |  | 			BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); | 
					
						
							|  |  |  | 			flow_ctrl = RFCOMM_RPN_FLOW_NONE; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_FLOW; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		xon_char = rpn->xon_char; | 
					
						
							|  |  |  | 		if (xon_char != RFCOMM_RPN_XON_CHAR) { | 
					
						
							|  |  |  | 			BT_DBG("RPN XON char mismatch 0x%x", xon_char); | 
					
						
							|  |  |  | 			xon_char = RFCOMM_RPN_XON_CHAR; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_XON; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-08 00:28:44 -08:00
										 |  |  | 	if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		xoff_char = rpn->xoff_char; | 
					
						
							|  |  |  | 		if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { | 
					
						
							|  |  |  | 			BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); | 
					
						
							|  |  |  | 			xoff_char = RFCOMM_RPN_XOFF_CHAR; | 
					
						
							|  |  |  | 			rpn_mask ^= RFCOMM_RPN_PM_XOFF; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rpn_out: | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 	rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits, | 
					
						
							|  |  |  | 			parity, flow_ctrl, xon_char, xoff_char, rpn_mask); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_rls *rls = (void *) skb->data; | 
					
						
							|  |  |  | 	u8 dlci = __get_dlci(rls->dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!cr) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 	/* We should probably do something with this information here. But
 | 
					
						
							|  |  |  | 	 * for now it's sufficient just to reply -- Bluetooth 1.1 says it's | 
					
						
							|  |  |  | 	 * mandatory to recognise and respond to RLS */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_send_rls(s, 0, dlci, rls->status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_msc *msc = (void *) skb->data; | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	u8 dlci = __get_dlci(msc->dlci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d = rfcomm_dlc_get(s, dlci); | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 	if (!d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cr) { | 
					
						
							|  |  |  | 		if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc) | 
					
						
							|  |  |  | 			set_bit(RFCOMM_TX_THROTTLED, &d->flags); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			clear_bit(RFCOMM_TX_THROTTLED, &d->flags); | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rfcomm_dlc_lock(d); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		d->remote_v24_sig = msc->v24_sig; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (d->modem_status) | 
					
						
							|  |  |  | 			d->modem_status(d, msc->v24_sig); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rfcomm_dlc_unlock(d); | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rfcomm_send_msc(s, 0, dlci, msc->v24_sig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		d->mscex |= RFCOMM_MSCEX_RX; | 
					
						
							| 
									
										
										
										
											2005-08-09 20:28:46 -07:00
										 |  |  | 	} else | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		d->mscex |= RFCOMM_MSCEX_TX; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_mcc *mcc = (void *) skb->data; | 
					
						
							|  |  |  | 	u8 type, cr, len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cr   = __test_cr(mcc->type); | 
					
						
							|  |  |  | 	type = __get_mcc_type(mcc->type); | 
					
						
							|  |  |  | 	len  = __get_mcc_len(mcc->len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p type 0x%x cr %d", s, type, cr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_pull(skb, 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case RFCOMM_PN: | 
					
						
							|  |  |  | 		rfcomm_recv_pn(s, cr, skb); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_RPN: | 
					
						
							|  |  |  | 		rfcomm_recv_rpn(s, cr, len, skb); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_RLS: | 
					
						
							|  |  |  | 		rfcomm_recv_rls(s, cr, skb); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_MSC: | 
					
						
							|  |  |  | 		rfcomm_recv_msc(s, cr, skb); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_FCOFF: | 
					
						
							|  |  |  | 		if (cr) { | 
					
						
							|  |  |  | 			set_bit(RFCOMM_TX_THROTTLED, &s->flags); | 
					
						
							|  |  |  | 			rfcomm_send_fcoff(s, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_FCON: | 
					
						
							|  |  |  | 		if (cr) { | 
					
						
							|  |  |  | 			clear_bit(RFCOMM_TX_THROTTLED, &s->flags); | 
					
						
							|  |  |  | 			rfcomm_send_fcon(s, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_TEST: | 
					
						
							|  |  |  | 		if (cr) | 
					
						
							|  |  |  | 			rfcomm_send_test(s, 0, skb->data, skb->len); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_NSC: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		BT_ERR("Unknown control type 0x%02x", type); | 
					
						
							|  |  |  | 		rfcomm_send_nsc(s, cr, type); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d = rfcomm_dlc_get(s, dlci); | 
					
						
							|  |  |  | 	if (!d) { | 
					
						
							|  |  |  | 		rfcomm_send_dm(s, dlci); | 
					
						
							|  |  |  | 		goto drop; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pf && d->cfc) { | 
					
						
							|  |  |  | 		u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		d->tx_credits += credits; | 
					
						
							|  |  |  | 		if (d->tx_credits) | 
					
						
							|  |  |  | 			clear_bit(RFCOMM_TX_THROTTLED, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (skb->len && d->state == BT_CONNECTED) { | 
					
						
							|  |  |  | 		rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 		d->rx_credits--; | 
					
						
							|  |  |  | 		d->data_ready(d, skb); | 
					
						
							|  |  |  | 		rfcomm_dlc_unlock(d); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | drop: | 
					
						
							|  |  |  | 	kfree_skb(skb); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s, | 
					
						
							|  |  |  | 						struct sk_buff *skb) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_hdr *hdr = (void *) skb->data; | 
					
						
							|  |  |  | 	u8 type, dlci, fcs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	if (!s) { | 
					
						
							|  |  |  | 		/* no session, so free socket data */ | 
					
						
							|  |  |  | 		kfree_skb(skb); | 
					
						
							|  |  |  | 		return s; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	dlci = __get_dlci(hdr->addr); | 
					
						
							|  |  |  | 	type = __get_type(hdr->ctrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Trim FCS */ | 
					
						
							|  |  |  | 	skb->len--; skb->tail--; | 
					
						
							| 
									
										
										
										
											2007-04-19 20:29:13 -07:00
										 |  |  | 	fcs = *(u8 *)skb_tail_pointer(skb); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (__check_fcs(skb->data, type, fcs)) { | 
					
						
							|  |  |  | 		BT_ERR("bad checksum in packet"); | 
					
						
							|  |  |  | 		kfree_skb(skb); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (__test_ea(hdr->len)) | 
					
						
							|  |  |  | 		skb_pull(skb, 3); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		skb_pull(skb, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case RFCOMM_SABM: | 
					
						
							|  |  |  | 		if (__test_pf(hdr->ctrl)) | 
					
						
							|  |  |  | 			rfcomm_recv_sabm(s, dlci); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_DISC: | 
					
						
							|  |  |  | 		if (__test_pf(hdr->ctrl)) | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_recv_disc(s, dlci); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_UA: | 
					
						
							|  |  |  | 		if (__test_pf(hdr->ctrl)) | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_recv_ua(s, dlci); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_DM: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		s = rfcomm_recv_dm(s, dlci); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case RFCOMM_UIH: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		if (dlci) { | 
					
						
							|  |  |  | 			rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); | 
					
						
							|  |  |  | 			return s; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		rfcomm_recv_mcc(s, skb); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2010-09-08 16:26:53 +03:00
										 |  |  | 		BT_ERR("Unknown packet type 0x%02x", type); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	kfree_skb(skb); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ---- Connection and data processing ---- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_process_connect(struct rfcomm_session *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &s->dlcs) { | 
					
						
							|  |  |  | 		d = list_entry(p, struct rfcomm_dlc, list); | 
					
						
							|  |  |  | 		if (d->state == BT_CONFIG) { | 
					
						
							|  |  |  | 			d->mtu = s->mtu; | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:40 +01:00
										 |  |  | 			if (rfcomm_check_security(d)) { | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | 				rfcomm_send_pn(s, 1, d); | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 				set_bit(RFCOMM_AUTH_PENDING, &d->flags); | 
					
						
							|  |  |  | 				rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Send data queued for the DLC.
 | 
					
						
							|  |  |  |  * Return number of frames left in the queue. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static int rfcomm_process_tx(struct rfcomm_dlc *d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sk_buff *skb; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 	BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			d, d->state, d->cfc, d->rx_credits, d->tx_credits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Send pending MSC */ | 
					
						
							|  |  |  | 	if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 		rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (d->cfc) { | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 		/* CFC enabled.
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		 * Give them some credits */ | 
					
						
							|  |  |  | 		if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 				d->rx_credits <= (d->cfc >> 2)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits); | 
					
						
							|  |  |  | 			d->rx_credits = d->cfc; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* CFC disabled.
 | 
					
						
							|  |  |  | 		 * Give ourselves some credits */ | 
					
						
							|  |  |  | 		d->tx_credits = 5; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) | 
					
						
							|  |  |  | 		return skb_queue_len(&d->tx_queue); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { | 
					
						
							| 
									
										
										
										
											2011-12-20 16:30:44 -02:00
										 |  |  | 		err = rfcomm_send_frame(d->session, skb->data, skb->len); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			skb_queue_head(&d->tx_queue, skb); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		kfree_skb(skb); | 
					
						
							|  |  |  | 		d->tx_credits--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (d->cfc && !d->tx_credits) { | 
					
						
							|  |  |  | 		/* We're out of TX credits.
 | 
					
						
							|  |  |  | 		 * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ | 
					
						
							|  |  |  | 		set_bit(RFCOMM_TX_THROTTLED, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return skb_queue_len(&d->tx_queue); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static void rfcomm_process_dlcs(struct rfcomm_session *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &s->dlcs) { | 
					
						
							|  |  |  | 		d = list_entry(p, struct rfcomm_dlc, list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { | 
					
						
							|  |  |  | 			__rfcomm_dlc_close(d, ETIMEDOUT); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-26 14:19:47 +02:00
										 |  |  | 		if (test_bit(RFCOMM_ENC_DROP, &d->flags)) { | 
					
						
							|  |  |  | 			__rfcomm_dlc_close(d, ECONNREFUSED); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) { | 
					
						
							|  |  |  | 			rfcomm_dlc_clear_timer(d); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 			if (d->out) { | 
					
						
							|  |  |  | 				rfcomm_send_pn(s, 1, d); | 
					
						
							|  |  |  | 				rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				if (d->defer_setup) { | 
					
						
							|  |  |  | 					set_bit(RFCOMM_DEFER_SETUP, &d->flags); | 
					
						
							|  |  |  | 					rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); | 
					
						
							| 
									
										
										
										
											2009-02-16 02:59:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					rfcomm_dlc_lock(d); | 
					
						
							|  |  |  | 					d->state = BT_CONNECT2; | 
					
						
							|  |  |  | 					d->state_change(d, 0); | 
					
						
							|  |  |  | 					rfcomm_dlc_unlock(d); | 
					
						
							| 
									
										
										
										
											2009-01-15 21:56:48 +01:00
										 |  |  | 				} else | 
					
						
							|  |  |  | 					rfcomm_dlc_accept(d); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { | 
					
						
							|  |  |  | 			rfcomm_dlc_clear_timer(d); | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 			if (!d->out) | 
					
						
							|  |  |  | 				rfcomm_send_dm(s, d->dlci); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				d->state = BT_CLOSED; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			__rfcomm_dlc_close(d, ECONNREFUSED); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-02 18:03:57 -08:00
										 |  |  | 		if (test_bit(RFCOMM_SEC_PENDING, &d->flags)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 						d->mscex == RFCOMM_MSCEX_OK) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			rfcomm_process_tx(d); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct socket *sock = s->sock; | 
					
						
							|  |  |  | 	struct sock *sk = sock->sk; | 
					
						
							|  |  |  | 	struct sk_buff *skb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->sk_receive_queue)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Get data directly from socket receive queue without copying it. */ | 
					
						
							|  |  |  | 	while ((skb = skb_dequeue(&sk->sk_receive_queue))) { | 
					
						
							|  |  |  | 		skb_orphan(skb); | 
					
						
							| 
									
										
										
										
											2011-07-22 14:53:58 -07:00
										 |  |  | 		if (!skb_linearize(skb)) | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_recv_frame(s, skb); | 
					
						
							| 
									
										
										
										
											2011-07-22 14:53:58 -07:00
										 |  |  | 		else | 
					
						
							|  |  |  | 			kfree_skb(skb); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:56 +00:00
										 |  |  | 	if (s && (sk->sk_state == BT_CLOSED)) | 
					
						
							|  |  |  | 		s = rfcomm_session_close(s, sk->sk_err); | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static void rfcomm_accept_connection(struct rfcomm_session *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct socket *sock = s->sock, *nsock; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Fast check for a new connection.
 | 
					
						
							|  |  |  | 	 * Avoids unnesesary socket allocations. */ | 
					
						
							|  |  |  | 	if (list_empty(&bt_sk(sock->sk)->accept_q)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("session %p", s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:06 +02:00
										 |  |  | 	err = kernel_accept(sock, &nsock, O_NONBLOCK); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set our callbacks */ | 
					
						
							|  |  |  | 	nsock->sk->sk_data_ready   = rfcomm_l2data_ready; | 
					
						
							|  |  |  | 	nsock->sk->sk_state_change = rfcomm_l2state_change; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s = rfcomm_session_add(nsock, BT_OPEN); | 
					
						
							|  |  |  | 	if (s) { | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | 		/* We should adjust MTU on incoming sessions.
 | 
					
						
							|  |  |  | 		 * L2CAP MTU minus UIH header and FCS. */ | 
					
						
							| 
									
										
										
										
											2011-04-13 17:20:49 -03:00
										 |  |  | 		s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, | 
					
						
							|  |  |  | 				l2cap_pi(nsock->sk)->chan->imtu) - 5; | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 		rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else | 
					
						
							|  |  |  | 		sock_release(nsock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sock *sk = s->sock->sk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("%p state %ld", s, s->state); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-01 16:58:23 +02:00
										 |  |  | 	switch (sk->sk_state) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	case BT_CONNECTED: | 
					
						
							|  |  |  | 		s->state = BT_CONNECT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* We can adjust MTU on outgoing sessions.
 | 
					
						
							|  |  |  | 		 * L2CAP MTU minus UIH header and FCS. */ | 
					
						
							| 
									
										
										
										
											2011-04-13 17:20:49 -03:00
										 |  |  | 		s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		rfcomm_send_sabm(s, 0); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case BT_CLOSED: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		s = rfcomm_session_close(s, sk->sk_err); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 	return s; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-23 04:04:18 -03:00
										 |  |  | static void rfcomm_process_sessions(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &session_list) { | 
					
						
							|  |  |  | 		struct rfcomm_session *s; | 
					
						
							|  |  |  | 		s = list_entry(p, struct rfcomm_session, list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-15 13:50:58 -03:00
										 |  |  | 		if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { | 
					
						
							|  |  |  | 			s->state = BT_DISCONN; | 
					
						
							|  |  |  | 			rfcomm_send_disc(s, 0); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (s->state == BT_LISTEN) { | 
					
						
							|  |  |  | 			rfcomm_accept_connection(s); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (s->state) { | 
					
						
							|  |  |  | 		case BT_BOUND: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_check_connection(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 			s = rfcomm_process_rx(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 14:21:55 +00:00
										 |  |  | 		if (s) | 
					
						
							|  |  |  | 			rfcomm_process_dlcs(s); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_unlock(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_add_listener(bdaddr_t *ba) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sockaddr_l2 addr; | 
					
						
							|  |  |  | 	struct socket *sock; | 
					
						
							|  |  |  | 	struct sock *sk; | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 	int    err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Create socket */ | 
					
						
							|  |  |  | 	err = rfcomm_l2sock_create(&sock); | 
					
						
							| 
									
										
										
										
											2007-02-09 23:24:33 +09:00
										 |  |  | 	if (err < 0) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		BT_ERR("Create socket failed %d", err); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Bind socket */ | 
					
						
							|  |  |  | 	bacpy(&addr.l2_bdaddr, ba); | 
					
						
							|  |  |  | 	addr.l2_family = AF_BLUETOOTH; | 
					
						
							| 
									
										
										
										
											2012-10-10 22:09:29 +05:30
										 |  |  | 	addr.l2_psm    = __constant_cpu_to_le16(RFCOMM_PSM); | 
					
						
							| 
									
										
										
										
											2009-02-17 21:49:33 +01:00
										 |  |  | 	addr.l2_cid    = 0; | 
					
						
							| 
									
										
										
										
											2013-11-05 16:02:24 +09:00
										 |  |  | 	addr.l2_bdaddr_type = BDADDR_BREDR; | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:06 +02:00
										 |  |  | 	err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		BT_ERR("Bind failed %d", err); | 
					
						
							|  |  |  | 		goto failed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set L2CAP options */ | 
					
						
							|  |  |  | 	sk = sock->sk; | 
					
						
							|  |  |  | 	lock_sock(sk); | 
					
						
							| 
									
										
										
										
											2011-04-13 17:20:49 -03:00
										 |  |  | 	l2cap_pi(sk)->chan->imtu = l2cap_mtu; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	release_sock(sk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Start listening on the socket */ | 
					
						
							| 
									
										
										
										
											2007-05-05 00:36:06 +02:00
										 |  |  | 	err = kernel_listen(sock, 10); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (err) { | 
					
						
							|  |  |  | 		BT_ERR("Listen failed %d", err); | 
					
						
							|  |  |  | 		goto failed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Add listening session */ | 
					
						
							|  |  |  | 	s = rfcomm_session_add(sock, BT_LISTEN); | 
					
						
							| 
									
										
										
										
											2013-03-20 20:23:37 +08:00
										 |  |  | 	if (!s) { | 
					
						
							|  |  |  | 		err = -ENOMEM; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		goto failed; | 
					
						
							| 
									
										
										
										
											2013-03-20 20:23:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | failed: | 
					
						
							|  |  |  | 	sock_release(sock); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfcomm_kill_listener(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &session_list) { | 
					
						
							|  |  |  | 		s = list_entry(p, struct rfcomm_session, list); | 
					
						
							|  |  |  | 		rfcomm_session_del(s); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_run(void *unused) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 	BT_DBG(""); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	set_user_nice(current, -10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_add_listener(BDADDR_ANY); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-24 00:10:35 -04:00
										 |  |  | 	while (1) { | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 		set_current_state(TASK_INTERRUPTIBLE); | 
					
						
							| 
									
										
										
										
											2011-07-24 00:10:35 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (kthread_should_stop()) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Process stuff */ | 
					
						
							|  |  |  | 		rfcomm_process_sessions(); | 
					
						
							| 
									
										
										
										
											2011-07-24 00:10:35 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		schedule(); | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-07-24 00:10:35 -04:00
										 |  |  | 	__set_current_state(TASK_RUNNING); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_kill_listener(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 	struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 	struct list_head *p, *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); | 
					
						
							|  |  |  | 	if (!s) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(p, n, &s->dlcs) { | 
					
						
							|  |  |  | 		d = list_entry(p, struct rfcomm_dlc, list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-16 08:17:51 +01:00
										 |  |  | 		if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { | 
					
						
							|  |  |  | 			rfcomm_dlc_clear_timer(d); | 
					
						
							|  |  |  | 			if (status || encrypt == 0x00) { | 
					
						
							| 
									
										
										
										
											2011-09-26 14:19:47 +02:00
										 |  |  | 				set_bit(RFCOMM_ENC_DROP, &d->flags); | 
					
						
							| 
									
										
										
										
											2009-01-16 08:17:51 +01:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { | 
					
						
							|  |  |  | 			if (d->sec_level == BT_SECURITY_MEDIUM) { | 
					
						
							|  |  |  | 				set_bit(RFCOMM_SEC_PENDING, &d->flags); | 
					
						
							|  |  |  | 				rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} else if (d->sec_level == BT_SECURITY_HIGH) { | 
					
						
							| 
									
										
										
										
											2011-09-26 14:19:47 +02:00
										 |  |  | 				set_bit(RFCOMM_ENC_DROP, &d->flags); | 
					
						
							| 
									
										
										
										
											2009-01-16 08:17:51 +01:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-07-14 20:13:45 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-06 09:42:31 +02:00
										 |  |  | 		if (!status && hci_conn_check_secure(conn, d->sec_level)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			set_bit(RFCOMM_AUTH_REJECT, &d->flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-01 12:05:11 +03:00
										 |  |  | 	rfcomm_schedule(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct hci_cb rfcomm_cb = { | 
					
						
							|  |  |  | 	.name		= "RFCOMM", | 
					
						
							| 
									
										
										
										
											2009-01-15 21:58:04 +01:00
										 |  |  | 	.security_cfm	= rfcomm_security_cfm | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-21 05:27:45 +01:00
										 |  |  | static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rfcomm_session *s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:56 +02:00
										 |  |  | 	list_for_each_entry(s, &session_list, list) { | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 		struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan; | 
					
						
							| 
									
										
										
										
											2011-11-01 10:58:56 +02:00
										 |  |  | 		struct rfcomm_dlc *d; | 
					
						
							|  |  |  | 		list_for_each_entry(d, &s->dlcs, list) { | 
					
						
							| 
									
										
										
										
											2012-09-25 12:49:44 +03:00
										 |  |  | 			seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n", | 
					
						
							| 
									
										
										
										
											2013-10-13 09:49:54 -07:00
										 |  |  | 				   &chan->src, &chan->dst, | 
					
						
							| 
									
										
										
										
											2012-09-25 12:49:44 +03:00
										 |  |  | 				   d->state, d->dlci, d->mtu, | 
					
						
							|  |  |  | 				   d->rx_credits, d->tx_credits); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-21 05:27:45 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-21 05:27:45 +01:00
										 |  |  | static const struct file_operations rfcomm_dlc_debugfs_fops = { | 
					
						
							|  |  |  | 	.open		= rfcomm_dlc_debugfs_open, | 
					
						
							|  |  |  | 	.read		= seq_read, | 
					
						
							|  |  |  | 	.llseek		= seq_lseek, | 
					
						
							|  |  |  | 	.release	= single_release, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct dentry *rfcomm_dlc_debugfs; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* ---- Initialization ---- */ | 
					
						
							|  |  |  | static int __init rfcomm_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2009-08-03 04:26:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	hci_register_cb(&rfcomm_cb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 	rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); | 
					
						
							|  |  |  | 	if (IS_ERR(rfcomm_thread)) { | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 		err = PTR_ERR(rfcomm_thread); | 
					
						
							|  |  |  | 		goto unregister; | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 	err = rfcomm_init_ttys(); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		goto stop; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 	err = rfcomm_init_sockets(); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		goto cleanup; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-08 09:57:38 -08:00
										 |  |  | 	BT_INFO("RFCOMM ver %s", VERSION); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 17:24:16 -07:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2009-08-03 04:26:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | cleanup: | 
					
						
							| 
									
										
										
										
											2009-08-03 04:26:16 +00:00
										 |  |  | 	rfcomm_cleanup_ttys(); | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | stop: | 
					
						
							| 
									
										
										
										
											2009-08-03 04:26:16 +00:00
										 |  |  | 	kthread_stop(rfcomm_thread); | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | unregister: | 
					
						
							| 
									
										
										
										
											2009-08-03 04:26:16 +00:00
										 |  |  | 	hci_unregister_cb(&rfcomm_cb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-22 14:49:36 -07:00
										 |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit rfcomm_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-21 05:27:45 +01:00
										 |  |  | 	debugfs_remove(rfcomm_dlc_debugfs); | 
					
						
							| 
									
										
										
										
											2005-11-08 09:57:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	hci_unregister_cb(&rfcomm_cb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-20 21:37:20 +02:00
										 |  |  | 	kthread_stop(rfcomm_thread); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_cleanup_ttys(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfcomm_cleanup_sockets(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(rfcomm_init); | 
					
						
							|  |  |  | module_exit(rfcomm_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-03 10:02:51 +02:00
										 |  |  | module_param(disable_cfc, bool, 0644); | 
					
						
							|  |  |  | MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-14 11:42:12 +02:00
										 |  |  | module_param(channel_mtu, int, 0644); | 
					
						
							|  |  |  | MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-13 11:39:57 +01:00
										 |  |  | module_param(l2cap_mtu, uint, 0644); | 
					
						
							|  |  |  | MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-05 12:23:48 +02:00
										 |  |  | module_param(l2cap_ertm, bool, 0644); | 
					
						
							|  |  |  | MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-18 13:23:53 +02:00
										 |  |  | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); | 
					
						
							|  |  |  | MODULE_VERSION(VERSION); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_ALIAS("bt-proto-3"); |