Lines Matching refs:ase
51 #define ASE_ID(_ase) ase->ep.status.id
114 static void ase_free(struct bt_ascs_ase *ase) in ase_free() argument
116 __ASSERT(ase && ase->conn, "Non-existing ASE"); in ase_free()
118 LOG_DBG("conn %p ase %p id 0x%02x", (void *)ase->conn, ase, ase->ep.status.id); in ase_free()
120 if (ase->ep.iso != NULL) { in ase_free()
121 bt_bap_iso_unbind_ep(ase->ep.iso, &ase->ep); in ase_free()
124 bt_conn_unref(ase->conn); in ase_free()
125 ase->conn = NULL; in ase_free()
127 (void)k_work_cancel(&ase->state_transition_work); in ase_free()
130 static void ase_status_changed(struct bt_ascs_ase *ase, uint8_t state) in ase_status_changed() argument
132 struct bt_conn *conn = ase->conn; in ase_status_changed()
134 LOG_DBG("ase %p id 0x%02x %s -> %s", ase, ase->ep.status.id, in ase_status_changed()
135 bt_bap_ep_state_str(ascs_ep_get_state(&ase->ep)), bt_bap_ep_state_str(state)); in ase_status_changed()
137 ase->ep.status.state = state; in ase_status_changed()
151 bt_gatt_is_subscribed(conn, ase->attr, BT_GATT_CCC_NOTIFY)) { in ase_status_changed()
163 ascs_ep_get_status(&ase->ep, &ase_buf); in ase_status_changed()
171 err = bt_gatt_notify(conn, ase->attr, ase_buf.data, ntf_size); in ase_status_changed()
182 struct bt_ascs_ase *ase = CONTAINER_OF(d_work, struct bt_ascs_ase, in ascs_disconnect_stream_work_handler() local
184 struct bt_bap_ep *ep = &ase->ep; in ascs_disconnect_stream_work_handler()
199 ase, ep, stream, pair_stream); in ascs_disconnect_stream_work_handler()
237 struct bt_ascs_ase *ase = CONTAINER_OF(stream->ep, struct bt_ascs_ase, in ascs_disconnect_stream() local
242 return k_work_reschedule(&ase->disconnect_work, in ascs_disconnect_stream()
246 static void ase_set_state_idle(struct bt_ascs_ase *ase) in ase_set_state_idle() argument
248 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_idle()
253 ase->ep.receiver_ready = false; in ase_set_state_idle()
255 ase_status_changed(ase, BT_BAP_EP_STATE_IDLE); in ase_set_state_idle()
267 ase_free(ase); in ase_set_state_idle()
270 static void ase_set_state_codec_configured(struct bt_ascs_ase *ase) in ase_set_state_codec_configured() argument
272 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_codec_configured()
277 ase->ep.receiver_ready = false; in ase_set_state_codec_configured()
279 ase_status_changed(ase, BT_BAP_EP_STATE_CODEC_CONFIGURED); in ase_set_state_codec_configured()
283 ops->configured(stream, &ase->ep.qos_pref); in ase_set_state_codec_configured()
287 static void ase_set_state_qos_configured(struct bt_ascs_ase *ase) in ase_set_state_qos_configured() argument
289 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_qos_configured()
294 ase->ep.receiver_ready = false; in ase_set_state_qos_configured()
296 ase_status_changed(ase, BT_BAP_EP_STATE_QOS_CONFIGURED); in ase_set_state_qos_configured()
304 static void ase_set_state_enabling(struct bt_ascs_ase *ase) in ase_set_state_enabling() argument
306 const bool state_changed = ascs_ep_get_state(&ase->ep) != BT_BAP_EP_STATE_ENABLING; in ase_set_state_enabling()
307 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_enabling()
312 ase_status_changed(ase, BT_BAP_EP_STATE_ENABLING); in ase_set_state_enabling()
322 if (ase->ep.dir == BT_AUDIO_DIR_SINK && ase->ep.receiver_ready && ase->ep.iso != NULL && in ase_set_state_enabling()
323 ase->ep.iso->chan.state == BT_ISO_STATE_CONNECTED) { in ase_set_state_enabling()
324 ascs_ep_set_state(&ase->ep, BT_BAP_EP_STATE_STREAMING); in ase_set_state_enabling()
328 static void ase_set_state_streaming(struct bt_ascs_ase *ase) in ase_set_state_streaming() argument
330 const bool state_changed = ascs_ep_get_state(&ase->ep) != BT_BAP_EP_STATE_STREAMING; in ase_set_state_streaming()
331 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_streaming()
336 ase_status_changed(ase, BT_BAP_EP_STATE_STREAMING); in ase_set_state_streaming()
346 static void ase_set_state_disabling(struct bt_ascs_ase *ase) in ase_set_state_disabling() argument
348 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_disabling()
353 ase->ep.receiver_ready = false; in ase_set_state_disabling()
355 ase_status_changed(ase, BT_BAP_EP_STATE_DISABLING); in ase_set_state_disabling()
363 static void ase_set_state_releasing(struct bt_ascs_ase *ase) in ase_set_state_releasing() argument
365 struct bt_bap_stream *stream = ase->ep.stream; in ase_set_state_releasing()
369 ase->ep.receiver_ready = false; in ase_set_state_releasing()
371 ase_status_changed(ase, BT_BAP_EP_STATE_RELEASING); in ase_set_state_releasing()
384 ascs_ep_set_state(&ase->ep, BT_BAP_EP_STATE_IDLE); in ase_set_state_releasing()
390 struct bt_ascs_ase *ase = CONTAINER_OF(work, struct bt_ascs_ase, state_transition_work); in state_transition_work_handler() local
391 const enum bt_bap_ep_state old_state = ascs_ep_get_state(&ase->ep); in state_transition_work_handler()
392 const enum bt_bap_ep_state new_state = ase->state_pending; in state_transition_work_handler()
393 struct bt_bap_stream *stream = ase->ep.stream; in state_transition_work_handler()
394 struct bt_bap_ep *ep = &ase->ep; in state_transition_work_handler()
417 ase_set_state_idle(ase); in state_transition_work_handler()
420 ase_set_state_codec_configured(ase); in state_transition_work_handler()
423 ase_set_state_qos_configured(ase); in state_transition_work_handler()
426 ase_set_state_enabling(ase); in state_transition_work_handler()
429 ase_set_state_streaming(ase); in state_transition_work_handler()
432 ase_set_state_disabling(ase); in state_transition_work_handler()
435 ase_set_state_releasing(ase); in state_transition_work_handler()
444 struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); in ascs_ep_set_state() local
445 const enum bt_bap_ep_state old_state = ascs_ep_get_state(&ase->ep); in ascs_ep_set_state()
471 valid_state_transition = ase->ep.dir == BT_AUDIO_DIR_SOURCE; in ascs_ep_set_state()
478 valid_state_transition = ase->ep.dir == BT_AUDIO_DIR_SINK || in ascs_ep_set_state()
479 ase->unexpected_iso_link_loss; in ascs_ep_set_state()
506 valid_state_transition = ase->ep.dir == BT_AUDIO_DIR_SOURCE; in ascs_ep_set_state()
520 valid_state_transition = ase->ep.dir == BT_AUDIO_DIR_SOURCE; in ascs_ep_set_state()
534 ase->state_pending = state; in ascs_ep_set_state()
536 err = k_work_submit(&ase->state_transition_work); in ascs_ep_set_state()
669 struct bt_ascs_ase *ase = &ase_pool[i]; in ascs_iso_accept() local
673 if (ase->conn != info->acl || in ascs_iso_accept()
674 ase->ep.cig_id != info->cig_id || in ascs_iso_accept()
675 ase->ep.cis_id != info->cis_id) { in ascs_iso_accept()
679 state = ascs_ep_get_state(&ase->ep); in ascs_iso_accept()
681 LOG_WRN("ase %p cannot accept ISO connection", ase); in ascs_iso_accept()
685 __ASSERT(ase->ep.iso != NULL, "ep %p not bound with ISO", &ase->ep); in ascs_iso_accept()
687 chan = &ase->ep.iso->chan; in ascs_iso_accept()
689 LOG_WRN("ase %p chan %p already connected", ase, chan); in ascs_iso_accept()
810 struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); in ascs_ep_iso_connected() local
827 ase->unexpected_iso_link_loss = false; in ascs_ep_iso_connected()
866 struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); in ascs_ep_iso_disconnected() local
878 (void)k_work_cancel_delayable(&ase->disconnect_work); in ascs_ep_iso_disconnected()
891 ase->unexpected_iso_link_loss = true; in ascs_ep_iso_disconnected()
982 static int ase_release(struct bt_ascs_ase *ase, uint8_t reason, struct bt_bap_ascs_rsp *rsp) in ase_release() argument
984 enum bt_bap_ep_state state = ascs_ep_get_state(&ase->ep); in ase_release()
1000 err = unicast_server_cb->release(ase->ep.stream, rsp); in ase_release()
1012 ase->ep.reason = reason; in ase_release()
1014 ascs_ep_set_state(&ase->ep, BT_BAP_EP_STATE_RELEASING); in ase_release()
1022 struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); in bt_ascs_release_ase() local
1024 return ase_release(ase, BT_HCI_ERR_LOCALHOST_TERM_CONN, BT_BAP_ASCS_RSP_NULL); in bt_ascs_release_ase()
1027 static int ase_disable(struct bt_ascs_ase *ase, uint8_t reason, struct bt_bap_ascs_rsp *rsp) in ase_disable() argument
1033 LOG_DBG("ase %p", ase); in ase_disable()
1035 ep = &ase->ep; in ase_disable()
1086 struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); in bt_ascs_disable_ase() local
1088 return ase_disable(ase, BT_HCI_ERR_LOCALHOST_TERM_CONN, BT_BAP_ASCS_RSP_NULL); in bt_ascs_disable_ase()
1094 struct bt_ascs_ase *ase = &ase_pool[i]; in disconnected() local
1096 if (ase->conn != conn) { in disconnected()
1100 if (ase->ep.status.state != BT_BAP_EP_STATE_IDLE) { in disconnected()
1109 ase->ep.reason = reason; in disconnected()
1110 ase->state_pending = BT_BAP_EP_STATE_IDLE; in disconnected()
1111 state_transition_work_handler(&ase->state_transition_work); in disconnected()
1174 struct bt_ascs_ase *ase = user_data; in ase_attr_cb() local
1176 if (ase->ep.status.id == POINTER_TO_UINT(BT_AUDIO_CHRC_USER_DATA(attr))) { in ase_attr_cb()
1177 ase->attr = attr; in ase_attr_cb()
1195 static void ase_init(struct bt_ascs_ase *ase, struct bt_conn *conn, uint8_t id) in ase_init() argument
1197 memset(ase, 0, sizeof(*ase)); in ase_init()
1199 ascs_ep_init(&ase->ep, id); in ase_init()
1201 ase->conn = bt_conn_ref(conn); in ase_init()
1204 bt_gatt_foreach_attr_type(0x0001, 0xffff, ASE_UUID(id), NULL, 0, ase_attr_cb, ase); in ase_init()
1206 __ASSERT(ase->attr, "ASE characteristic not found\n"); in ase_init()
1208 k_work_init_delayable(&ase->disconnect_work, in ase_init()
1210 k_work_init(&ase->state_transition_work, state_transition_work_handler); in ase_init()
1215 struct bt_ascs_ase *ase = NULL; in ase_new() local
1221 ase = &ase_pool[i]; in ase_new()
1226 if (ase == NULL) { in ase_new()
1230 ase_init(ase, conn, id); in ase_new()
1232 LOG_DBG("conn %p new ase %p id 0x%02x", (void *)conn, ase, id); in ase_new()
1234 return ase; in ase_new()
1240 struct bt_ascs_ase *ase = &ase_pool[i]; in ase_find() local
1242 if (ase->conn == conn && ase->ep.status.id == id) { in ase_find()
1243 return ase; in ase_find()
1255 struct bt_ascs_ase *ase = NULL; in ascs_ase_read() local
1263 ase = ase_find(conn, ase_id); in ascs_ase_read()
1267 if (ase == NULL) { in ascs_ase_read()
1278 ascs_ep_get_status(&ase->ep, &ase_buf); in ascs_ase_read()
1357 static int ase_config(struct bt_ascs_ase *ase, const struct bt_ascs_config *cfg) in ase_config() argument
1367 ase, cfg->latency, cfg->phy, cfg->codec.id, cfg->codec.cid, cfg->codec.vid, in ase_config()
1373 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_CONF_INVALID, in ase_config()
1381 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_CONF_INVALID, in ase_config()
1392 switch (ase->ep.status.state) { in ase_config()
1402 bt_bap_ep_state_str(ase->ep.status.state)); in ase_config()
1403 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_config()
1411 (void)memcpy(&codec_cfg, &ase->ep.codec_cfg, sizeof(codec_cfg)); in ase_config()
1413 err = ascs_ep_set_codec(&ase->ep, cfg->codec.id, sys_le16_to_cpu(cfg->codec.cid), in ase_config()
1417 (void)memcpy(&ase->ep.codec_cfg, &codec_cfg, sizeof(codec_cfg)); in ase_config()
1418 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_config()
1422 if (ase->ep.stream != NULL) { in ase_config()
1425 err = unicast_server_cb->reconfig(ase->ep.stream, ase->ep.dir, in ase_config()
1426 &ase->ep.codec_cfg, &ase->ep.qos_pref, in ase_config()
1443 (void)memcpy(&ase->ep.codec_cfg, &codec_cfg, sizeof(codec_cfg)); in ase_config()
1444 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_config()
1449 stream = ase->ep.stream; in ase_config()
1454 err = unicast_server_cb->config(ase->conn, &ase->ep, ase->ep.dir, in ase_config()
1455 &ase->ep.codec_cfg, &stream, in ase_config()
1456 &ase->ep.qos_pref, &rsp); in ase_config()
1472 (void)memcpy(&ase->ep.codec_cfg, &codec_cfg, sizeof(codec_cfg)); in ase_config()
1473 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_config()
1481 ascs_cp_rsp_success(ASE_ID(ase)); in ase_config()
1483 bt_bap_stream_attach(ase->conn, stream, &ase->ep, &ase->ep.codec_cfg); in ase_config()
1485 ascs_ep_set_state(&ase->ep, BT_BAP_EP_STATE_CODEC_CONFIGURED); in ase_config()
1493 struct bt_ascs_ase *ase = &ase_pool[i]; in ep_lookup_stream() local
1495 if (ase->conn == conn && ase->ep.stream == stream) { in ep_lookup_stream()
1496 return &ase->ep; in ep_lookup_stream()
1508 struct bt_ascs_ase *ase = NULL; in bt_ascs_config_ase() local
1526 ase = ase_new(conn, i); in bt_ascs_config_ase()
1531 if (ase == NULL) { in bt_ascs_config_ase()
1536 ep = &ase->ep; in bt_ascs_config_ase()
1563 ase_free(ase); in bt_ascs_config_ase()
1629 struct bt_ascs_ase *ase; in ascs_config() local
1635 LOG_DBG("ase 0x%02x cc_len %u", cfg->ase, cfg->cc_len); in ascs_config()
1637 if (!cfg->ase || cfg->ase > ASE_COUNT) { in ascs_config()
1638 LOG_WRN("Invalid ASE ID: %u", cfg->ase); in ascs_config()
1639 ascs_cp_rsp_add(cfg->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE, in ascs_config()
1644 ase = ase_find(conn, cfg->ase); in ascs_config()
1645 if (ase != NULL) { in ascs_config()
1646 ase_config(ase, cfg); in ascs_config()
1650 ase = ase_new(conn, cfg->ase); in ascs_config()
1651 if (!ase) { in ascs_config()
1652 ascs_cp_rsp_add(cfg->ase, BT_BAP_ASCS_RSP_CODE_NO_MEM, in ascs_config()
1654 LOG_WRN("No free ASE found for config ASE ID 0x%02x", cfg->ase); in ascs_config()
1658 err = ase_config(ase, cfg); in ascs_config()
1660 ase_free(ase); in ascs_config()
1670 struct bt_ascs_ase *ase = &ase_pool[i]; in bt_ascs_foreach_ep() local
1672 if (ase->conn == conn) { in bt_ascs_foreach_ep()
1673 func(&ase->ep, user_data); in bt_ascs_foreach_ep()
1788 static void ase_qos(struct bt_ascs_ase *ase, uint8_t cig_id, uint8_t cis_id, in ase_qos() argument
1791 struct bt_bap_ep *ep = &ase->ep; in ase_qos()
1796 "latency %u pd %u", ase, cig_id, cis_id, cqos->interval, cqos->framing, cqos->phy, in ase_qos()
1799 err = ase_stream_qos(stream, cqos, ase->conn, cig_id, cis_id, rsp); in ase_qos()
1867 struct bt_ascs_ase *ase; in ascs_qos() local
1871 LOG_DBG("ase 0x%02x", qos->ase); in ascs_qos()
1873 if (!is_valid_ase_id(qos->ase)) { in ascs_qos()
1874 ascs_cp_rsp_add(qos->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE, in ascs_qos()
1876 LOG_WRN("Unknown ase 0x%02x", qos->ase); in ascs_qos()
1880 ase = ase_find(conn, qos->ase); in ascs_qos()
1881 if (!ase) { in ascs_qos()
1883 ascs_cp_rsp_add(qos->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ascs_qos()
1896 ase_qos(ase, qos->cig, qos->cis, &cqos, &rsp); in ascs_qos()
1897 ascs_cp_rsp_add(qos->ase, rsp.code, rsp.reason); in ascs_qos()
2064 static void ase_metadata(struct bt_ascs_ase *ase, struct bt_ascs_metadata *meta) in ase_metadata() argument
2073 LOG_DBG("ase %p meta->len %u", ase, meta->len); in ase_metadata()
2075 ep = &ase->ep; in ase_metadata()
2086 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_metadata()
2113 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_metadata()
2122 ascs_cp_rsp_success(ASE_ID(ase)); in ase_metadata()
2125 static int ase_enable(struct bt_ascs_ase *ase, struct bt_ascs_metadata *meta) in ase_enable() argument
2133 LOG_DBG("ase %p meta->len %u", ase, meta->len); in ase_enable()
2135 ep = &ase->ep; in ase_enable()
2141 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_enable()
2168 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_enable()
2178 ascs_cp_rsp_success(ASE_ID(ase)); in ase_enable()
2243 struct bt_ascs_ase *ase; in ascs_enable() local
2248 if (!is_valid_ase_id(meta->ase)) { in ascs_enable()
2249 ascs_cp_rsp_add(meta->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE, in ascs_enable()
2251 LOG_WRN("Unknown ase 0x%02x", meta->ase); in ascs_enable()
2255 ase = ase_find(conn, meta->ase); in ascs_enable()
2256 if (!ase) { in ascs_enable()
2257 LOG_DBG("Invalid operation for idle ase 0x%02x", meta->ase); in ascs_enable()
2258 ascs_cp_rsp_add(meta->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ascs_enable()
2263 ase_enable(ase, meta); in ascs_enable()
2269 static void ase_start(struct bt_ascs_ase *ase) in ase_start() argument
2276 LOG_DBG("ase %p", ase); in ase_start()
2278 ep = &ase->ep; in ase_start()
2283 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_start()
2294 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_start()
2314 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_start()
2323 ascs_cp_rsp_success(ASE_ID(ase)); in ase_start()
2368 struct bt_ascs_ase *ase; in ascs_start() local
2395 ase = ase_find(conn, id); in ascs_start()
2396 if (!ase) { in ascs_start()
2403 ase_start(ase); in ascs_start()
2452 struct bt_ascs_ase *ase; in ascs_disable() local
2466 ase = ase_find(conn, id); in ascs_disable()
2467 if (!ase) { in ascs_disable()
2474 ase_disable(ase, BT_HCI_ERR_REMOTE_USER_TERM_CONN, &rsp); in ascs_disable()
2481 static void ase_stop(struct bt_ascs_ase *ase) in ase_stop() argument
2489 LOG_DBG("ase %p", ase); in ase_stop()
2491 ep = &ase->ep; in ase_stop()
2495 ascs_cp_rsp_add(ASE_ID(ase), BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ase_stop()
2516 ascs_cp_rsp_add(ASE_ID(ase), rsp.code, rsp.reason); in ase_stop()
2534 ascs_cp_rsp_success(ASE_ID(ase)); in ase_stop()
2579 struct bt_ascs_ase *ase; in ascs_stop() local
2606 ase = ase_find(conn, id); in ascs_stop()
2607 if (!ase) { in ascs_stop()
2614 ase_stop(ase); in ascs_stop()
2680 struct bt_ascs_ase *ase; in ascs_metadata() local
2688 ascs_cp_rsp_add(meta->ase, BT_BAP_ASCS_RSP_CODE_NO_MEM, in ascs_metadata()
2693 if (!is_valid_ase_id(meta->ase)) { in ascs_metadata()
2694 ascs_cp_rsp_add(meta->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE, in ascs_metadata()
2696 LOG_WRN("Unknown ase 0x%02x", meta->ase); in ascs_metadata()
2700 ase = ase_find(conn, meta->ase); in ascs_metadata()
2701 if (!ase) { in ascs_metadata()
2702 LOG_DBG("Invalid operation for idle ase 0x%02x", meta->ase); in ascs_metadata()
2703 ascs_cp_rsp_add(meta->ase, BT_BAP_ASCS_RSP_CODE_INVALID_ASE_STATE, in ascs_metadata()
2708 ase_metadata(ase, meta); in ascs_metadata()
2758 struct bt_ascs_ase *ase; in ascs_release() local
2772 ase = ase_find(conn, id); in ascs_release()
2773 if (!ase) { in ascs_release()
2780 ase_release(ase, BT_HCI_ERR_REMOTE_USER_TERM_CONN, &rsp); in ascs_release()
2917 struct bt_ascs_ase *ase = &ase_pool[i]; in bt_ascs_cleanup() local
2919 if (ase->conn != NULL) { in bt_ascs_cleanup()
2920 bt_ascs_release_ase(&ase->ep); in bt_ascs_cleanup()