L2CAP Flow Control 5th stage
Subsystems (2)
Hello together,
this is an update on my attempt to implement L2CAP flow control for the BlueZ stack. I attached the current patch from my snapshot. Be aware that this patch is far from being complete. Instead I'd like to present some results of my coding in the past. So the current patch compiles and should still work with devices that do not want to use flow control. But this code is not meant to be tested in binary mode ;-)
I already implemented the whole configuration procedure in the past. I consider this part as being done in terms of functionality. This time I'd like to emphasize more on the real flow control procedural work that I currently work on. For the beginning I started working on the reception of I- and S-Frames.
Whenever we get a frame from the layer beneath us, l2cap_recv_acldata() is called. Within this function the protocol specific headers of the ACL layer is extracted, error checks are performed and finally the ACL information payload, which consists of a plain L2CAP frame, is being passed to l2cap_recv_frame() in form of a socket buffer. l2cap_recv_frame() decides whether we are using a signalling channel, a connectionless channel or a data channel. We are only concerned for the latter one as flow control mode can only be used in therein.
(1) l2cap_data_channel()
I did not change any of the old functionality that is used for B-Frames. But when a link is configured to be used in flow control mode I-Frames and S-Frames are used instead of B-Frames, so I added some functionality there. First of all, we check if the frame that is being sent is valid. This check is performed in l2cap_invalid_frame_detection(). If this check fails the frame is dropped. The next check is based on the frame's type itself -- the last bit shows whether we are getting an I-Frame or a S-Frame. In both cases we are checking the ReqSeq value and in case of an I-Frame also the TxSeq value. Further more we need to make sure that segmented I-Frames that form one SDU are reassembled and passed to the upper layer. This is done in l2cap_reassembly().
(2) l2cap_invalid_frame_detection()
This is an easy to follow function. The first check ensures if the length field within the L2CAP header is true -- so it must be the same as the length of the socket buffer (the whole frame) excluding the l2cap header. All the other checks are the ones mentioned in the check list of the specification on page 40 (section 3.3.6 Invalid frame detection). Worth being mentioned is only that check number six is not implemented yet within this function: »I-Frame with SAR bits that do not correspond to a normal sequence of either unsegmented or start, continuation, end for the given CID« -- this check is done later within the reassembly function. We are not making use of any error reporting technique yet. But interesting is the use of the crc16() function of the crc16 module. I assume this should do the trick as the generator polynomial is the same. Unfortunately this would require to have the crc16 module loaded to use BlueZ in the future.
(3) l2cap_process_txseq()
The TxSeq is extracted and checked for validity. In case a frame gets lost in L2CAP flow control mode, the specification forces us not to make use of any retransmission nor can we use a special buffer for out-of-sequence frames. Instead those frames considered lost are just ignored and the ExpectedTxSeq value is based upon the last I-Frame received. Duplicated I-Frames are dropped as well es invalid frames. Unfortunately the specification mentions that the ReqSeq value shall be processed according to Section 8.5.4 in case of a duplicated I-Frame, but I didn't figure out what that means practically in this case ...
(4) l2cap_process_reqseq()
This function is alike the above one. First there is a "magical byte-check" which only checks whether we have either an I-Frame (last bit is a 0) or we are getting an S-Frame where the last four bits must be of the form 0001. The next check is concerned about the ReqSeq sequence error. If something went wrong the specification indicates:
»The L2CAP entity shall close the channel as a consequence of an ReqSeq Sequence error.« -- so do we.
In case everything went fine, one of the two timers will be started -- but the timer stuff hasn't been finished yet.
(5) l2cap_reassembly()
This function is currently the most interesting one. Whenever a SDU is segmented into more than one I-Frames we will reassemble those segments here. In case of an unsegmented I-Frame the already complete SDU is passed to the application via sock_queue_rcv_skb() -- I assume this is the function to be used for this action ... is that right? In case of segmentation we have to perform some more logic and to be a bit careful. The logic means we are going to allocate a new socket buffer which we provide per socket (l2cap_pi(sk)->sdu) with the length that is provided within the SDU length field of the first segmented I-Frame. We memcpy the current frame into this new skb data field. In case we receive a frame with the continuation or end flags set, we make two error checks. If both went well we memcpy this frame into the sdu skb. Just in case the SAR_END flag is set and(!) the sdu length matches the length of all the frames that were put into this sdu, we are passing the SDU to the above layer.
It is important to make sure this technique works also when one or more frames get lost. Therefore I provided the last mentioned mechanism and to avoid unnecessary memory consumption I made sure to free the currently allocated sdu memory block when we receive a new frame with SAR_START flags set. So I think this should already work for the cases that the start frame is lost, onw or more of the continuation frames are lost or even if the end frame is lost. But maybe somebody wants to dive into this error checking once more?!
That's basically it. I didn't do anything regarding timer control mechanism nor did I investigate the send mechanism closer. But to give some interested developers the chance to take a look into my code, make some proposals or find bugs, I post this message here.
Martin
include/net/bluetooth/l2cap.h +135 -37
@@ -27,7 +27,19 @@/* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 +#define L2CAP_MIN_MTU 48#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF +#define L2CAP_DEFAULT_TXW 32 +#define L2CAP_MIN_TXW 1 +#define L2CAP_MAX_TXW 32 +#define L2CAP_DEFAULT_MAXT 32 +#define L2CAP_DEFAULT_RETTO 1000 +#define L2CAP_DEFAULT_MONTO 1000 +#define L2CAP_DEFAULT_MPS 65531 +#define L2CAP_MAX_MPS 65531 + +/* maximum possible options for one request/response */ +#define L2CAP_MAX_OPTS 128#define L2CAP_CONN_TIMEOUT (HZ * 40)
@@ -47,6 +59,10 @@__u8 mode; }; +#define L2CAP_MODE_BASIC 0x00 +#define L2CAP_MODE_RET 0x01 +#define L2CAP_MODE_FLOW 0x02 +#define L2CAP_CONNINFO 0x02struct l2cap_conninfo {__u16 hci_handle;
@@ -76,32 +92,38 @@/* L2CAP structures */ struct l2cap_hdr { -__u16 len; -__u16 cid; +__le16 len; +__le16 cid; } __attribute__ ((packed));#define L2CAP_HDR_SIZE 4struct l2cap_cmd_hdr {__u8 code;__u8 ident; -__u16 len; +__le16 len; } __attribute__ ((packed));#define L2CAP_CMD_HDR_SIZE 4struct l2cap_cmd_rej { -__u16 reason; +__le16 reason; +__le16 data[0 ]; } __attribute__ ((packed)); +/* command reject reasons */ +#define L2CAP_CMD_NOT_UNDERSTOOD 0x0000 +#define L2CAP_MTU_EXCEEDED 0x0001 +#define L2CAP_INVALID_CID 0x0002 +struct l2cap_conn_req { -__u16 psm; -__u16 scid; +__le16 psm; +__le16 scid; } __attribute__ ((packed));struct l2cap_conn_rsp { -__u16 dcid; -__u16 scid; -__u16 result; -__u16 status; +__le16 dcid; +__le16 scid; +__le16 result; +__le16 status; } __attribute__ ((packed));/* connect result */
@@ -117,20 +139,22 @@#define L2CAP_CS_AUTHOR_PEND 0x0002struct l2cap_conf_req { -__u16 dcid; -__u16 flags; +__le16 dcid; +__le16 flags;__u8 data[0 ]; } __attribute__ ((packed));struct l2cap_conf_rsp { -__u16 scid; -__u16 flags; -__u16 result; +__le16 scid; +__le16 flags; +__le16 result;__u8 data[0 ]; } __attribute__ ((packed)); -#define L2CAP_CONF_SUCCESS 0x00 -#define L2CAP_CONF_UNACCEPT 0x01 +#define L2CAP_CONF_SUCCESS 0x0000 +#define L2CAP_CONF_UNACCEPT 0x0001 +#define L2CAP_CONF_REJ 0x0002 +#define L2CAP_CONF_UNKNOWN 0x0003struct l2cap_conf_opt {__u8 type;
@@ -146,34 +170,47 @@#define L2CAP_CONF_MAX_SIZE 22 +struct l2cap_conf_rfc { +__u8 mode; +__u8 txw; +__u8 maxt; +__le16 ret_to; +__le16 mon_to; +__le16 mps; +} __attribute__ ((packed)); +struct l2cap_disconn_req { -__u16 dcid; -__u16 scid; +__le16 dcid; +__le16 scid; } __attribute__ ((packed));struct l2cap_disconn_rsp { -__u16 dcid; -__u16 scid; +__le16 dcid; +__le16 scid; } __attribute__ ((packed));struct l2cap_info_req { -__u16 type; -__u8 data[0 ]; +__le16 type; } __attribute__ ((packed));struct l2cap_info_rsp { -__u16 type; -__u16 result; +__le16 type; +__le16 result;__u8 data[0 ]; } __attribute__ ((packed));/* info type */ -#define L2CAP_IT_CL_MTU 0x0001 -#define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_CL_MTU 0x0001 +#define L2CAP_IT_FEAT_MASK 0x0002 + +/* bits for extended features */ +#define L2CAP_EXT_FCM 0x01 +#define L2CAP_EXT_RTM 0x02 +#define L2CAP_EXT_QOS 0x04/* info result */ -#define L2CAP_IR_SUCCESS 0x0000 -#define L2CAP_IR_NOTSUPP 0x0001 +#define L2CAP_IR_SUCCESS 0x0000 +#define L2CAP_IR_NOTSUPP 0x0001/* ----- L2CAP connections ----- */ struct l2cap_chan_list {
@@ -205,33 +242,94 @@struct l2cap_pinfo {struct bt_sock bt; -__u16 psm; +__le16 psm; +__le16 sport;__u16 dcid;__u16 scid;__u16 imtu;__u16 omtu;__u16 flush_to; +__u8 mode;__u32 link_mode; +/* connectionless MTU size from info response */ +__u16 info_mtu; +/* extended feature mask from info response */ +__u32 info_ext; -__u8 conf_state; +/* bitmask for current config state */ +__u16 conf_state;__u8 conf_retry;__u16 conf_mtu; -__u8 ident; +/* Configuration Request RFC Options */ +__u8 conf_mode; +__u8 conf_txw; +__u8 conf_maxt; +__u16 conf_ret_to; +__u16 conf_mon_to; +__u16 conf_mps; + +/* incoming RFC Options */ +__u8 itxw; +__u8 imaxt; +__u16 iret_to; +__u16 imon_to; +__u16 imps; + +/* outgoing RFC Options */ +__u8 otxw; +__u8 omaxt; +__u16 oret_to; +__u16 omon_to; +__u16 omps; + +/* flow control mode */ +__le16 tx_seq; +__le16 next_txseq; +__le16 exp_ackseq; +__le16 req_seq; +__le16 exp_txseq; +__le16 buffer_seq; +__u16 sdu_len; +struct sk_buff *sdu; -__u16 sport; +__u8 ident; +struct timer_list ret_timer; +struct timer_list mon_timer;struct l2cap_conn *conn;struct sock *next_c;struct sock *prev_c; }; -#define L2CAP_CONF_REQ_SENT 0x01 -#define L2CAP_CONF_INPUT_DONE 0x02 -#define L2CAP_CONF_OUTPUT_DONE 0x04 -#define L2CAP_CONF_MAX_RETRIES 2 +#define L2CAP_CONF_MAX_RETRIES 2 + +/* different conf_states */ +#define L2CAP_INFO_REQ_SENT 0x01 +#define L2CAP_CONF_REQ_SENT 0x02 +#define L2CAP_CONF_INPUT_DONE 0x04 +#define L2CAP_CONF_OUTPUT_DONE 0x08 +#define L2CAP_CONF_UNACCEPT_MTU 0x10 +#define L2CAP_CONF_UNACCEPT_RFC 0x20 + +/* RFC definitions */ +#define L2CAP_MAX_DATA_LEN 65531 +#define L2CAP_CONTROL_SIZE 2 +#define L2CAP_FCS_SIZE 2 + +#define L2CAP_SAR_UNSEGMENTED 0x0000 +#define L2CAP_SAR_START 0x4000 +#define L2CAP_SAR_END 0x8000 +#define L2CAP_SAR_CONTINUE 0xC000 +#define L2CAP_SAR_MASK 0xC000 +#define L2CAP_TXSEQ_MASK 0x007E +#define L2CAP_REQSEQ_MASK 0x3F00 + +#define L2CAP_GET_TXSEQ (control) ((__le16_to_cpu(control) & L2CAP_TXSEQ_MASK) >> 1) +#define L2CAP_GET_REQSEQ (control) ((__le16_to_cpu(control) & L2CAP_REQSEQ_MASK) >> 8) +#define L2CAP_GET_SAR (control) (__le16_to_cpu(control) & L2CAP_SAR_MASK)void l2cap_load (void );
net/bluetooth/l2cap.c +743 -115
1,084 lines not on this page · raw message
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel