<commit_before>/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2006-2010  Nokia Corporation
 *  Copyright (C) 2004-2010  <NAME> <<EMAIL>>
 *  Copyright (C) 2011  Texas Instruments, Inc.
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <gdbus.h>

#include "log.h"
#include "error.h"
#include "uinput.h"
#include "adapter.h"
#include "../src/device.h"
#include "device.h"
#include "manager.h"
#include "avdtp.h"
#include "control.h"
#include "sdpd.h"
#include "glib-helper.h"
#include "btio.h"
#include "dbus-common.h"

#define AVCTP_PSM 23

/* Message types */
#define AVCTP_COMMAND		0
#define AVCTP_RESPONSE		1

/* Packet types */
#define AVCTP_PACKET_SINGLE	0
#define AVCTP_PACKET_START	1
#define AVCTP_PACKET_CONTINUE	2
#define AVCTP_PACKET_END	3

/* ctype entries */
#define CTYPE_CONTROL		0x0
#define CTYPE_STATUS		0x1
#define CTYPE_NOTIFY		0x3
#define CTYPE_NOT_IMPLEMENTED	0x8
#define CTYPE_ACCEPTED		0x9
#define CTYPE_REJECTED		0xA
#define CTYPE_STABLE		0xC
#define CTYPE_CHANGED		0xD
#define CTYPE_INTERIM		0xF

/* opcodes */
#define OP_VENDORDEP		0x00
#define OP_UNITINFO		0x30
#define OP_SUBUNITINFO		0x31
#define OP_PASSTHROUGH		0x7c

/* subunits of interest */
#define SUBUNIT_PANEL		0x09

/* operands in passthrough commands */
#define VOL_UP_OP		0x41
#define VOL_DOWN_OP		0x42
#define MUTE_OP			0x43
#define PLAY_OP			0x44
#define STOP_OP			0x45
#define PAUSE_OP		0x46
#define RECORD_OP		0x47
#define REWIND_OP		0x48
#define FAST_FORWARD_OP		0x49
#define EJECT_OP		0x4a
#define FORWARD_OP		0x4b
#define BACKWARD_OP		0x4c

/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG		0x001958

/* Error codes for metadata transfer */
#define E_INVALID_COMMAND	0x00
#define E_INVALID_PARAM		0x01
#define E_PARAM_NOT_FOUND	0x02
#define E_INTERNAL		0x03

/* PDU types for metadata transfer */
#define AVRCP_GET_CAPABILITIES		0x10
#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
#define AVRCP_LIST_PLAYER_VALUES	0x12
#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
#define AVRCP_SET_PLAYER_VALUE		0x14
#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
#define AVRCP_DISPLAYABLE_CHARSET	0x17
#define AVRCP_CT_BATTERY_STATUS		0x18
#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
#define AVRCP_GET_PLAY_STATUS		0x30
#define AVRCP_REGISTER_NOTIFICATION	0x31

/* Notification events */
#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
#define AVRCP_EVENT_TRACK_CHANGED			0x02

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID		0x02
#define CAP_EVENTS_SUPPORTED	0x03

#define QUIRK_NO_RELEASE	1 << 0

enum player_setting {
	PLAYER_SETTING_EQUALIZER =	1,
	PLAYER_SETTING_REPEAT =		2,
	PLAYER_SETTING_SHUFFLE =	3,
	PLAYER_SETTING_SCAN =		4,
};

enum equalizer_mode {
	EQUALIZER_MODE_OFF =	1,
	EQUALIZER_MODE_ON =	2,
};

enum repeat_mode {
	REPEAT_MODE_OFF =	1,
	REPEAT_MODE_SINGLE =	2,
	REPEAT_MODE_ALL =	3,
	REPEAT_MODE_GROUP =	4,
};

enum shuffle_mode {
	SHUFFLE_MODE_OFF =	1,
	SHUFFLE_MODE_ALL =	2,
	SHUFFLE_MODE_GROUP =	3,
};

enum scan_mode {
	SCAN_MODE_OFF =		1,
	SCAN_MODE_ALL =		2,
	SCAN_MODE_GROUP =	3,
};

enum play_status {
	PLAY_STATUS_STOPPED =		0x00,
	PLAY_STATUS_PLAYING =		0x01,
	PLAY_STATUS_PAUSED =		0x02,
	PLAY_STATUS_FWD_SEEK =		0x03,
	PLAY_STATUS_REV_SEEK =		0x04,
	PLAY_STATUS_ERROR =		0xFF
};

enum battery_status {
	BATTERY_STATUS_NORMAL =		0,
	BATTERY_STATUS_WARNING =	1,
	BATTERY_STATUS_CRITICAL =	2,
	BATTERY_STATUS_EXTERNAL =	3,
	BATTERY_STATUS_FULL_CHARGE =	4,
};

enum media_info_id {
	MEDIA_INFO_TITLE =		1,
	MEDIA_INFO_ARTIST =		2,
	MEDIA_INFO_ALBUM =		3,
	MEDIA_INFO_TRACK =		4,
	MEDIA_INFO_N_TRACKS =		5,
	MEDIA_INFO_GENRE =		6,
	MEDIA_INFO_CURRENT_POSITION =	7,
};

static DBusConnection *connection = NULL;

static GSList *servers = NULL;

#if __BYTE_ORDER == __LITTLE_ENDIAN

struct avctp_header {
	uint8_t ipid:1;
	uint8_t cr:1;
	uint8_t packet_type:2;
	uint8_t transaction:4;
	uint16_t pid;
} __attribute__ ((packed));
#define AVCTP_HEADER_LENGTH 3

struct avrcp_header {
	uint8_t code:4;
	uint8_t _hdr0:4;
	uint8_t subunit_id:3;
	uint8_t subunit_type:5;
	uint8_t opcode;
} __attribute__ ((packed));
#define AVRCP_HEADER_LENGTH 3

struct avrcp_spec_avc_pdu {
	uint8_t company_id[3];
	uint8_t pdu_id;
	uint8_t packet_type:2;
	uint8_t rsvd:6;
	uint16_t params_len;
	uint8_t params[0];
} __attribute__ ((packed));
#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7

#elif __BYTE_ORDER == __BIG_ENDIAN

struct avctp_header {
	uint8_t transaction:4;
	uint8_t packet_type:2;
	uint8_t cr:1;
	uint8_t ipid:1;
	uint16_t pid;
} __attribute__ ((packed));
#define AVCTP_HEADER_LENGTH 3

struct avrcp_header {
	uint8_t _hdr0:4;
	uint8_t code:4;
	uint8_t subunit_type:5;
	uint8_t subunit_id:3;
	uint8_t opcode;
} __attribute__ ((packed));
#define AVRCP_HEADER_LENGTH 3

struct avrcp_spec_avc_pdu {
	uint8_t company_id[3];
	uint8_t pdu_id;
	uint8_t rsvd:6;
	uint8_t packet_type:2;
	uint16_t params_len;
	uint8_t params[0];
} __attribute__ ((packed));
#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7

#else
#error "Unknown byte order"
#endif

struct avctp_state_callback {
	avctp_state_cb cb;
	void *user_data;
	unsigned int id;
};

struct avctp_server {
	bdaddr_t src;
	GIOChannel *io;
	uint32_t tg_record_id;
	uint32_t ct_record_id;
};

struct media_info {
	char *title;
	char *artist;
	char *album;
	char *genre;
	uint32_t ntracks;
	uint32_t track;
	uint32_t track_len;
	uint32_t elapsed;
};

struct media_player {
	uint8_t settings[PLAYER_SETTING_SCAN + 1];
	enum play_status status;

	struct media_info mi;
	GTimer *timer;
};

struct control {
	struct audio_device *dev;
	struct media_player *mp;

	avctp_state_t state;

	int uinput;

	GIOChannel *io;
	guint io_id;

	uint16_t mtu;

	gboolean target;

	uint8_t key_quirks[256];

	uint16_t registered_events;
	uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
};

static struct {
	const char *name;
	uint8_t avrcp;
	uint16_t uinput;
} key_map[] = {
	{ "PLAY",		PLAY_OP,		KEY_PLAYCD },
	{ "STOP",		STOP_OP,		KEY_STOPCD },
	{ "PAUSE",		PAUSE_OP,		KEY_PAUSECD },
	{ "FORWARD",		FORWARD_OP,		KEY_NEXTSONG },
	{ "BACKWARD",		BACKWARD_OP,		KEY_PREVIOUSSONG },
	{ "REWIND",		REWIND_OP,		KEY_REWIND },
	{ "FAST FORWARD",	FAST_FORWARD_OP,	KEY_FASTFORWARD },
	{ NULL }
};

/* Company IDs supported by this device */
static uint32_t company_ids[] = {
	IEEEID_BTSIG,
};

static GSList *avctp_callbacks = NULL;

static void auth_cb(DBusError *derr, void *user_data);

static sdp_record_t *avrcp_ct_record(void)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, l2cap, avctp, avrct;
	sdp_profile_desc_t profile[1];
	sdp_list_t *aproto, *proto[2];
	sdp_record_t *record;
	sdp_data_t *psm, *version, *features;
	uint16_t lp = AVCTP_PSM;
	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrct);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	sdp_uuid16_create(&l2cap, L2CAP_UUID);
	proto[0] = sdp_list_append(0, &l2cap);
	psm = sdp_data_alloc(SDP_UINT16, &lp);
	proto[0] = sdp_list_append(proto[0], psm);
	apseq = sdp_list_append(0, proto[0]);

	sdp_uuid16_create(&avctp, AVCTP_UUID);
	proto[1] = sdp_list_append(0, &avctp);
	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
	proto[1] = sdp_list_append(proto[1], version);
	apseq = sdp_list_append(apseq, proto[1]);

	aproto = sdp_list_append(0, apseq);
	sdp_set_access_protos(record, aproto);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

	features = sdp_data_alloc(SDP_UINT16, &feat);
	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);

	sdp_set_info_attr(record, "AVRCP CT", 0, 0);

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}

static sdp_record_t *avrcp_tg_record(void)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, l2cap, avctp, avrtg;
	sdp_profile_desc_t profile[1];
	sdp_list_t *aproto, *proto[2];
	sdp_record_t *record;
	sdp_data_t *psm, *version, *features;
	uint16_t lp = AVCTP_PSM;
	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrtg);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	sdp_uuid16_create(&l2cap, L2CAP_UUID);
	proto[0] = sdp_list_append(0, &l2cap);
	psm = sdp_data_alloc(SDP_UINT16, &lp);
	proto[0] = sdp_list_append(proto[0], psm);
	apseq = sdp_list_append(0, proto[0]);

	sdp_uuid16_create(&avctp, AVCTP_UUID);
	proto[1] = sdp_list_append(0, &avctp);
	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
	proto[1] = sdp_list_append(proto[1], version);
	apseq = sdp_list_append(apseq, proto[1]);

	aproto = sdp_list_append(0, apseq);
	sdp_set_access_protos(record, aproto);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

	features = sdp_data_alloc(SDP_UINT16, &feat);
	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);

	sdp_set_info_attr(record, "AVRCP TG", 0, 0);

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}

static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
	struct uinput_event event;

	memset(&event, 0, sizeof(event));
	event.type	= type;
	event.code	= code;
	event.value	= value;

	return write(fd, &event, sizeof(event));
}

static void send_key(int fd, uint16_t key, int pressed)
{
	if (fd < 0)
		return;

	send_event(fd, EV_KEY, key, pressed);
	send_event(fd, EV_SYN, SYN_REPORT, 0);
}

static void handle_panel_passthrough(struct control *control,
					const unsigned char *operands,
					int operand_count)
{
	const char *status;
	int pressed, i;

	if (operand_count == 0)
		return;

	if (operands[0] & 0x80) {
		status = "released";
		pressed = 0;
	} else {
		status = "pressed";
		pressed = 1;
	}

	for (i = 0; key_map[i].name != NULL; i++) {
		uint8_t key_quirks;

		if ((operands[0] & 0x7F) != key_map[i].avrcp)
			continue;

		DBG("AVRCP: %s %s", key_map[i].name, status);

		key_quirks = control->key_quirks[key_map[i].avrcp];

		if (key_quirks & QUIRK_NO_RELEASE) {
			if (!pressed) {
				DBG("AVRCP: Ignoring release");
				break;
			}

			DBG("AVRCP: treating key press as press + release");
			send_key(control->uinput, key_map[i].uinput, 1);
			send_key(control->uinput, key_map[i].uinput, 0);
			break;
		}

		send_key(control->uinput, key_map[i].uinput, pressed);
		break;
	}

	if (key_map[i].name == NULL)
		DBG("AVRCP: unknown button 0x%02X %s",
						operands[0] & 0x7F, status);
}

static unsigned int attr_get_max_val(uint8_t attr)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		return EQUALIZER_MODE_ON;
	case PLAYER_SETTING_REPEAT:
		return REPEAT_MODE_GROUP;
	case PLAYER_SETTING_SHUFFLE:
		return SHUFFLE_MODE_GROUP;
	case PLAYER_SETTING_SCAN:
		return SCAN_MODE_GROUP;
	}

	return 0;
}

static const char *attrval_to_str(uint8_t attr, uint8_t value)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		switch (value) {
		case EQUALIZER_MODE_ON:
			return "on";
		case EQUALIZER_MODE_OFF:
			return "off";
		}

		break;
	case PLAYER_SETTING_REPEAT:
		switch (value) {
		case REPEAT_MODE_OFF:
			return "off";
		case REPEAT_MODE_SINGLE:
			return "singletrack";
		case REPEAT_MODE_ALL:
			return "alltracks";
		case REPEAT_MODE_GROUP:
			return "group";
		}

		break;
	/* Shuffle and scan have the same values */
	case PLAYER_SETTING_SHUFFLE:
	case PLAYER_SETTING_SCAN:
		switch (value) {
		case SCAN_MODE_OFF:
			return "off";
		case SCAN_MODE_ALL:
			return "alltracks";
		case SCAN_MODE_GROUP:
			return "group";
		}

		break;
	}

	return NULL;
}

static int attrval_to_val(uint8_t attr, const char *value)
{
	int ret;

	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		if (!strcmp(value, "off"))
			ret = EQUALIZER_MODE_OFF;
		else if (!strcmp(value, "on"))
			ret = EQUALIZER_MODE_ON;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_REPEAT:
		if (!strcmp(value, "off"))
			ret = REPEAT_MODE_OFF;
		else if (!strcmp(value, "singletrack"))
			ret = REPEAT_MODE_SINGLE;
		else if (!strcmp(value, "alltracks"))
			ret = REPEAT_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = REPEAT_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_SHUFFLE:
		if (!strcmp(value, "off"))
			ret = SHUFFLE_MODE_OFF;
		else if (!strcmp(value, "alltracks"))
			ret = SHUFFLE_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = SHUFFLE_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_SCAN:
		if (!strcmp(value, "off"))
			ret = SCAN_MODE_OFF;
		else if (!strcmp(value, "alltracks"))
			ret = SCAN_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = SCAN_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	}

	return -EINVAL;
}

static const char *attr_to_str(uint8_t attr)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		return "Equalizer";
	case PLAYER_SETTING_REPEAT:
		return "Repeat";
	case PLAYER_SETTING_SHUFFLE:
		return "Shuffle";
	case PLAYER_SETTING_SCAN:
		return "Scan";
	}

	return NULL;
}

static int attr_to_val(const char *str)
{
	if (!strcmp(str, "Equalizer"))
		return PLAYER_SETTING_EQUALIZER;
	else if (!strcmp(str, "Repeat"))
		return PLAYER_SETTING_REPEAT;
	else if (!strcmp(str, "Shuffle"))
		return PLAYER_SETTING_SHUFFLE;
	else if (!strcmp(str, "Scan"))
		return PLAYER_SETTING_SCAN;

	return -EINVAL;
}

static int play_status_to_val(const char *status)
{
	if (!strcmp(status, "stopped"))
		return PLAY_STATUS_STOPPED;
	else if (!strcmp(status, "playing"))
		return PLAY_STATUS_PLAYING;
	else if (!strcmp(status, "paused"))
		return PLAY_STATUS_PAUSED;
	else if (!strcmp(status, "forward-seek"))
		return PLAY_STATUS_FWD_SEEK;
	else if (!strcmp(status, "reverse-seek"))
		return PLAY_STATUS_REV_SEEK;
	else if (!strcmp(status, "error"))
		return PLAY_STATUS_ERROR;

	return -EINVAL;
}

static const char *battery_status_to_str(enum battery_status status)
{
	switch (status) {
	case BATTERY_STATUS_NORMAL:
		return "normal";
	case BATTERY_STATUS_WARNING:
		return "warning";
	case BATTERY_STATUS_CRITICAL:
		return "critical";
	case BATTERY_STATUS_EXTERNAL:
		return "external";
	case BATTERY_STATUS_FULL_CHARGE:
		return "fullcharge";
	}

	return NULL;
}

static int avctp_send_event(struct control *control, uint8_t id, void *data)
{
	uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
					AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
	struct avctp_header *avctp = (void *) buf;
	struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
	struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
							AVRCP_HEADER_LENGTH];
	int sk = g_io_channel_unix_get_fd(control->io);
	uint16_t size;

	memset(buf, 0, sizeof(buf));

	avctp->transaction = control->transaction_events[id];
	avctp->packet_type = AVCTP_PACKET_SINGLE;
	avctp->cr = AVCTP_RESPONSE;
	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);

	avrcp->code = CTYPE_CHANGED;
	avrcp->subunit_type = SUBUNIT_PANEL;
	avrcp->opcode = OP_VENDORDEP;

	pdu->company_id[0] = IEEEID_BTSIG >> 16;
	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
	pdu->company_id[2] = IEEEID_BTSIG & 0xFF;

	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
	pdu->params[0] = id;

	DBG("id=%u", id);

	switch (id) {
	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
		size = 2;
		pdu->params[1] = *((uint8_t *)data);

		break;
	case AVRCP_EVENT_TRACK_CHANGED: {
		size = 9;

		/*
		 * AVRCP 1.3 supports only one track identifier: PLAYING
		 * (0x0). When 1.4 version is added, this shall be changed to
		 * contain the identifier of the track.
		 */
		memset(&pdu->params[1], 0, 8);

		break;
	}
	default:
		error("Unknown event %u", id);
		return -EINVAL;
	}

	pdu->params_len = htons(size);
	size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
					AVRCP_SPECAVCPDU_HEADER_LENGTH;

	if (write(sk, buf, size) < 0)
		return -errno;

	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
	control->registered_events ^= 1 << id;

	return 0;
}

static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
					uint32_t *elapsed, uint32_t *track_len)
{
	if (status)
		*status = mp->status;
	if (track_len)
		*track_len = mp->mi.track_len;

	if (!elapsed)
		return;

	*elapsed = mp->mi.elapsed;

	if (mp->status == PLAY_STATUS_PLAYING) {
		double timedelta = g_timer_elapsed(mp->timer, NULL);
		uint32_t sec, msec;

		sec = (uint32_t) timedelta;
		msec = (uint32_t)((timedelta - sec) * 1000);

		*elapsed += sec * 1000 + msec;
	}
}

static void mp_set_playback_status(struct control *control, uint8_t status,
							uint32_t elapsed)
{
	struct media_player *mp = control->mp;

	DBG("Change playback: %u %u", status, elapsed);

	mp->mi.elapsed = elapsed;
	g_timer_start(mp->timer);

	if (status == mp->status)
		return;

	mp->status = status;

	if (control->state == AVCTP_STATE_CONNECTED &&
				(control->registered_events &
				(1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED))) {
		avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
								&status);
	}
}

/*
 * Copy media_info field to a buffer, intended to be used in a response to
 * GetElementAttributes message.
 *
 * It assumes there's enough space in the buffer and on success it returns the
 * size written.
 *
 * If @param id is not valid, -EINVAL is returned. If there's no such media
 * attribute, -ENOENT is returned.
 */
