1 /*
2 * Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <init.h>
8 #include <errno.h>
9 #include <lorawan/lorawan.h>
10 #include <zephyr.h>
11
12 #include "lw_priv.h"
13
14 #include <LoRaMac.h>
15 #include <Region.h>
16
17 BUILD_ASSERT(!IS_ENABLED(CONFIG_LORAMAC_REGION_UNKNOWN),
18 "Unknown region specified for LoRaWAN in Kconfig");
19
20 #ifdef CONFIG_LORAMAC_REGION_AS923
21 #define LORAWAN_REGION LORAMAC_REGION_AS923
22 #elif CONFIG_LORAMAC_REGION_AU915
23 #define LORAWAN_REGION LORAMAC_REGION_AU915
24 #elif CONFIG_LORAMAC_REGION_CN470
25 #define LORAWAN_REGION LORAMAC_REGION_CN470
26 #elif CONFIG_LORAMAC_REGION_CN779
27 #define LORAWAN_REGION LORAMAC_REGION_CN779
28 #elif CONFIG_LORAMAC_REGION_EU433
29 #define LORAWAN_REGION LORAMAC_REGION_EU433
30 #elif CONFIG_LORAMAC_REGION_EU868
31 #define LORAWAN_REGION LORAMAC_REGION_EU868
32 #elif CONFIG_LORAMAC_REGION_KR920
33 #define LORAWAN_REGION LORAMAC_REGION_KR920
34 #elif CONFIG_LORAMAC_REGION_IN865
35 #define LORAWAN_REGION LORAMAC_REGION_IN865
36 #elif CONFIG_LORAMAC_REGION_US915
37 #define LORAWAN_REGION LORAMAC_REGION_US915
38 #elif CONFIG_LORAMAC_REGION_RU864
39 #define LORAWAN_REGION LORAMAC_REGION_RU864
40 #else
41 #error "At least one LoRaWAN region should be selected"
42 #endif
43
44 /* Use version 1.0.3.0 for ABP */
45 #define LORAWAN_ABP_VERSION 0x01000300
46
47 #define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL
48 #include <logging/log.h>
49 LOG_MODULE_REGISTER(lorawan);
50
51 K_SEM_DEFINE(mlme_confirm_sem, 0, 1);
52 K_SEM_DEFINE(mcps_confirm_sem, 0, 1);
53
54 K_MUTEX_DEFINE(lorawan_join_mutex);
55 K_MUTEX_DEFINE(lorawan_send_mutex);
56
57 /* We store both the default datarate requested through lorawan_set_datarate
58 * and the current datarate so that we can use the default datarate for all
59 * join requests, even as the current datarate changes due to ADR.
60 */
61 static enum lorawan_datarate default_datarate;
62 static enum lorawan_datarate current_datarate;
63 static bool lorawan_adr_enable;
64
65 static sys_slist_t dl_callbacks;
66
67 static LoRaMacPrimitives_t macPrimitives;
68 static LoRaMacCallback_t macCallbacks;
69
70 static LoRaMacEventInfoStatus_t last_mcps_confirm_status;
71 static LoRaMacEventInfoStatus_t last_mlme_confirm_status;
72 static LoRaMacEventInfoStatus_t last_mcps_indication_status;
73 static LoRaMacEventInfoStatus_t last_mlme_indication_status;
74
75 static uint8_t (*getBatteryLevelUser)(void);
76 static void (*dr_change_cb)(enum lorawan_datarate dr);
77
BoardGetUniqueId(uint8_t * id)78 void BoardGetUniqueId(uint8_t *id)
79 {
80 /* Do not change the default value */
81 }
82
getBatteryLevelLocal(void)83 static uint8_t getBatteryLevelLocal(void)
84 {
85 if (getBatteryLevelUser != NULL) {
86 return getBatteryLevelUser();
87 }
88
89 return 255;
90 }
91
OnMacProcessNotify(void)92 static void OnMacProcessNotify(void)
93 {
94 LoRaMacProcess();
95 }
96
datarate_observe(bool force_notification)97 static void datarate_observe(bool force_notification)
98 {
99 MibRequestConfirm_t mibGet;
100
101 mibGet.Type = MIB_CHANNELS_DATARATE;
102 LoRaMacMibGetRequestConfirm(&mibGet);
103
104 if ((mibGet.Param.ChannelsDatarate != current_datarate) ||
105 (force_notification)) {
106 current_datarate = mibGet.Param.ChannelsDatarate;
107 if (dr_change_cb) {
108 dr_change_cb(current_datarate);
109 }
110 LOG_INF("Datarate changed: DR_%d", current_datarate);
111 }
112 }
113
McpsConfirm(McpsConfirm_t * mcpsConfirm)114 static void McpsConfirm(McpsConfirm_t *mcpsConfirm)
115 {
116 LOG_DBG("Received McpsConfirm (for McpsRequest %d)",
117 mcpsConfirm->McpsRequest);
118
119 if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
120 LOG_ERR("McpsRequest failed : %s",
121 lorawan_eventinfo2str(mcpsConfirm->Status));
122 } else {
123 LOG_DBG("McpsRequest success!");
124 }
125
126 /* Datarate may have changed due to a missed ADRACK */
127 if (lorawan_adr_enable) {
128 datarate_observe(false);
129 }
130
131 last_mcps_confirm_status = mcpsConfirm->Status;
132 k_sem_give(&mcps_confirm_sem);
133 }
134
McpsIndication(McpsIndication_t * mcpsIndication)135 static void McpsIndication(McpsIndication_t *mcpsIndication)
136 {
137 struct lorawan_downlink_cb *cb;
138
139 LOG_DBG("Received McpsIndication %d", mcpsIndication->McpsIndication);
140
141 if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
142 LOG_ERR("McpsIndication failed : %s",
143 lorawan_eventinfo2str(mcpsIndication->Status));
144 return;
145 }
146
147 /* Datarate can change as result of ADR command from server */
148 if (lorawan_adr_enable) {
149 datarate_observe(false);
150 }
151
152 /* Iterate over all registered downlink callbacks */
153 SYS_SLIST_FOR_EACH_CONTAINER(&dl_callbacks, cb, node) {
154 if ((cb->port == LW_RECV_PORT_ANY) ||
155 (cb->port == mcpsIndication->Port)) {
156 cb->cb(mcpsIndication->Port,
157 !!mcpsIndication->FramePending,
158 mcpsIndication->Rssi, mcpsIndication->Snr,
159 mcpsIndication->BufferSize,
160 mcpsIndication->Buffer);
161 }
162 }
163
164 last_mcps_indication_status = mcpsIndication->Status;
165 }
166
MlmeConfirm(MlmeConfirm_t * mlmeConfirm)167 static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm)
168 {
169 MibRequestConfirm_t mibGet;
170
171 LOG_DBG("Received MlmeConfirm (for MlmeRequest %d)",
172 mlmeConfirm->MlmeRequest);
173
174 if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
175 LOG_ERR("MlmeConfirm failed : %s",
176 lorawan_eventinfo2str(mlmeConfirm->Status));
177 goto out_sem;
178 }
179
180 switch (mlmeConfirm->MlmeRequest) {
181 case MLME_JOIN:
182 mibGet.Type = MIB_DEV_ADDR;
183 LoRaMacMibGetRequestConfirm(&mibGet);
184 LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr);
185 break;
186 case MLME_LINK_CHECK:
187 /* Not implemented */
188 LOG_INF("Link check not implemented yet!");
189 break;
190 default:
191 break;
192 }
193
194 out_sem:
195 last_mlme_confirm_status = mlmeConfirm->Status;
196 k_sem_give(&mlme_confirm_sem);
197 }
198
MlmeIndication(MlmeIndication_t * mlmeIndication)199 static void MlmeIndication(MlmeIndication_t *mlmeIndication)
200 {
201 LOG_DBG("Received MlmeIndication %d", mlmeIndication->MlmeIndication);
202 last_mlme_indication_status = mlmeIndication->Status;
203 }
204
lorawan_join_otaa(const struct lorawan_join_config * join_cfg)205 static LoRaMacStatus_t lorawan_join_otaa(
206 const struct lorawan_join_config *join_cfg)
207 {
208 MlmeReq_t mlme_req;
209 MibRequestConfirm_t mib_req;
210
211 mlme_req.Type = MLME_JOIN;
212 mlme_req.Req.Join.Datarate = default_datarate;
213 mlme_req.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA;
214
215 mib_req.Type = MIB_DEV_EUI;
216 mib_req.Param.DevEui = join_cfg->dev_eui;
217 LoRaMacMibSetRequestConfirm(&mib_req);
218
219 mib_req.Type = MIB_JOIN_EUI;
220 mib_req.Param.JoinEui = join_cfg->otaa.join_eui;
221 LoRaMacMibSetRequestConfirm(&mib_req);
222
223 mib_req.Type = MIB_NWK_KEY;
224 mib_req.Param.NwkKey = join_cfg->otaa.nwk_key;
225 LoRaMacMibSetRequestConfirm(&mib_req);
226
227 mib_req.Type = MIB_APP_KEY;
228 mib_req.Param.AppKey = join_cfg->otaa.app_key;
229 LoRaMacMibSetRequestConfirm(&mib_req);
230
231 return LoRaMacMlmeRequest(&mlme_req);
232 }
233
lorawan_join_abp(const struct lorawan_join_config * join_cfg)234 static LoRaMacStatus_t lorawan_join_abp(
235 const struct lorawan_join_config *join_cfg)
236 {
237 MibRequestConfirm_t mib_req;
238
239 mib_req.Type = MIB_ABP_LORAWAN_VERSION;
240 mib_req.Param.AbpLrWanVersion.Value = LORAWAN_ABP_VERSION;
241 LoRaMacMibSetRequestConfirm(&mib_req);
242
243 mib_req.Type = MIB_NET_ID;
244 mib_req.Param.NetID = 0;
245 LoRaMacMibSetRequestConfirm(&mib_req);
246
247 mib_req.Type = MIB_DEV_ADDR;
248 mib_req.Param.DevAddr = join_cfg->abp.dev_addr;
249 LoRaMacMibSetRequestConfirm(&mib_req);
250
251 mib_req.Type = MIB_F_NWK_S_INT_KEY;
252 mib_req.Param.FNwkSIntKey = join_cfg->abp.nwk_skey;
253 LoRaMacMibSetRequestConfirm(&mib_req);
254
255 mib_req.Type = MIB_S_NWK_S_INT_KEY;
256 mib_req.Param.SNwkSIntKey = join_cfg->abp.nwk_skey;
257 LoRaMacMibSetRequestConfirm(&mib_req);
258
259 mib_req.Type = MIB_NWK_S_ENC_KEY;
260 mib_req.Param.NwkSEncKey = join_cfg->abp.nwk_skey;
261 LoRaMacMibSetRequestConfirm(&mib_req);
262
263 mib_req.Type = MIB_APP_S_KEY;
264 mib_req.Param.AppSKey = join_cfg->abp.app_skey;
265 LoRaMacMibSetRequestConfirm(&mib_req);
266
267 mib_req.Type = MIB_NETWORK_ACTIVATION;
268 mib_req.Param.NetworkActivation = ACTIVATION_TYPE_ABP;
269 LoRaMacMibSetRequestConfirm(&mib_req);
270
271 return LORAMAC_STATUS_OK;
272 }
273
lorawan_join(const struct lorawan_join_config * join_cfg)274 int lorawan_join(const struct lorawan_join_config *join_cfg)
275 {
276 MibRequestConfirm_t mib_req;
277 LoRaMacStatus_t status;
278 int ret = 0;
279
280 k_mutex_lock(&lorawan_join_mutex, K_FOREVER);
281
282 /* MIB_PUBLIC_NETWORK powers on the radio and does not turn it off */
283 mib_req.Type = MIB_PUBLIC_NETWORK;
284 mib_req.Param.EnablePublicNetwork = true;
285 LoRaMacMibSetRequestConfirm(&mib_req);
286
287 if (join_cfg->mode == LORAWAN_ACT_OTAA) {
288 status = lorawan_join_otaa(join_cfg);
289 if (status != LORAMAC_STATUS_OK) {
290 LOG_ERR("OTAA join failed: %s",
291 lorawan_status2str(status));
292 ret = lorawan_status2errno(status);
293 goto out;
294 }
295
296 LOG_DBG("Network join request sent!");
297
298 /*
299 * We can be sure that the semaphore will be released for
300 * both success and failure cases after a specific time period.
301 * So we can use K_FOREVER and no need to check the return val.
302 */
303 k_sem_take(&mlme_confirm_sem, K_FOREVER);
304 if (last_mlme_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) {
305 ret = lorawan_eventinfo2errno(last_mlme_confirm_status);
306 goto out;
307 }
308 } else if (join_cfg->mode == LORAWAN_ACT_ABP) {
309 status = lorawan_join_abp(join_cfg);
310 if (status != LORAMAC_STATUS_OK) {
311 LOG_ERR("ABP join failed: %s",
312 lorawan_status2str(status));
313 ret = lorawan_status2errno(status);
314 goto out;
315 }
316 } else {
317 ret = -EINVAL;
318 }
319
320 out:
321 /* If the join succeeded */
322 if (ret == 0) {
323 /*
324 * Several regions (AS923, AU915, US915) overwrite the
325 * datarate as part of the join process. Reset the datarate
326 * to the value requested (and validated) in
327 * lorawan_set_datarate so that the MAC layer is aware of the
328 * set datarate for LoRaMacQueryTxPossible. This is only
329 * performed when ADR is disabled as it the network servers
330 * responsibility to increase datarates when ADR is enabled.
331 */
332 if (!lorawan_adr_enable) {
333 MibRequestConfirm_t mib_req;
334
335 mib_req.Type = MIB_CHANNELS_DATARATE;
336 mib_req.Param.ChannelsDatarate = default_datarate;
337 LoRaMacMibSetRequestConfirm(&mib_req);
338 }
339
340 /*
341 * Force a notification of the datarate on network join as the
342 * user may not have explicitly set a datarate to use.
343 */
344 datarate_observe(true);
345 }
346
347 k_mutex_unlock(&lorawan_join_mutex);
348 return ret;
349 }
350
lorawan_set_class(enum lorawan_class dev_class)351 int lorawan_set_class(enum lorawan_class dev_class)
352 {
353 LoRaMacStatus_t status;
354 MibRequestConfirm_t mib_req;
355
356 mib_req.Type = MIB_DEVICE_CLASS;
357
358 switch (dev_class) {
359 case LORAWAN_CLASS_A:
360 mib_req.Param.Class = CLASS_A;
361 break;
362 case LORAWAN_CLASS_B:
363 case LORAWAN_CLASS_C:
364 LOG_ERR("Device class not supported yet!");
365 return -ENOTSUP;
366 default:
367 return -EINVAL;
368 }
369
370 status = LoRaMacMibSetRequestConfirm(&mib_req);
371 if (status != LORAMAC_STATUS_OK) {
372 LOG_ERR("Failed to set device class: %s",
373 lorawan_status2str(status));
374 return lorawan_status2errno(status);
375 }
376
377 return 0;
378 }
379
lorawan_set_datarate(enum lorawan_datarate dr)380 int lorawan_set_datarate(enum lorawan_datarate dr)
381 {
382 MibRequestConfirm_t mib_req;
383
384 /* Bail out if using ADR */
385 if (lorawan_adr_enable) {
386 return -EINVAL;
387 }
388
389 /* Notify MAC layer of the requested datarate */
390 mib_req.Type = MIB_CHANNELS_DATARATE;
391 mib_req.Param.ChannelsDatarate = dr;
392 if (LoRaMacMibSetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
393 /* Datarate is invalid for this region */
394 return -EINVAL;
395 }
396
397 default_datarate = dr;
398 current_datarate = dr;
399
400 return 0;
401 }
402
lorawan_get_payload_sizes(uint8_t * max_next_payload_size,uint8_t * max_payload_size)403 void lorawan_get_payload_sizes(uint8_t *max_next_payload_size,
404 uint8_t *max_payload_size)
405 {
406 LoRaMacTxInfo_t txInfo;
407
408 /* QueryTxPossible cannot fail */
409 (void)LoRaMacQueryTxPossible(0, &txInfo);
410
411 *max_next_payload_size = txInfo.MaxPossibleApplicationDataSize;
412 *max_payload_size = txInfo.CurrentPossiblePayloadSize;
413 }
414
lorawan_get_min_datarate(void)415 enum lorawan_datarate lorawan_get_min_datarate(void)
416 {
417 MibRequestConfirm_t mibGet;
418
419 mibGet.Type = MIB_CHANNELS_MIN_TX_DATARATE;
420 LoRaMacMibGetRequestConfirm(&mibGet);
421
422 return mibGet.Param.ChannelsMinTxDatarate;
423 }
424
lorawan_enable_adr(bool enable)425 void lorawan_enable_adr(bool enable)
426 {
427 MibRequestConfirm_t mib_req;
428
429 if (enable != lorawan_adr_enable) {
430 lorawan_adr_enable = enable;
431
432 mib_req.Type = MIB_ADR;
433 mib_req.Param.AdrEnable = lorawan_adr_enable;
434 LoRaMacMibSetRequestConfirm(&mib_req);
435 }
436 }
437
lorawan_set_conf_msg_tries(uint8_t tries)438 int lorawan_set_conf_msg_tries(uint8_t tries)
439 {
440 MibRequestConfirm_t mib_req;
441
442 mib_req.Type = MIB_CHANNELS_NB_TRANS;
443 mib_req.Param.ChannelsNbTrans = tries;
444 if (LoRaMacMibSetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
445 return -EINVAL;
446 }
447
448 return 0;
449 }
450
lorawan_send(uint8_t port,uint8_t * data,uint8_t len,uint8_t flags)451 int lorawan_send(uint8_t port, uint8_t *data, uint8_t len, uint8_t flags)
452 {
453 LoRaMacStatus_t status;
454 McpsReq_t mcpsReq;
455 LoRaMacTxInfo_t txInfo;
456 int ret = 0;
457 bool empty_frame = false;
458
459 if (data == NULL) {
460 return -EINVAL;
461 }
462
463 k_mutex_lock(&lorawan_send_mutex, K_FOREVER);
464
465 status = LoRaMacQueryTxPossible(len, &txInfo);
466 if (status != LORAMAC_STATUS_OK) {
467 /*
468 * If status indicates an error, then most likely the payload
469 * has exceeded the maximum possible length for the current
470 * region and datarate. We can't do much other than sending
471 * empty frame in order to flush MAC commands in stack and
472 * hoping the application to lower the payload size for
473 * next try.
474 */
475 LOG_ERR("LoRaWAN Query Tx Possible Failed: %s",
476 lorawan_status2str(status));
477 empty_frame = true;
478 mcpsReq.Type = MCPS_UNCONFIRMED;
479 mcpsReq.Req.Unconfirmed.fBuffer = NULL;
480 mcpsReq.Req.Unconfirmed.fBufferSize = 0;
481 mcpsReq.Req.Unconfirmed.Datarate = DR_0;
482 } else {
483 if (flags & LORAWAN_MSG_CONFIRMED) {
484 mcpsReq.Type = MCPS_CONFIRMED;
485 mcpsReq.Req.Confirmed.fPort = port;
486 mcpsReq.Req.Confirmed.fBuffer = data;
487 mcpsReq.Req.Confirmed.fBufferSize = len;
488 mcpsReq.Req.Confirmed.Datarate = current_datarate;
489 } else {
490 /* default message type */
491 mcpsReq.Type = MCPS_UNCONFIRMED;
492 mcpsReq.Req.Unconfirmed.fPort = port;
493 mcpsReq.Req.Unconfirmed.fBuffer = data;
494 mcpsReq.Req.Unconfirmed.fBufferSize = len;
495 mcpsReq.Req.Unconfirmed.Datarate = current_datarate;
496 }
497 }
498
499 status = LoRaMacMcpsRequest(&mcpsReq);
500 if (status != LORAMAC_STATUS_OK) {
501 LOG_ERR("LoRaWAN Send failed: %s", lorawan_status2str(status));
502 ret = lorawan_status2errno(status);
503 goto out;
504 }
505
506 /*
507 * Always wait for MAC operations to complete.
508 * We can be sure that the semaphore will be released for
509 * both success and failure cases after a specific time period.
510 * So we can use K_FOREVER and no need to check the return val.
511 */
512 k_sem_take(&mcps_confirm_sem, K_FOREVER);
513 if (last_mcps_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) {
514 ret = lorawan_eventinfo2errno(last_mcps_confirm_status);
515 }
516
517 /*
518 * Indicate to the application that the provided data was not sent and
519 * it has to resend the packet.
520 */
521 if (empty_frame) {
522 ret = -EAGAIN;
523 }
524
525 out:
526 k_mutex_unlock(&lorawan_send_mutex);
527 return ret;
528 }
529
lorawan_set_battery_level_callback(uint8_t (* battery_lvl_cb)(void))530 int lorawan_set_battery_level_callback(uint8_t (*battery_lvl_cb)(void))
531 {
532 if (battery_lvl_cb == NULL) {
533 return -EINVAL;
534 }
535
536 getBatteryLevelUser = battery_lvl_cb;
537
538 return 0;
539 }
540
lorawan_register_downlink_callback(struct lorawan_downlink_cb * cb)541 void lorawan_register_downlink_callback(struct lorawan_downlink_cb *cb)
542 {
543 sys_slist_append(&dl_callbacks, &cb->node);
544 }
545
lorawan_register_dr_changed_callback(void (* cb)(enum lorawan_datarate))546 void lorawan_register_dr_changed_callback(void (*cb)(enum lorawan_datarate))
547 {
548 dr_change_cb = cb;
549 }
550
lorawan_start(void)551 int lorawan_start(void)
552 {
553 LoRaMacStatus_t status;
554 MibRequestConfirm_t mib_req;
555 GetPhyParams_t phy_params;
556 PhyParam_t phy_param;
557
558 status = LoRaMacStart();
559 if (status != LORAMAC_STATUS_OK) {
560 LOG_ERR("Failed to start the LoRaMAC stack: %s",
561 lorawan_status2str(status));
562 return -EINVAL;
563 }
564
565 /* Retrieve the default TX datarate for selected region */
566 phy_params.Attribute = PHY_DEF_TX_DR;
567 phy_param = RegionGetPhyParam(LORAWAN_REGION, &phy_params);
568 default_datarate = phy_param.Value;
569 current_datarate = default_datarate;
570
571 /* TODO: Move these to a proper location */
572 mib_req.Type = MIB_SYSTEM_MAX_RX_ERROR;
573 mib_req.Param.SystemMaxRxError = CONFIG_LORAWAN_SYSTEM_MAX_RX_ERROR;
574 LoRaMacMibSetRequestConfirm(&mib_req);
575
576 return 0;
577 }
578
lorawan_init(const struct device * dev)579 static int lorawan_init(const struct device *dev)
580 {
581 LoRaMacStatus_t status;
582
583 sys_slist_init(&dl_callbacks);
584
585 macPrimitives.MacMcpsConfirm = McpsConfirm;
586 macPrimitives.MacMcpsIndication = McpsIndication;
587 macPrimitives.MacMlmeConfirm = MlmeConfirm;
588 macPrimitives.MacMlmeIndication = MlmeIndication;
589 macCallbacks.GetBatteryLevel = getBatteryLevelLocal;
590 macCallbacks.GetTemperatureLevel = NULL;
591 macCallbacks.NvmDataChange = NULL;
592 macCallbacks.MacProcessNotify = OnMacProcessNotify;
593
594 status = LoRaMacInitialization(&macPrimitives, &macCallbacks,
595 LORAWAN_REGION);
596 if (status != LORAMAC_STATUS_OK) {
597 LOG_ERR("LoRaMacInitialization failed: %s",
598 lorawan_status2str(status));
599 return -EINVAL;
600 }
601
602 LOG_DBG("LoRaMAC Initialized");
603
604 return 0;
605 }
606
607 SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
608