static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan) { struct net_buf *buf; struct bt_l2cap_le_credits *ev; uint16_t credits; /* Only give more credits if it went bellow the defined threshold */ if (k_sem_count_get(&chan->rx.credits) > L2CAP_LE_CREDITS_THRESHOLD(chan->rx.init_credits)) { goto done; } /* Restore credits */ credits = chan->rx.init_credits - k_sem_count_get(&chan->rx.credits); l2cap_chan_rx_give_credits(chan, credits); buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CREDITS, get_ident(), sizeof(*ev)); ev = net_buf_add(buf, sizeof(*ev)); ev->cid = sys_cpu_to_le16(chan->rx.cid); ev->credits = sys_cpu_to_le16(credits); bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); done: BT_DBG("chan %p credits %u", chan, k_sem_count_get(&chan->rx.credits)); }
/* must be called with interrupts locked */ static inline int is_condition_met(struct k_poll_event *event, u32_t *state) { switch (event->type) { case K_POLL_TYPE_SEM_AVAILABLE: if (k_sem_count_get(event->sem) > 0) { *state = K_POLL_STATE_SEM_AVAILABLE; return 1; } break; case K_POLL_TYPE_DATA_AVAILABLE: if (!k_queue_is_empty(event->queue)) { *state = K_POLL_STATE_FIFO_DATA_AVAILABLE; return 1; } break; case K_POLL_TYPE_SIGNAL: if (event->signal->signaled) { *state = K_POLL_STATE_SIGNALED; return 1; } break; case K_POLL_TYPE_IGNORE: return 0; default: __ASSERT(0, "invalid event type (0x%x)\n", event->type); break; } return 0; }
static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, uint16_t sdu_hdr_len) { int len; /* Wait for credits */ if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) { BT_DBG("No credits to transmit packet"); return -EAGAIN; } buf = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); if (!buf) { return -ENOMEM; } /* Channel may have been disconnected while waiting for credits */ if (!ch->chan.conn) { net_buf_unref(buf); return -ECONNRESET; } BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, buf->len, k_sem_count_get(&ch->tx.credits)); len = buf->len; bt_l2cap_send(ch->chan.conn, ch->tx.cid, buf); return len; }
void test_sema_count_get(void) { k_sem_init(&sema, SEM_INITIAL, SEM_LIMIT); /**TESTPOINT: sem count get upon init*/ zassert_equal(k_sem_count_get(&sema), SEM_INITIAL, NULL); k_sem_give(&sema); /**TESTPOINT: sem count get after give*/ zassert_equal(k_sem_count_get(&sema), SEM_INITIAL + 1, NULL); k_sem_take(&sema, K_FOREVER); /**TESTPOINT: sem count get after take*/ for (int i = 0; i < SEM_LIMIT; i++) { zassert_equal(k_sem_count_get(&sema), SEM_INITIAL + i, NULL); k_sem_give(&sema); } /**TESTPOINT: sem give above limit*/ k_sem_give(&sema); zassert_equal(k_sem_count_get(&sema), SEM_LIMIT, NULL); }
static void le_credits(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_l2cap_chan *chan; struct bt_l2cap_le_credits *ev = (void *)buf->data; struct bt_l2cap_le_chan *ch; uint16_t credits, cid; if (buf->len < sizeof(*ev)) { BT_ERR("Too small LE Credits packet size"); return; } cid = sys_le16_to_cpu(ev->cid); credits = sys_le16_to_cpu(ev->credits); BT_DBG("cid 0x%04x credits %u", cid, credits); chan = bt_l2cap_le_lookup_tx_cid(conn, cid); if (!chan) { BT_ERR("Unable to find channel of LE Credits packet"); return; } ch = BT_L2CAP_LE_CHAN(chan); if (k_sem_count_get(&ch->tx.credits) + credits > UINT16_MAX) { BT_ERR("Credits overflow"); bt_l2cap_chan_disconnect(chan); return; } l2cap_chan_tx_give_credits(ch, credits); BT_DBG("chan %p total credits %u", ch, k_sem_count_get(&ch->tx.credits)); l2cap_chan_le_send_resume(ch); }
void test_sema_reset(void) { k_sem_init(&sema, SEM_INITIAL, SEM_LIMIT); k_sem_give(&sema); k_sem_reset(&sema); zassert_false(k_sem_count_get(&sema), NULL); /**TESTPOINT: sem take return -EBUSY*/ zassert_equal(k_sem_take(&sema, K_NO_WAIT), -EBUSY, NULL); /**TESTPOINT: sem take return -EAGAIN*/ zassert_equal(k_sem_take(&sema, TIMEOUT), -EAGAIN, NULL); k_sem_give(&sema); zassert_false(k_sem_take(&sema, K_FOREVER), NULL); }