static int mp_get_media_attribute(struct media_player *mp,
						uint32_t id, uint8_t *buf)
{
	struct media_info_elem {
		uint32_t id;
		uint16_t charset;
		uint16_t len;
		uint8_t val[];
	};
	const struct media_info *mi = &mp->mi;
	struct media_info_elem *elem = (void *)buf;
	uint16_t len;
	char valstr[20];

	switch (id) {
	case MEDIA_INFO_TITLE:
		if (mi->title) {
			len = strlen(mi->title);
			memcpy(elem->val, mi->title, len);
		} else {
			len = 0;
		}

		break;
	case MEDIA_INFO_ARTIST:
		if (mi->artist == NULL)
			return -ENOENT;

		len = strlen(mi->artist);
		memcpy(elem->val, mi->artist, len);
		break;
	case MEDIA_INFO_ALBUM:
		if (mi->album == NULL)
			return -ENOENT;

		len = strlen(mi->album);
		memcpy(elem->val, mi->album, len);
		break;
	case MEDIA_INFO_GENRE:
		if (mi->genre == NULL)
			return -ENOENT;

		len = strlen(mi->genre);
		memcpy(elem->val, mi->genre, len);
		break;

	case MEDIA_INFO_TRACK:
		if (!mi->track)
			return -ENOENT;

		snprintf(valstr, 20, "%u", mi->track);
		len = strlen(valstr);
		memcpy(elem->val, valstr, len);
		break;
	case MEDIA_INFO_N_TRACKS:
		if (!mi->ntracks)
			return -ENOENT;

		snprintf(valstr, 20, "%u", mi->ntracks);
		len = strlen(valstr);
		memcpy(elem->val, valstr, len);
		break;
	case MEDIA_INFO_CURRENT_POSITION:
		if (mi->elapsed != 0xFFFFFFFF) {
			uint32_t elapsed;

			mp_get_playback_status(mp, NULL, &elapsed, NULL);

			snprintf(valstr, 20, "%u", elapsed);
			len = strlen(valstr);
			memcpy(elem->val, valstr, len);
		} else {
			return -ENOENT;
		}

		break;
	default:
		return -EINVAL;
	}

	elem->id = htonl(id);
	elem->charset = htons(0x6A); /* Always use UTF-8 */
	elem->len = htons(len);

	return sizeof(struct media_info_elem) + len;
}

static void mp_set_attribute(struct media_player *mp,
						uint8_t attr, uint8_t val)
{
	DBG("Change attribute: %u %u", attr, val);

	mp->settings[attr] = val;
}

static int mp_get_attribute(struct media_player *mp, uint8_t attr)
{
	DBG("Get attribute: %u", attr);

	return mp->settings[attr];
}

static void mp_set_media_attributes(struct control *control,
							struct media_info *mi)
{
	struct media_player *mp = control->mp;

	g_free(mp->mi.title);
	mp->mi.title = g_strdup(mi->title);

	g_free(mp->mi.artist);
	mp->mi.artist = g_strdup(mi->artist);

	g_free(mp->mi.album);
	mp->mi.album = g_strdup(mi->album);

	g_free(mp->mi.genre);
	mp->mi.genre = g_strdup(mi->genre);

	mp->mi.ntracks = mi->ntracks;
	mp->mi.track = mi->track;
	mp->mi.track_len = mi->track_len;

	/*
	 * elapsed is special. Whenever the track changes, we reset it to 0,
	 * so client doesn't have to make another call to change_playback
	 */
	mp->mi.elapsed = 0;
	g_timer_start(mp->timer);

	DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
			   "\tgenre: %s\n\tNumber of tracks: %u\n"
			   "\tTrack number: %u\n\tTrack duration: %u",
			   mi->title, mi->artist, mi->album, mi->genre,
			   mi->ntracks, mi->track, mi->track_len);

	if (control->state == AVCTP_STATE_CONNECTED &&
					(control->registered_events &
					 (1 << AVRCP_EVENT_TRACK_CHANGED))) {
		avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
	}
}

static int avrcp_handle_get_capabilities(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	unsigned int i;

	if (len != 1)
		goto err;

	DBG("id=%u", pdu->params[0]);

	switch (pdu->params[0]) {
	case CAP_COMPANY_ID:
		for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
			pdu->params[2 + i * 3] = company_ids[i] >> 16;
			pdu->params[3 + i * 3] = (company_ids[i] >> 8) & 0xFF;
			pdu->params[4 + i * 3] = company_ids[i] & 0xFF;
		}

		pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
		pdu->params[1] = G_N_ELEMENTS(company_ids);

		return 2 + (3 * G_N_ELEMENTS(company_ids));
	case CAP_EVENTS_SUPPORTED:
		pdu->params_len = htons(4);
		pdu->params[1] = 2;
		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;

		return 4;
	}

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_list_player_attributes(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	unsigned int i;

	if (len != 0) {
		pdu->params[0] = E_INVALID_PARAM;
		return -EINVAL;
	}

	if (!mp)
		goto done;

	for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
		if (!mp_get_attribute(mp, i)) {
			DBG("Ignoring setting %u: not supported by player", i);
			continue;
		}

		len++;
		pdu->params[len] = i;
	}

done:
	pdu->params[0] = len;
	pdu->params_len = htons(len + 1);

	return len + 1;
}

