diff options
author | Mat Martineau <mathewm@codeaurora.org> | 2012-05-17 23:53:44 -0400 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2012-06-04 23:34:04 -0400 |
commit | bed68bde7ebdb591cc67921261307626c8f37936 (patch) | |
tree | 67a920542cc82703bf00fffcca6ab03e69f0dba6 /net/bluetooth/l2cap_core.c | |
parent | e1fbd4c19a5c4d4f490d70e73745cf2cf0dc1955 (diff) |
Bluetooth: Send SREJ frames when packets go missing
The ERTM specification lays out three scenarios for sending SREJ
frames to request retransmission of specific frames. l2cap_send_srej
requests all frames up to a given txseq that are not already queued
for reassembly. l2cap_send_srej_tail only requests the most recent
missing frame. l2cap_send_srej_list resends SREJ frames for data that
was requested for resend but never received.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Diffstat (limited to 'net/bluetooth/l2cap_core.c')
-rw-r--r-- | net/bluetooth/l2cap_core.c | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 26963a5e3f5..5823697cf9d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -2239,17 +2239,67 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, | |||
2239 | 2239 | ||
2240 | static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq) | 2240 | static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq) |
2241 | { | 2241 | { |
2242 | /* Placeholder */ | 2242 | struct l2cap_ctrl control; |
2243 | u16 seq; | ||
2244 | |||
2245 | BT_DBG("chan %p, txseq %d", chan, txseq); | ||
2246 | |||
2247 | memset(&control, 0, sizeof(control)); | ||
2248 | control.sframe = 1; | ||
2249 | control.super = L2CAP_SUPER_SREJ; | ||
2250 | |||
2251 | for (seq = chan->expected_tx_seq; seq != txseq; | ||
2252 | seq = __next_seq(chan, seq)) { | ||
2253 | if (!l2cap_ertm_seq_in_queue(&chan->srej_q, seq)) { | ||
2254 | control.reqseq = seq; | ||
2255 | l2cap_send_sframe(chan, &control); | ||
2256 | l2cap_seq_list_append(&chan->srej_list, seq); | ||
2257 | } | ||
2258 | } | ||
2259 | |||
2260 | chan->expected_tx_seq = __next_seq(chan, txseq); | ||
2243 | } | 2261 | } |
2244 | 2262 | ||
2245 | static void l2cap_send_srej_tail(struct l2cap_chan *chan) | 2263 | static void l2cap_send_srej_tail(struct l2cap_chan *chan) |
2246 | { | 2264 | { |
2247 | /* Placeholder */ | 2265 | struct l2cap_ctrl control; |
2266 | |||
2267 | BT_DBG("chan %p", chan); | ||
2268 | |||
2269 | if (chan->srej_list.tail == L2CAP_SEQ_LIST_CLEAR) | ||
2270 | return; | ||
2271 | |||
2272 | memset(&control, 0, sizeof(control)); | ||
2273 | control.sframe = 1; | ||
2274 | control.super = L2CAP_SUPER_SREJ; | ||
2275 | control.reqseq = chan->srej_list.tail; | ||
2276 | l2cap_send_sframe(chan, &control); | ||
2248 | } | 2277 | } |
2249 | 2278 | ||
2250 | static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq) | 2279 | static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq) |
2251 | { | 2280 | { |
2252 | /* Placeholder */ | 2281 | struct l2cap_ctrl control; |
2282 | u16 initial_head; | ||
2283 | u16 seq; | ||
2284 | |||
2285 | BT_DBG("chan %p, txseq %d", chan, txseq); | ||
2286 | |||
2287 | memset(&control, 0, sizeof(control)); | ||
2288 | control.sframe = 1; | ||
2289 | control.super = L2CAP_SUPER_SREJ; | ||
2290 | |||
2291 | /* Capture initial list head to allow only one pass through the list. */ | ||
2292 | initial_head = chan->srej_list.head; | ||
2293 | |||
2294 | do { | ||
2295 | seq = l2cap_seq_list_pop(&chan->srej_list); | ||
2296 | if (seq == txseq || seq == L2CAP_SEQ_LIST_CLEAR) | ||
2297 | break; | ||
2298 | |||
2299 | control.reqseq = seq; | ||
2300 | l2cap_send_sframe(chan, &control); | ||
2301 | l2cap_seq_list_append(&chan->srej_list, seq); | ||
2302 | } while (chan->srej_list.head != initial_head); | ||
2253 | } | 2303 | } |
2254 | 2304 | ||
2255 | static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq) | 2305 | static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq) |