loragw_usb.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. / _____) _ | |
  3. ( (____ _____ ____ _| |_ _____ ____| |__
  4. \____ \| ___ | (_ _) ___ |/ ___) _ \
  5. _____) ) ____| | | || |_| ____( (___| | | |
  6. (______/|_____)_|_|_| \__)_____)\____)_| |_|
  7. (C)2020 Semtech
  8. Description:
  9. Host specific functions to address the LoRa concentrator registers through
  10. a USB interface.
  11. Single-byte read/write and burst read/write.
  12. License: Revised BSD License, see LICENSE.TXT file include in the project
  13. */
  14. /* -------------------------------------------------------------------------- */
  15. /* --- DEPENDANCIES --------------------------------------------------------- */
  16. #include <stdint.h> /* C99 types */
  17. #include <stdbool.h> /* bool type */
  18. #include <stdio.h> /* printf fprintf */
  19. #include <stdlib.h> /* malloc free */
  20. #include <unistd.h> /* lseek, close */
  21. #include <fcntl.h> /* open */
  22. #include <string.h> /* strncmp */
  23. #include <errno.h> /* Error number definitions */
  24. #include <termios.h> /* POSIX terminal control definitions */
  25. #include "loragw_com.h"
  26. #include "loragw_usb.h"
  27. #include "loragw_mcu.h"
  28. #include "loragw_aux.h"
  29. /* -------------------------------------------------------------------------- */
  30. /* --- PRIVATE MACROS ------------------------------------------------------- */
  31. #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  32. #if DEBUG_COM == 1
  33. #define DEBUG_MSG(str) fprintf(stdout, str)
  34. #define DEBUG_PRINTF(fmt, args...) fprintf(stdout, fmt, args)
  35. #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_USB_ERROR;}
  36. #else
  37. #define DEBUG_MSG(str)
  38. #define DEBUG_PRINTF(fmt, args...)
  39. #define CHECK_NULL(a) if(a==NULL){return LGW_USB_ERROR;}
  40. #endif
  41. /* -------------------------------------------------------------------------- */
  42. /* --- PRIVATE CONSTANTS ---------------------------------------------------- */
  43. /* -------------------------------------------------------------------------- */
  44. /* --- PRIVATE TYPES -------------------------------------------------------- */
  45. /* -------------------------------------------------------------------------- */
  46. /* --- PRIVATE VARIABLES --------------------------------------------------- */
  47. static lgw_com_write_mode_t _lgw_write_mode = LGW_COM_WRITE_MODE_SINGLE;
  48. static uint8_t _lgw_spi_req_nb = 0;
  49. /* -------------------------------------------------------------------------- */
  50. /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
  51. int set_interface_attribs_linux(int fd, int speed) {
  52. struct termios tty;
  53. memset(&tty, 0, sizeof tty);
  54. /* Get current attributes */
  55. if (tcgetattr(fd, &tty) != 0) {
  56. DEBUG_PRINTF("ERROR: tcgetattr failed with %d - %s", errno, strerror(errno));
  57. return LGW_USB_ERROR;
  58. }
  59. cfsetospeed(&tty, speed);
  60. cfsetispeed(&tty, speed);
  61. /* Control Modes */
  62. tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; /* set 8-bit characters */
  63. tty.c_cflag |= CLOCAL; /* local connection, no modem control */
  64. tty.c_cflag |= CREAD; /* enable receiving characters */
  65. tty.c_cflag &= ~PARENB; /* no parity */
  66. tty.c_cflag &= ~CSTOPB; /* one stop bit */
  67. /* Input Modes */
  68. tty.c_iflag &= ~IGNBRK;
  69. tty.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
  70. /* Output Modes */
  71. tty.c_oflag &= ~IGNBRK;
  72. tty.c_oflag &= ~(IXON | IXOFF | IXANY | ICRNL);
  73. /* Local Modes */
  74. tty.c_lflag = 0;
  75. /* Settings for non-canonical mode */
  76. tty.c_cc[VMIN] = 0; /* non-blocking mode */
  77. tty.c_cc[VTIME] = 0; /* wait for (n * 0.1) seconds before returning */
  78. /* Set attributes */
  79. if (tcsetattr(fd, TCSANOW, &tty) != 0) {
  80. DEBUG_PRINTF("ERROR: tcsetattr failed with %d - %s", errno, strerror(errno));
  81. return LGW_USB_ERROR;
  82. }
  83. return LGW_USB_SUCCESS;
  84. }
  85. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  86. /* configure serial interface to be read blocking or not*/
  87. int set_blocking_linux(int fd, bool blocking) {
  88. struct termios tty;
  89. memset(&tty, 0, sizeof tty);
  90. /* Get current attributes */
  91. if (tcgetattr(fd, &tty) != 0) {
  92. DEBUG_PRINTF("ERROR: tcgetattr failed with %d - %s", errno, strerror(errno));
  93. return LGW_USB_ERROR;
  94. }
  95. tty.c_cc[VMIN] = (blocking == true) ? 1 : 0; /* set blocking or non-blocking mode */
  96. tty.c_cc[VTIME] = 1; /* wait for (n * 0.1) seconds before returning */
  97. /* Set attributes */
  98. if (tcsetattr(fd, TCSANOW, &tty) != 0) {
  99. DEBUG_PRINTF("ERROR: tcsetattr failed with %d - %s", errno, strerror(errno));
  100. return LGW_USB_ERROR;
  101. }
  102. return LGW_USB_SUCCESS;
  103. }
  104. /* -------------------------------------------------------------------------- */
  105. /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
  106. int lgw_usb_open(const char * com_path, void **com_target_ptr) {
  107. int *usb_device = NULL;
  108. char portname[50];
  109. int x;
  110. int fd;
  111. s_ping_info gw_info;
  112. s_status mcu_status;
  113. uint8_t data;
  114. ssize_t n;
  115. /*check input variables*/
  116. CHECK_NULL(com_target_ptr);
  117. usb_device = malloc(sizeof(int));
  118. if (usb_device == NULL) {
  119. DEBUG_MSG("ERROR : MALLOC FAIL\n");
  120. return LGW_USB_ERROR;
  121. }
  122. /* open tty port */
  123. sprintf(portname, "%s", com_path);
  124. fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
  125. if (fd < 0) {
  126. printf("ERROR: failed to open COM port %s - %s\n", portname, strerror(errno));
  127. } else {
  128. printf("INFO: Configuring TTY\n");
  129. x = set_interface_attribs_linux(fd, B115200);
  130. if (x != 0) {
  131. printf("ERROR: failed to configure COM port %s\n", portname);
  132. free(usb_device);
  133. return LGW_USB_ERROR;
  134. }
  135. /* flush tty port before setting it as blocking */
  136. printf("INFO: Flushing TTY\n");
  137. do {
  138. n = read(fd, &data, 1);
  139. if (n > 0) {
  140. printf("NOTE: flushing serial port (0x%2X)\n", data);
  141. }
  142. } while (n > 0);
  143. /* set tty port blocking */
  144. printf("INFO: Setting TTY in blocking mode\n");
  145. x = set_blocking_linux(fd, true);
  146. if (x != 0) {
  147. printf("ERROR: failed to configure COM port %s\n", portname);
  148. free(usb_device);
  149. return LGW_USB_ERROR;
  150. }
  151. *usb_device = fd;
  152. *com_target_ptr = (void*)usb_device;
  153. /* Initialize pseudo-random generator for MCU request ID */
  154. srand(0);
  155. /* Check MCU version (ignore first char of the received version (release/debug) */
  156. printf("INFO: Connect to MCU\n");
  157. if (mcu_ping(fd, &gw_info) != 0) {
  158. printf("ERROR: failed to ping the concentrator MCU\n");
  159. return LGW_USB_ERROR;
  160. }
  161. if (strncmp(gw_info.version + 1, mcu_version_string, sizeof mcu_version_string) != 0) {
  162. printf("WARNING: MCU version mismatch (expected:%s, got:%s)\n", mcu_version_string, gw_info.version);
  163. }
  164. printf("INFO: Concentrator MCU version is %s\n", gw_info.version);
  165. /* Get MCU status */
  166. if (mcu_get_status(fd, &mcu_status) != 0) {
  167. printf("ERROR: failed to get status from the concentrator MCU\n");
  168. return LGW_USB_ERROR;
  169. }
  170. printf("INFO: MCU status: sys_time:%u temperature:%.1foC\n", mcu_status.system_time_ms, mcu_status.temperature);
  171. /* Reset SX1302 */
  172. x = mcu_gpio_write(fd, 0, 1, 1); /* set PA1 : POWER_EN */
  173. x |= mcu_gpio_write(fd, 0, 2, 1); /* set PA2 : SX1302_RESET active */
  174. x |= mcu_gpio_write(fd, 0, 2, 0); /* unset PA2 : SX1302_RESET inactive */
  175. /* Reset SX1261 (LBT / Spectral Scan) */
  176. x |= mcu_gpio_write(fd, 0, 8, 0); /* set PA8 : SX1261_NRESET active */
  177. x |= mcu_gpio_write(fd, 0, 8, 1); /* unset PA8 : SX1261_NRESET inactive */
  178. if (x != 0) {
  179. printf("ERROR: failed to reset SX1302\n");
  180. free(usb_device);
  181. return LGW_USB_ERROR;
  182. }
  183. return LGW_USB_SUCCESS;
  184. }
  185. free(usb_device);
  186. return LGW_USB_ERROR;
  187. }
  188. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  189. /* SPI release */
  190. int lgw_usb_close(void *com_target) {
  191. int usb_device;
  192. int x, err = LGW_USB_SUCCESS;
  193. /* check input variables */
  194. CHECK_NULL(com_target);
  195. usb_device = *(int *)com_target;
  196. /* Reset SX1302 before closing */
  197. x = mcu_gpio_write(usb_device, 0, 1, 1); /* set PA1 : POWER_EN */
  198. x |= mcu_gpio_write(usb_device, 0, 2, 1); /* set PA2 : SX1302_RESET active */
  199. x |= mcu_gpio_write(usb_device, 0, 2, 0); /* unset PA2 : SX1302_RESET inactive */
  200. /* Reset SX1261 (LBT / Spectral Scan) */
  201. x |= mcu_gpio_write(usb_device, 0, 8, 0); /* set PA8 : SX1261_NRESET active */
  202. x |= mcu_gpio_write(usb_device, 0, 8, 1); /* unset PA8 : SX1261_NRESET inactive */
  203. if (x != 0) {
  204. printf("ERROR: failed to reset SX1302\n");
  205. err = LGW_USB_ERROR;
  206. }
  207. /* close file & deallocate file descriptor */
  208. x = close(usb_device);
  209. free(com_target);
  210. if (x != 0) {
  211. printf("ERROR: failed to close USB file\n");
  212. err = LGW_USB_ERROR;
  213. }
  214. /* determine return code */
  215. if (err != 0) {
  216. printf("ERROR: USB PORT FAILED TO CLOSE\n");
  217. return LGW_USB_ERROR;
  218. } else {
  219. DEBUG_MSG("Note: USB port closed\n");
  220. return LGW_USB_SUCCESS;
  221. }
  222. }
  223. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  224. /* Simple write */
  225. int lgw_usb_w(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t data) {
  226. return lgw_usb_wb(com_target, spi_mux_target, address, &data, 1);
  227. }
  228. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  229. /* Simple read */
  230. int lgw_usb_r(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data) {
  231. return lgw_usb_rb(com_target, spi_mux_target, address, data, 1);
  232. }
  233. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  234. /* Single Byte Read-Modify-Write */
  235. int lgw_usb_rmw(void *com_target, uint16_t address, uint8_t offs, uint8_t leng, uint8_t data) {
  236. int usb_device;
  237. uint8_t command_size = 6;
  238. uint8_t in_out_buf[command_size];
  239. int a = 0;
  240. /* check input variables */
  241. CHECK_NULL(com_target);
  242. usb_device = *(int *)com_target;
  243. DEBUG_PRINTF("==> RMW register @ 0x%04X, offs:%u leng:%u value:0x%02X\n", address, offs, leng, data);
  244. /* prepare frame to be sent */
  245. in_out_buf[0] = _lgw_spi_req_nb; /* Req ID */
  246. in_out_buf[1] = MCU_SPI_REQ_TYPE_READ_MODIFY_WRITE; /* Req type */
  247. in_out_buf[2] = (uint8_t)(address >> 8); /* Register address MSB */
  248. in_out_buf[3] = (uint8_t)(address >> 0); /* Register address LSB */
  249. in_out_buf[4] = ((1 << leng) - 1) << offs; /* Register bitmask */
  250. in_out_buf[5] = data << offs;
  251. if (_lgw_write_mode == LGW_COM_WRITE_MODE_BULK) {
  252. a = mcu_spi_store(in_out_buf, command_size);
  253. _lgw_spi_req_nb += 1;
  254. } else {
  255. a = mcu_spi_write(usb_device, in_out_buf, command_size);
  256. }
  257. /* determine return code */
  258. if (a != 0) {
  259. DEBUG_MSG("ERROR: USB WRITE FAILURE\n");
  260. return -1;
  261. } else {
  262. DEBUG_MSG("Note: USB write success\n");
  263. return 0;
  264. }
  265. }
  266. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  267. /* Burst (multiple-byte) write */
  268. int lgw_usb_wb(void *com_target, uint8_t spi_mux_target, uint16_t address, const uint8_t *data, uint16_t size) {
  269. int usb_device;
  270. uint16_t command_size = size + 8; /* 5 bytes: REQ metadata (MCU), 3 bytes: SPI header (SX1302) */
  271. uint8_t in_out_buf[command_size];
  272. int i;
  273. int a = 0;
  274. /* check input parameters */
  275. CHECK_NULL(com_target);
  276. CHECK_NULL(data);
  277. usb_device = *(int *)com_target;
  278. /* prepare command */
  279. /* Request metadata */
  280. in_out_buf[0] = _lgw_spi_req_nb; /* Req ID */
  281. in_out_buf[1] = MCU_SPI_REQ_TYPE_READ_WRITE; /* Req type */
  282. in_out_buf[2] = MCU_SPI_TARGET_SX1302; /* MCU -> SX1302 */
  283. in_out_buf[3] = (uint8_t)((size + 3) >> 8); /* payload size + spi_mux_target + address */
  284. in_out_buf[4] = (uint8_t)((size + 3) >> 0); /* payload size + spi_mux_target + address */
  285. /* RAW SPI frame */
  286. in_out_buf[5] = spi_mux_target; /* SX1302 -> RADIO_A or RADIO_B */
  287. in_out_buf[6] = 0x80 | ((address >> 8) & 0x7F);
  288. in_out_buf[7] = ((address >> 0) & 0xFF);
  289. for (i = 0; i < size; i++) {
  290. in_out_buf[i + 8] = data[i];
  291. }
  292. if (_lgw_write_mode == LGW_COM_WRITE_MODE_BULK) {
  293. a = mcu_spi_store(in_out_buf, command_size);
  294. _lgw_spi_req_nb += 1;
  295. } else {
  296. a = mcu_spi_write(usb_device, in_out_buf, command_size);
  297. }
  298. /* determine return code */
  299. if (a != 0) {
  300. DEBUG_MSG("ERROR: USB WRITE BURST FAILURE\n");
  301. return -1;
  302. } else {
  303. DEBUG_MSG("Note: USB write burst success\n");
  304. return 0;
  305. }
  306. }
  307. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  308. /* Burst (multiple-byte) read */
  309. int lgw_usb_rb(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data, uint16_t size) {
  310. int usb_device;
  311. uint16_t command_size = size + 9; /* 5 bytes: REQ metadata (MCU), 3 bytes: SPI header (SX1302), 1 byte: dummy*/
  312. uint8_t in_out_buf[command_size];
  313. int i;
  314. int a = 0;
  315. /* check input parameters */
  316. CHECK_NULL(com_target);
  317. CHECK_NULL(data);
  318. usb_device = *(int *)com_target;
  319. /* prepare command */
  320. /* Request metadata */
  321. in_out_buf[0] = 0; /* Req ID */
  322. in_out_buf[1] = MCU_SPI_REQ_TYPE_READ_WRITE; /* Req type */
  323. in_out_buf[2] = MCU_SPI_TARGET_SX1302; /* MCU -> SX1302 */
  324. in_out_buf[3] = (uint8_t)((size + 4) >> 8); /* payload size + spi_mux_target + address + dummy byte */
  325. in_out_buf[4] = (uint8_t)((size + 4) >> 0); /* payload size + spi_mux_target + address + dummy byte */
  326. /* RAW SPI frame */
  327. in_out_buf[5] = spi_mux_target; /* SX1302 -> RADIO_A or RADIO_B */
  328. in_out_buf[6] = 0x00 | ((address >> 8) & 0x7F);
  329. in_out_buf[7] = ((address >> 0) & 0xFF);
  330. in_out_buf[8] = 0x00; /* dummy byte */
  331. for (i = 0; i < size; i++) {
  332. in_out_buf[i + 9] = data[i];
  333. }
  334. if (_lgw_write_mode == LGW_COM_WRITE_MODE_BULK) {
  335. /* makes no sense to read in bulk mode, as we can't get the result */
  336. printf("ERROR: USB READ BURST FAILURE - bulk mode is enabled\n");
  337. return -1;
  338. } else {
  339. a = mcu_spi_write(usb_device, in_out_buf, command_size);
  340. }
  341. /* determine return code */
  342. if (a != 0) {
  343. DEBUG_MSG("ERROR: USB READ BURST FAILURE\n");
  344. return -1;
  345. } else {
  346. DEBUG_MSG("Note: USB read burst success\n");
  347. memcpy(data, in_out_buf + 9, size); /* remove the first bytes, keep only the payload */
  348. return 0;
  349. }
  350. }
  351. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  352. int lgw_usb_set_write_mode(lgw_com_write_mode_t write_mode) {
  353. if (write_mode >= LGW_COM_WRITE_MODE_UNKNOWN) {
  354. printf("ERROR: wrong write mode\n");
  355. return -1;
  356. }
  357. DEBUG_PRINTF("INFO: setting USB write mode to %s\n", (write_mode == LGW_COM_WRITE_MODE_SINGLE) ? "SINGLE" : "BULK");
  358. _lgw_write_mode = write_mode;
  359. return 0;
  360. }
  361. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  362. int lgw_usb_flush(void *com_target) {
  363. int usb_device;
  364. int a = 0;
  365. /* Check input parameters */
  366. CHECK_NULL(com_target);
  367. if (_lgw_write_mode != LGW_COM_WRITE_MODE_BULK) {
  368. printf("ERROR: %s: cannot flush in single write mode\n", __FUNCTION__);
  369. return -1;
  370. }
  371. /* Restore single mode after flushing */
  372. _lgw_write_mode = LGW_COM_WRITE_MODE_SINGLE;
  373. if (_lgw_spi_req_nb == 0) {
  374. printf("INFO: no SPI request to flush\n");
  375. return 0;
  376. }
  377. usb_device = *(int *)com_target;
  378. DEBUG_MSG("INFO: flushing USB write buffer\n");
  379. a = mcu_spi_flush(usb_device);
  380. if (a != 0) {
  381. printf("ERROR: Failed to flush USB write buffer\n");
  382. }
  383. /* reset the pending request number */
  384. _lgw_spi_req_nb = 0;
  385. return a;
  386. }
  387. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  388. uint16_t lgw_usb_chunk_size(void) {
  389. return (uint16_t)LGW_USB_BURST_CHUNK;
  390. }
  391. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  392. int lgw_usb_get_temperature(void *com_target, float * temperature) {
  393. int usb_device;
  394. s_status mcu_status;
  395. /* check input parameters */
  396. CHECK_NULL(com_target);
  397. CHECK_NULL(temperature);
  398. usb_device = *(int *)com_target;
  399. if (mcu_get_status(usb_device, &mcu_status) != 0) {
  400. printf("ERROR: failed to get status from the concentrator MCU\n");
  401. return -1;
  402. }
  403. DEBUG_PRINTF("INFO: temperature:%.1foC\n", mcu_status.temperature);
  404. *temperature = mcu_status.temperature;
  405. return 0;
  406. }
  407. /* --- EOF ------------------------------------------------------------------ */