static int avrcp_handle_list_player_values(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	unsigned int i;

	if (len != 1 || !mp)
		goto err;

	len = attr_get_max_val(pdu->params[0]);
	if (!len) {
		error("Attribute is invalid: %u", pdu->params[0]);
		goto err;
	}

	for (i = 1; i <= len; i++)
		pdu->params[i] = i;

	pdu->params[0] = len;
	pdu->params_len = htons(len + 1);

	return len + 1;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_element_attributes(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	uint64_t *identifier = (void *) &pdu->params[0];
	uint16_t pos;
	uint8_t nattr;
	int size;
	unsigned int i;

	if (len < 8 || *identifier != 0 || !control->mp)
		goto err;

	len = 0;
	pos = 1; /* Keep track of current position in reponse */
	nattr = pdu->params[8];

	if (!control->mp)
		goto done;

	if (!nattr) {
		/*
		 * Return all available information, at least
		 * title must be returned.
		 */
		for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
			size = mp_get_media_attribute(control->mp, i,
							&pdu->params[pos]);

			if (size > 0) {
				len++;
				pos += size;
			}
		}
	} else {
		uint32_t *attr_ids = g_malloc(sizeof(uint32_t) * nattr);

		/* save a copy of requested attributes */
		memcpy(&attr_ids[0], &pdu->params[9], nattr * 4);

		for (i = 0; i < nattr; i++) {
			uint32_t attr = ntohl(attr_ids[i]);

			size = mp_get_media_attribute(control->mp, attr,
							&pdu->params[pos]);

			if (size > 0) {
				len++;
				pos += size;
			}
		}

		g_free(attr_ids);

		if (!len)
			goto err;
	}

done:
	pdu->params[0] = len;
	pdu->params_len = htons(pos);

	return pos;
err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_current_player_value(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	uint8_t *settings;
	unsigned int i;

	if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
		goto err;

	/*
	 * Save a copy of requested settings because we can override them
	 * while responding
	 */
	settings = g_malloc(pdu->params[0]);
	memcpy(settings, &pdu->params[1], pdu->params[0]);
	len = 0;

	/*
	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
	 * and send a response with the existent ones. Only if all IDs are
	 * non-existent we should send an error.
	 */
	for (i = 0; i < pdu->params[0]; i++) {
		uint8_t val;

		if (settings[i] < PLAYER_SETTING_EQUALIZER ||
				settings[i] > PLAYER_SETTING_SCAN) {
			DBG("Ignoring %u", settings[i]);
			continue;
		}

		val = mp_get_attribute(mp, settings[i]);
		if (!val) {
			DBG("Ignoring %u: not supported by player",
								settings[i]);
			continue;
		}

		pdu->params[len] = settings[i];
		pdu->params[len + 1] = val;
		len += 2;
	}

	g_free(settings);

	if (len) {
		pdu->params[0] = len;
		pdu->params_len = htons(2 * len + 1);

		return 2 * len + 1;
	}

	error("No valid attributes in request");

err:
	pdu->params[0] = E_INVALID_PARAM;

	return -EINVAL;
}

static int avrcp_handle_set_player_value(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	unsigned int i;

	if (len < 3 || !control->mp)
		goto err;

	len = 0;

	/*
	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
	 * and set the existent ones. Sec. 5.2.4 is not clear however how to
	 * indicate that a certain ID was not accepted. If at least one
	 * attribute is valid, we respond with no parameters. Otherwise an
	 * E_INVALID_PARAM is sent.
	 */
	for (i = 1; i < pdu->params[0]; i += 2) {
		uint8_t attr = pdu->params[i];
		uint8_t val = pdu->params[i + 1];
		const char *attrstr;
		const char *valstr;

		attrstr = attr_to_str(attr);
		if (!attrstr)
			continue;

		valstr = attrval_to_str(attr, val);
		if (!valstr)
			continue;

		len++;

		mp_set_attribute(control->mp, attr, val);
		emit_property_changed(control->dev->conn, control->dev->path,
					MEDIA_PLAYER_INTERFACE, attrstr,
					DBUS_TYPE_STRING, &valstr);
	}

	if (len) {
		pdu->params_len = 0;

		return 0;
	}

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_ct_battery_status(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	const char *valstr;

	if (len != 1)
		goto err;

	valstr = battery_status_to_str(pdu->params[0]);
	if (valstr == NULL)
		goto err;

	emit_property_changed(control->dev->conn, control->dev->path,
					MEDIA_PLAYER_INTERFACE, "Battery",
					DBUS_TYPE_STRING, &valstr);
	pdu->params_len = 0;

	return 0;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_play_status(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	uint32_t elapsed;
	uint32_t track_len;
	uint8_t status;

	if (len != 0) {
		pdu->params[0] = E_INVALID_PARAM;
		return -EINVAL;
	}

	if (control->mp) {
		mp_get_playback_status(control->mp, &status,
							&elapsed, &track_len);
		track_len = htonl(track_len);
		elapsed = htonl(elapsed);
	} else {
		track_len = 0xFFFFFFFF;
		elapsed = 0xFFFFFFFF;
		status = PLAY_STATUS_ERROR;
	}

	memcpy(&pdu->params[0], &track_len, 4);
	memcpy(&pdu->params[4], &elapsed, 4);
	pdu->params[8] = status;

	pdu->params_len = htons(9);

	return 9;
}

static int avrcp_handle_register_notification(struct control *control,
						struct avrcp_spec_avc_pdu *pdu,
						uint8_t transaction)
{
	uint16_t len = ntohs(pdu->params_len);
	uint8_t status;

	/*
	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
	 * 1.3 spec, section 5.4.2.
	 */
	if (len != 5)
		goto err;

	switch (pdu->params[0]) {
	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
		len = 2;
		if (control->mp) {
			mp_get_playback_status(control->mp, &status,
								NULL, NULL);
			pdu->params[1] = status;
		} else {
			pdu->params[1] = PLAY_STATUS_ERROR;
		}

		break;
	case AVRCP_EVENT_TRACK_CHANGED:
		len = 9;

		if (!control->mp)
			memset(&pdu->params[1], 0xFF, 8);
		else
			memset(&pdu->params[1], 0, 8);

		break;
	default:
		/* All other events are not supported yet */
		goto err;
	}

	/* Register event and save the transaction used */
	control->registered_events |= (1 << pdu->params[0]);
	control->transaction_events[pdu->params[0]] = transaction;

	pdu->params_len = htons(len);

	return len;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
					struct avctp_header *avctp,
					struct avrcp_header *avrcp,
					int operand_count)
{
	struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
	uint32_t company_id = (pdu->company_id[0] << 16) |
				(pdu->company_id[1] << 8) |
				(pdu->company_id[2]);
	int len;

	if (company_id != IEEEID_BTSIG ||
				pdu->packet_type != AVCTP_PACKET_SINGLE) {
		avrcp->code = CTYPE_NOT_IMPLEMENTED;
		return AVRCP_HEADER_LENGTH;
	}

	pdu->packet_type = 0;
	pdu->rsvd = 0;

	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
		pdu->params[0] = E_INVALID_COMMAND;
		goto err_metadata;
	}

	switch (pdu->pdu_id) {
	case AVRCP_GET_CAPABILITIES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_capabilities(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_LIST_PLAYER_ATTRIBUTES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_list_player_attributes(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_LIST_PLAYER_VALUES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_list_player_values(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_ELEMENT_ATTRIBUTES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_element_attributes(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_CURRENT_PLAYER_VALUE:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_current_player_value(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_SET_PLAYER_VALUE:
		if (avrcp->code != CTYPE_CONTROL) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_set_player_value(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
	case AVRCP_GET_PLAYER_VALUE_TEXT:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		/*
		 * As per sec. 5.2.5 of AVRCP 1.3 spec, this command is
		 * expected to be used only for extended attributes, i.e.
		 * custom attributes defined by the application. As we
		 * currently don't have any such attribute, we respond with
		 * invalid param id.
		 */
		pdu->params[0] = E_INVALID_PARAM;
		goto err_metadata;
	case AVRCP_DISPLAYABLE_CHARSET:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		if (pdu->params[0] < 3) {
			pdu->params[0] = E_INVALID_PARAM;
			goto err_metadata;
		}

		/*
		 * We acknowledge the commands, but we always use UTF-8 for
		 * encoding since CT is obliged to support it.
		 */
		pdu->params_len = 0;
		avrcp->code = CTYPE_STABLE;
		len = 0;

		break;
	case AVRCP_CT_BATTERY_STATUS:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_ct_battery_status(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_PLAY_STATUS:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_play_status(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_REGISTER_NOTIFICATION:
		if (avrcp->code != CTYPE_NOTIFY) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_register_notification(control, pdu,
							avctp->transaction);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_INTERIM;

		break;
	default:
		/* Invalid pdu_id */
		pdu->params[0] = E_INVALID_COMMAND;
		goto err_metadata;
	}

	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;

err_metadata:
	avrcp->code = CTYPE_REJECTED;
	pdu->params_len = htons(1);

	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
}

static void avctp_disconnected(struct audio_device *dev)
{
	struct control *control = dev->control;

	if (!control)
		return;

	if (control->io) {
		g_io_channel_shutdown(control->io, TRUE, NULL);
		g_io_channel_unref(control->io);
		control->io = NULL;
	}

	if (control->io_id) {
		g_source_remove(control->io_id);
		control->io_id = 0;

		if (control->state == AVCTP_STATE_CONNECTING)
			audio_device_cancel_authorization(dev, auth_cb,
								control);
	}

	if (control->uinput >= 0) {
		char address[18];

		ba2str(&dev->dst, address);
		DBG("AVRCP: closing uinput for %s", address);

		ioctl(control->uinput, UI_DEV_DESTROY);
		close(control->uinput);
		control->uinput = -1;
	}
}

static void avctp_set_state(struct control *control, avctp_state_t new_state)
{
	GSList *l;
	struct audio_device *dev = control->dev;
	avctp_state_t old_state = control->state;
	gboolean value;

	switch (new_state) {
	case AVCTP_STATE_DISCONNECTED:
		DBG("AVCTP Disconnected");

		avctp_disconnected(control->dev);

		if (old_state != AVCTP_STATE_CONNECTED)
			break;

		value = FALSE;
		g_dbus_emit_signal(dev->conn, dev->path,
					AUDIO_CONTROL_INTERFACE,
					"Disconnected", DBUS_TYPE_INVALID);
		emit_property_changed(dev->conn, dev->path,
					AUDIO_CONTROL_INTERFACE, "Connected",
					DBUS_TYPE_BOOLEAN, &value);

		if (!audio_device_is_active(dev, NULL))
			audio_device_set_authorized(dev, FALSE);

		break;
	case AVCTP_STATE_CONNECTING:
		DBG("AVCTP Connecting");
		break;
	case AVCTP_STATE_CONNECTED:
		DBG("AVCTP Connected");
		value = TRUE;
		g_dbus_emit_signal(control->dev->conn, control->dev->path,
				AUDIO_CONTROL_INTERFACE, "Connected",
				DBUS_TYPE_INVALID);
		emit_property_changed(control->dev->conn, control->dev->path,
				AUDIO_CONTROL_INTERFACE, "Connected",
				DBUS_TYPE_BOOLEAN, &value);
		break;
	default:
		error("Invalid AVCTP state %d", new_state);
		return;
	}

	control->state = new_state;

	for (l = avctp_callbacks; l != NULL; l = l->next) {
		struct avctp_state_callback *cb = l->data;
		cb->cb(control->dev, old_state, new_state, cb->user_data);
	}
}

static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
				gpointer data)
{
	struct control *control = data;
	unsigned char buf[1024], *operands;
	struct avctp_header *avctp;
	struct avrcp_header *avrcp;
	int ret, packet_size, operand_count, sock;

	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
		goto failed;

	sock = g_io_channel_unix_get_fd(control->io);

	ret = read(sock, buf, sizeof(buf));
	if (ret <= 0)
		goto failed;

	DBG("Got %d bytes of data for AVCTP session %p", ret, control);

	if ((unsigned int) ret < sizeof(struct avctp_header)) {
		error("Too small AVCTP packet");
		goto failed;
	}

	packet_size = ret;

	avctp = (struct avctp_header *) buf;

	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
			"PID 0x%04X",
			avctp->transaction, avctp->packet_type,
			avctp->cr, avctp->ipid, ntohs(avctp->pid));

	ret -= sizeof(struct avctp_header);
	if ((unsigned int) ret < sizeof(struct avrcp_header)) {
		error("Too small AVRCP packet");
		goto failed;
	}

	avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header));

	ret -= sizeof(struct avrcp_header);

	operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header);
	operand_count = ret;

	DBG("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
			"opcode 0x%02X, %d operands",
			avctp->cr ? "response" : "command",
			avrcp->code, avrcp->subunit_type, avrcp->subunit_id,
			avrcp->opcode, operand_count);

	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_NOT_IMPLEMENTED;
	} else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
		avctp->ipid = 1;
		avctp->cr = AVCTP_RESPONSE;
		packet_size = sizeof(*avctp);
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->code == CTYPE_CONTROL &&
			avrcp->subunit_type == SUBUNIT_PANEL &&
			avrcp->opcode == OP_PASSTHROUGH) {
		handle_panel_passthrough(control, operands, operand_count);
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_ACCEPTED;
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->code == CTYPE_STATUS &&
			(avrcp->opcode == OP_UNITINFO
			|| avrcp->opcode == OP_SUBUNITINFO)) {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_STABLE;
		/* The first operand should be 0x07 for the UNITINFO response.
		 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
		 * Interface Command Set (section 9.2.1, page 45) specs
		 * explain this value but both use it */
		if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO)
			operands[0] = 0x07;
		if (operand_count >= 2)
			operands[1] = SUBUNIT_PANEL << 3;
		DBG("reply to %s", avrcp->opcode == OP_UNITINFO ?
				"OP_UNITINFO" : "OP_SUBUNITINFO");
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->opcode == OP_VENDORDEP) {
		int r_size;
		operand_count -= 3;
		avctp->cr = AVCTP_RESPONSE;
		r_size = handle_vendordep_pdu(control, avctp, avrcp,
								operand_count);
		packet_size = AVCTP_HEADER_LENGTH + r_size;
	} else {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_REJECTED;
	}
	ret = write(sock, buf, packet_size);
	if (ret != packet_size)
		goto failed;

	return TRUE;

failed:
	DBG("AVCTP session %p got disconnected", control);
	avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
	return FALSE;
}

static int uinput_create(char *name)
{<commit_msg>Clean up code in control.c<commit_after>/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2006-2010  Nokia Corporation
 *  Copyright (C) 2004-2010  <NAME> <<EMAIL>>
 *  Copyright (C) 2011  Texas Instruments, Inc.
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#include <glib.h>
#include <dbus/dbus.h>
#include <gdbus.h>

#include "log.h"
#include "error.h"
#include "uinput.h"
#include "adapter.h"
#include "../src/device.h"
#include "device.h"
#include "manager.h"
#include "avdtp.h"
#include "control.h"
#include "sdpd.h"
#include "glib-helper.h"
#include "btio.h"
#include "dbus-common.h"

#define AVCTP_PSM 23

/* Message types */
#define AVCTP_COMMAND		0
#define AVCTP_RESPONSE		1

/* Packet types */
#define AVCTP_PACKET_SINGLE	0
#define AVCTP_PACKET_START	1
#define AVCTP_PACKET_CONTINUE	2
#define AVCTP_PACKET_END	3

/* ctype entries */
#define CTYPE_CONTROL		0x0
#define CTYPE_STATUS		0x1
#define CTYPE_NOTIFY		0x3
#define CTYPE_NOT_IMPLEMENTED	0x8
#define CTYPE_ACCEPTED		0x9
#define CTYPE_REJECTED		0xA
#define CTYPE_STABLE		0xC
#define CTYPE_CHANGED		0xD
#define CTYPE_INTERIM		0xF

/* opcodes */
#define OP_VENDORDEP		0x00
#define OP_UNITINFO		0x30
#define OP_SUBUNITINFO		0x31
#define OP_PASSTHROUGH		0x7c

/* subunits of interest */
#define SUBUNIT_PANEL		0x09

/* operands in passthrough commands */
#define VOL_UP_OP		0x41
#define VOL_DOWN_OP		0x42
#define MUTE_OP			0x43
#define PLAY_OP			0x44
#define STOP_OP			0x45
#define PAUSE_OP		0x46
#define RECORD_OP		0x47
#define REWIND_OP		0x48
#define FAST_FORWARD_OP		0x49
#define EJECT_OP		0x4a
#define FORWARD_OP		0x4b
#define BACKWARD_OP		0x4c

/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG		0x001958

/* Error codes for metadata transfer */
#define E_INVALID_COMMAND	0x00
#define E_INVALID_PARAM		0x01
#define E_PARAM_NOT_FOUND	0x02
#define E_INTERNAL		0x03

/* PDU types for metadata transfer */
#define AVRCP_GET_CAPABILITIES		0x10
#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
#define AVRCP_LIST_PLAYER_VALUES	0x12
#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
#define AVRCP_SET_PLAYER_VALUE		0x14
#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
#define AVRCP_DISPLAYABLE_CHARSET	0x17
#define AVRCP_CT_BATTERY_STATUS		0x18
#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
#define AVRCP_GET_PLAY_STATUS		0x30
#define AVRCP_REGISTER_NOTIFICATION	0x31

/* Notification events */
#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
#define AVRCP_EVENT_TRACK_CHANGED			0x02

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID		0x02
#define CAP_EVENTS_SUPPORTED	0x03

#define QUIRK_NO_RELEASE	1 << 0

enum player_setting {
	PLAYER_SETTING_EQUALIZER =	1,
	PLAYER_SETTING_REPEAT =		2,
	PLAYER_SETTING_SHUFFLE =	3,
	PLAYER_SETTING_SCAN =		4,
};

enum equalizer_mode {
	EQUALIZER_MODE_OFF =	1,
	EQUALIZER_MODE_ON =	2,
};

enum repeat_mode {
	REPEAT_MODE_OFF =	1,
	REPEAT_MODE_SINGLE =	2,
	REPEAT_MODE_ALL =	3,
	REPEAT_MODE_GROUP =	4,
};

enum shuffle_mode {
	SHUFFLE_MODE_OFF =	1,
	SHUFFLE_MODE_ALL =	2,
	SHUFFLE_MODE_GROUP =	3,
};

enum scan_mode {
	SCAN_MODE_OFF =		1,
	SCAN_MODE_ALL =		2,
	SCAN_MODE_GROUP =	3,
};

enum play_status {
	PLAY_STATUS_STOPPED =		0x00,
	PLAY_STATUS_PLAYING =		0x01,
	PLAY_STATUS_PAUSED =		0x02,
	PLAY_STATUS_FWD_SEEK =		0x03,
	PLAY_STATUS_REV_SEEK =		0x04,
	PLAY_STATUS_ERROR =		0xFF
};

enum battery_status {
	BATTERY_STATUS_NORMAL =		0,
	BATTERY_STATUS_WARNING =	1,
	BATTERY_STATUS_CRITICAL =	2,
	BATTERY_STATUS_EXTERNAL =	3,
	BATTERY_STATUS_FULL_CHARGE =	4,
};

enum media_info_id {
	MEDIA_INFO_TITLE =		1,
	MEDIA_INFO_ARTIST =		2,
	MEDIA_INFO_ALBUM =		3,
	MEDIA_INFO_TRACK =		4,
	MEDIA_INFO_N_TRACKS =		5,
	MEDIA_INFO_GENRE =		6,
	MEDIA_INFO_CURRENT_POSITION =	7,
};

static DBusConnection *connection = NULL;

static GSList *servers = NULL;

#if __BYTE_ORDER == __LITTLE_ENDIAN

struct avctp_header {
	uint8_t ipid:1;
	uint8_t cr:1;
	uint8_t packet_type:2;
	uint8_t transaction:4;
	uint16_t pid;
} __attribute__ ((packed));
#define AVCTP_HEADER_LENGTH 3

struct avrcp_header {
	uint8_t code:4;
	uint8_t _hdr0:4;
	uint8_t subunit_id:3;
	uint8_t subunit_type:5;
	uint8_t opcode;
} __attribute__ ((packed));
#define AVRCP_HEADER_LENGTH 3

struct avrcp_spec_avc_pdu {
	uint8_t company_id[3];
	uint8_t pdu_id;
	uint8_t packet_type:2;
	uint8_t rsvd:6;
	uint16_t params_len;
	uint8_t params[0];
} __attribute__ ((packed));
#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7

#elif __BYTE_ORDER == __BIG_ENDIAN

struct avctp_header {
	uint8_t transaction:4;
	uint8_t packet_type:2;
	uint8_t cr:1;
	uint8_t ipid:1;
	uint16_t pid;
} __attribute__ ((packed));
#define AVCTP_HEADER_LENGTH 3

struct avrcp_header {
	uint8_t _hdr0:4;
	uint8_t code:4;
	uint8_t subunit_type:5;
	uint8_t subunit_id:3;
	uint8_t opcode;
} __attribute__ ((packed));
#define AVRCP_HEADER_LENGTH 3

struct avrcp_spec_avc_pdu {
	uint8_t company_id[3];
	uint8_t pdu_id;
	uint8_t rsvd:6;
	uint8_t packet_type:2;
	uint16_t params_len;
	uint8_t params[0];
} __attribute__ ((packed));
#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7

#else
#error "Unknown byte order"
#endif

struct avctp_state_callback {
	avctp_state_cb cb;
	void *user_data;
	unsigned int id;
};

struct avctp_server {
	bdaddr_t src;
	GIOChannel *io;
	uint32_t tg_record_id;
	uint32_t ct_record_id;
};

struct media_info {
	char *title;
	char *artist;
	char *album;
	char *genre;
	uint32_t ntracks;
	uint32_t track;
	uint32_t track_len;
	uint32_t elapsed;
};

struct media_player {
	uint8_t settings[PLAYER_SETTING_SCAN + 1];
	enum play_status status;

	struct media_info mi;
	GTimer *timer;
};

struct control {
	struct audio_device *dev;
	struct media_player *mp;

	avctp_state_t state;

	int uinput;

	GIOChannel *io;
	guint io_id;

	uint16_t mtu;

	gboolean target;

	uint8_t key_quirks[256];

	uint16_t registered_events;
	uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
};

static struct {
	const char *name;
	uint8_t avrcp;
	uint16_t uinput;
} key_map[] = {
	{ "PLAY",		PLAY_OP,		KEY_PLAYCD },
	{ "STOP",		STOP_OP,		KEY_STOPCD },
	{ "PAUSE",		PAUSE_OP,		KEY_PAUSECD },
	{ "FORWARD",		FORWARD_OP,		KEY_NEXTSONG },
	{ "BACKWARD",		BACKWARD_OP,		KEY_PREVIOUSSONG },
	{ "REWIND",		REWIND_OP,		KEY_REWIND },
	{ "FAST FORWARD",	FAST_FORWARD_OP,	KEY_FASTFORWARD },
	{ NULL }
};

/* Company IDs supported by this device */
static uint32_t company_ids[] = {
	IEEEID_BTSIG,
};

static GSList *avctp_callbacks = NULL;

static void auth_cb(DBusError *derr, void *user_data);

static sdp_record_t *avrcp_ct_record(void)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, l2cap, avctp, avrct;
	sdp_profile_desc_t profile[1];
	sdp_list_t *aproto, *proto[2];
	sdp_record_t *record;
	sdp_data_t *psm, *version, *features;
	uint16_t lp = AVCTP_PSM;
	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrct);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	sdp_uuid16_create(&l2cap, L2CAP_UUID);
	proto[0] = sdp_list_append(0, &l2cap);
	psm = sdp_data_alloc(SDP_UINT16, &lp);
	proto[0] = sdp_list_append(proto[0], psm);
	apseq = sdp_list_append(0, proto[0]);

	sdp_uuid16_create(&avctp, AVCTP_UUID);
	proto[1] = sdp_list_append(0, &avctp);
	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
	proto[1] = sdp_list_append(proto[1], version);
	apseq = sdp_list_append(apseq, proto[1]);

	aproto = sdp_list_append(0, apseq);
	sdp_set_access_protos(record, aproto);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

	features = sdp_data_alloc(SDP_UINT16, &feat);
	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);

	sdp_set_info_attr(record, "AVRCP CT", 0, 0);

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}

static sdp_record_t *avrcp_tg_record(void)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, l2cap, avctp, avrtg;
	sdp_profile_desc_t profile[1];
	sdp_list_t *aproto, *proto[2];
	sdp_record_t *record;
	sdp_data_t *psm, *version, *features;
	uint16_t lp = AVCTP_PSM;
	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrtg);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	sdp_uuid16_create(&l2cap, L2CAP_UUID);
	proto[0] = sdp_list_append(0, &l2cap);
	psm = sdp_data_alloc(SDP_UINT16, &lp);
	proto[0] = sdp_list_append(proto[0], psm);
	apseq = sdp_list_append(0, proto[0]);

	sdp_uuid16_create(&avctp, AVCTP_UUID);
	proto[1] = sdp_list_append(0, &avctp);
	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
	proto[1] = sdp_list_append(proto[1], version);
	apseq = sdp_list_append(apseq, proto[1]);

	aproto = sdp_list_append(0, apseq);
	sdp_set_access_protos(record, aproto);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

	features = sdp_data_alloc(SDP_UINT16, &feat);
	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);

	sdp_set_info_attr(record, "AVRCP TG", 0, 0);

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}

static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
	struct uinput_event event;

	memset(&event, 0, sizeof(event));
	event.type	= type;
	event.code	= code;
	event.value	= value;

	return write(fd, &event, sizeof(event));
}

