1/*
2 * Copyright 2024, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5#include "L2capEndpointManager.h"
6
7#include <AutoDeleter.h>
8
9#include <bluetooth/bdaddrUtils.h>
10
11
12L2capEndpointManager gL2capEndpointManager;
13
14
15L2capEndpointManager::L2capEndpointManager()
16	:
17	fNextChannelID(L2CAP_FIRST_CID)
18{
19	rw_lock_init(&fBoundEndpointsLock, "l2cap bound endpoints");
20	rw_lock_init(&fChannelEndpointsLock, "l2cap channel endpoints");
21}
22
23
24L2capEndpointManager::~L2capEndpointManager()
25{
26	rw_lock_destroy(&fBoundEndpointsLock);
27	rw_lock_destroy(&fChannelEndpointsLock);
28}
29
30
31status_t
32L2capEndpointManager::Bind(L2capEndpoint* endpoint, const sockaddr_l2cap& address)
33{
34	// TODO: Support binding to specific addresses?
35	if (!Bluetooth::bdaddrUtils::Compare(address.l2cap_bdaddr, BDADDR_ANY))
36		return EINVAL;
37
38	// PSM values must be odd.
39	if ((address.l2cap_psm & 1) == 0)
40		return EINVAL;
41
42	WriteLocker _(fBoundEndpointsLock);
43
44	if (fBoundEndpoints.Find(address.l2cap_psm) != NULL)
45		return EADDRINUSE;
46
47	memcpy(*endpoint->LocalAddress(), &address, sizeof(struct sockaddr_l2cap));
48	fBoundEndpoints.Insert(endpoint);
49	gSocketModule->acquire_socket(endpoint->socket);
50
51	return B_OK;
52}
53
54
55status_t
56L2capEndpointManager::Unbind(L2capEndpoint* endpoint)
57{
58	WriteLocker _(fBoundEndpointsLock);
59
60	fBoundEndpoints.Remove(endpoint);
61	(*endpoint->LocalAddress())->sa_len = 0;
62	gSocketModule->release_socket(endpoint->socket);
63
64	return B_OK;
65}
66
67
68L2capEndpoint*
69L2capEndpointManager::ForPSM(uint16 psm)
70{
71	ReadLocker _(fBoundEndpointsLock);
72	// TODO: Acquire reference?
73	return fBoundEndpoints.Find(psm);
74}
75
76
77status_t
78L2capEndpointManager::BindToChannel(L2capEndpoint* endpoint)
79{
80	WriteLocker _(fChannelEndpointsLock);
81
82	for (uint16 i = 0; i < (L2CAP_LAST_CID - L2CAP_FIRST_CID); i++) {
83		const uint16 cid = fNextChannelID;
84		fNextChannelID++;
85		if (fNextChannelID < L2CAP_FIRST_CID)
86			fNextChannelID = L2CAP_FIRST_CID;
87
88		if (fChannelEndpoints.Find(cid) != NULL)
89			continue;
90
91		endpoint->fChannelID = cid;
92		fChannelEndpoints.Insert(endpoint);
93		gSocketModule->acquire_socket(endpoint->socket);
94		return B_OK;
95	}
96
97	return EADDRINUSE;
98}
99
100
101status_t
102L2capEndpointManager::UnbindFromChannel(L2capEndpoint* endpoint)
103{
104	WriteLocker _(fChannelEndpointsLock);
105
106	fChannelEndpoints.Remove(endpoint);
107	endpoint->fChannelID = 0;
108	gSocketModule->release_socket(endpoint->socket);
109
110	return B_OK;
111}
112
113
114L2capEndpoint*
115L2capEndpointManager::ForChannel(uint16 cid)
116{
117	ReadLocker _(fChannelEndpointsLock);
118	// TODO: Acquire reference?
119	return fChannelEndpoints.Find(cid);
120}
121
122
123void
124L2capEndpointManager::Disconnected(HciConnection* connection)
125{
126	ReadLocker _(fChannelEndpointsLock);
127	auto iter = fChannelEndpoints.GetIterator();
128	while (iter.HasNext()) {
129		L2capEndpoint* endpoint = iter.Next();
130		if (endpoint->fConnection != connection)
131			continue;
132
133		endpoint->fConnection = NULL;
134		endpoint->fState = L2capEndpoint::CLOSED;
135	}
136}
137
138