mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Bluetooth: Add support for BT_PKT_STATUS CMSG data for SCO connections
This change adds support for reporting the BT_PKT_STATUS to the socket CMSG data to allow the implementation of a packet loss correction on erroneous data received on the SCO socket. The patch was partially developed by Marcel Holtmann and validated by Hsin-yu Chao. Signed-off-by: Alain Michaud <alainm@chromium.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
							parent
							
								
									e660b3510e
								
							
						
					
					
						commit
						00398e1d51
					
				
					 5 changed files with 48 additions and 0 deletions
				
			
		| 
						 | 
					@ -147,6 +147,10 @@ struct bt_voice {
 | 
				
			||||||
#define BT_MODE_LE_FLOWCTL	0x03
 | 
					#define BT_MODE_LE_FLOWCTL	0x03
 | 
				
			||||||
#define BT_MODE_EXT_FLOWCTL	0x04
 | 
					#define BT_MODE_EXT_FLOWCTL	0x04
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BT_PKT_STATUS          16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BT_SCM_PKT_STATUS	0x03
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__printf(1, 2)
 | 
					__printf(1, 2)
 | 
				
			||||||
void bt_info(const char *fmt, ...);
 | 
					void bt_info(const char *fmt, ...);
 | 
				
			||||||
__printf(1, 2)
 | 
					__printf(1, 2)
 | 
				
			||||||
| 
						 | 
					@ -286,6 +290,7 @@ struct bt_sock {
 | 
				
			||||||
	struct sock *parent;
 | 
						struct sock *parent;
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	void (*skb_msg_name)(struct sk_buff *, void *, int *);
 | 
						void (*skb_msg_name)(struct sk_buff *, void *, int *);
 | 
				
			||||||
 | 
						void (*skb_put_cmsg)(struct sk_buff *, struct msghdr *, struct sock *);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
| 
						 | 
					@ -335,6 +340,10 @@ struct l2cap_ctrl {
 | 
				
			||||||
	struct l2cap_chan *chan;
 | 
						struct l2cap_chan *chan;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct sco_ctrl {
 | 
				
			||||||
 | 
						u8	pkt_status;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hci_dev;
 | 
					struct hci_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
 | 
					typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
 | 
				
			||||||
| 
						 | 
					@ -361,6 +370,7 @@ struct bt_skb_cb {
 | 
				
			||||||
	u8 incoming:1;
 | 
						u8 incoming:1;
 | 
				
			||||||
	union {
 | 
						union {
 | 
				
			||||||
		struct l2cap_ctrl l2cap;
 | 
							struct l2cap_ctrl l2cap;
 | 
				
			||||||
 | 
							struct sco_ctrl sco;
 | 
				
			||||||
		struct hci_ctrl hci;
 | 
							struct hci_ctrl hci;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,4 +46,6 @@ struct sco_conninfo {
 | 
				
			||||||
	__u8  dev_class[3];
 | 
						__u8  dev_class[3];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SCO_CMSG_PKT_STATUS	0x01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* __SCO_H */
 | 
					#endif /* __SCO_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,6 +286,9 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 | 
				
			||||||
		if (msg->msg_name && bt_sk(sk)->skb_msg_name)
 | 
							if (msg->msg_name && bt_sk(sk)->skb_msg_name)
 | 
				
			||||||
			bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
 | 
								bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
 | 
				
			||||||
						&msg->msg_namelen);
 | 
											&msg->msg_namelen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (bt_sk(sk)->skb_put_cmsg)
 | 
				
			||||||
 | 
								bt_sk(sk)->skb_put_cmsg(skb, msg, sk);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	skb_free_datagram(sk, skb);
 | 
						skb_free_datagram(sk, skb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4554,6 +4554,7 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (conn) {
 | 
						if (conn) {
 | 
				
			||||||
		/* Send to upper protocol */
 | 
							/* Send to upper protocol */
 | 
				
			||||||
 | 
							bt_cb(skb)->sco.pkt_status = flags & 0x03;
 | 
				
			||||||
		sco_recv_scodata(conn, skb);
 | 
							sco_recv_scodata(conn, skb);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,7 @@ struct sco_pinfo {
 | 
				
			||||||
	bdaddr_t	dst;
 | 
						bdaddr_t	dst;
 | 
				
			||||||
	__u32		flags;
 | 
						__u32		flags;
 | 
				
			||||||
	__u16		setting;
 | 
						__u16		setting;
 | 
				
			||||||
 | 
						__u8		cmsg_mask;
 | 
				
			||||||
	struct sco_conn	*conn;
 | 
						struct sco_conn	*conn;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -449,6 +450,15 @@ static void sco_sock_close(struct sock *sk)
 | 
				
			||||||
	sco_sock_kill(sk);
 | 
						sco_sock_kill(sk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void sco_skb_put_cmsg(struct sk_buff *skb, struct msghdr *msg,
 | 
				
			||||||
 | 
								     struct sock *sk)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (sco_pi(sk)->cmsg_mask & SCO_CMSG_PKT_STATUS)
 | 
				
			||||||
 | 
							put_cmsg(msg, SOL_BLUETOOTH, BT_SCM_PKT_STATUS,
 | 
				
			||||||
 | 
								 sizeof(bt_cb(skb)->sco.pkt_status),
 | 
				
			||||||
 | 
								 &bt_cb(skb)->sco.pkt_status);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sco_sock_init(struct sock *sk, struct sock *parent)
 | 
					static void sco_sock_init(struct sock *sk, struct sock *parent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	BT_DBG("sk %p", sk);
 | 
						BT_DBG("sk %p", sk);
 | 
				
			||||||
| 
						 | 
					@ -457,6 +467,8 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
 | 
				
			||||||
		sk->sk_type = parent->sk_type;
 | 
							sk->sk_type = parent->sk_type;
 | 
				
			||||||
		bt_sk(sk)->flags = bt_sk(parent)->flags;
 | 
							bt_sk(sk)->flags = bt_sk(parent)->flags;
 | 
				
			||||||
		security_sk_clone(parent, sk);
 | 
							security_sk_clone(parent, sk);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bt_sk(sk)->skb_put_cmsg = sco_skb_put_cmsg;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -846,6 +858,18 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
		sco_pi(sk)->setting = voice.setting;
 | 
							sco_pi(sk)->setting = voice.setting;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BT_PKT_STATUS:
 | 
				
			||||||
 | 
							if (get_user(opt, (u32 __user *)optval)) {
 | 
				
			||||||
 | 
								err = -EFAULT;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (opt)
 | 
				
			||||||
 | 
								sco_pi(sk)->cmsg_mask |= SCO_CMSG_PKT_STATUS;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								sco_pi(sk)->cmsg_mask &= SCO_CMSG_PKT_STATUS;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -923,6 +947,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
	int len, err = 0;
 | 
						int len, err = 0;
 | 
				
			||||||
	struct bt_voice voice;
 | 
						struct bt_voice voice;
 | 
				
			||||||
	u32 phys;
 | 
						u32 phys;
 | 
				
			||||||
 | 
						int pkt_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BT_DBG("sk %p", sk);
 | 
						BT_DBG("sk %p", sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -969,6 +994,13 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
			err = -EFAULT;
 | 
								err = -EFAULT;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BT_PKT_STATUS:
 | 
				
			||||||
 | 
							pkt_status = (sco_pi(sk)->cmsg_mask & SCO_CMSG_PKT_STATUS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (put_user(pkt_status, (int __user *)optval))
 | 
				
			||||||
 | 
								err = -EFAULT;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue