Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a5e903c authored by J. Suter's avatar J. Suter Committed by David S. Miller
Browse files

[Bluetooth]: Implement RFCOMM remote port negotiation



This patch implements the remote port negotiation (RPN) of the RFCOMM
protocol for Bluetooth.

Signed-off-by: default avatarJ. Suter <jsuter@hardwave.de>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7b9eb9e2
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -80,9 +80,9 @@
#define RFCOMM_RPN_STOP_15	1

#define RFCOMM_RPN_PARITY_NONE	0x0
#define RFCOMM_RPN_PARITY_ODD	0x4
#define RFCOMM_RPN_PARITY_EVEN	0x5
#define RFCOMM_RPN_PARITY_MARK	0x6
#define RFCOMM_RPN_PARITY_ODD	0x1
#define RFCOMM_RPN_PARITY_EVEN	0x3
#define RFCOMM_RPN_PARITY_MARK	0x5
#define RFCOMM_RPN_PARITY_SPACE	0x7

#define RFCOMM_RPN_FLOW_NONE	0x00
@@ -223,6 +223,12 @@ struct rfcomm_dlc {
#define RFCOMM_CFC_DISABLED 0
#define RFCOMM_CFC_ENABLED  RFCOMM_MAX_CREDITS

/* ---- RFCOMM SEND RPN ---- */
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
			u8 bit_rate, u8 data_bits, u8 stop_bits,
			u8 parity, u8 flow_ctrl_settings, 
			u8 xon_char, u8 xoff_char, u16 param_mask);

/* ---- RFCOMM DLCs (channels) ---- */
struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
void rfcomm_dlc_free(struct rfcomm_dlc *d);
+36 −35
Original line number Diff line number Diff line
@@ -21,10 +21,6 @@
   SOFTWARE IS DISCLAIMED.
*/

/* 
   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
*/

/*
 * Bluetooth RFCOMM core.
 *
@@ -115,10 +111,10 @@ static void rfcomm_session_del(struct rfcomm_session *s);
#define __get_mcc_len(b)  ((b & 0xfe) >> 1)

/* RPN macros */
#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3))
#define __get_rpn_data_bits(line) ((line) & 0x3)
#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
#define __get_rpn_parity(line)    (((line) >> 3) & 0x3)
#define __get_rpn_parity(line)    (((line) >> 3) & 0x7)

static inline void rfcomm_schedule(uint event)
{
@@ -780,7 +776,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
	return rfcomm_send_frame(s, buf, ptr - buf);
}

static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
			u8 bit_rate, u8 data_bits, u8 stop_bits,
			u8 parity, u8 flow_ctrl_settings, 
			u8 xon_char, u8 xoff_char, u16 param_mask)
@@ -1274,7 +1270,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
		return 0;

	if (len == 1) {
		/* request: return default setting */
		/* This is a request, return default settings */
		bit_rate  = RFCOMM_RPN_BR_115200;
		data_bits = RFCOMM_RPN_DATA_8;
		stop_bits = RFCOMM_RPN_STOP_1;
@@ -1282,11 +1278,12 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
		flow_ctrl = RFCOMM_RPN_FLOW_NONE;
		xon_char  = RFCOMM_RPN_XON_CHAR;
		xoff_char = RFCOMM_RPN_XOFF_CHAR;

		goto rpn_out;
	}
	/* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
	                          no flow control lines, normal XON/XOFF chars */

	/* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
	 * no parity, no flow control lines, normal XON/XOFF chars */

	if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
		bit_rate = rpn->bit_rate;
		if (bit_rate != RFCOMM_RPN_BR_115200) {
@@ -1295,6 +1292,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
		data_bits = __get_rpn_data_bits(rpn->line_settings);
		if (data_bits != RFCOMM_RPN_DATA_8) {
@@ -1303,6 +1301,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_DATA;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
		stop_bits = __get_rpn_stop_bits(rpn->line_settings);
		if (stop_bits != RFCOMM_RPN_STOP_1) {
@@ -1311,6 +1310,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_STOP;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
		parity = __get_rpn_parity(rpn->line_settings);
		if (parity != RFCOMM_RPN_PARITY_NONE) {
@@ -1319,6 +1319,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_PARITY;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
		flow_ctrl = rpn->flow_ctrl;
		if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
@@ -1327,6 +1328,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_FLOW;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
		xon_char = rpn->xon_char;
		if (xon_char != RFCOMM_RPN_XON_CHAR) {
@@ -1335,6 +1337,7 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
			rpn_mask ^= RFCOMM_RPN_PM_XON;
		}
	}

	if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
		xoff_char = rpn->xoff_char;
		if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
@@ -1345,9 +1348,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
	}

rpn_out:
	rfcomm_send_rpn(s, 0, dlci, 
			bit_rate, data_bits, stop_bits, parity, flow_ctrl,
			xon_char, xoff_char, rpn_mask);
	rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits,
			parity, flow_ctrl, xon_char, xoff_char, rpn_mask);

	return 0;
}
@@ -1362,10 +1364,9 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb
	if (!cr)
		return 0;

	/* FIXME: 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 */
	/* 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 */

	rfcomm_send_rls(s, 0, dlci, rls->status);

+160 −37
Original line number Diff line number Diff line
@@ -745,20 +745,143 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned
	return -ENOIOCTLCMD;
}

#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
{
	BT_DBG("tty %p", tty);
	struct termios *new = (struct termios *) tty->termios;
	int old_baud_rate = tty_termios_baud_rate(old);
	int new_baud_rate = tty_termios_baud_rate(new);

	if ((tty->termios->c_cflag == old->c_cflag) &&
		(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
		return;
	u8 baud, data_bits, stop_bits, parity, x_on, x_off;
	u16 changes = 0;

	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;

	BT_DBG("tty %p termios %p", tty, old);

	/* Handle turning off CRTSCTS */
	if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) 
		BT_DBG("Turning off CRTSCTS unsupported");

	/* handle turning off CRTSCTS */
	if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
		BT_DBG("turning off CRTSCTS");
	/* Parity on/off and when on, odd/even */
	if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) ||
			((old->c_cflag & PARODD) != (new->c_cflag & PARODD)) ) {
		changes |= RFCOMM_RPN_PM_PARITY;
		BT_DBG("Parity change detected.");
	}

	/* Mark and space parity are not supported! */
	if (new->c_cflag & PARENB) {
		if (new->c_cflag & PARODD) {
			BT_DBG("Parity is ODD");
			parity = RFCOMM_RPN_PARITY_ODD;
		} else {
			BT_DBG("Parity is EVEN");
			parity = RFCOMM_RPN_PARITY_EVEN;
		}
	} else {
		BT_DBG("Parity is OFF");
		parity = RFCOMM_RPN_PARITY_NONE;
	}

	/* Setting the x_on / x_off characters */
	if (old->c_cc[VSTOP] != new->c_cc[VSTOP]) {
		BT_DBG("XOFF custom");
		x_on = new->c_cc[VSTOP];
		changes |= RFCOMM_RPN_PM_XON;
	} else {
		BT_DBG("XOFF default");
		x_on = RFCOMM_RPN_XON_CHAR;
	}

	if (old->c_cc[VSTART] != new->c_cc[VSTART]) {
		BT_DBG("XON custom");
		x_off = new->c_cc[VSTART];
		changes |= RFCOMM_RPN_PM_XOFF;
	} else {
		BT_DBG("XON default");
		x_off = RFCOMM_RPN_XOFF_CHAR;
	}

	/* Handle setting of stop bits */
	if ((old->c_cflag & CSTOPB) != (new->c_cflag & CSTOPB))
		changes |= RFCOMM_RPN_PM_STOP;

	/* POSIX does not support 1.5 stop bits and RFCOMM does not
	 * support 2 stop bits. So a request for 2 stop bits gets
	 * translated to 1.5 stop bits */
	if (new->c_cflag & CSTOPB) {
		stop_bits = RFCOMM_RPN_STOP_15;
	} else {
		stop_bits = RFCOMM_RPN_STOP_1;
	}

	/* Handle number of data bits [5-8] */
	if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) 
		changes |= RFCOMM_RPN_PM_DATA;

	switch (new->c_cflag & CSIZE) {
	case CS5:
		data_bits = RFCOMM_RPN_DATA_5;
		break;
	case CS6:
		data_bits = RFCOMM_RPN_DATA_6;
		break;
	case CS7:
		data_bits = RFCOMM_RPN_DATA_7;
		break;
	case CS8:
		data_bits = RFCOMM_RPN_DATA_8;
		break;
	default:
		data_bits = RFCOMM_RPN_DATA_8;
		break;
	}

	/* Handle baudrate settings */
	if (old_baud_rate != new_baud_rate)
		changes |= RFCOMM_RPN_PM_BITRATE;

	switch (new_baud_rate) {
	case 2400:
		baud = RFCOMM_RPN_BR_2400;
		break;
	case 4800:
		baud = RFCOMM_RPN_BR_4800;
		break;
	case 7200:
		baud = RFCOMM_RPN_BR_7200;
		break;
	case 9600:
		baud = RFCOMM_RPN_BR_9600;
		break;
	case 19200: 
		baud = RFCOMM_RPN_BR_19200;
		break;
	case 38400:
		baud = RFCOMM_RPN_BR_38400;
		break;
	case 57600:
		baud = RFCOMM_RPN_BR_57600;
		break;
	case 115200:
		baud = RFCOMM_RPN_BR_115200;
		break;
	case 230400:
		baud = RFCOMM_RPN_BR_230400;
		break;
	default:
		/* 9600 is standard accordinag to the RFCOMM specification */
		baud = RFCOMM_RPN_BR_9600;
		break;
	
	}

	if (changes)
		rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud,
				data_bits, stop_bits, parity,
				RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);

	return;
}

static void rfcomm_tty_throttle(struct tty_struct *tty)