diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 73f827b..8f7f187 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
uint16_t params_len, uint8_t *params, void *user_data);
};
+struct avrcp_browsing_handler {
+ uint8_t id;
+ ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+ uint16_t params_len, uint8_t *params, void *user_data);
+};
+
struct avrcp {
struct avctp *conn;
struct avrcp_player *player;
void *passthrough_data;
unsigned int passthrough_id;
+ const struct avrcp_browsing_handler *browsing_handlers;
+ void *browsing_data;
+ unsigned int browsing_id;
+
avrcp_destroy_cb_t destroy;
void *destroy_data;
};
return pdu;
}
+static uint8_t errno2status(int err)
+{
+ switch (err) {
+ case -ENOSYS:
+ return AVRCP_STATUS_INVALID_COMMAND;
+ case -EINVAL:
+ return AVRCP_STATUS_INVALID_PARAM;
+ case 0:
+ return AVRCP_STATUS_SUCCESS;
+ case -ENOTDIR:
+ return AVRCP_STATUS_NOT_DIRECTORY;
+ case -EBADRQC:
+ return AVRCP_STATUS_INVALID_SCOPE;
+ case -ERANGE:
+ return AVRCP_STATUS_OUT_OF_BOUNDS;
+ case -ENOENT:
+ return AVRCP_STATUS_DOES_NOT_EXIST;
+ default:
+ return AVRCP_STATUS_INTERNAL_ERROR;
+ }
+}
+
static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
uint8_t *code, uint8_t *subunit,
uint8_t *operands, size_t operand_count,
ret = handler->func(session, transaction, pdu->params_len, pdu->params,
session->control_data);
if (ret < 0) {
- switch (ret) {
- case -EAGAIN:
+ if (ret == -EAGAIN)
return ret;
- case -ENOSYS:
- pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
- goto reject;
- case -EINVAL:
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- goto reject;
- default:
- pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
- goto reject;
- }
+ pdu->params[0] = errno2status(ret);
+ goto reject;
}
*code = handler->rsp;
return session;
}
+static ssize_t handle_browsing_pdu(struct avctp *conn,
+ uint8_t transaction, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ struct avrcp *session = user_data;
+ const struct avrcp_browsing_handler *handler;
+ struct avrcp_browsing_header *pdu;
+ int ret;
+
+ pdu = parse_browsing_pdu(operands, operand_count);
+ if (!pdu) {
+ pdu = (void *) operands;
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+ pdu->params_len);
+
+ if (!session->browsing_handlers) {
+ pdu->pdu_id = AVRCP_GENERAL_REJECT;
+ pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+ goto reject;
+ }
+
+ for (handler = session->browsing_handlers; handler->id; handler++) {
+ if (handler->id == pdu->pdu_id)
+ break;
+ }
+
+ if (handler->id != pdu->pdu_id) {
+ pdu->pdu_id = AVRCP_GENERAL_REJECT;
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ goto reject;
+ }
+
+ if (!handler->func) {
+ pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+ goto reject;
+ }
+
+ ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+ session->control_data);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return ret;
+ pdu->params[0] = errno2status(ret);
+ goto reject;
+ }
+
+ pdu->params_len = htons(ret);
+
+ return AVRCP_BROWSING_HEADER_LENGTH + ret;
+
+reject:
+ pdu->params_len = htons(1);
+
+ return AVRCP_BROWSING_HEADER_LENGTH + 1;
+}
+
+static void browsing_disconnect_cb(void *data)
+{
+ struct avrcp *session = data;
+
+ session->browsing_id = 0;
+}
+
int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
size_t omtu)
{
- return avctp_connect_browsing(session->conn, fd, imtu, omtu);
+ int err;
+
+ err = avctp_connect_browsing(session->conn, fd, imtu, omtu);
+ if (err < 0)
+ return err;
+
+ session->browsing_id = avctp_register_browsing_pdu_handler(
+ session->conn,
+ handle_browsing_pdu,
+ session,
+ browsing_disconnect_cb);
+
+ return 0;
}
void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
session->control_data = user_data;
}
+static const struct avrcp_browsing_handler browsing_handlers[] = {
+ { },
+};
+
+static void avrcp_set_browsing_handlers(struct avrcp *session,
+ const struct avrcp_browsing_handler *handlers,
+ void *user_data)
+{
+ session->browsing_handlers = handlers;
+ session->browsing_data = user_data;
+}
+
void avrcp_register_player(struct avrcp *session,
const struct avrcp_control_ind *ind,
const struct avrcp_control_cfm *cfm,
player->user_data = user_data;
avrcp_set_control_handlers(session, player_handlers, player);
+ avrcp_set_browsing_handlers(session, browsing_handlers, player);
session->player = player;
}
return -EINVAL;
case AVRCP_STATUS_SUCCESS:
return 0;
+ case AVRCP_STATUS_NOT_DIRECTORY:
+ return -ENOTDIR;
+ case AVRCP_STATUS_INVALID_SCOPE:
+ return -EBADRQC;
case AVRCP_STATUS_OUT_OF_BOUNDS:
- return -EOVERFLOW;
+ return -ERANGE;
case AVRCP_STATUS_INTERNAL_ERROR:
case AVRCP_STATUS_INVALID_PLAYER_ID:
case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 4d1bbcc..a2cff01 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
#define AVRCP_STATUS_PARAM_NOT_FOUND 0x02
#define AVRCP_STATUS_INTERNAL_ERROR 0x03
#define AVRCP_STATUS_SUCCESS 0x04
+#define AVRCP_STATUS_NOT_DIRECTORY 0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST 0x09
+#define AVRCP_STATUS_INVALID_SCOPE 0x0a
#define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b
#define AVRCP_STATUS_INVALID_PLAYER_ID 0x11
#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12