static void send_key(int fd, uint16_t key, int pressed)
{
	if (fd < 0)
		return;

	send_event(fd, EV_KEY, key, pressed);
	send_event(fd, EV_SYN, SYN_REPORT, 0);
}

static void handle_panel_passthrough(struct control *control,
					const unsigned char *operands,
					int operand_count)
{
	const char *status;
	int pressed, i;

	if (operand_count == 0)
		return;

	if (operands[0] & 0x80) {
		status = "released";
		pressed = 0;
	} else {
		status = "pressed";
		pressed = 1;
	}

	for (i = 0; key_map[i].name != NULL; i++) {
		uint8_t key_quirks;

		if ((operands[0] & 0x7F) != key_map[i].avrcp)
			continue;

		DBG("AVRCP: %s %s", key_map[i].name, status);

		key_quirks = control->key_quirks[key_map[i].avrcp];

		if (key_quirks & QUIRK_NO_RELEASE) {
			if (!pressed) {
				DBG("AVRCP: Ignoring release");
				break;
			}

			DBG("AVRCP: treating key press as press + release");
			send_key(control->uinput, key_map[i].uinput, 1);
			send_key(control->uinput, key_map[i].uinput, 0);
			break;
		}

		send_key(control->uinput, key_map[i].uinput, pressed);
		break;
	}

	if (key_map[i].name == NULL)
		DBG("AVRCP: unknown button 0x%02X %s",
						operands[0] & 0x7F, status);
}

static unsigned int attr_get_max_val(uint8_t attr)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		return EQUALIZER_MODE_ON;
	case PLAYER_SETTING_REPEAT:
		return REPEAT_MODE_GROUP;
	case PLAYER_SETTING_SHUFFLE:
		return SHUFFLE_MODE_GROUP;
	case PLAYER_SETTING_SCAN:
		return SCAN_MODE_GROUP;
	}

	return 0;
}

static const char *attrval_to_str(uint8_t attr, uint8_t value)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		switch (value) {
		case EQUALIZER_MODE_ON:
			return "on";
		case EQUALIZER_MODE_OFF:
			return "off";
		}

		break;
	case PLAYER_SETTING_REPEAT:
		switch (value) {
		case REPEAT_MODE_OFF:
			return "off";
		case REPEAT_MODE_SINGLE:
			return "singletrack";
		case REPEAT_MODE_ALL:
			return "alltracks";
		case REPEAT_MODE_GROUP:
			return "group";
		}

		break;
	/* Shuffle and scan have the same values */
	case PLAYER_SETTING_SHUFFLE:
	case PLAYER_SETTING_SCAN:
		switch (value) {
		case SCAN_MODE_OFF:
			return "off";
		case SCAN_MODE_ALL:
			return "alltracks";
		case SCAN_MODE_GROUP:
			return "group";
		}

		break;
	}

	return NULL;
}

static int attrval_to_val(uint8_t attr, const char *value)
{
	int ret;

	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		if (!strcmp(value, "off"))
			ret = EQUALIZER_MODE_OFF;
		else if (!strcmp(value, "on"))
			ret = EQUALIZER_MODE_ON;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_REPEAT:
		if (!strcmp(value, "off"))
			ret = REPEAT_MODE_OFF;
		else if (!strcmp(value, "singletrack"))
			ret = REPEAT_MODE_SINGLE;
		else if (!strcmp(value, "alltracks"))
			ret = REPEAT_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = REPEAT_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_SHUFFLE:
		if (!strcmp(value, "off"))
			ret = SHUFFLE_MODE_OFF;
		else if (!strcmp(value, "alltracks"))
			ret = SHUFFLE_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = SHUFFLE_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	case PLAYER_SETTING_SCAN:
		if (!strcmp(value, "off"))
			ret = SCAN_MODE_OFF;
		else if (!strcmp(value, "alltracks"))
			ret = SCAN_MODE_ALL;
		else if (!strcmp(value, "group"))
			ret = SCAN_MODE_GROUP;
		else
			ret = -EINVAL;

		return ret;
	}

	return -EINVAL;
}

static const char *attr_to_str(uint8_t attr)
{
	switch (attr) {
	case PLAYER_SETTING_EQUALIZER:
		return "Equalizer";
	case PLAYER_SETTING_REPEAT:
		return "Repeat";
	case PLAYER_SETTING_SHUFFLE:
		return "Shuffle";
	case PLAYER_SETTING_SCAN:
		return "Scan";
	}

	return NULL;
}

static int attr_to_val(const char *str)
{
	if (!strcmp(str, "Equalizer"))
		return PLAYER_SETTING_EQUALIZER;
	else if (!strcmp(str, "Repeat"))
		return PLAYER_SETTING_REPEAT;
	else if (!strcmp(str, "Shuffle"))
		return PLAYER_SETTING_SHUFFLE;
	else if (!strcmp(str, "Scan"))
		return PLAYER_SETTING_SCAN;

	return -EINVAL;
}

static int play_status_to_val(const char *status)
{
	if (!strcmp(status, "stopped"))
		return PLAY_STATUS_STOPPED;
	else if (!strcmp(status, "playing"))
		return PLAY_STATUS_PLAYING;
	else if (!strcmp(status, "paused"))
		return PLAY_STATUS_PAUSED;
	else if (!strcmp(status, "forward-seek"))
		return PLAY_STATUS_FWD_SEEK;
	else if (!strcmp(status, "reverse-seek"))
		return PLAY_STATUS_REV_SEEK;
	else if (!strcmp(status, "error"))
		return PLAY_STATUS_ERROR;

	return -EINVAL;
}

static const char *battery_status_to_str(enum battery_status status)
{
	switch (status) {
	case BATTERY_STATUS_NORMAL:
		return "normal";
	case BATTERY_STATUS_WARNING:
		return "warning";
	case BATTERY_STATUS_CRITICAL:
		return "critical";
	case BATTERY_STATUS_EXTERNAL:
		return "external";
	case BATTERY_STATUS_FULL_CHARGE:
		return "fullcharge";
	}

	return NULL;
}

static int avctp_send_event(struct control *control, uint8_t id, void *data)
{
	uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
					AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
	struct avctp_header *avctp = (void *) buf;
	struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
	struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
							AVRCP_HEADER_LENGTH];
	int sk = g_io_channel_unix_get_fd(control->io);
	uint16_t size;

	if (control->state != AVCTP_STATE_CONNECTED)
		return -ENOTCONN;

	if (!(control->registered_events & (1 << id)))
		return 0;

	memset(buf, 0, sizeof(buf));

	avctp->transaction = control->transaction_events[id];
	avctp->packet_type = AVCTP_PACKET_SINGLE;
	avctp->cr = AVCTP_RESPONSE;
	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);

	avrcp->code = CTYPE_CHANGED;
	avrcp->subunit_type = SUBUNIT_PANEL;
	avrcp->opcode = OP_VENDORDEP;

	pdu->company_id[0] = IEEEID_BTSIG >> 16;
	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
	pdu->company_id[2] = IEEEID_BTSIG & 0xFF;

	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
	pdu->params[0] = id;

	DBG("id=%u", id);

	switch (id) {
	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
		size = 2;
		pdu->params[1] = *((uint8_t *)data);

		break;
	case AVRCP_EVENT_TRACK_CHANGED: {
		size = 9;

		/*
		 * AVRCP 1.3 supports only one track identifier: PLAYING
		 * (0x0). When 1.4 version is added, this shall be changed to
		 * contain the identifier of the track.
		 */
		memset(&pdu->params[1], 0, 8);

		break;
	}
	default:
		error("Unknown event %u", id);
		return -EINVAL;
	}

	pdu->params_len = htons(size);
	size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
					AVRCP_SPECAVCPDU_HEADER_LENGTH;

	if (write(sk, buf, size) < 0)
		return -errno;

	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
	control->registered_events ^= 1 << id;

	return 0;
}

static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
					uint32_t *elapsed, uint32_t *track_len)
{
	if (status)
		*status = mp->status;
	if (track_len)
		*track_len = mp->mi.track_len;

	if (!elapsed)
		return;

	*elapsed = mp->mi.elapsed;

	if (mp->status == PLAY_STATUS_PLAYING) {
		double timedelta = g_timer_elapsed(mp->timer, NULL);
		uint32_t sec, msec;

		sec = (uint32_t) timedelta;
		msec = (uint32_t)((timedelta - sec) * 1000);

		*elapsed += sec * 1000 + msec;
	}
}

static void mp_set_playback_status(struct control *control, uint8_t status,
							uint32_t elapsed)
{
	struct media_player *mp = control->mp;

	DBG("Change playback: %u %u", status, elapsed);

	mp->mi.elapsed = elapsed;
	g_timer_start(mp->timer);

	if (status == mp->status)
		return;

	mp->status = status;

	avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
								&status);
}

/*
 * Copy media_info field to a buffer, intended to be used in a response to
 * GetElementAttributes message.
 *
 * It assumes there's enough space in the buffer and on success it returns the
 * size written.
 *
 * If @param id is not valid, -EINVAL is returned. If there's no such media
 * attribute, -ENOENT is returned.
 */
