1 // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <errno.h>
16 
17 #include "mesh.h"
18 #include "mesh_config.h"
19 #include "access.h"
20 #include "mesh_common.h"
21 #include "generic_server.h"
22 #include "lighting_server.h"
23 
24 #if CONFIG_BLE_MESH_SERVER_MODEL
25 
26 /**
27  * According to Mesh Model Spec:
28  * If the Transition Time field is not present and the Generic Default Transition
29  * Time state is supported, the Generic Default Transition Time state shall be
30  * used. Otherwise the transition shall be instantaneous.
31  */
32 #define INSTANTANEOUS_TRANS_TIME      0
33 
bt_mesh_get_default_trans_time(struct bt_mesh_model * model)34 uint8_t bt_mesh_get_default_trans_time(struct bt_mesh_model *model)
35 {
36     /**
37      * 1. If a Generic Default Transition Time Server model is present on the
38      *    main element of the model, that model instance shall be used.
39      * 2. If a Generic Default Transition Time Server model is not present on
40      *    the main element of the model, then the Generic Default Transition
41      *    Time Server model instance that is present on the element with the
42      *    largest address that is smaller than the address of the main element
43      *    of the node shall be used; if no model instance is present on any
44      *    element with an address smaller than the address of the main element,
45      *    then the Generic Default Transition Time Server is not supported.
46      */
47     struct bt_mesh_elem *element = bt_mesh_model_elem(model);
48     struct bt_mesh_gen_def_trans_time_srv *state = NULL;
49     uint16_t primary_addr = bt_mesh_primary_addr();
50     struct bt_mesh_model *srv = NULL;
51 
52     for (uint16_t addr = element->addr; addr >= primary_addr; addr--) {
53         element = bt_mesh_elem_find(addr);
54         if (element) {
55             srv = bt_mesh_model_find(element, BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV);
56             if (srv) {
57                 state = (struct bt_mesh_gen_def_trans_time_srv *)srv->user_data;
58                 if (state) {
59                     return state->state.trans_time;
60                 }
61             }
62         }
63     }
64 
65     return INSTANTANEOUS_TRANS_TIME;
66 }
67 
bt_mesh_get_light_lc_trans_time(struct bt_mesh_model * model,uint8_t * trans_time)68 int bt_mesh_get_light_lc_trans_time(struct bt_mesh_model *model, uint8_t *trans_time)
69 {
70     struct bt_mesh_light_lc_srv *srv = NULL;
71     uint32_t value = 0U;
72 
73     if (model == NULL || trans_time == NULL) {
74         BT_ERR("%s, Invalid parameter", __func__);
75         return -EINVAL;
76     }
77 
78     if (model->id != BLE_MESH_MODEL_ID_LIGHT_LC_SRV) {
79         BT_ERR("Invalid a Light LC Server 0x%04x", model->id);
80         return -EINVAL;
81     }
82 
83     srv = (struct bt_mesh_light_lc_srv *)model->user_data;
84     if (srv == NULL) {
85         BT_ERR("Invalid Light LC Server user data");
86         return -EINVAL;
87     }
88 
89     /**
90      * 1. Set transition time to 0x54 for BQB test case MESH/SR/LLC/BV-04-C.
91      *    Light LC Property Set: 0x3C, 0x004E20 -> Light LC Time Run On
92      *    Light LC Property Set: 0x37, 0x004E20 -> Light LC Time Fade On
93      *    Light LC Property Set: 0x39, 0x004E20 -> Light LC Time Fade Standby Manual
94      *
95      * 2. Set transition time to 0x0 for BQB test case MESH/SR/LLC/BV-08-C.
96      *
97      * TODO: Based on Light LC state and choose property property value as the
98      * transition time. Currently directly use Light LC Time Run On property value.
99      * Unit: Millisecond, range: [0, 16777214(0xFFFFFE)]
100      */
101     value = srv->lc->prop_state.time_run_on & 0xFFFFFF;
102 
103     /**
104      * Convert value into Default Transition Time state format.
105      * 0b00: 0 ~ 6.2s, 100 millisecond step resolution
106      * 0b01: 0 ~ 62s,  1 second step resolution
107      * 0b10: 0 ~ 620s, 10 seconds step resolution
108      * 0b11: 0 ~ 620m, 10 minutes step resolution
109      */
110     if (value <= 6200) {
111         *trans_time = (0 << 6) | (value / 100);
112     } else if (value <= 62000) {
113         *trans_time = (1 << 6) | (value / 1000);
114     } else if (value <= 620000) {
115         *trans_time = (2 << 6) | (value / 10000);
116     } else {
117         *trans_time = (3 << 6) | (value / 600000);
118     }
119 
120     return 0;
121 }
122 
bt_mesh_server_get_optional(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf,uint8_t * trans_time,uint8_t * delay,bool * optional)123 int bt_mesh_server_get_optional(struct bt_mesh_model *model,
124                                 struct bt_mesh_msg_ctx *ctx,
125                                 struct net_buf_simple *buf,
126                                 uint8_t *trans_time, uint8_t *delay,
127                                 bool *optional)
128 {
129     if (model == NULL || buf == NULL || trans_time == NULL ||
130             delay == NULL || optional == NULL) {
131         BT_ERR("%s, Invalid parameter", __func__);
132         return -EINVAL;
133     }
134 
135     if (buf->len != 0x00 && buf->len != 0x02) {
136         BT_ERR("Invalid optional message length %d", buf->len);
137         return -EINVAL;
138     }
139 
140     /* Currently we only get optional msg info which dst is set to a unicast address */
141     if (!BLE_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) {
142         *trans_time = 0U;
143         *delay = 0U;
144         *optional = false;
145         return 0;
146     }
147 
148     /* No optional fields are available */
149     if (buf->len == 0x00) {
150         if (model->id == BLE_MESH_MODEL_ID_LIGHT_LC_SRV) {
151             /**
152              * Both messages(i.e. Light LC OnOff Set/Set Unack) may optionally include
153              * a Transition Time field indicating the transition time to the target state.
154              * If the Transition Time is not included, the Light LC Server shall use
155              * its appropriate transition times defined by the Light LC Property states.
156              */
157             if (bt_mesh_get_light_lc_trans_time(model, trans_time)) {
158                 BT_ERR("Failed to get Light LC transition time");
159                 return -EIO;
160             }
161         } else {
162             *trans_time = bt_mesh_get_default_trans_time(model);
163         }
164         *delay = 0U;
165         *optional = false;
166         return 0;
167     }
168 
169     /* Optional fields are available */
170     *trans_time = net_buf_simple_pull_u8(buf);
171     if ((*trans_time & 0x3F) == 0x3F) {
172         BT_ERR("Invalid Transaction Number of Steps 0x3f");
173         return -EINVAL;
174     }
175 
176     *delay = net_buf_simple_pull_u8(buf);
177     *optional = true;
178     return 0;
179 }
180 
bt_mesh_server_alloc_ctx(struct k_work * work)181 void bt_mesh_server_alloc_ctx(struct k_work *work)
182 {
183     /**
184      * This function is used to allocate memory for storing "struct bt_mesh_msg_ctx"
185      * of the received messages, because some server models will callback the "struct
186      * bt_mesh_msg_ctx" info to the application layer after a certain delay.
187      * Here we use the allocated heap memory to store the "struct bt_mesh_msg_ctx".
188      */
189     __ASSERT(work, "Invalid parameter");
190     if (!work->_reserved) {
191         work->_reserved = bt_mesh_calloc(sizeof(struct bt_mesh_msg_ctx));
192         __ASSERT(work->_reserved, "Out of memory");
193     }
194 }
195 
196 #if CONFIG_BLE_MESH_DEINIT
bt_mesh_server_free_ctx(struct k_work * work)197 void bt_mesh_server_free_ctx(struct k_work *work)
198 {
199     __ASSERT(work, "Invalid parameter");
200     if (work->_reserved) {
201         bt_mesh_free(work->_reserved);
202         work->_reserved = NULL;
203     }
204 }
205 #endif /* CONFIG_BLE_MESH_DEINIT */
206 
bt_mesh_is_server_recv_last_msg(struct bt_mesh_last_msg_info * last,uint8_t tid,uint16_t src,uint16_t dst,int64_t * now)207 bool bt_mesh_is_server_recv_last_msg(struct bt_mesh_last_msg_info *last,
208                                      uint8_t tid, uint16_t src, uint16_t dst, int64_t *now)
209 {
210     *now = k_uptime_get();
211 
212     /* Currently we only compare msg info which dst is set to a unicast address */
213     if (!BLE_MESH_ADDR_IS_UNICAST(dst)) {
214         return false;
215     }
216 
217     if (last->tid == tid && last->src == src && last->dst == dst &&
218             (*now - last->timestamp <= K_SECONDS(6))) {
219         return true;
220     }
221 
222     return false;
223 }
224 
bt_mesh_server_update_last_msg(struct bt_mesh_last_msg_info * last,uint8_t tid,uint16_t src,uint16_t dst,int64_t * now)225 void bt_mesh_server_update_last_msg(struct bt_mesh_last_msg_info *last,
226                                     uint8_t tid, uint16_t src, uint16_t dst, int64_t *now)
227 {
228     /* Currently we only update msg info which dst is set to a unicast address */
229     if (!BLE_MESH_ADDR_IS_UNICAST(dst)) {
230         return;
231     }
232 
233     last->tid = tid;
234     last->src = src;
235     last->dst = dst;
236     last->timestamp = *now;
237     return;
238 }
239 
bt_mesh_server_get_pub_msg(struct bt_mesh_model * model,uint16_t msg_len)240 struct net_buf_simple *bt_mesh_server_get_pub_msg(struct bt_mesh_model *model, uint16_t msg_len)
241 {
242     struct net_buf_simple *buf = NULL;
243 
244     if (model == NULL) {
245         BT_ERR("%s, Invalid parameter", __func__);
246         return NULL;
247     }
248 
249     if (model->pub == NULL || model->pub->msg == NULL ||
250             model->pub->addr == BLE_MESH_ADDR_UNASSIGNED) {
251         BT_DBG("No publication support, model id 0x%04x", model->id);
252         return NULL;
253     }
254 
255     buf = model->pub->msg;
256     if (buf->size < msg_len) {
257         BT_ERR("Too small publication msg size %d, model id 0x%04x",
258                 buf->size, model->id);
259         return NULL;
260     }
261 
262     return buf;
263 }
264 
265 #endif /* CONFIG_BLE_MESH_SERVER_MODEL */
266