1 /** @file
2  * @brief HL7800 modem public API header file.
3  *
4  * Allows an application to control the HL7800 modem.
5  *
6  * Copyright (c) 2020 Laird Connectivity
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_HL7800_H_
12 #define ZEPHYR_INCLUDE_DRIVERS_MODEM_HL7800_H_
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
18 #include <zephyr/types.h>
19 
20 #include <time.h>
21 
22 /* The size includes the NUL character, the strlen doesn't */
23 #define MDM_HL7800_REVISION_MAX_SIZE 29
24 #define MDM_HL7800_REVISION_MAX_STRLEN (MDM_HL7800_REVISION_MAX_SIZE - 1)
25 
26 #define MDM_HL7800_IMEI_SIZE 16
27 #define MDM_HL7800_IMEI_STRLEN (MDM_HL7800_IMEI_SIZE - 1)
28 
29 #define MDM_HL7800_ICCID_MAX_SIZE 21
30 #define MDM_HL7800_ICCID_MAX_STRLEN (MDM_HL7800_ICCID_MAX_SIZE - 1)
31 
32 #define MDM_HL7800_SERIAL_NUMBER_SIZE 15
33 #define MDM_HL7800_SERIAL_NUMBER_STRLEN (MDM_HL7800_SERIAL_NUMBER_SIZE - 1)
34 
35 #define MDM_HL7800_APN_MAX_SIZE 64
36 #define MDM_HL7800_APN_USERNAME_MAX_SIZE 65
37 #define MDM_HL7800_APN_PASSWORD_MAX_SIZE 65
38 
39 #define MDM_HL7800_APN_MAX_STRLEN (MDM_HL7800_APN_MAX_SIZE - 1)
40 #define MDM_HL7800_APN_USERNAME_MAX_STRLEN                                     \
41 	(MDM_HL7800_APN_USERNAME_MAX_SIZE - 1)
42 #define MDM_HL7800_APN_PASSWORD_MAX_STRLEN                                     \
43 	(MDM_HL7800_APN_PASSWORD_MAX_SIZE - 1)
44 
45 #define MDM_HL7800_APN_CMD_MAX_SIZE                                            \
46 	(32 + MDM_HL7800_APN_USERNAME_MAX_STRLEN +                             \
47 	 MDM_HL7800_APN_PASSWORD_MAX_STRLEN)
48 
49 #define MDM_HL7800_APN_CMD_MAX_STRLEN (MDM_HL7800_APN_CMD_MAX_SIZE - 1)
50 
51 struct mdm_hl7800_apn {
52 	char value[MDM_HL7800_APN_MAX_SIZE];
53 	char username[MDM_HL7800_APN_USERNAME_MAX_SIZE];
54 	char password[MDM_HL7800_APN_PASSWORD_MAX_SIZE];
55 };
56 
57 #define MDM_HL7800_LTE_BAND_STR_SIZE 21
58 #define MDM_HL7800_LTE_BAND_STRLEN (MDM_HL7800_LTE_BAND_STR_SIZE - 1)
59 
60 #define MDM_HL7800_OPERATOR_INDEX_SIZE 3
61 #define MDM_HL7800_OPERATOR_INDEX_STRLEN (MDM_HL7800_OPERATOR_INDEX_SIZE - 1)
62 
63 #define MDM_HL7800_IMSI_MIN_STR_SIZE 15
64 #define MDM_HL7800_IMSI_MAX_STR_SIZE 16
65 #define MDM_HL7800_IMSI_MAX_STRLEN (MDM_HL7800_IMSI_MAX_STR_SIZE - 1)
66 
67 #define MDM_HL7800_MODEM_FUNCTIONALITY_SIZE 2
68 #define MDM_HL7800_MODEM_FUNCTIONALITY_STRLEN                                  \
69 	(MDM_HL7800_MODEM_FUNCTIONALITY_SIZE - 1)
70 
71 #define MDM_HL7800_MAX_GPS_STR_SIZE 33
72 
73 #define MDM_HL7800_MAX_POLTE_USER_ID_SIZE 16
74 #define MDM_HL7800_MAX_POLTE_PASSWORD_SIZE 16
75 #define MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE 33
76 
77 /* Assign the server error code (location response) to a value
78  * that isn't used by locate response so that a single status
79  * callback can be used.
80  */
81 #define MDM_HL7800_POLTE_SERVER_ERROR 10
82 
83 #define MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR "AT%%POLTECMD=\"SERVERAUTH\",\"%s\",\"%s\""
84 
85 struct mdm_hl7800_site_survey {
86 	uint32_t earfcn; /* EUTRA Absolute Radio Frequency Channel Number */
87 	uint32_t cell_id;
88 	int rsrp;
89 	int rsrq;
90 };
91 
92 enum mdm_hl7800_radio_mode { MDM_RAT_CAT_M1 = 0, MDM_RAT_CAT_NB1 };
93 
94 enum mdm_hl7800_event {
95 	HL7800_EVENT_RESERVED = 0,
96 	HL7800_EVENT_NETWORK_STATE_CHANGE,
97 	HL7800_EVENT_APN_UPDATE,
98 	HL7800_EVENT_RSSI,
99 	HL7800_EVENT_SINR,
100 	HL7800_EVENT_STARTUP_STATE_CHANGE,
101 	HL7800_EVENT_SLEEP_STATE_CHANGE,
102 	HL7800_EVENT_RAT,
103 	HL7800_EVENT_BANDS,
104 	HL7800_EVENT_ACTIVE_BANDS,
105 	HL7800_EVENT_FOTA_STATE,
106 	HL7800_EVENT_FOTA_COUNT,
107 	HL7800_EVENT_REVISION,
108 	HL7800_EVENT_GPS,
109 	HL7800_EVENT_GPS_POSITION_STATUS,
110 	HL7800_EVENT_POLTE_REGISTRATION,
111 	HL7800_EVENT_POLTE_LOCATE_STATUS,
112 	HL7800_EVENT_POLTE,
113 	HL7800_EVENT_SITE_SURVEY,
114 	HL7800_EVENT_STATE,
115 };
116 
117 enum mdm_hl7800_state {
118 	HL7800_STATE_NOT_READY = 0,
119 	HL7800_STATE_INITIALIZED,
120 };
121 
122 enum mdm_hl7800_startup_state {
123 	HL7800_STARTUP_STATE_READY = 0,
124 	HL7800_STARTUP_STATE_WAITING_FOR_ACCESS_CODE,
125 	HL7800_STARTUP_STATE_SIM_NOT_PRESENT,
126 	HL7800_STARTUP_STATE_SIMLOCK,
127 	HL7800_STARTUP_STATE_UNRECOVERABLE_ERROR,
128 	HL7800_STARTUP_STATE_UNKNOWN,
129 	HL7800_STARTUP_STATE_INACTIVE_SIM
130 };
131 
132 enum mdm_hl7800_network_state {
133 	HL7800_NOT_REGISTERED = 0,
134 	HL7800_HOME_NETWORK,
135 	HL7800_SEARCHING,
136 	HL7800_REGISTRATION_DENIED,
137 	HL7800_OUT_OF_COVERAGE,
138 	HL7800_ROAMING,
139 	HL7800_EMERGENCY = 8,
140 	/* Laird defined states */
141 	HL7800_UNABLE_TO_CONFIGURE = 0xf0
142 };
143 
144 enum mdm_hl7800_sleep {
145 	HL7800_SLEEP_UNINITIALIZED = 0,
146 	HL7800_SLEEP_HIBERNATE,
147 	HL7800_SLEEP_AWAKE,
148 	HL7800_SLEEP_LITE_HIBERNATE,
149 	HL7800_SLEEP_SLEEP,
150 };
151 
152 enum mdm_hl7800_fota_state {
153 	HL7800_FOTA_IDLE,
154 	HL7800_FOTA_START,
155 	HL7800_FOTA_WIP,
156 	HL7800_FOTA_PAD,
157 	HL7800_FOTA_SEND_EOT,
158 	HL7800_FOTA_FILE_ERROR,
159 	HL7800_FOTA_INSTALL,
160 	HL7800_FOTA_REBOOT_AND_RECONFIGURE,
161 	HL7800_FOTA_COMPLETE,
162 };
163 
164 enum mdm_hl7800_functionality {
165 	HL7800_FUNCTIONALITY_MINIMUM = 0,
166 	HL7800_FUNCTIONALITY_FULL = 1,
167 	HL7800_FUNCTIONALITY_AIRPLANE = 4
168 };
169 
170 /* The modem reports state values as an enumeration and a string.
171  * GPS values are reported with a type of value and string.
172  */
173 struct mdm_hl7800_compound_event {
174 	uint8_t code;
175 	char *string;
176 };
177 
178 enum mdm_hl7800_gnss_event {
179 	HL7800_GNSS_EVENT_INVALID = -1,
180 	HL7800_GNSS_EVENT_INIT,
181 	HL7800_GNSS_EVENT_START,
182 	HL7800_GNSS_EVENT_STOP,
183 	HL7800_GNSS_EVENT_POSITION,
184 };
185 
186 enum mdm_hl7800_gnss_status {
187 	HL7800_GNSS_STATUS_INVALID = -1,
188 	HL7800_GNSS_STATUS_FAILURE,
189 	HL7800_GNSS_STATUS_SUCCESS,
190 };
191 
192 enum mdm_hl7800_gnss_position_event {
193 	HL7800_GNSS_POSITION_EVENT_INVALID = -1,
194 	HL7800_GNSS_POSITION_EVENT_LOST_OR_NOT_AVAILABLE_YET,
195 	HL7800_GNSS_POSITION_EVENT_PREDICTION_AVAILABLE,
196 	HL7800_GNSS_POSITION_EVENT_2D_AVAILABLE,
197 	HL7800_GNSS_POSITION_EVENT_3D_AVAILABLE,
198 	HL7800_GNSS_POSITION_EVENT_FIXED_TO_INVALID,
199 };
200 
201 enum mdm_hl7800_gps_string_types {
202 	HL7800_GPS_STR_LATITUDE,
203 	HL7800_GPS_STR_LONGITUDE,
204 	HL7800_GPS_STR_GPS_TIME,
205 	HL7800_GPS_STR_FIX_TYPE,
206 	HL7800_GPS_STR_HEPE,
207 	HL7800_GPS_STR_ALTITUDE,
208 	HL7800_GPS_STR_ALT_UNC,
209 	HL7800_GPS_STR_DIRECTION,
210 	HL7800_GPS_STR_HOR_SPEED,
211 	HL7800_GPS_STR_VER_SPEED
212 };
213 
214 /* status: negative errno, 0 on success
215  * user and password aren't valid if status is non-zero.
216  */
217 struct mdm_hl7800_polte_registration_event_data {
218 	int status;
219 	char *user;
220 	char *password;
221 };
222 
223 /* status: negative errno, 0 on success, non-zero error code
224  * Data is not valid if status is non-zero.
225  */
226 struct mdm_hl7800_polte_location_data {
227 	uint32_t timestamp;
228 	int status;
229 	char latitude[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
230 	char longitude[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
231 	char confidence_in_meters[MDM_HL7800_MAX_POLTE_LOCATION_STR_SIZE];
232 };
233 
234 /**
235  * event - The type of event
236  * event_data - Pointer to event specific data structure:
237  * HL7800_EVENT_NETWORK_STATE_CHANGE - mdm_hl7800_compound_event
238  * HL7800_EVENT_APN_UPDATE - struct mdm_hl7800_apn
239  * HL7800_EVENT_RSSI - int
240  * HL7800_EVENT_SINR - int
241  * HL7800_EVENT_STARTUP_STATE_CHANGE - mdm_hl7800_compound_event
242  * HL7800_EVENT_SLEEP_STATE_CHANGE - mdm_hl7800_compound_event
243  * HL7800_EVENT_RAT - int
244  * HL7800_EVENT_BANDS - string
245  * HL7800_EVENT_ACTIVE_BANDS - string
246  * HL7800_EVENT_FOTA_STATE - mdm_hl7800_compound_event
247  * HL7800_EVENT_FOTA_COUNT - uint32_t
248  * HL7800_EVENT_REVISION - string
249  * HL7800_EVENT_GPS - mdm_hl7800_compound_event
250  * HL7800_EVENT_GPS_POSITION_STATUS int
251  * HL7800_EVENT_POLTE_REGISTRATION mdm_hl7800_polte_registration_event_data
252  * HL7800_EVENT_POLTE mdm_hl7800_polte_location_data
253  * HL7800_EVENT_POLTE_LOCATE_STATUS int
254  * HL7800_EVENT_SITE_SURVEY mdm_hl7800_site_survey
255  * HL7800_EVENT_STATE mdm_hl7800_compound_event
256  */
257 typedef void (*mdm_hl7800_event_callback_t)(enum mdm_hl7800_event event,
258 					    void *event_data);
259 
260 struct mdm_hl7800_callback_agent {
261 	sys_snode_t node;
262 	mdm_hl7800_event_callback_t event_callback;
263 };
264 
265 /**
266  * @brief Power off the HL7800
267  *
268  * @return int32_t 0 for success
269  */
270 int32_t mdm_hl7800_power_off(void);
271 
272 /**
273  * @brief Reset the HL7800 (and allow it to reconfigure).
274  *
275  * @return int32_t >= 0 for success, < 0 for failure
276  */
277 int32_t mdm_hl7800_reset(void);
278 
279 /**
280  * @brief Control the wake signals to the HL7800.
281  * @note this API should only be used for debug purposes.
282  *
283  * @param awake True to keep the HL7800 awake, False to allow sleep
284  */
285 void mdm_hl7800_wakeup(bool awake);
286 
287 /**
288  * @brief Send an AT command to the HL7800.
289  * @note This API should only be used for debug purposes.
290  *   It is possible to break the driver using this API.
291  *
292  * @param data AT command string
293  * @param resp_timeout Timeout in seconds to wait for the response
294  * @param resp Pointer to the response buffer. This can be NULL to ignore the response.
295  * @param resp_len Input: length of the response buffer, Output: length of the response.
296  *   This can be NULL.
297  * @return int32_t 0 for success
298  */
299 int32_t mdm_hl7800_send_at_cmd(const uint8_t *data, uint8_t resp_timeout, char *resp,
300 			       uint16_t *resp_len);
301 
302 /**
303  * @brief Get the signal quality of the HL7800.
304  * If CONFIG_MODEM_HL7800_RSSI_RATE_SECONDS is non-zero, then
305  * this function returns the value from the last periodic read.
306  * If CONFIG_MODEM_HL7800_RSSI_RATE_SECONDS is 0, then this
307  * may cause the modem to be woken so that the values can be queried.
308  *
309  * @param rsrp Reference Signals Received Power (dBm)
310  *             Range = -140 dBm to -44 dBm
311  * @param sinr Signal to Interference plus Noise Ratio (dB)
312  *             Range = -128 dB to 40 dB
313  */
314 void mdm_hl7800_get_signal_quality(int *rsrp, int *sinr);
315 
316 /**
317  * @brief Get the SIM card ICCID
318  *
319  */
320 char *mdm_hl7800_get_iccid(void);
321 
322 /**
323  * @brief Get the HL7800 serial number
324  *
325  */
326 char *mdm_hl7800_get_sn(void);
327 
328 /**
329  * @brief Get the HL7800 IMEI
330  *
331  */
332 char *mdm_hl7800_get_imei(void);
333 
334 /**
335  * @brief Get the HL7800 firmware version
336  *
337  */
338 char *mdm_hl7800_get_fw_version(void);
339 
340 /**
341  * @brief Get the IMSI
342  *
343  */
344 char *mdm_hl7800_get_imsi(void);
345 
346 /**
347  * @brief Update the Access Point Name in the modem.
348  *
349  * @retval 0 on success, negative on failure.
350  */
351 int32_t mdm_hl7800_update_apn(char *access_point_name);
352 
353 /**
354  * @brief Update the Radio Access Technology (mode).
355  *
356  * @retval 0 on success, negative on failure.
357  */
358 int32_t mdm_hl7800_update_rat(enum mdm_hl7800_radio_mode value);
359 
360 /**
361  * @retval true if RAT value is valid
362  */
363 bool mdm_hl7800_valid_rat(uint8_t value);
364 
365 /**
366  * @brief Register a function that is called when a modem event occurs.
367  * Multiple users registering for callbacks is supported.
368  *
369  * @param agent event callback agent
370  *
371  * @retval 0 on success, negative on failure
372  */
373 int mdm_hl7800_register_event_callback(struct mdm_hl7800_callback_agent *agent);
374 
375 /**
376  * @brief Unregister a callback event function
377  *
378  * @param agent event callback agent
379  *
380  * @retval 0 on success, negative on failure
381  */
382 int mdm_hl7800_unregister_event_callback(struct mdm_hl7800_callback_agent *agent);
383 
384 /**
385  * @brief Force modem module to generate status events.
386  *
387  * @note This can be used to get the current state when a module initializes
388  * later than the modem.
389  */
390 void mdm_hl7800_generate_status_events(void);
391 
392 /**
393  * @brief Get the local time from the modem's real time clock.
394  *
395  * @param tm time structure
396  * @param offset The amount the local time is offset from GMT/UTC in seconds.
397  * @return int32_t 0 if successful
398  */
399 int32_t mdm_hl7800_get_local_time(struct tm *tm, int32_t *offset);
400 
401 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
402 /**
403  * @brief Update the HL7800 via XMODEM protocol.  During the firmware update
404  * no other modem functions will be available.
405  *
406  * @param file_path Absolute path of the update file
407  *
408  * @param 0 if successful
409  */
410 int32_t mdm_hl7800_update_fw(char *file_path);
411 #endif
412 
413 /**
414  * @brief Read the operator index from the modem.
415  *
416  * @retval negative error code, 0 on success
417  */
418 int32_t mdm_hl7800_get_operator_index(void);
419 
420 /**
421  * @brief Get modem functionality
422  *
423  * @return int32_t negative errno on failure, else mdm_hl7800_functionality
424  */
425 int32_t mdm_hl7800_get_functionality(void);
426 
427 /**
428  * @brief Set airplane, normal, or reduced functionality mode.
429  * Airplane mode persists when reset.
430  *
431  * @note Boot functionality is also controlled by Kconfig
432  * MODEM_HL7800_BOOT_IN_AIRPLANE_MODE.
433  *
434  * @param mode
435  * @return int32_t negative errno, 0 on success
436  */
437 int32_t mdm_hl7800_set_functionality(enum mdm_hl7800_functionality mode);
438 
439 /**
440  * @brief When rate is non-zero: Put modem into Airplane mode. Enable GPS and
441  * generate HL7800_EVENT_GPS events.
442  * When zero: Disable GPS and put modem into normal mode.
443  *
444  * @note Airplane mode isn't cleared when the modem is reset.
445  *
446  * @param rate in seconds to query location
447  * @return int32_t negative errno, 0 on success
448  */
449 int32_t mdm_hl7800_set_gps_rate(uint32_t rate);
450 
451 /**
452  * @brief Register modem/SIM with polte.io
453  *
454  * @note It takes around 30 seconds for HL7800_EVENT_POLTE_REGISTRATION to
455  * be generated.  If the applications saves the user and password
456  * information into non-volatile memory, then this command
457  * only needs to be run once.
458  *
459  * @return int32_t negative errno, 0 on success
460  */
461 int32_t mdm_hl7800_polte_register(void);
462 
463 /**
464  * @brief Enable PoLTE.
465  *
466  * @param user from polte.io or register command callback
467  * @param password from polte.io register command callback
468  * @return int32_t negative errno, 0 on success
469  */
470 int32_t mdm_hl7800_polte_enable(char *user, char *password);
471 
472 /**
473  * @brief Locate device using PoLTE.
474  *
475  * @note The first HL7800_EVENT_POLTE_LOCATE_STATUS event indicates
476  * the status of issuing the locate command. The second event
477  * requires 20-120 seconds to be generated and it contains the
478  * location information (or indicates server failure).
479  *
480  * @return int32_t negative errno, 0 on success
481  */
482 int32_t mdm_hl7800_polte_locate(void);
483 
484 /**
485  * @brief Perform a site survey.  This command may return different values
486  * each time it is run (depending on what is in range).
487  *
488  * HL7800_EVENT_SITE_SURVEY is generated for each response received from modem.
489  *
490  * @retval negative error code, 0 on success
491  */
492 int32_t mdm_hl7800_perform_site_survey(void);
493 
494 /**
495  * @brief Set desired sleep level. Requires MODEM_HL7800_LOW_POWER_MODE
496  *
497  * @param level (sleep, lite hibernate, or hibernate)
498  * @return int negative errno, 0 on success
499  */
500 int mdm_hl7800_set_desired_sleep_level(enum mdm_hl7800_sleep level);
501 
502 /**
503  * @brief Allows mapping of WAKE_UP signal
504  * to a user accessible test point on the development board.
505  *
506  * @param func to be called when application requests modem wake/sleep.
507  * The state parameter of the callback is 1 when modem should stay awake,
508  * 0 when modem can sleep
509  */
510 void mdm_hl7800_register_wake_test_point_callback(void (*func)(int state));
511 
512 /**
513  * @brief Allows mapping of P1.12_GPIO6 signal
514  * to a user accessible test point on the development board.
515  *
516  * @param func to be called when modem wakes/sleeps is sleep level is
517  * hibernate or lite hibernate.
518  * The state parameter of the callback follows gpio_pin_get definitions,
519  * but will default high if there is an error reading pin
520  */
521 void mdm_hl7800_register_gpio6_callback(void (*func)(int state));
522 
523 /**
524  * @brief Allows mapping of UART1_CTS signal
525  * to a user accessible test point on the development board.
526  *
527  * @param func to be called when CTS state changes if sleep level is sleep.
528  * The state parameter of the callback follows gpio_pin_get definitions,
529  * but will default low if there is an error reading pin
530  */
531 void mdm_hl7800_register_cts_callback(void (*func)(int state));
532 
533 /**
534  * @brief Set the bands available for the LTE connection.
535  * NOTE: This will cause the modem to reboot. This call returns before the reboot.
536  *
537  * @param bands Band bitmap in hexadecimal format without the 0x prefix.
538  * Leading 0's for the value can be omitted.
539  *
540  * @return int32_t negative errno, 0 on success
541  */
542 int32_t mdm_hl7800_set_bands(const char *bands);
543 
544 /**
545  * @brief Set the log level for the modem.
546  *
547  * @note It cannot be set higher than CONFIG_MODEM_LOG_LEVEL.
548  * If debug level is desired, then it must be compiled with that level.
549  *
550  * @param level 0 (None) - 4 (Debug)
551  *
552  * @retval new log level
553  */
554 uint32_t mdm_hl7800_log_filter_set(uint32_t level);
555 
556 #ifdef __cplusplus
557 }
558 #endif
559 
560 #endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_HL7800_H_ */
561