static int mp_get_media_attribute(struct media_player *mp,
						uint32_t id, uint8_t *buf)
{
	struct media_info_elem {
		uint32_t id;
		uint16_t charset;
		uint16_t len;
		uint8_t val[];
	};
	const struct media_info *mi = &mp->mi;
	struct media_info_elem *elem = (void *)buf;
	uint16_t len;
	char valstr[20];

	switch (id) {
	case MEDIA_INFO_TITLE:
		if (mi->title) {
			len = strlen(mi->title);
			memcpy(elem->val, mi->title, len);
		} else {
			len = 0;
		}

		break;
	case MEDIA_INFO_ARTIST:
		if (mi->artist == NULL)
			return -ENOENT;

		len = strlen(mi->artist);
		memcpy(elem->val, mi->artist, len);
		break;
	case MEDIA_INFO_ALBUM:
		if (mi->album == NULL)
			return -ENOENT;

		len = strlen(mi->album);
		memcpy(elem->val, mi->album, len);
		break;
	case MEDIA_INFO_GENRE:
		if (mi->genre == NULL)
			return -ENOENT;

		len = strlen(mi->genre);
		memcpy(elem->val, mi->genre, len);
		break;

	case MEDIA_INFO_TRACK:
		if (!mi->track)
			return -ENOENT;

		snprintf(valstr, 20, "%u", mi->track);
		len = strlen(valstr);
		memcpy(elem->val, valstr, len);
		break;
	case MEDIA_INFO_N_TRACKS:
		if (!mi->ntracks)
			return -ENOENT;

		snprintf(valstr, 20, "%u", mi->ntracks);
		len = strlen(valstr);
		memcpy(elem->val, valstr, len);
		break;
	case MEDIA_INFO_CURRENT_POSITION:
		if (mi->elapsed != 0xFFFFFFFF) {
			uint32_t elapsed;

			mp_get_playback_status(mp, NULL, &elapsed, NULL);

			snprintf(valstr, 20, "%u", elapsed);
			len = strlen(valstr);
			memcpy(elem->val, valstr, len);
		} else {
			return -ENOENT;
		}

		break;
	default:
		return -EINVAL;
	}

	elem->id = htonl(id);
	elem->charset = htons(0x6A); /* Always use UTF-8 */
	elem->len = htons(len);

	return sizeof(struct media_info_elem) + len;
}

static void mp_set_attribute(struct media_player *mp,
						uint8_t attr, uint8_t val)
{
	DBG("Change attribute: %u %u", attr, val);

	mp->settings[attr] = val;
}

static int mp_get_attribute(struct media_player *mp, uint8_t attr)
{
	DBG("Get attribute: %u", attr);

	return mp->settings[attr];
}

static void mp_set_media_attributes(struct control *control,
							struct media_info *mi)
{
	struct media_player *mp = control->mp;

	g_free(mp->mi.title);
	mp->mi.title = g_strdup(mi->title);

	g_free(mp->mi.artist);
	mp->mi.artist = g_strdup(mi->artist);

	g_free(mp->mi.album);
	mp->mi.album = g_strdup(mi->album);

	g_free(mp->mi.genre);
	mp->mi.genre = g_strdup(mi->genre);

	mp->mi.ntracks = mi->ntracks;
	mp->mi.track = mi->track;
	mp->mi.track_len = mi->track_len;

	/*
	 * elapsed is special. Whenever the track changes, we reset it to 0,
	 * so client doesn't have to make another call to change_playback
	 */
	mp->mi.elapsed = 0;
	g_timer_start(mp->timer);

	DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
			"\tgenre: %s\n\tNumber of tracks: %u\n"
			"\tTrack number: %u\n\tTrack duration: %u",
			mi->title, mi->artist, mi->album, mi->genre,
			mi->ntracks, mi->track, mi->track_len);

	avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
}

static int avrcp_handle_get_capabilities(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	unsigned int i;

	if (len != 1)
		goto err;

	DBG("id=%u", pdu->params[0]);

	switch (pdu->params[0]) {
	case CAP_COMPANY_ID:
		for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
			pdu->params[2 + i * 3] = company_ids[i] >> 16;
			pdu->params[3 + i * 3] = (company_ids[i] >> 8) & 0xFF;
			pdu->params[4 + i * 3] = company_ids[i] & 0xFF;
		}

		pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
		pdu->params[1] = G_N_ELEMENTS(company_ids);

		return 2 + (3 * G_N_ELEMENTS(company_ids));
	case CAP_EVENTS_SUPPORTED:
		pdu->params_len = htons(4);
		pdu->params[1] = 2;
		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;

		return 4;
	}

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_list_player_attributes(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	unsigned int i;

	if (len != 0) {
		pdu->params[0] = E_INVALID_PARAM;
		return -EINVAL;
	}

	if (!mp)
		goto done;

	for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
		if (!mp_get_attribute(mp, i)) {
			DBG("Ignoring setting %u: not supported by player", i);
			continue;
		}

		len++;
		pdu->params[len] = i;
	}

done:
	pdu->params[0] = len;
	pdu->params_len = htons(len + 1);

	return len + 1;
}

