1ac726063SMatthias Ringwald /*
2ac726063SMatthias Ringwald * Copyright (C) 2023 BlueKitchen GmbH
3ac726063SMatthias Ringwald *
4ac726063SMatthias Ringwald * Redistribution and use in source and binary forms, with or without
5ac726063SMatthias Ringwald * modification, are permitted provided that the following conditions
6ac726063SMatthias Ringwald * are met:
7ac726063SMatthias Ringwald *
8ac726063SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright
9ac726063SMatthias Ringwald * notice, this list of conditions and the following disclaimer.
10ac726063SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright
11ac726063SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the
12ac726063SMatthias Ringwald * documentation and/or other materials provided with the distribution.
13ac726063SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of
14ac726063SMatthias Ringwald * contributors may be used to endorse or promote products derived
15ac726063SMatthias Ringwald * from this software without specific prior written permission.
16ac726063SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for
17ac726063SMatthias Ringwald * personal benefit and not for any commercial purpose or for
18ac726063SMatthias Ringwald * monetary gain.
19ac726063SMatthias Ringwald *
20ac726063SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21ac726063SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22ac726063SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23ac726063SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24ac726063SMatthias Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25ac726063SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26ac726063SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27ac726063SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28ac726063SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29ac726063SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30ac726063SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31ac726063SMatthias Ringwald * SUCH DAMAGE.
32ac726063SMatthias Ringwald *
33ac726063SMatthias Ringwald * Please inquire about commercial licensing options at
34ac726063SMatthias Ringwald * [email protected]
35ac726063SMatthias Ringwald *
36ac726063SMatthias Ringwald */
37ac726063SMatthias Ringwald
38ac726063SMatthias Ringwald #define BTSTACK_FILE__ "avrcp_cover_art_client.c"
39ac726063SMatthias Ringwald
40ac726063SMatthias Ringwald #include <string.h>
41ac726063SMatthias Ringwald
42ac726063SMatthias Ringwald #include "btstack_debug.h"
43ac726063SMatthias Ringwald #include "btstack_event.h"
44ac726063SMatthias Ringwald #include "classic/avrcp.h"
45ac726063SMatthias Ringwald #include "classic/avrcp_cover_art_client.h"
46ac726063SMatthias Ringwald #include "classic/obex.h"
47ac726063SMatthias Ringwald #include "hci_event.h"
48ac726063SMatthias Ringwald
49ac726063SMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART
50ac726063SMatthias Ringwald
51ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_connected = {
52ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META,
53ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_CONNECTION_ESTABLISHED,
54ac726063SMatthias Ringwald .format = "1B22"
55ac726063SMatthias Ringwald };
56ac726063SMatthias Ringwald
57ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_disconnected = {
58ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META,
59ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_CONNECTION_RELEASED,
60ac726063SMatthias Ringwald .format = "2"
61ac726063SMatthias Ringwald };
62ac726063SMatthias Ringwald
63ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_operation_complete = {
64ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META,
65ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_OPERATION_COMPLETE,
66ac726063SMatthias Ringwald .format = "21"
67ac726063SMatthias Ringwald };
68ac726063SMatthias Ringwald
69ac726063SMatthias Ringwald // 7163DD54-4A7E-11E2-B47C-0050C2490048
70ac726063SMatthias Ringwald static const uint8_t avrcp_cover_art_uuid[] = { 0x71, 0x63, 0xDD, 0x54, 0x4A, 0x7E, 0x11, 0xE2, 0xB4, 0x7C, 0x00, 0x50, 0xC2, 0x49, 0x00, 0x48 };
71ac726063SMatthias Ringwald
72ac726063SMatthias Ringwald // OBEX types
73ac726063SMatthias Ringwald const char * avrcp_cover_art_image_properties_type = "x-bt/img-properties";
74ac726063SMatthias Ringwald const char * avrcp_cover_art_image_type = "x-bt/img-img";
75ac726063SMatthias Ringwald const char * avrcp_cover_art_linked_thumbnail_type = "x-bt/img-thm";
76ac726063SMatthias Ringwald
77ac726063SMatthias Ringwald static btstack_linked_list_t avrcp_cover_art_client_connections;
78ac726063SMatthias Ringwald
avrcp_cover_art_client_next_cid(void)7957ff2ef1SMatthias Ringwald static uint16_t avrcp_cover_art_client_next_cid(void) {
80ac726063SMatthias Ringwald static uint16_t cid = 0;
81ac726063SMatthias Ringwald cid++;
82ac726063SMatthias Ringwald if (cid == 0){
83ac726063SMatthias Ringwald cid = 1;
84ac726063SMatthias Ringwald }
85ac726063SMatthias Ringwald return cid;
86ac726063SMatthias Ringwald }
87ac726063SMatthias Ringwald
avrcp_cover_art_client_for_goep_cid(uint16_t goep_cid)88ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_goep_cid(uint16_t goep_cid){
89ac726063SMatthias Ringwald btstack_linked_list_iterator_t it;
90ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections);
91ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
92ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it);
93ac726063SMatthias Ringwald if (connection->goep_cid == goep_cid) {
94ac726063SMatthias Ringwald return connection;
95ac726063SMatthias Ringwald };
96ac726063SMatthias Ringwald }
97ac726063SMatthias Ringwald return NULL;
98ac726063SMatthias Ringwald }
99ac726063SMatthias Ringwald
avrcp_cover_art_client_for_avrcp_cid(uint16_t avrcp_cid)100ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_avrcp_cid(uint16_t avrcp_cid){
101ac726063SMatthias Ringwald btstack_linked_list_iterator_t it;
102ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections);
103ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
104ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it);
105ac726063SMatthias Ringwald if (connection->avrcp_cid == avrcp_cid) {
106ac726063SMatthias Ringwald return connection;
107ac726063SMatthias Ringwald };
108ac726063SMatthias Ringwald }
109ac726063SMatthias Ringwald return NULL;
110ac726063SMatthias Ringwald }
111ac726063SMatthias Ringwald
avrcp_cover_art_client_for_cover_art_cid(uint16_t cover_art_cid)112ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_cover_art_cid(uint16_t cover_art_cid){
113ac726063SMatthias Ringwald btstack_linked_list_iterator_t it;
114ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections);
115ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
116ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it);
117ac726063SMatthias Ringwald if (connection->cover_art_cid == cover_art_cid) {
118ac726063SMatthias Ringwald return connection;
119ac726063SMatthias Ringwald };
120ac726063SMatthias Ringwald }
121ac726063SMatthias Ringwald return NULL;
122ac726063SMatthias Ringwald }
123ac726063SMatthias Ringwald
avrcp_cover_art_client_emit_connection_established(btstack_packet_handler_t packet_handler,uint8_t status,bd_addr_t addr,uint16_t avrcp_cid,uint16_t cover_art_cid)124ac726063SMatthias Ringwald static void avrcp_cover_art_client_emit_connection_established(btstack_packet_handler_t packet_handler, uint8_t status,
125ac726063SMatthias Ringwald bd_addr_t addr, uint16_t avrcp_cid,
126ac726063SMatthias Ringwald uint16_t cover_art_cid) {
127ac726063SMatthias Ringwald uint8_t buffer[20];
128ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_connected, status, addr, avrcp_cid, cover_art_cid);
129ac726063SMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, size);
130ac726063SMatthias Ringwald }
131ac726063SMatthias Ringwald
cover_art_client_emit_operation_complete_event(avrcp_cover_art_client_t * cover_art_client,uint8_t status)132ac726063SMatthias Ringwald static void cover_art_client_emit_operation_complete_event(avrcp_cover_art_client_t * cover_art_client, uint8_t status) {
133ac726063SMatthias Ringwald uint8_t buffer[20];
134ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_operation_complete, cover_art_client->cover_art_cid, status);
135ac726063SMatthias Ringwald (*cover_art_client->packet_handler)(HCI_EVENT_PACKET, 0, buffer, size);
136ac726063SMatthias Ringwald }
137ac726063SMatthias Ringwald
avrcp_cover_art_client_emit_connection_released(btstack_packet_handler_t packet_handler,uint16_t cover_art_cid)138ac726063SMatthias Ringwald static void avrcp_cover_art_client_emit_connection_released(btstack_packet_handler_t packet_handler, uint16_t cover_art_cid) {
139ac726063SMatthias Ringwald uint8_t buffer[20];
140ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_disconnected, cover_art_cid);
141ac726063SMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, size);
142ac726063SMatthias Ringwald }
143ac726063SMatthias Ringwald
avrcp_cover_art_finalize_connection(avrcp_cover_art_client_t * cover_art_client)144ac726063SMatthias Ringwald static void avrcp_cover_art_finalize_connection(avrcp_cover_art_client_t *cover_art_client) {
145ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL);
146ac726063SMatthias Ringwald memset(cover_art_client, 0, sizeof(avrcp_cover_art_client_t));
147ac726063SMatthias Ringwald btstack_linked_list_remove(&avrcp_cover_art_client_connections, (btstack_linked_item_t *) cover_art_client);
148ac726063SMatthias Ringwald }
149ac726063SMatthias Ringwald
avrcp_cover_art_client_parser_callback_connect(void * user_data,uint8_t header_id,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)150ac726063SMatthias Ringwald static void avrcp_cover_art_client_parser_callback_connect(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
151ac726063SMatthias Ringwald avrcp_cover_art_client_t * client = (avrcp_cover_art_client_t *) user_data;
152ac726063SMatthias Ringwald switch (header_id){
153ac726063SMatthias Ringwald case OBEX_HEADER_CONNECTION_ID:
154ac726063SMatthias Ringwald if (obex_parser_header_store(client->obex_header_buffer, sizeof(client->obex_header_buffer), total_len, data_offset, data_buffer, data_len) == OBEX_PARSER_HEADER_COMPLETE){
155ac726063SMatthias Ringwald goep_client_set_connection_id(client->goep_cid, big_endian_read_32(client->obex_header_buffer, 0));
156ac726063SMatthias Ringwald }
157ac726063SMatthias Ringwald break;
158ac726063SMatthias Ringwald default:
159ac726063SMatthias Ringwald break;
160ac726063SMatthias Ringwald }
161ac726063SMatthias Ringwald }
162ac726063SMatthias Ringwald
avrcp_cover_art_client_prepare_srm_header(avrcp_cover_art_client_t * cover_art_client)163ac726063SMatthias Ringwald static void avrcp_cover_art_client_prepare_srm_header(avrcp_cover_art_client_t * cover_art_client){
164ac726063SMatthias Ringwald if (cover_art_client->flow_control_enabled == false){
1650de94e45SMatthias Ringwald obex_srm_client_prepare_header(&cover_art_client->obex_srm, cover_art_client->goep_cid);
166ac726063SMatthias Ringwald }
167ac726063SMatthias Ringwald }
168ac726063SMatthias Ringwald
avrcp_cover_art_client_parser_callback_get_operation(void * user_data,uint8_t header_id,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)169ac726063SMatthias Ringwald static void avrcp_cover_art_client_parser_callback_get_operation(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
170ac726063SMatthias Ringwald avrcp_cover_art_client_t *client = (avrcp_cover_art_client_t *) user_data;
171ac726063SMatthias Ringwald switch (header_id) {
172ac726063SMatthias Ringwald case OBEX_HEADER_SINGLE_RESPONSE_MODE:
173ac726063SMatthias Ringwald obex_parser_header_store(&client->obex_srm.srm_value, 1, total_len, data_offset, data_buffer, data_len);
174ac726063SMatthias Ringwald break;
175ac726063SMatthias Ringwald case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER:
176ac726063SMatthias Ringwald obex_parser_header_store(&client->obex_srm.srmp_value, 1, total_len, data_offset, data_buffer, data_len);
177ac726063SMatthias Ringwald break;
178ac726063SMatthias Ringwald case OBEX_HEADER_BODY:
179ac726063SMatthias Ringwald case OBEX_HEADER_END_OF_BODY:
180ac726063SMatthias Ringwald switch(client->state){
181ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_OBJECT:
182ac726063SMatthias Ringwald client->packet_handler(BIP_DATA_PACKET, client->cover_art_cid, (uint8_t *) data_buffer, data_len);
183ac726063SMatthias Ringwald if (data_offset + data_len == total_len){
184ac726063SMatthias Ringwald client->flow_wait_for_user = true;
185ac726063SMatthias Ringwald }
186ac726063SMatthias Ringwald break;
187ac726063SMatthias Ringwald default:
188ac726063SMatthias Ringwald btstack_unreachable();
189ac726063SMatthias Ringwald break;
190ac726063SMatthias Ringwald }
191ac726063SMatthias Ringwald break;
192ac726063SMatthias Ringwald default:
193ac726063SMatthias Ringwald // ignore other headers
194ac726063SMatthias Ringwald break;
195ac726063SMatthias Ringwald }
196ac726063SMatthias Ringwald }
197ac726063SMatthias Ringwald
avrcp_cover_art_client_prepare_get_operation(avrcp_cover_art_client_t * cover_art_client)198ac726063SMatthias Ringwald static void avrcp_cover_art_client_prepare_get_operation(avrcp_cover_art_client_t * cover_art_client){
199ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_GET, avrcp_cover_art_client_parser_callback_get_operation, cover_art_client);
200*5c3f674cSMatthias Ringwald obex_srm_client_reset_fields(&cover_art_client->obex_srm);
201ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true;
202ac726063SMatthias Ringwald }
203ac726063SMatthias Ringwald
avrcp_cover_art_client_handle_can_send_now(avrcp_cover_art_client_t * cover_art_client)204ac726063SMatthias Ringwald static void avrcp_cover_art_client_handle_can_send_now(avrcp_cover_art_client_t * cover_art_client){
205ac726063SMatthias Ringwald switch (cover_art_client->state) {
206ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST:
207ac726063SMatthias Ringwald // prepare request
208ac726063SMatthias Ringwald goep_client_request_create_connect(cover_art_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
209ac726063SMatthias Ringwald goep_client_header_add_target(cover_art_client->goep_cid, avrcp_cover_art_uuid, 16);
210ac726063SMatthias Ringwald // state
211ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_CONNECT_RESPONSE;
212ac726063SMatthias Ringwald // prepare response
213ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_CONNECT,
214ac726063SMatthias Ringwald avrcp_cover_art_client_parser_callback_connect, cover_art_client);
2150de94e45SMatthias Ringwald obex_srm_client_init(&cover_art_client->obex_srm);
216ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true;
217ac726063SMatthias Ringwald // send packet
218ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid);
219ac726063SMatthias Ringwald break;
220ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_GET_OBJECT:
221ac726063SMatthias Ringwald goep_client_request_create_get(cover_art_client->goep_cid);
222ac726063SMatthias Ringwald if (cover_art_client->first_request){
223ac726063SMatthias Ringwald cover_art_client->first_request = false;
224*5c3f674cSMatthias Ringwald obex_srm_client_init(&cover_art_client->obex_srm);
225ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_srm_header(cover_art_client);
226ac726063SMatthias Ringwald goep_client_header_add_type(cover_art_client->goep_cid, cover_art_client->object_type);
227ac726063SMatthias Ringwald if (cover_art_client->image_descriptor != NULL){
2281a3bc516SMatthias Ringwald goep_client_header_add_variable(cover_art_client->goep_cid, OBEX_HEADER_IMG_DESCRIPTOR, (const uint8_t *) cover_art_client->image_descriptor, (uint16_t) strlen(cover_art_client->image_descriptor));
229ac726063SMatthias Ringwald }
2301a3bc516SMatthias Ringwald uint8_t image_handle_len = btstack_max(7, (uint16_t) strlen(cover_art_client->image_handle));
231ac726063SMatthias Ringwald goep_client_header_add_unicode_prefix(cover_art_client->goep_cid, OBEX_HEADER_IMG_HANDLE, cover_art_client->image_handle, image_handle_len);
232ac726063SMatthias Ringwald }
233ac726063SMatthias Ringwald // state
234ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_OBJECT;
235ac726063SMatthias Ringwald cover_art_client->flow_next_triggered = 0;
236ac726063SMatthias Ringwald cover_art_client->flow_wait_for_user = 0;
237ac726063SMatthias Ringwald // prepare response
238ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_get_operation(cover_art_client);
239ac726063SMatthias Ringwald // send packet
240ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid);
241ac726063SMatthias Ringwald break;
242ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST:
243ac726063SMatthias Ringwald // prepare request
244ac726063SMatthias Ringwald goep_client_request_create_disconnect(cover_art_client->goep_cid);
245ac726063SMatthias Ringwald // state
246ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE;
247ac726063SMatthias Ringwald // prepare response
248ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_DISCONNECT, NULL, cover_art_client);
249ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true;
250ac726063SMatthias Ringwald // send packet
251ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid);
252ac726063SMatthias Ringwald return;
253ac726063SMatthias Ringwald default:
254ac726063SMatthias Ringwald break;
255ac726063SMatthias Ringwald }
256ac726063SMatthias Ringwald }
257ac726063SMatthias Ringwald
avrcp_cover_art_goep_event_handler(const uint8_t * packet,uint16_t size)258ac726063SMatthias Ringwald static void avrcp_cover_art_goep_event_handler(const uint8_t *packet, uint16_t size) {
259ac726063SMatthias Ringwald UNUSED(size);
260ac726063SMatthias Ringwald uint8_t status;
261ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client;
262ac726063SMatthias Ringwald btstack_packet_handler_t packet_handler;
263ac726063SMatthias Ringwald uint16_t cover_art_cid;
264ac726063SMatthias Ringwald
265ac726063SMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
266ac726063SMatthias Ringwald case HCI_EVENT_GOEP_META:
267ac726063SMatthias Ringwald switch (hci_event_goep_meta_get_subevent_code(packet)){
268ac726063SMatthias Ringwald case GOEP_SUBEVENT_CONNECTION_OPENED:
269ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet));
270ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL);
271ac726063SMatthias Ringwald status = goep_subevent_connection_opened_get_status(packet);
272ac726063SMatthias Ringwald if (status){
273ac726063SMatthias Ringwald log_info("connection failed %u", status);
274ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client);
275ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler, status,
276ac726063SMatthias Ringwald cover_art_client->addr,
277ac726063SMatthias Ringwald cover_art_client->avrcp_cid,
278ac726063SMatthias Ringwald cover_art_client->cover_art_cid);
279ac726063SMatthias Ringwald } else {
280ac726063SMatthias Ringwald log_info("connection established");
281ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST;
282ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid);
283ac726063SMatthias Ringwald }
284ac726063SMatthias Ringwald break;
285ac726063SMatthias Ringwald case GOEP_SUBEVENT_CONNECTION_CLOSED:
286ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet));
287ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL);
288ac726063SMatthias Ringwald if (cover_art_client->state > AVRCP_COVER_ART_CONNECTED){
289ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_DISCONNECTED);
290ac726063SMatthias Ringwald }
291ac726063SMatthias Ringwald cover_art_cid = cover_art_client->cover_art_cid;
292ac726063SMatthias Ringwald packet_handler = cover_art_client->packet_handler;
293ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client);
294ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_released(packet_handler, cover_art_cid);
295ac726063SMatthias Ringwald break;
296ac726063SMatthias Ringwald case GOEP_SUBEVENT_CAN_SEND_NOW:
297ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_can_send_now_get_goep_cid(packet));
298ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL);
299ac726063SMatthias Ringwald avrcp_cover_art_client_handle_can_send_now(cover_art_client);
300ac726063SMatthias Ringwald break;
301ac726063SMatthias Ringwald default:
302ac726063SMatthias Ringwald break;
303ac726063SMatthias Ringwald }
304ac726063SMatthias Ringwald break;
305ac726063SMatthias Ringwald default:
306ac726063SMatthias Ringwald break;
307ac726063SMatthias Ringwald }
308ac726063SMatthias Ringwald }
309ac726063SMatthias Ringwald
avrcp_cover_art_client_goep_data_handler(avrcp_cover_art_client_t * cover_art_client,uint8_t * packet,uint16_t size)310ac726063SMatthias Ringwald static void avrcp_cover_art_client_goep_data_handler(avrcp_cover_art_client_t * cover_art_client, uint8_t *packet, uint16_t size){
311ac726063SMatthias Ringwald btstack_assert(cover_art_client->obex_parser_waiting_for_response);
312ac726063SMatthias Ringwald
313ac726063SMatthias Ringwald obex_parser_object_state_t parser_state;
314ac726063SMatthias Ringwald parser_state = obex_parser_process_data(&cover_art_client->obex_parser, packet, size);
315ac726063SMatthias Ringwald if (parser_state == OBEX_PARSER_OBJECT_STATE_COMPLETE){
316ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = false;
317ac726063SMatthias Ringwald obex_parser_operation_info_t op_info;
318ac726063SMatthias Ringwald obex_parser_get_operation_info(&cover_art_client->obex_parser, &op_info);
319ac726063SMatthias Ringwald switch (cover_art_client->state){
320ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_CONNECT_RESPONSE:
321ac726063SMatthias Ringwald switch (op_info.response_code) {
322ac726063SMatthias Ringwald case OBEX_RESP_SUCCESS:
323ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
324ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler,
325ac726063SMatthias Ringwald ERROR_CODE_SUCCESS,
326ac726063SMatthias Ringwald cover_art_client->addr,
327ac726063SMatthias Ringwald cover_art_client->avrcp_cid,
328ac726063SMatthias Ringwald cover_art_client->cover_art_cid);
329ac726063SMatthias Ringwald break;
330ac726063SMatthias Ringwald default:
331ac726063SMatthias Ringwald log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
332ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_INIT;
333ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler,
334ac726063SMatthias Ringwald OBEX_CONNECT_FAILED,
335ac726063SMatthias Ringwald cover_art_client->addr,
336ac726063SMatthias Ringwald cover_art_client->avrcp_cid,
337ac726063SMatthias Ringwald cover_art_client->cover_art_cid);
338ac726063SMatthias Ringwald break;
339ac726063SMatthias Ringwald }
340ac726063SMatthias Ringwald break;
341ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_OBJECT:
342ac726063SMatthias Ringwald switch (op_info.response_code) {
343ac726063SMatthias Ringwald case OBEX_RESP_CONTINUE:
3440de94e45SMatthias Ringwald obex_srm_client_handle_headers(&cover_art_client->obex_srm);
345*5c3f674cSMatthias Ringwald if (obex_srm_client_is_srm_active(&cover_art_client->obex_srm)) {
346ac726063SMatthias Ringwald // prepare response
347ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_get_operation(cover_art_client);
348ac726063SMatthias Ringwald break;
349ac726063SMatthias Ringwald }
350ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT;
351ac726063SMatthias Ringwald if (!cover_art_client->flow_control_enabled || !cover_art_client->flow_wait_for_user ||
352ac726063SMatthias Ringwald cover_art_client->flow_next_triggered) {
353ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid);
354ac726063SMatthias Ringwald }
355ac726063SMatthias Ringwald break;
356ac726063SMatthias Ringwald case OBEX_RESP_SUCCESS:
357ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
358ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, ERROR_CODE_SUCCESS);
359ac726063SMatthias Ringwald break;
360ac726063SMatthias Ringwald default:
361ac726063SMatthias Ringwald log_info("unexpected response 0x%02x", packet[0]);
362ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
363ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_UNKNOWN_ERROR);
364ac726063SMatthias Ringwald break;
365ac726063SMatthias Ringwald }
366ac726063SMatthias Ringwald break;
367ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE:
368ac726063SMatthias Ringwald goep_client_disconnect(cover_art_client->goep_cid);
369ac726063SMatthias Ringwald break;
370ac726063SMatthias Ringwald default:
371ac726063SMatthias Ringwald btstack_unreachable();
372ac726063SMatthias Ringwald break;
373ac726063SMatthias Ringwald }
374ac726063SMatthias Ringwald }
375ac726063SMatthias Ringwald }
376ac726063SMatthias Ringwald
avrcp_cover_art_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)377ac726063SMatthias Ringwald static void avrcp_cover_art_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
378ac726063SMatthias Ringwald UNUSED(channel); // ok: there is no channel
379ac726063SMatthias Ringwald UNUSED(size); // ok: handling own goep events
380ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client;
381ac726063SMatthias Ringwald switch (packet_type){
382ac726063SMatthias Ringwald case HCI_EVENT_PACKET:
383ac726063SMatthias Ringwald avrcp_cover_art_goep_event_handler(packet, size);
384ac726063SMatthias Ringwald break;
385ac726063SMatthias Ringwald case GOEP_DATA_PACKET:
386ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(channel);
387ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL);
388ac726063SMatthias Ringwald avrcp_cover_art_client_goep_data_handler(cover_art_client, packet, size);
389ac726063SMatthias Ringwald break;
390ac726063SMatthias Ringwald default:
391ac726063SMatthias Ringwald break;
392ac726063SMatthias Ringwald }
393ac726063SMatthias Ringwald }
394ac726063SMatthias Ringwald
avrcp_cover_art_client_setup_connection(avrcp_cover_art_client_t * cover_art_client,uint16_t l2cap_psm)395ac726063SMatthias Ringwald static uint8_t avrcp_cover_art_client_setup_connection(avrcp_cover_art_client_t * cover_art_client, uint16_t l2cap_psm){
396ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_GOEP_CONNECTION;
397ac726063SMatthias Ringwald return goep_client_connect_l2cap(&cover_art_client->goep_client,
398ac726063SMatthias Ringwald (l2cap_ertm_config_t *) cover_art_client->ertm_config,
399ac726063SMatthias Ringwald cover_art_client->ertm_buffer,
400ac726063SMatthias Ringwald cover_art_client->ertm_buffer_size,
401ac726063SMatthias Ringwald &avrcp_cover_art_packet_handler,
402ac726063SMatthias Ringwald cover_art_client->addr,
403ac726063SMatthias Ringwald l2cap_psm,
404ac726063SMatthias Ringwald &cover_art_client->goep_cid);
405ac726063SMatthias Ringwald }
406ac726063SMatthias Ringwald
avrcp_cover_art_handle_sdp_query_complete(avrcp_connection_t * connection,uint8_t status)407ac726063SMatthias Ringwald static void avrcp_cover_art_handle_sdp_query_complete(avrcp_connection_t * connection, uint8_t status){
408ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_avrcp_cid(connection->avrcp_cid);
409ac726063SMatthias Ringwald
410ac726063SMatthias Ringwald if (cover_art_client == NULL) {
411ac726063SMatthias Ringwald return;
412ac726063SMatthias Ringwald }
413ac726063SMatthias Ringwald if (cover_art_client->state != AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE){
414ac726063SMatthias Ringwald return;
415ac726063SMatthias Ringwald }
416ac726063SMatthias Ringwald
417ac726063SMatthias Ringwald // l2cap available?
418ac726063SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
419ac726063SMatthias Ringwald if (connection->cover_art_psm == 0){
420ac726063SMatthias Ringwald status = SDP_SERVICE_NOT_FOUND;
421ac726063SMatthias Ringwald }
422ac726063SMatthias Ringwald }
423ac726063SMatthias Ringwald
424ac726063SMatthias Ringwald if (status == ERROR_CODE_SUCCESS) {
425ac726063SMatthias Ringwald // ready to connect
426ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_GOEP_CONNECT;
42765444753SMatthias Ringwald status = avrcp_cover_art_client_setup_connection(cover_art_client, connection->cover_art_psm);
42865444753SMatthias Ringwald }
42965444753SMatthias Ringwald
43065444753SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
431ac726063SMatthias Ringwald btstack_packet_handler_t packet_handler = cover_art_client->packet_handler;
432ac726063SMatthias Ringwald uint16_t cover_art_cid = cover_art_client->cover_art_cid;
433ac726063SMatthias Ringwald uint16_t avrcp_cid = cover_art_client->avrcp_cid;
434ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client);
435ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(packet_handler, status, connection->remote_addr,
436ac726063SMatthias Ringwald avrcp_cid, cover_art_cid);
437ac726063SMatthias Ringwald }
438ac726063SMatthias Ringwald }
439ac726063SMatthias Ringwald
avrcp_cover_art_client_init(void)440ac726063SMatthias Ringwald void avrcp_cover_art_client_init(void){
441ac726063SMatthias Ringwald avrcp_register_cover_art_sdp_query_complete_handler(&avrcp_cover_art_handle_sdp_query_complete);
442ac726063SMatthias Ringwald }
443ac726063SMatthias Ringwald
444ac726063SMatthias Ringwald uint8_t
avrcp_cover_art_client_connect(avrcp_cover_art_client_t * cover_art_client,btstack_packet_handler_t packet_handler,bd_addr_t remote_addr,uint8_t * ertm_buffer,uint32_t ertm_buffer_size,const l2cap_ertm_config_t * ertm_config,uint16_t * avrcp_cover_art_cid)445ac726063SMatthias Ringwald avrcp_cover_art_client_connect(avrcp_cover_art_client_t *cover_art_client, btstack_packet_handler_t packet_handler,
446ac726063SMatthias Ringwald bd_addr_t remote_addr, uint8_t *ertm_buffer, uint32_t ertm_buffer_size,
447ac726063SMatthias Ringwald const l2cap_ertm_config_t *ertm_config, uint16_t *avrcp_cover_art_cid) {
448ac726063SMatthias Ringwald
449ac726063SMatthias Ringwald avrcp_connection_t * connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, remote_addr);
450ac726063SMatthias Ringwald avrcp_connection_t * connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, remote_addr);
451ac726063SMatthias Ringwald if ((connection_target == NULL) || (connection_controller == NULL)){
452ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
453ac726063SMatthias Ringwald }
454ac726063SMatthias Ringwald
455ac726063SMatthias Ringwald cover_art_client->cover_art_cid = avrcp_cover_art_client_next_cid();
456ac726063SMatthias Ringwald memcpy(cover_art_client->addr, remote_addr, 6);
457ac726063SMatthias Ringwald cover_art_client->avrcp_cid = connection_controller->avrcp_cid;
458ac726063SMatthias Ringwald cover_art_client->packet_handler = packet_handler;
459ac726063SMatthias Ringwald cover_art_client->flow_control_enabled = false;
460ac726063SMatthias Ringwald
461ac726063SMatthias Ringwald // store ERTM config
462ac726063SMatthias Ringwald cover_art_client->ertm_config = ertm_config;
463ac726063SMatthias Ringwald cover_art_client->ertm_buffer = ertm_buffer;
464ac726063SMatthias Ringwald cover_art_client->ertm_buffer_size = ertm_buffer_size;
465ac726063SMatthias Ringwald
466ac726063SMatthias Ringwald if (avrcp_cover_art_cid != NULL){
467ac726063SMatthias Ringwald *avrcp_cover_art_cid = cover_art_client->cover_art_cid;
468ac726063SMatthias Ringwald }
469ac726063SMatthias Ringwald
470ac726063SMatthias Ringwald btstack_linked_list_add(&avrcp_cover_art_client_connections, (btstack_linked_item_t *) cover_art_client);
471ac726063SMatthias Ringwald
472ac726063SMatthias Ringwald if (connection_controller->cover_art_psm == 0){
473ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE;
474ac726063SMatthias Ringwald avrcp_trigger_sdp_query(connection_controller, connection_controller);
475ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS;
476ac726063SMatthias Ringwald } else {
477ac726063SMatthias Ringwald return avrcp_cover_art_client_setup_connection(cover_art_client, connection_controller->cover_art_psm);
478ac726063SMatthias Ringwald }
479ac726063SMatthias Ringwald }
480ac726063SMatthias Ringwald
avrcp_cover_art_client_get_object(uint16_t avrcp_cover_art_cid,const char * object_type,const char * image_handle,const char * image_descriptor)481ac726063SMatthias Ringwald static uint8_t avrcp_cover_art_client_get_object(uint16_t avrcp_cover_art_cid, const char * object_type, const char * image_handle, const char * image_descriptor){
482ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid);
483ac726063SMatthias Ringwald if (cover_art_client == NULL){
484ac726063SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
485ac726063SMatthias Ringwald }
486ac726063SMatthias Ringwald if (cover_art_client->state != AVRCP_COVER_ART_CONNECTED){
487ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
488ac726063SMatthias Ringwald }
489ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT;
490ac726063SMatthias Ringwald cover_art_client->first_request = true;
491ac726063SMatthias Ringwald cover_art_client->image_handle = image_handle;
492ac726063SMatthias Ringwald cover_art_client->image_descriptor = image_descriptor;
493ac726063SMatthias Ringwald cover_art_client->object_type = object_type;
494ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid);
495ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS;
496ac726063SMatthias Ringwald }
497ac726063SMatthias Ringwald
avrcp_cover_art_client_get_linked_thumbnail(uint16_t avrcp_cover_art_cid,const char * image_handle)498ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_linked_thumbnail(uint16_t avrcp_cover_art_cid, const char * image_handle){
499ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
500ac726063SMatthias Ringwald avrcp_cover_art_linked_thumbnail_type,
501ac726063SMatthias Ringwald image_handle,
502ac726063SMatthias Ringwald NULL);
503ac726063SMatthias Ringwald }
504ac726063SMatthias Ringwald
avrcp_cover_art_client_get_image(uint16_t avrcp_cover_art_cid,const char * image_handle,const char * image_descriptor)505ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image(uint16_t avrcp_cover_art_cid, const char * image_handle, const char * image_descriptor){
506ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
507ac726063SMatthias Ringwald avrcp_cover_art_image_type,
508ac726063SMatthias Ringwald image_handle,
509ac726063SMatthias Ringwald image_descriptor);
510ac726063SMatthias Ringwald }
511ac726063SMatthias Ringwald
avrcp_cover_art_client_get_image_properties(uint16_t avrcp_cover_art_cid,const char * image_handle)512ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image_properties(uint16_t avrcp_cover_art_cid, const char * image_handle){
513ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
514ac726063SMatthias Ringwald avrcp_cover_art_image_properties_type,
515ac726063SMatthias Ringwald image_handle,
516ac726063SMatthias Ringwald NULL);
517ac726063SMatthias Ringwald }
518ac726063SMatthias Ringwald
avrcp_cover_art_client_disconnect(uint16_t avrcp_cover_art_cid)519ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_disconnect(uint16_t avrcp_cover_art_cid){
520ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid);
521ac726063SMatthias Ringwald if (cover_art_client == NULL){
522ac726063SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
523ac726063SMatthias Ringwald }
524ac726063SMatthias Ringwald if (cover_art_client->state < AVRCP_COVER_ART_CONNECTED){
525ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
526ac726063SMatthias Ringwald }
527ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST;
528ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid);
529ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS;
530ac726063SMatthias Ringwald }
531ac726063SMatthias Ringwald
avrcp_cover_art_client_deinit(void)532ac726063SMatthias Ringwald void avrcp_cover_art_client_deinit(void){
533ac726063SMatthias Ringwald avrcp_cover_art_client_connections = NULL;
534ac726063SMatthias Ringwald }
535ac726063SMatthias Ringwald
536ac726063SMatthias Ringwald #endif /* ENABLE_AVRCP_COVER_ART */
537