xref: /btstack/src/classic/avrcp_browsing_controller.c (revision d2fe8c23c6b783fcff39f9d743ccc579d1a15ef2)
1462aa085SMilanka Ringwald /*
2462aa085SMilanka Ringwald  * Copyright (C) 2016 BlueKitchen GmbH
3462aa085SMilanka Ringwald  *
4462aa085SMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5462aa085SMilanka Ringwald  * modification, are permitted provided that the following conditions
6462aa085SMilanka Ringwald  * are met:
7462aa085SMilanka Ringwald  *
8462aa085SMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9462aa085SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10462aa085SMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11462aa085SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12462aa085SMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13462aa085SMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14462aa085SMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15462aa085SMilanka Ringwald  *    from this software without specific prior written permission.
16462aa085SMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17462aa085SMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18462aa085SMilanka Ringwald  *    monetary gain.
19462aa085SMilanka Ringwald  *
20462aa085SMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21462aa085SMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22462aa085SMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25462aa085SMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26462aa085SMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27462aa085SMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28462aa085SMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29462aa085SMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30462aa085SMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31462aa085SMilanka Ringwald  * SUCH DAMAGE.
32462aa085SMilanka Ringwald  *
33462aa085SMilanka Ringwald  * Please inquire about commercial licensing options at
34462aa085SMilanka Ringwald  * contact@bluekitchen-gmbh.com
35462aa085SMilanka Ringwald  *
36462aa085SMilanka Ringwald  */
37462aa085SMilanka Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "avrcp_browsing_controller.c"
39462aa085SMilanka Ringwald 
40462aa085SMilanka Ringwald #include <stdint.h>
41462aa085SMilanka Ringwald #include <string.h>
424614c049SMilanka Ringwald #include <inttypes.h>
43665a00cbSMilanka Ringwald #include "classic/avrcp_browsing.h"
44462aa085SMilanka Ringwald #include "classic/avrcp_browsing_controller.h"
457c76cd61SMatthias Ringwald #include "classic/avrcp_controller.h"
467c76cd61SMatthias Ringwald 
477c76cd61SMatthias Ringwald #include "bluetooth_sdp.h"
487c76cd61SMatthias Ringwald #include "btstack_debug.h"
497c76cd61SMatthias Ringwald #include "btstack_event.h"
50462aa085SMilanka Ringwald 
51c0a054f6SMilanka Ringwald 
52247956eaSMilanka Ringwald static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
53462aa085SMilanka Ringwald 
avrcp_browsing_controller_send_get_folder_items_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)54ed0df7b2SMilanka Ringwald static int avrcp_browsing_controller_send_get_folder_items_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
55ed0df7b2SMilanka Ringwald     uint8_t command[100];
56ed0df7b2SMilanka Ringwald     int pos = 0;
57ed0df7b2SMilanka Ringwald     // transport header
58ed0df7b2SMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
59ed0df7b2SMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
60ed0df7b2SMilanka Ringwald     // Profile IDentifier (PID)
61ed0df7b2SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
62ed0df7b2SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
63ed0df7b2SMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
64ed0df7b2SMilanka Ringwald 
652f511ffbSMilanka Ringwald     uint32_t attribute_count = 0;
662f511ffbSMilanka Ringwald     uint32_t attributes_to_copy = 0;
672f511ffbSMilanka Ringwald 
682f511ffbSMilanka Ringwald     switch (connection->attr_bitmap){
692f511ffbSMilanka Ringwald         case AVRCP_MEDIA_ATTR_NONE:
702f511ffbSMilanka Ringwald             attribute_count = AVRCP_MEDIA_ATTR_NONE; // 0xFFFFFFFF
712f511ffbSMilanka Ringwald             break;
722f511ffbSMilanka Ringwald         case AVRCP_MEDIA_ATTR_ALL:
732f511ffbSMilanka Ringwald             attribute_count = AVRCP_MEDIA_ATTR_ALL;  // 0
742f511ffbSMilanka Ringwald             break;
752f511ffbSMilanka Ringwald         default:
7620c6ab95SMilanka Ringwald             attribute_count    = count_set_bits_uint32(connection->attr_bitmap & ((1 << AVRCP_MEDIA_ATTR_NUM)-1));
772f511ffbSMilanka Ringwald             attributes_to_copy = attribute_count;
782f511ffbSMilanka Ringwald             break;
792f511ffbSMilanka Ringwald     }
80c1ab6cc1SMatthias Ringwald     big_endian_store_16(command, pos, 9 + 1 + (attribute_count*4));
812f511ffbSMilanka Ringwald     pos += 2;
8265ca3506SMilanka Ringwald 
83ed0df7b2SMilanka Ringwald     command[pos++] = connection->scope;
84ed0df7b2SMilanka Ringwald     big_endian_store_32(command, pos, connection->start_item);
85ed0df7b2SMilanka Ringwald     pos += 4;
86ed0df7b2SMilanka Ringwald     big_endian_store_32(command, pos, connection->end_item);
87ed0df7b2SMilanka Ringwald     pos += 4;
882f511ffbSMilanka Ringwald     command[pos++] = attribute_count;
892f511ffbSMilanka Ringwald 
902f511ffbSMilanka Ringwald     int bit_position = 1;
912f511ffbSMilanka Ringwald     while (attributes_to_copy){
922f511ffbSMilanka Ringwald         if (connection->attr_bitmap & (1 << bit_position)){
932f511ffbSMilanka Ringwald             big_endian_store_32(command, pos, bit_position);
942f511ffbSMilanka Ringwald             pos += 4;
952f511ffbSMilanka Ringwald             attributes_to_copy--;
96ed0df7b2SMilanka Ringwald         }
972f511ffbSMilanka Ringwald         bit_position++;
982f511ffbSMilanka Ringwald     }
99ed0df7b2SMilanka Ringwald     return l2cap_send(cid, command, pos);
100ed0df7b2SMilanka Ringwald }
101ed0df7b2SMilanka Ringwald 
1025d6a28a5SMilanka Ringwald 
avrcp_browsing_controller_send_get_item_attributes_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)1035d6a28a5SMilanka Ringwald static int avrcp_browsing_controller_send_get_item_attributes_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
1045d6a28a5SMilanka Ringwald     uint8_t command[100];
1055d6a28a5SMilanka Ringwald     int pos = 0;
1065d6a28a5SMilanka Ringwald     // transport header
1075d6a28a5SMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
1085d6a28a5SMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
1095d6a28a5SMilanka Ringwald     // Profile IDentifier (PID)
1105d6a28a5SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
1115d6a28a5SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
1125d6a28a5SMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES;
1135d6a28a5SMilanka Ringwald 
11465ca3506SMilanka Ringwald     uint32_t attribute_count;
1155d6a28a5SMilanka Ringwald     uint32_t attributes_to_copy = 0;
1165d6a28a5SMilanka Ringwald 
1175d6a28a5SMilanka Ringwald     switch (connection->attr_bitmap){
1185d6a28a5SMilanka Ringwald         case AVRCP_MEDIA_ATTR_NONE:
1195d6a28a5SMilanka Ringwald         case AVRCP_MEDIA_ATTR_ALL:
12065ca3506SMilanka Ringwald             attribute_count = 0;
1215d6a28a5SMilanka Ringwald             break;
1225d6a28a5SMilanka Ringwald         default:
12320c6ab95SMilanka Ringwald             attribute_count    = count_set_bits_uint32(connection->attr_bitmap & ((1 << AVRCP_MEDIA_ATTR_NUM)-1));
1245d6a28a5SMilanka Ringwald             attributes_to_copy = attribute_count;
1255d6a28a5SMilanka Ringwald             break;
1265d6a28a5SMilanka Ringwald     }
1275d6a28a5SMilanka Ringwald 
128c1ab6cc1SMatthias Ringwald     big_endian_store_16(command, pos, 12 + (attribute_count*4));
1295d6a28a5SMilanka Ringwald     pos += 2;
1305d6a28a5SMilanka Ringwald 
1315d6a28a5SMilanka Ringwald     command[pos++] = connection->scope;
132*d2fe8c23SMilanka Ringwald     (void)memcpy(command + pos, connection->item_uid, 8);
1335d6a28a5SMilanka Ringwald     pos += 8;
1345d6a28a5SMilanka Ringwald     big_endian_store_16(command, pos, connection->uid_counter);
1355d6a28a5SMilanka Ringwald     pos += 2;
1365d6a28a5SMilanka Ringwald     command[pos++] = attribute_count;
1375d6a28a5SMilanka Ringwald 
1385d6a28a5SMilanka Ringwald     int bit_position = 1;
1395d6a28a5SMilanka Ringwald     while (attributes_to_copy){
1405d6a28a5SMilanka Ringwald         if (connection->attr_bitmap & (1 << bit_position)){
1415d6a28a5SMilanka Ringwald             big_endian_store_32(command, pos, bit_position);
1425d6a28a5SMilanka Ringwald             pos += 4;
1435d6a28a5SMilanka Ringwald             attributes_to_copy--;
1445d6a28a5SMilanka Ringwald         }
1455d6a28a5SMilanka Ringwald         bit_position++;
1465d6a28a5SMilanka Ringwald     }
1475d6a28a5SMilanka Ringwald 
1485d6a28a5SMilanka Ringwald     return l2cap_send(cid, command, pos);
1495d6a28a5SMilanka Ringwald }
1505d6a28a5SMilanka Ringwald 
1515d6a28a5SMilanka Ringwald 
avrcp_browsing_controller_send_change_path_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)152db79553aSMilanka Ringwald static int avrcp_browsing_controller_send_change_path_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
153db79553aSMilanka Ringwald     uint8_t command[100];
154db79553aSMilanka Ringwald     int pos = 0;
155db79553aSMilanka Ringwald     // transport header
156db79553aSMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
157db79553aSMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
158db79553aSMilanka Ringwald     // Profile IDentifier (PID)
159db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
160db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
161db79553aSMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_CHANGE_PATH;
162db79553aSMilanka Ringwald 
163db79553aSMilanka Ringwald     big_endian_store_16(command, pos, 11);
164db79553aSMilanka Ringwald     pos += 2;
165db79553aSMilanka Ringwald     pos += 2;
166db79553aSMilanka Ringwald     command[pos++] = connection->direction;
167*d2fe8c23SMilanka Ringwald     (void)memcpy(command + pos, connection->item_uid, 8);
168db79553aSMilanka Ringwald     pos += 8;
169db79553aSMilanka Ringwald     return l2cap_send(cid, command, pos);
170db79553aSMilanka Ringwald }
171db79553aSMilanka Ringwald 
avrcp_browsing_controller_send_search_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)17273fba538SMilanka Ringwald static int avrcp_browsing_controller_send_search_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
17373fba538SMilanka Ringwald     uint8_t command[100];
17473fba538SMilanka Ringwald     int pos = 0;
17573fba538SMilanka Ringwald     // transport header
17673fba538SMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
17773fba538SMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
17873fba538SMilanka Ringwald     // Profile IDentifier (PID)
17973fba538SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
18073fba538SMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
18173fba538SMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_SEARCH;
18273fba538SMilanka Ringwald 
18373fba538SMilanka Ringwald     big_endian_store_16(command, pos, 4 + connection->search_str_len);
18473fba538SMilanka Ringwald     pos += 2;
18573fba538SMilanka Ringwald 
18673fba538SMilanka Ringwald     big_endian_store_16(command, pos, 0x006A);
18773fba538SMilanka Ringwald     pos += 2;
18873fba538SMilanka Ringwald     big_endian_store_16(command, pos, connection->search_str_len);
18973fba538SMilanka Ringwald     pos += 2;
19073fba538SMilanka Ringwald 
1916535961aSMatthias Ringwald     (void)memcpy(command + pos, connection->search_str,
1926535961aSMatthias Ringwald                  connection->search_str_len);
19373fba538SMilanka Ringwald     pos += connection->search_str_len;
19473fba538SMilanka Ringwald     return l2cap_send(cid, command, pos);
19573fba538SMilanka Ringwald }
19673fba538SMilanka Ringwald 
avrcp_browsing_controller_send_set_browsed_player_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)197db79553aSMilanka Ringwald static int avrcp_browsing_controller_send_set_browsed_player_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
198db79553aSMilanka Ringwald     uint8_t command[100];
199db79553aSMilanka Ringwald     int pos = 0;
200db79553aSMilanka Ringwald     // transport header
201db79553aSMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
202db79553aSMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
203db79553aSMilanka Ringwald     // Profile IDentifier (PID)
204db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
205db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
206db79553aSMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
207db79553aSMilanka Ringwald 
208db79553aSMilanka Ringwald     big_endian_store_16(command, pos, 2);
209db79553aSMilanka Ringwald     pos += 2;
210db79553aSMilanka Ringwald     big_endian_store_16(command, pos, connection->browsed_player_id);
211db79553aSMilanka Ringwald     pos += 2;
212db79553aSMilanka Ringwald     return l2cap_send(cid, command, pos);
213db79553aSMilanka Ringwald }
214db79553aSMilanka Ringwald 
avrcp_browsing_controller_send_get_total_nr_items_cmd(uint16_t cid,avrcp_browsing_connection_t * connection)215864d08b0SMilanka Ringwald static int avrcp_browsing_controller_send_get_total_nr_items_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
216864d08b0SMilanka Ringwald     uint8_t command[7];
217db79553aSMilanka Ringwald     int pos = 0;
218db79553aSMilanka Ringwald     // transport header
219db79553aSMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
220db79553aSMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
221db79553aSMilanka Ringwald     // Profile IDentifier (PID)
222db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
223db79553aSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
224864d08b0SMilanka Ringwald     command[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
225db79553aSMilanka Ringwald 
226864d08b0SMilanka Ringwald     big_endian_store_16(command, pos, 1);
227db79553aSMilanka Ringwald     pos += 2;
228864d08b0SMilanka Ringwald     command[pos++] = connection->get_total_nr_items_scope;
229db79553aSMilanka Ringwald     return l2cap_send(cid, command, pos);
230db79553aSMilanka Ringwald }
231ed0df7b2SMilanka Ringwald 
avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection)232ed0df7b2SMilanka Ringwald static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection){
233ed0df7b2SMilanka Ringwald     switch (connection->state){
234ed0df7b2SMilanka Ringwald         case AVCTP_CONNECTION_OPENED:
235db79553aSMilanka Ringwald             if (connection->set_browsed_player_id){
236db79553aSMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
237db79553aSMilanka Ringwald                 connection->set_browsed_player_id = 0;
238db79553aSMilanka Ringwald                 avrcp_browsing_controller_send_set_browsed_player_cmd(connection->l2cap_browsing_cid, connection);
239db79553aSMilanka Ringwald                 break;
240db79553aSMilanka Ringwald             }
241db79553aSMilanka Ringwald 
242864d08b0SMilanka Ringwald             if (connection->get_total_nr_items){
243db79553aSMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
244864d08b0SMilanka Ringwald                 connection->get_total_nr_items = 0;
245864d08b0SMilanka Ringwald                 avrcp_browsing_controller_send_get_total_nr_items_cmd(connection->l2cap_browsing_cid, connection);
246db79553aSMilanka Ringwald                 break;
247db79553aSMilanka Ringwald             }
248ad75da14SMilanka Ringwald 
2495d6a28a5SMilanka Ringwald             if (connection->get_folder_items){
250ad75da14SMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
2515d6a28a5SMilanka Ringwald                 connection->get_folder_items = 0;
252ad75da14SMilanka Ringwald                 avrcp_browsing_controller_send_get_folder_items_cmd(connection->l2cap_browsing_cid, connection);
253ad75da14SMilanka Ringwald                 break;
254ad75da14SMilanka Ringwald             }
2555d6a28a5SMilanka Ringwald 
2565d6a28a5SMilanka Ringwald             if (connection->get_item_attributes){
2575d6a28a5SMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
2585d6a28a5SMilanka Ringwald                 connection->get_item_attributes = 0;
2595d6a28a5SMilanka Ringwald                 avrcp_browsing_controller_send_get_item_attributes_cmd(connection->l2cap_browsing_cid, connection);
2605d6a28a5SMilanka Ringwald                 break;
2615d6a28a5SMilanka Ringwald             }
2625d6a28a5SMilanka Ringwald 
263ad75da14SMilanka Ringwald             if (connection->change_path){
264ad75da14SMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
265ad75da14SMilanka Ringwald                 connection->change_path = 0;
266ad75da14SMilanka Ringwald                 avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection);
267ad75da14SMilanka Ringwald                 break;
268ad75da14SMilanka Ringwald             }
2695d6a28a5SMilanka Ringwald 
27073fba538SMilanka Ringwald             if (connection->search){
27173fba538SMilanka Ringwald                 connection->state = AVCTP_W2_RECEIVE_RESPONSE;
27273fba538SMilanka Ringwald                 connection->search = 0;
27373fba538SMilanka Ringwald                 avrcp_browsing_controller_send_search_cmd(connection->l2cap_browsing_cid, connection);
27473fba538SMilanka Ringwald                 break;
27573fba538SMilanka Ringwald             }
276be65baf4SMilanka Ringwald             break;
277ed0df7b2SMilanka Ringwald         default:
278ed0df7b2SMilanka Ringwald             return;
279ed0df7b2SMilanka Ringwald     }
280ed0df7b2SMilanka Ringwald }
281462aa085SMilanka Ringwald 
282be65baf4SMilanka Ringwald 
avrcp_browsing_controller_emit_done_with_uid_counter(btstack_packet_handler_t callback,uint16_t browsing_cid,uint16_t uid_counter,uint8_t browsing_status,uint8_t bluetooth_status)283be65baf4SMilanka Ringwald static void avrcp_browsing_controller_emit_done_with_uid_counter(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, uint8_t browsing_status, uint8_t bluetooth_status){
284be4cc80aSMilanka Ringwald     btstack_assert(callback != NULL);
285be4cc80aSMilanka Ringwald 
286be65baf4SMilanka Ringwald     uint8_t event[9];
287e30788a3SMilanka Ringwald     int pos = 0;
288e30788a3SMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
289e30788a3SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
290be65baf4SMilanka Ringwald     event[pos++] = AVRCP_SUBEVENT_BROWSING_DONE;
291e30788a3SMilanka Ringwald     little_endian_store_16(event, pos, browsing_cid);
292e30788a3SMilanka Ringwald     pos += 2;
293be65baf4SMilanka Ringwald     little_endian_store_16(event, pos, uid_counter);
294be65baf4SMilanka Ringwald     pos += 2;
295954cc391SMilanka Ringwald     event[pos++] = browsing_status;
296954cc391SMilanka Ringwald     event[pos++] = bluetooth_status;
297e30788a3SMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
298e30788a3SMilanka Ringwald }
299e30788a3SMilanka Ringwald 
avrcp_parser_reset(avrcp_browsing_connection_t * connection)30014e95846SMilanka Ringwald static void avrcp_parser_reset(avrcp_browsing_connection_t * connection){
30114e95846SMilanka Ringwald     connection->parser_attribute_header_pos = 0;
30214e95846SMilanka Ringwald     connection->parsed_attribute_value_offset = 0;
30314e95846SMilanka Ringwald     connection->parsed_num_attributes = 0;
30414e95846SMilanka Ringwald     connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
30514e95846SMilanka Ringwald }
30614e95846SMilanka Ringwald 
30714e95846SMilanka Ringwald 
avrcp_browsing_parser_process_byte(uint8_t byte,avrcp_browsing_connection_t * connection)30814e95846SMilanka Ringwald static void avrcp_browsing_parser_process_byte(uint8_t byte, avrcp_browsing_connection_t * connection){
30914e95846SMilanka Ringwald     uint8_t prepended_header_size = 1;
310be4cc80aSMilanka Ringwald     uint16_t attribute_total_value_len;
311be4cc80aSMilanka Ringwald 
31214e95846SMilanka Ringwald     switch(connection->parser_state){
313be4cc80aSMilanka Ringwald         case AVRCP_PARSER_GET_ATTRIBUTE_HEADER:
31414e95846SMilanka Ringwald             connection->parser_attribute_header[connection->parser_attribute_header_pos++] = byte;
3159bb24eb5SMilanka Ringwald             if (connection->parser_attribute_header_pos < AVRCP_BROWSING_ITEM_HEADER_LEN) break;
3169bb24eb5SMilanka Ringwald 
317be4cc80aSMilanka Ringwald             attribute_total_value_len = big_endian_read_16(connection->parser_attribute_header, 1);
31814e95846SMilanka Ringwald             connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = connection->parser_attribute_header[0];   // prepend with item type
319aeb99916SMilanka Ringwald             connection->parsed_attribute_value_len = btstack_min(attribute_total_value_len, AVRCP_MAX_ATTRIBUTE_SIZE - prepended_header_size);                 // reduce AVRCP_MAX_ATTRIBUTE_SIZE for the size ot item type
32014e95846SMilanka Ringwald             connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_VALUE;
32114e95846SMilanka Ringwald             break;
322be4cc80aSMilanka Ringwald 
323be4cc80aSMilanka Ringwald         case AVRCP_PARSER_GET_ATTRIBUTE_VALUE:
32414e95846SMilanka Ringwald             connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = byte;
325c1ab6cc1SMatthias Ringwald             if (connection->parsed_attribute_value_offset < (connection->parsed_attribute_value_len + prepended_header_size)){
32614e95846SMilanka Ringwald                 break;
32714e95846SMilanka Ringwald             }
32814e95846SMilanka Ringwald             if (connection->parsed_attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 1)){
32914e95846SMilanka Ringwald                 connection->parser_state = AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE;
33014e95846SMilanka Ringwald                 break;
33114e95846SMilanka Ringwald             }
33289d1b089SMilanka Ringwald             connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
33314e95846SMilanka Ringwald             (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset);
33489d1b089SMilanka Ringwald             connection->parsed_num_attributes++;
33589d1b089SMilanka Ringwald             connection->parsed_attribute_value_offset = 0;
33689d1b089SMilanka Ringwald             connection->parser_attribute_header_pos = 0;
33714e95846SMilanka Ringwald 
33814e95846SMilanka Ringwald             if (connection->parsed_num_attributes == connection->num_items){
33914e95846SMilanka Ringwald                 avrcp_parser_reset(connection);
34014e95846SMilanka Ringwald                 break;
34114e95846SMilanka Ringwald             }
34214e95846SMilanka Ringwald             break;
343be4cc80aSMilanka Ringwald 
34414e95846SMilanka Ringwald         case AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE:
34514e95846SMilanka Ringwald             connection->parsed_attribute_value_offset++;
3465df9dc78SMatthias Ringwald             if (connection->parsed_attribute_value_offset < (big_endian_read_16(connection->parser_attribute_header, 1) + prepended_header_size)){
34714e95846SMilanka Ringwald                 break;
34814e95846SMilanka Ringwald             }
34989d1b089SMilanka Ringwald             connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
35014e95846SMilanka Ringwald             (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset);
35189d1b089SMilanka Ringwald             connection->parsed_num_attributes++;
35289d1b089SMilanka Ringwald             connection->parsed_attribute_value_offset = 0;
35389d1b089SMilanka Ringwald             connection->parser_attribute_header_pos = 0;
35414e95846SMilanka Ringwald 
35514e95846SMilanka Ringwald             if (connection->parsed_num_attributes == connection->num_items){
35614e95846SMilanka Ringwald                 avrcp_parser_reset(connection);
35714e95846SMilanka Ringwald                 break;
35814e95846SMilanka Ringwald             }
35914e95846SMilanka Ringwald             break;
36014e95846SMilanka Ringwald         default:
36114e95846SMilanka Ringwald             break;
36214e95846SMilanka Ringwald     }
36314e95846SMilanka Ringwald }
36414e95846SMilanka Ringwald 
avrcp_browsing_parse_and_emit_element_attrs(uint8_t * packet,uint16_t num_bytes_to_read,avrcp_browsing_connection_t * connection)36514e95846SMilanka Ringwald static void avrcp_browsing_parse_and_emit_element_attrs(uint8_t * packet, uint16_t num_bytes_to_read, avrcp_browsing_connection_t * connection){
36614e95846SMilanka Ringwald     int i;
36714e95846SMilanka Ringwald     for (i=0;i<num_bytes_to_read;i++){
36814e95846SMilanka Ringwald         avrcp_browsing_parser_process_byte(packet[i], connection);
36914e95846SMilanka Ringwald     }
37014e95846SMilanka Ringwald }
37114e95846SMilanka Ringwald 
avrcp_browsing_controller_emit_failed(btstack_packet_handler_t callback,uint16_t browsing_cid,uint8_t browsing_status,uint8_t bluetooth_status)3725c4bbc4eSMilanka Ringwald static void avrcp_browsing_controller_emit_failed(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){
373be65baf4SMilanka Ringwald     avrcp_browsing_controller_emit_done_with_uid_counter(callback, browsing_cid, 0, browsing_status, bluetooth_status);
374be65baf4SMilanka Ringwald }
375be65baf4SMilanka Ringwald 
37614e95846SMilanka Ringwald 
avrcp_browsing_controller_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)377954cc391SMilanka Ringwald static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
378954cc391SMilanka Ringwald     avrcp_browsing_connection_t * browsing_connection;
379be4cc80aSMilanka Ringwald     uint8_t transport_header;
380a912d067SMatthias Ringwald     uint32_t pos;
381954cc391SMilanka Ringwald     switch (packet_type) {
382be4cc80aSMilanka Ringwald         case L2CAP_DATA_PACKET:
3831945fe3eSMilanka Ringwald             browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_CONTROLLER, channel);
384954cc391SMilanka Ringwald             if (!browsing_connection) break;
385a912d067SMatthias Ringwald             if (size < 6) break;
386be4cc80aSMilanka Ringwald             pos = 0;
387be4cc80aSMilanka Ringwald             transport_header = packet[pos++];
388ad75da14SMilanka Ringwald             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
3898ed86b46SMilanka Ringwald             browsing_connection->transaction_label = transport_header >> 4;
390ad75da14SMilanka Ringwald             avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
39114e95846SMilanka Ringwald             switch (avctp_packet_type){
39214e95846SMilanka Ringwald                 case AVRCP_SINGLE_PACKET:
39314e95846SMilanka Ringwald                 case AVRCP_START_PACKET:
394ad75da14SMilanka Ringwald                     pos += 2;
395ad75da14SMilanka Ringwald                     browsing_connection->num_packets = 1;
396be65baf4SMilanka Ringwald                     if (avctp_packet_type == AVRCP_START_PACKET){
397ad75da14SMilanka Ringwald                         browsing_connection->num_packets = packet[pos++];
398ad75da14SMilanka Ringwald                     }
399c1ab6cc1SMatthias Ringwald                     if ((pos + 4) > size){
4005d6a28a5SMilanka Ringwald                         browsing_connection->state = AVCTP_CONNECTION_OPENED;
4015c4bbc4eSMilanka Ringwald                         avrcp_browsing_controller_emit_failed(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
402ad75da14SMilanka Ringwald                         return;
403954cc391SMilanka Ringwald                     }
40414e95846SMilanka Ringwald                     browsing_connection->pdu_id = packet[pos++];
405954cc391SMilanka Ringwald                     pos += 2;
40614e95846SMilanka Ringwald                     browsing_connection->browsing_status = packet[pos++];
40714e95846SMilanka Ringwald                     if (browsing_connection->browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
4085d6a28a5SMilanka Ringwald                         browsing_connection->state = AVCTP_CONNECTION_OPENED;
4095c4bbc4eSMilanka Ringwald                         avrcp_browsing_controller_emit_failed(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
410ad75da14SMilanka Ringwald                         return;
411db79553aSMilanka Ringwald                     }
41214e95846SMilanka Ringwald                     break;
41314e95846SMilanka Ringwald                 default:
41414e95846SMilanka Ringwald                     break;
415be65baf4SMilanka Ringwald             }
416db79553aSMilanka Ringwald 
417db79553aSMilanka Ringwald             uint32_t i;
418be4cc80aSMilanka Ringwald             uint8_t folder_depth;
419be4cc80aSMilanka Ringwald 
42014e95846SMilanka Ringwald             switch(browsing_connection->pdu_id){
421db79553aSMilanka Ringwald                 case AVRCP_PDU_ID_CHANGE_PATH:
422ad75da14SMilanka Ringwald                     break;
423db79553aSMilanka Ringwald                 case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER:
424db79553aSMilanka Ringwald                     break;
425be4cc80aSMilanka Ringwald                 case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:
426864d08b0SMilanka Ringwald                     break;
427be4cc80aSMilanka Ringwald                 case AVRCP_PDU_ID_SET_BROWSED_PLAYER:
428a912d067SMatthias Ringwald                     if ((pos + 9) > size) break;
429a912d067SMatthias Ringwald 
4304614c049SMilanka Ringwald                     browsing_connection->uid_counter =  big_endian_read_16(packet, pos);
4314614c049SMilanka Ringwald                     pos += 2;
432be4cc80aSMilanka Ringwald                     // num_items
433db79553aSMilanka Ringwald                     pos += 4;
434be4cc80aSMilanka Ringwald                     // charset
435be65baf4SMilanka Ringwald                     pos += 2;
436be4cc80aSMilanka Ringwald                     folder_depth = packet[pos++];
437db79553aSMilanka Ringwald 
438be65baf4SMilanka Ringwald                     for (i = 0; i < folder_depth; i++){
439a912d067SMatthias Ringwald                         if ((pos + 2) > size) return;
440be65baf4SMilanka Ringwald                         uint16_t folder_name_length = big_endian_read_16(packet, pos);
441be65baf4SMilanka Ringwald                         pos += 2;
442be65baf4SMilanka Ringwald                         // reuse packet and add data type as a header
443a912d067SMatthias Ringwald                         if ((pos + folder_name_length) > size) return;
444db79553aSMilanka Ringwald                         packet[pos-1] = AVRCP_BROWSING_MEDIA_ROOT_FOLDER;
445be65baf4SMilanka Ringwald                         (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, folder_name_length+1);
446be65baf4SMilanka Ringwald                         pos += folder_name_length;
447be65baf4SMilanka Ringwald                     }
448db79553aSMilanka Ringwald                     break;
449be4cc80aSMilanka Ringwald 
450db79553aSMilanka Ringwald                 case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{
45114e95846SMilanka Ringwald                     switch (avctp_packet_type){
45214e95846SMilanka Ringwald                         case AVRCP_SINGLE_PACKET:
45314e95846SMilanka Ringwald                         case AVRCP_START_PACKET:
454a912d067SMatthias Ringwald                             if ((pos + 4) > size) return;
45514e95846SMilanka Ringwald                             avrcp_parser_reset(browsing_connection);
4564614c049SMilanka Ringwald                             browsing_connection->uid_counter =  big_endian_read_16(packet, pos);
4574614c049SMilanka Ringwald                             pos += 2;
45814e95846SMilanka Ringwald                             browsing_connection->num_items = big_endian_read_16(packet, pos); //num_items
459954cc391SMilanka Ringwald                             pos += 2;
46014e95846SMilanka Ringwald                             avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
46114e95846SMilanka Ringwald                             break;
462be65baf4SMilanka Ringwald 
46314e95846SMilanka Ringwald                         case AVRCP_CONTINUE_PACKET:
46414e95846SMilanka Ringwald                             avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
465be65baf4SMilanka Ringwald                             break;
46614e95846SMilanka Ringwald 
46714e95846SMilanka Ringwald                         case AVRCP_END_PACKET:
46814e95846SMilanka Ringwald                             avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
46914e95846SMilanka Ringwald                             avrcp_parser_reset(browsing_connection);
470be65baf4SMilanka Ringwald                             break;
471be4cc80aSMilanka Ringwald                         default:
472be4cc80aSMilanka Ringwald                             break;
473be65baf4SMilanka Ringwald                     }
474db79553aSMilanka Ringwald                     break;
475db79553aSMilanka Ringwald                 }
476be4cc80aSMilanka Ringwald                 case AVRCP_PDU_ID_SEARCH:
477a912d067SMatthias Ringwald                     if ((pos + 2) > size) return;
4784614c049SMilanka Ringwald                     browsing_connection->uid_counter =  big_endian_read_16(packet, pos);
4794614c049SMilanka Ringwald                     pos += 2;
48073fba538SMilanka Ringwald                     break;
4814614c049SMilanka Ringwald                 case AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES:
4824614c049SMilanka Ringwald                     packet[pos-1] = AVRCP_BROWSING_MEDIA_ELEMENT_ITEM_ATTRIBUTE;
4834614c049SMilanka Ringwald                     (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, size - pos + 1);
4844614c049SMilanka Ringwald                     break;
485db79553aSMilanka Ringwald                 default:
486e72b716bSMilanka Ringwald                     log_info(" not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
487ad75da14SMilanka Ringwald                     break;
488954cc391SMilanka Ringwald             }
489be65baf4SMilanka Ringwald 
490ad75da14SMilanka Ringwald             switch (avctp_packet_type){
491ad75da14SMilanka Ringwald                 case AVRCP_SINGLE_PACKET:
492ad75da14SMilanka Ringwald                 case AVRCP_END_PACKET:
493be65baf4SMilanka Ringwald                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
4945c4bbc4eSMilanka Ringwald                     avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
495ad75da14SMilanka Ringwald                     break;
49614e95846SMilanka Ringwald                 default:
497ad75da14SMilanka Ringwald                     break;
498ad75da14SMilanka Ringwald             }
499e30788a3SMilanka Ringwald             break;
500be4cc80aSMilanka Ringwald 
501462aa085SMilanka Ringwald         case HCI_EVENT_PACKET:
502462aa085SMilanka Ringwald             switch (hci_event_packet_get_type(packet)){
503462aa085SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
5041945fe3eSMilanka Ringwald                     browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_CONTROLLER,channel);
505954cc391SMilanka Ringwald                     avrcp_browsing_controller_handle_can_send_now(browsing_connection);
506462aa085SMilanka Ringwald                     break;
507462aa085SMilanka Ringwald                 default:
508462aa085SMilanka Ringwald                     break;
509462aa085SMilanka Ringwald             }
5105d6a28a5SMilanka Ringwald             break;
5115d6a28a5SMilanka Ringwald 
512462aa085SMilanka Ringwald         default:
513462aa085SMilanka Ringwald             break;
514462aa085SMilanka Ringwald     }
515462aa085SMilanka Ringwald }
516462aa085SMilanka Ringwald 
avrcp_browsing_controller_init(void)517462aa085SMilanka Ringwald void avrcp_browsing_controller_init(void){
518247956eaSMilanka Ringwald     avrcp_controller_context.browsing_packet_handler = avrcp_browsing_controller_packet_handler;
5190eebc132SMilanka Ringwald     avrcp_browsing_register_controller_packet_handler(avrcp_browsing_controller_packet_handler);
520c0a054f6SMilanka Ringwald }
521c0a054f6SMilanka Ringwald 
avrcp_browsing_controller_deinit(void)522680af5dcSMatthias Ringwald void avrcp_browsing_controller_deinit(void){
523680af5dcSMatthias Ringwald     avrcp_controller_context.browsing_packet_handler = NULL;
524680af5dcSMatthias Ringwald }
525680af5dcSMatthias Ringwald 
avrcp_browsing_controller_register_packet_handler(btstack_packet_handler_t callback)526c0a054f6SMilanka Ringwald void avrcp_browsing_controller_register_packet_handler(btstack_packet_handler_t callback){
527be4cc80aSMilanka Ringwald     btstack_assert(callback != NULL);
528c0a054f6SMilanka Ringwald     avrcp_controller_context.browsing_avrcp_callback = callback;
529462aa085SMilanka Ringwald }
530462aa085SMilanka Ringwald 
avrcp_browsing_controller_get_item_attributes_for_scope(uint16_t avrcp_browsing_cid,uint8_t * uid,uint16_t uid_counter,uint32_t attr_bitmap,avrcp_browsing_scope_t scope)5314614c049SMilanka Ringwald uint8_t avrcp_browsing_controller_get_item_attributes_for_scope(uint16_t avrcp_browsing_cid, uint8_t * uid, uint16_t uid_counter, uint32_t attr_bitmap, avrcp_browsing_scope_t scope){
5321945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
5335d6a28a5SMilanka Ringwald     if (!avrcp_connection){
5345d6a28a5SMilanka Ringwald         log_error("avrcp_browsing_controller_get_item_attributes: could not find a connection.");
5355d6a28a5SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
5365d6a28a5SMilanka Ringwald     }
5375d6a28a5SMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
5385d6a28a5SMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED){
5395d6a28a5SMilanka Ringwald         log_error("avrcp_browsing_controller_get_item_attributes: connection in wrong state %d, expected %d.", connection->state, AVCTP_CONNECTION_OPENED);
5405d6a28a5SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
5415d6a28a5SMilanka Ringwald     }
5425d6a28a5SMilanka Ringwald 
5435d6a28a5SMilanka Ringwald     connection->get_item_attributes = 1;
5445d6a28a5SMilanka Ringwald     connection->scope = scope;
545*d2fe8c23SMilanka Ringwald     (void)memcpy(connection->item_uid, uid, 8);
5465d6a28a5SMilanka Ringwald     connection->uid_counter = uid_counter;
5475d6a28a5SMilanka Ringwald     connection->attr_bitmap = attr_bitmap;
5485d6a28a5SMilanka Ringwald 
5490eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
5505d6a28a5SMilanka Ringwald     return ERROR_CODE_SUCCESS;
5515d6a28a5SMilanka Ringwald }
5525d6a28a5SMilanka Ringwald 
553ed0df7b2SMilanka Ringwald /**
554ed0df7b2SMilanka Ringwald  * @brief Retrieve a listing of the contents of a folder.
555ed0df7b2SMilanka Ringwald  * @param scope    0-player list, 1-virtual file system, 2-search, 3-now playing
556ed0df7b2SMilanka Ringwald  * @param start_item
557ed0df7b2SMilanka Ringwald  * @param end_item
558ed0df7b2SMilanka Ringwald  * @param attribute_count
559ed0df7b2SMilanka Ringwald  * @param attribute_list
560ed0df7b2SMilanka Ringwald  **/
avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid,avrcp_browsing_scope_t scope,uint32_t start_item,uint32_t end_item,uint32_t attr_bitmap)561be65baf4SMilanka Ringwald static uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, avrcp_browsing_scope_t scope, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){
5621945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
563ed0df7b2SMilanka Ringwald     if (!avrcp_connection){
564ed0df7b2SMilanka Ringwald         log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
565ed0df7b2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
566ed0df7b2SMilanka Ringwald     }
567ed0df7b2SMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
5685d6a28a5SMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) {
5695d6a28a5SMilanka Ringwald         log_error("avrcp_browsing_controller_get_folder_items: connection in wrong state %d, expected %d.", connection->state, AVCTP_CONNECTION_OPENED);
5705d6a28a5SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
5715d6a28a5SMilanka Ringwald     }
572ed0df7b2SMilanka Ringwald 
5735d6a28a5SMilanka Ringwald     connection->get_folder_items = 1;
574ed0df7b2SMilanka Ringwald     connection->scope = scope;
575ed0df7b2SMilanka Ringwald     connection->start_item = start_item;
576ed0df7b2SMilanka Ringwald     connection->end_item = end_item;
5772f511ffbSMilanka Ringwald     connection->attr_bitmap = attr_bitmap;
578ed0df7b2SMilanka Ringwald 
5790eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
580ed0df7b2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
581ed0df7b2SMilanka Ringwald }
582ed0df7b2SMilanka Ringwald 
avrcp_browsing_controller_get_media_players(uint16_t avrcp_browsing_cid,uint32_t start_item,uint32_t end_item,uint32_t attr_bitmap)5832f511ffbSMilanka Ringwald uint8_t avrcp_browsing_controller_get_media_players(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){
584be65baf4SMilanka Ringwald     return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_LIST, start_item, end_item, attr_bitmap);
585db79553aSMilanka Ringwald }
586db79553aSMilanka Ringwald 
avrcp_browsing_controller_browse_file_system(uint16_t avrcp_browsing_cid,uint32_t start_item,uint32_t end_item,uint32_t attr_bitmap)5872f511ffbSMilanka Ringwald uint8_t avrcp_browsing_controller_browse_file_system(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){
5882f511ffbSMilanka Ringwald     // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, 0, 0xFFFFFFFF, attr_bitmap);
589be65baf4SMilanka Ringwald     return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_VIRTUAL_FILESYSTEM, start_item, end_item, attr_bitmap);
590db79553aSMilanka Ringwald }
591db79553aSMilanka Ringwald 
avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid,uint32_t start_item,uint32_t end_item,uint32_t attr_bitmap)5922f511ffbSMilanka Ringwald uint8_t avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){
593db79553aSMilanka Ringwald     // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, 0, 0xFFFFFFFF, 0, NULL);
594be65baf4SMilanka Ringwald     return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_SEARCH, start_item, end_item, attr_bitmap);
595db79553aSMilanka Ringwald }
596db79553aSMilanka Ringwald 
avrcp_browsing_controller_browse_now_playing_list(uint16_t avrcp_browsing_cid,uint32_t start_item,uint32_t end_item,uint32_t attr_bitmap)5972f511ffbSMilanka Ringwald uint8_t avrcp_browsing_controller_browse_now_playing_list(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){
598be65baf4SMilanka Ringwald     return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_NOW_PLAYING, start_item, end_item, attr_bitmap);
599db79553aSMilanka Ringwald }
600db79553aSMilanka Ringwald 
601db79553aSMilanka Ringwald 
avrcp_browsing_controller_set_browsed_player(uint16_t avrcp_browsing_cid,uint16_t browsed_player_id)602db79553aSMilanka Ringwald uint8_t avrcp_browsing_controller_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t browsed_player_id){
6031945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
604db79553aSMilanka Ringwald     if (!avrcp_connection){
605db79553aSMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: could not find a connection.");
606db79553aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
607db79553aSMilanka Ringwald     }
608db79553aSMilanka Ringwald 
609db79553aSMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
610db79553aSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED){
611db79553aSMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
612db79553aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
613db79553aSMilanka Ringwald     }
614db79553aSMilanka Ringwald 
615db79553aSMilanka Ringwald     connection->set_browsed_player_id = 1;
616db79553aSMilanka Ringwald     connection->browsed_player_id = browsed_player_id;
6170eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
618db79553aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
619db79553aSMilanka Ringwald }
620db79553aSMilanka Ringwald 
621db79553aSMilanka Ringwald /**
622db79553aSMilanka Ringwald  * @brief Retrieve a listing of the contents of a folder.
623db79553aSMilanka Ringwald  * @param direction     0-folder up, 1-folder down
624db79553aSMilanka Ringwald  * @param folder_uid    8 bytes long
625db79553aSMilanka Ringwald  **/
avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid,uint8_t direction,uint8_t * folder_uid)626db79553aSMilanka Ringwald uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8_t direction, uint8_t * folder_uid){
6271945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
628db79553aSMilanka Ringwald     if (!avrcp_connection){
629db79553aSMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: could not find a connection.");
630db79553aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
631db79553aSMilanka Ringwald     }
632db79553aSMilanka Ringwald 
633db79553aSMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
634ad75da14SMilanka Ringwald 
6350e588213SMatthias Ringwald     if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){
636db79553aSMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
637db79553aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
638db79553aSMilanka Ringwald     }
639db79553aSMilanka Ringwald 
640db79553aSMilanka Ringwald     if (!connection->browsed_player_id){
641db79553aSMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: no browsed player set.");
642db79553aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
643db79553aSMilanka Ringwald     }
644db79553aSMilanka Ringwald     connection->change_path = 1;
645db79553aSMilanka Ringwald     connection->direction = direction;
646*d2fe8c23SMilanka Ringwald     memset(connection->item_uid, 0, 8);
647be65baf4SMilanka Ringwald     if (folder_uid){
648*d2fe8c23SMilanka Ringwald         (void)memcpy(connection->item_uid, folder_uid, 8);
649be65baf4SMilanka Ringwald     }
650be65baf4SMilanka Ringwald 
6510eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
652db79553aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
653db79553aSMilanka Ringwald }
654db79553aSMilanka Ringwald 
avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid)655e5b24613SMilanka Ringwald uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid){
656be65baf4SMilanka Ringwald     return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 0, NULL);
657db79553aSMilanka Ringwald }
658db79553aSMilanka Ringwald 
avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid,uint8_t * folder_uid)659db79553aSMilanka Ringwald uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid){
660db79553aSMilanka Ringwald     return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 1, folder_uid);
661ed0df7b2SMilanka Ringwald }
66273fba538SMilanka Ringwald 
avrcp_browsing_controller_search(uint16_t avrcp_browsing_cid,uint16_t search_str_len,char * search_str)66373fba538SMilanka Ringwald uint8_t avrcp_browsing_controller_search(uint16_t avrcp_browsing_cid, uint16_t search_str_len, char * search_str){
6641945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
66573fba538SMilanka Ringwald     if (!avrcp_connection){
66673fba538SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: could not find a connection.");
66773fba538SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
66873fba538SMilanka Ringwald     }
66973fba538SMilanka Ringwald 
67073fba538SMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
67173fba538SMilanka Ringwald 
6720e588213SMatthias Ringwald     if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){
67373fba538SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
67473fba538SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
67573fba538SMilanka Ringwald     }
67673fba538SMilanka Ringwald 
67773fba538SMilanka Ringwald     if (!connection->browsed_player_id){
67873fba538SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: no browsed player set.");
67973fba538SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
68073fba538SMilanka Ringwald     }
681c1ab6cc1SMatthias Ringwald     if (!search_str || (search_str_len == 0)){
68273fba538SMilanka Ringwald         return AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND;
68373fba538SMilanka Ringwald     }
68473fba538SMilanka Ringwald 
68573fba538SMilanka Ringwald     connection->search = 1;
68673fba538SMilanka Ringwald 
68773fba538SMilanka Ringwald     connection->search_str_len = btstack_min(search_str_len, sizeof(connection->search_str)-1);
68873fba538SMilanka Ringwald     memset(connection->search_str, 0, sizeof(connection->search_str));
6896535961aSMatthias Ringwald     (void)memcpy(connection->search_str, search_str,
6906535961aSMatthias Ringwald                  connection->search_str_len);
6910eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
69273fba538SMilanka Ringwald     return ERROR_CODE_SUCCESS;
69373fba538SMilanka Ringwald }
694864d08b0SMilanka Ringwald 
avrcp_browsing_controller_get_total_nr_items_for_scope(uint16_t avrcp_browsing_cid,avrcp_browsing_scope_t scope)6954614c049SMilanka Ringwald uint8_t avrcp_browsing_controller_get_total_nr_items_for_scope(uint16_t avrcp_browsing_cid, avrcp_browsing_scope_t scope){
6961945fe3eSMilanka Ringwald     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid);
697864d08b0SMilanka Ringwald     if (!avrcp_connection){
698864d08b0SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: could not find a connection.");
699864d08b0SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
700864d08b0SMilanka Ringwald     }
701864d08b0SMilanka Ringwald 
702864d08b0SMilanka Ringwald     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
703864d08b0SMilanka Ringwald 
7040e588213SMatthias Ringwald     if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){
705864d08b0SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
706864d08b0SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
707864d08b0SMilanka Ringwald     }
708864d08b0SMilanka Ringwald 
709864d08b0SMilanka Ringwald     if (!connection->browsed_player_id){
710864d08b0SMilanka Ringwald         log_error("avrcp_browsing_controller_change_path: no browsed player set.");
711864d08b0SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
712864d08b0SMilanka Ringwald     }
713864d08b0SMilanka Ringwald     connection->get_total_nr_items = 1;
714864d08b0SMilanka Ringwald     connection->get_total_nr_items_scope = scope;
7150eebc132SMilanka Ringwald     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
716864d08b0SMilanka Ringwald     return ERROR_CODE_SUCCESS;
717864d08b0SMilanka Ringwald }
718