static int avrcp_handle_list_player_values(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	unsigned int i;

	if (len != 1 || !mp)
		goto err;

	len = attr_get_max_val(pdu->params[0]);
	if (!len) {
		error("Attribute is invalid: %u", pdu->params[0]);
		goto err;
	}

	for (i = 1; i <= len; i++)
		pdu->params[i] = i;

	pdu->params[0] = len;
	pdu->params_len = htons(len + 1);

	return len + 1;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_element_attributes(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	uint64_t *identifier = (void *) &pdu->params[0];
	uint16_t pos;
	uint8_t nattr;
	int size;
	unsigned int i;

	if (len < 8 || *identifier != 0 || !control->mp)
		goto err;

	len = 0;
	pos = 1; /* Keep track of current position in reponse */
	nattr = pdu->params[8];

	if (!control->mp)
		goto done;

	if (!nattr) {
		/*
		 * Return all available information, at least
		 * title must be returned.
		 */
		for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
			size = mp_get_media_attribute(control->mp, i,
							&pdu->params[pos]);

			if (size > 0) {
				len++;
				pos += size;
			}
		}
	} else {
		uint32_t *attr_ids;

		attr_ids = g_memdup(&pdu->params[9], sizeof(uint32_t) * nattr);

		for (i = 0; i < nattr; i++) {
			uint32_t attr = ntohl(attr_ids[i]);

			size = mp_get_media_attribute(control->mp, attr,
							&pdu->params[pos]);

			if (size > 0) {
				len++;
				pos += size;
			}
		}

		g_free(attr_ids);

		if (!len)
			goto err;
	}

done:
	pdu->params[0] = len;
	pdu->params_len = htons(pos);

	return pos;
err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_current_player_value(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	struct media_player *mp = control->mp;
	uint8_t *settings;
	unsigned int i;

	if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
		goto err;

	/*
	 * Save a copy of requested settings because we can override them
	 * while responding
	 */
	settings = g_malloc(pdu->params[0]);
	memcpy(settings, &pdu->params[1], pdu->params[0]);
	len = 0;

	/*
	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
	 * and send a response with the existent ones. Only if all IDs are
	 * non-existent we should send an error.
	 */
	for (i = 0; i < pdu->params[0]; i++) {
		uint8_t val;

		if (settings[i] < PLAYER_SETTING_EQUALIZER ||
					settings[i] > PLAYER_SETTING_SCAN) {
			DBG("Ignoring %u", settings[i]);
			continue;
		}

		val = mp_get_attribute(mp, settings[i]);
		if (!val) {
			DBG("Ignoring %u: not supported by player",
								settings[i]);
			continue;
		}

		pdu->params[len] = settings[i];
		pdu->params[len + 1] = val;
		len += 2;
	}

	g_free(settings);

	if (len) {
		pdu->params[0] = len;
		pdu->params_len = htons(2 * len + 1);

		return 2 * len + 1;
	}

	error("No valid attributes in request");

err:
	pdu->params[0] = E_INVALID_PARAM;

	return -EINVAL;
}

static int avrcp_handle_set_player_value(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	unsigned int i;

	if (len < 3 || !control->mp)
		goto err;

	len = 0;

	/*
	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
	 * and set the existent ones. Sec. 5.2.4 is not clear however how to
	 * indicate that a certain ID was not accepted. If at least one
	 * attribute is valid, we respond with no parameters. Otherwise an
	 * E_INVALID_PARAM is sent.
	 */
	for (i = 1; i < pdu->params[0]; i += 2) {
		uint8_t attr = pdu->params[i];
		uint8_t val = pdu->params[i + 1];
		const char *attrstr;
		const char *valstr;

		attrstr = attr_to_str(attr);
		if (!attrstr)
			continue;

		valstr = attrval_to_str(attr, val);
		if (!valstr)
			continue;

		len++;

		mp_set_attribute(control->mp, attr, val);
		emit_property_changed(control->dev->conn, control->dev->path,
					MEDIA_PLAYER_INTERFACE, attrstr,
					DBUS_TYPE_STRING, &valstr);
	}

	if (len) {
		pdu->params_len = 0;

		return 0;
	}

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_ct_battery_status(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	const char *valstr;

	if (len != 1)
		goto err;

	valstr = battery_status_to_str(pdu->params[0]);
	if (valstr == NULL)
		goto err;

	emit_property_changed(control->dev->conn, control->dev->path,
					MEDIA_PLAYER_INTERFACE, "Battery",
					DBUS_TYPE_STRING, &valstr);
	pdu->params_len = 0;

	return 0;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

static int avrcp_handle_get_play_status(struct control *control,
						struct avrcp_spec_avc_pdu *pdu)
{
	uint16_t len = ntohs(pdu->params_len);
	uint32_t elapsed;
	uint32_t track_len;
	uint8_t status;

	if (len != 0) {
		pdu->params[0] = E_INVALID_PARAM;
		return -EINVAL;
	}

	if (control->mp) {
		mp_get_playback_status(control->mp, &status,
							&elapsed, &track_len);
		track_len = htonl(track_len);
		elapsed = htonl(elapsed);
	} else {
		track_len = 0xFFFFFFFF;
		elapsed = 0xFFFFFFFF;
		status = PLAY_STATUS_ERROR;
	}

	memcpy(&pdu->params[0], &track_len, 4);
	memcpy(&pdu->params[4], &elapsed, 4);
	pdu->params[8] = status;

	pdu->params_len = htons(9);

	return 9;
}

static int avrcp_handle_register_notification(struct control *control,
						struct avrcp_spec_avc_pdu *pdu,
						uint8_t transaction)
{
	uint16_t len = ntohs(pdu->params_len);
	uint8_t status;

	/*
	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
	 * 1.3 spec, section 5.4.2.
	 */
	if (len != 5)
		goto err;

	switch (pdu->params[0]) {
	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
		len = 2;
		if (control->mp) {
			mp_get_playback_status(control->mp, &status,
								NULL, NULL);
			pdu->params[1] = status;
		} else {
			pdu->params[1] = PLAY_STATUS_ERROR;
		}

		break;
	case AVRCP_EVENT_TRACK_CHANGED:
		len = 9;

		if (!control->mp)
			memset(&pdu->params[1], 0xFF, 8);
		else
			memset(&pdu->params[1], 0, 8);

		break;
	default:
		/* All other events are not supported yet */
		goto err;
	}

	/* Register event and save the transaction used */
	control->registered_events |= (1 << pdu->params[0]);
	control->transaction_events[pdu->params[0]] = transaction;

	pdu->params_len = htons(len);

	return len;

err:
	pdu->params[0] = E_INVALID_PARAM;
	return -EINVAL;
}

/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
					struct avctp_header *avctp,
					struct avrcp_header *avrcp,
					int operand_count)
{
	struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
	uint32_t company_id = (pdu->company_id[0] << 16) |
				(pdu->company_id[1] << 8) |
				(pdu->company_id[2]);
	int len;

	if (company_id != IEEEID_BTSIG ||
				pdu->packet_type != AVCTP_PACKET_SINGLE) {
		avrcp->code = CTYPE_NOT_IMPLEMENTED;
		return AVRCP_HEADER_LENGTH;
	}

	pdu->packet_type = 0;
	pdu->rsvd = 0;

	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
		pdu->params[0] = E_INVALID_COMMAND;
		goto err_metadata;
	}

	switch (pdu->pdu_id) {
	case AVRCP_GET_CAPABILITIES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_capabilities(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_LIST_PLAYER_ATTRIBUTES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_list_player_attributes(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_LIST_PLAYER_VALUES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_list_player_values(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_ELEMENT_ATTRIBUTES:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_element_attributes(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_CURRENT_PLAYER_VALUE:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_current_player_value(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_SET_PLAYER_VALUE:
		if (avrcp->code != CTYPE_CONTROL) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_set_player_value(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
	case AVRCP_GET_PLAYER_VALUE_TEXT:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		/*
		 * As per sec. 5.2.5 of AVRCP 1.3 spec, this command is
		 * expected to be used only for extended attributes, i.e.
		 * custom attributes defined by the application. As we
		 * currently don't have any such attribute, we respond with
		 * invalid param id.
		 */
		pdu->params[0] = E_INVALID_PARAM;
		goto err_metadata;
	case AVRCP_DISPLAYABLE_CHARSET:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		if (pdu->params[0] < 3) {
			pdu->params[0] = E_INVALID_PARAM;
			goto err_metadata;
		}

		/*
		 * We acknowledge the commands, but we always use UTF-8 for
		 * encoding since CT is obliged to support it.
		 */
		pdu->params_len = 0;
		avrcp->code = CTYPE_STABLE;
		len = 0;

		break;
	case AVRCP_CT_BATTERY_STATUS:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_ct_battery_status(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_GET_PLAY_STATUS:
		if (avrcp->code != CTYPE_STATUS) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_get_play_status(control, pdu);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_STABLE;

		break;
	case AVRCP_REGISTER_NOTIFICATION:
		if (avrcp->code != CTYPE_NOTIFY) {
			pdu->params[0] = E_INVALID_COMMAND;
			goto err_metadata;
		}

		len = avrcp_handle_register_notification(control, pdu,
							avctp->transaction);
		if (len < 0)
			goto err_metadata;

		avrcp->code = CTYPE_INTERIM;

		break;
	default:
		/* Invalid pdu_id */
		pdu->params[0] = E_INVALID_COMMAND;
		goto err_metadata;
	}

	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;

err_metadata:
	avrcp->code = CTYPE_REJECTED;
	pdu->params_len = htons(1);

	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
}

static void avctp_disconnected(struct audio_device *dev)
{
	struct control *control = dev->control;

	if (!control)
		return;

	if (control->io) {
		g_io_channel_shutdown(control->io, TRUE, NULL);
		g_io_channel_unref(control->io);
		control->io = NULL;
	}

	if (control->io_id) {
		g_source_remove(control->io_id);
		control->io_id = 0;

		if (control->state == AVCTP_STATE_CONNECTING)
			audio_device_cancel_authorization(dev, auth_cb,
								control);
	}

	if (control->uinput >= 0) {
		char address[18];

		ba2str(&dev->dst, address);
		DBG("AVRCP: closing uinput for %s", address);

		ioctl(control->uinput, UI_DEV_DESTROY);
		close(control->uinput);
		control->uinput = -1;
	}
}

static void avctp_set_state(struct control *control, avctp_state_t new_state)
{
	GSList *l;
	struct audio_device *dev = control->dev;
	avctp_state_t old_state = control->state;
	gboolean value;

	switch (new_state) {
	case AVCTP_STATE_DISCONNECTED:
		DBG("AVCTP Disconnected");

		avctp_disconnected(control->dev);

		if (old_state != AVCTP_STATE_CONNECTED)
			break;

		value = FALSE;
		g_dbus_emit_signal(dev->conn, dev->path,
					AUDIO_CONTROL_INTERFACE,
					"Disconnected", DBUS_TYPE_INVALID);
		emit_property_changed(dev->conn, dev->path,
					AUDIO_CONTROL_INTERFACE, "Connected",
					DBUS_TYPE_BOOLEAN, &value);

		if (!audio_device_is_active(dev, NULL))
			audio_device_set_authorized(dev, FALSE);

		break;
	case AVCTP_STATE_CONNECTING:
		DBG("AVCTP Connecting");
		break;
	case AVCTP_STATE_CONNECTED:
		DBG("AVCTP Connected");
		value = TRUE;
		g_dbus_emit_signal(control->dev->conn, control->dev->path,
				AUDIO_CONTROL_INTERFACE, "Connected",
				DBUS_TYPE_INVALID);
		emit_property_changed(control->dev->conn, control->dev->path,
				AUDIO_CONTROL_INTERFACE, "Connected",
				DBUS_TYPE_BOOLEAN, &value);
		break;
	default:
		error("Invalid AVCTP state %d", new_state);
		return;
	}

	control->state = new_state;

	for (l = avctp_callbacks; l != NULL; l = l->next) {
		struct avctp_state_callback *cb = l->data;
		cb->cb(control->dev, old_state, new_state, cb->user_data);
	}
}

static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
				gpointer data)
{
	struct control *control = data;
	unsigned char buf[1024], *operands;
	struct avctp_header *avctp;
	struct avrcp_header *avrcp;
	int ret, packet_size, operand_count, sock;

	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
		goto failed;

	sock = g_io_channel_unix_get_fd(control->io);

	ret = read(sock, buf, sizeof(buf));
	if (ret <= 0)
		goto failed;

	DBG("Got %d bytes of data for AVCTP session %p", ret, control);

	if ((unsigned int) ret < sizeof(struct avctp_header)) {
		error("Too small AVCTP packet");
		goto failed;
	}

	packet_size = ret;

	avctp = (struct avctp_header *) buf;

	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
			"PID 0x%04X",
			avctp->transaction, avctp->packet_type,
			avctp->cr, avctp->ipid, ntohs(avctp->pid));

	ret -= sizeof(struct avctp_header);
	if ((unsigned int) ret < sizeof(struct avrcp_header)) {
		error("Too small AVRCP packet");
		goto failed;
	}

	avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header));

	ret -= sizeof(struct avrcp_header);

	operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header);
	operand_count = ret;

	DBG("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
			"opcode 0x%02X, %d operands",
			avctp->cr ? "response" : "command",
			avrcp->code, avrcp->subunit_type, avrcp->subunit_id,
			avrcp->opcode, operand_count);

	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_NOT_IMPLEMENTED;
	} else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
		avctp->ipid = 1;
		avctp->cr = AVCTP_RESPONSE;
		packet_size = sizeof(*avctp);
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->code == CTYPE_CONTROL &&
			avrcp->subunit_type == SUBUNIT_PANEL &&
			avrcp->opcode == OP_PASSTHROUGH) {
		handle_panel_passthrough(control, operands, operand_count);
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_ACCEPTED;
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->code == CTYPE_STATUS &&
			(avrcp->opcode == OP_UNITINFO
			|| avrcp->opcode == OP_SUBUNITINFO)) {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_STABLE;
		/* The first operand should be 0x07 for the UNITINFO response.
		 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
		 * Interface Command Set (section 9.2.1, page 45) specs
		 * explain this value but both use it */
		if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO)
			operands[0] = 0x07;
		if (operand_count >= 2)
			operands[1] = SUBUNIT_PANEL << 3;
		DBG("reply to %s", avrcp->opcode == OP_UNITINFO ?
				"OP_UNITINFO" : "OP_SUBUNITINFO");
	} else if (avctp->cr == AVCTP_COMMAND &&
			avrcp->opcode == OP_VENDORDEP) {
		int r_size;
		operand_count -= 3;
		avctp->cr = AVCTP_RESPONSE;
		r_size = handle_vendordep_pdu(control, avctp, avrcp,
								operand_count);
		packet_size = AVCTP_HEADER_LENGTH + r_size;
	} else {
		avctp->cr = AVCTP_RESPONSE;
		avrcp->code = CTYPE_REJECTED;
	}

	ret = write(sock, buf, packet_size);
	if (ret != packet_size)
		goto failed;

	return TRUE;

failed:
	DBG("AVCTP session %p got disconnected", control);
	avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
	return FALSE;
}

static int uinput_create(char *name)
{
{'fraction_non_alphanumeric': 0.12610972846476728, 'fraction_numerical': 0.020950187585899484, 'mean_word_length': 4.292876335277541, 'pattern_counts': {'":': 0, '<': 127, '<?xml version=': 0, '>': 687, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7270591', 'n_tokens_mistral': 36428, 'n_tokens_neox': 33275, 'n_words': 7217}