+/* read from OpenBSC via MNCC socket */
+static int mncc_fd_read(struct lcr_fd *lfd, void *inst, int idx)
+{
+ struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst;
+ int rc;
+ static char buf[sizeof(struct gsm_mncc)+1024];
+ struct gsm_mncc *mncc_prim = (struct gsm_mncc *) buf;
+ struct gsm_mncc_hello *hello = (struct gsm_mncc_hello *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ rc = recv(lfd->fd, buf, sizeof(buf), 0);
+ if (rc == 0)
+ return mncc_fd_close(lcr_gsm, lfd);
+ if (rc < 0)
+ return rc;
+
+ /* TODO: size check? */
+ switch (mncc_prim->msg_type) {
+ case MNCC_SOCKET_HELLO:
+ if (hello->version != MNCC_SOCK_VERSION) {
+ PERROR("MNCC version different. BSC version is %u\n", hello->version);
+ mncc_fd_close(lcr_gsm, lfd);
+ return 0;
+ }
+ if (hello->mncc_size != sizeof(struct gsm_mncc)) {
+ PERROR("MNCC gsm_mncc size differs: %u %u\n",
+ hello->mncc_size, sizeof(struct gsm_mncc));
+ mncc_fd_close(lcr_gsm, lfd);
+ return 0;
+ }
+ if (hello->data_frame_size != sizeof(struct gsm_data_frame)) {
+ PERROR("MNCC gsm_mncc size differs: %u %u\n",
+ hello->data_frame_size, sizeof(struct gsm_data_frame));
+ mncc_fd_close(lcr_gsm, lfd);
+ return 0;
+ }
+
+#define CHECK_OFFSET(hello, field, lcr_gsm, lfd) \
+ if (hello->field ##_offset != __builtin_offsetof(struct gsm_mncc, field)) { \
+ PERROR("MNCC gsm_mncc offset of %s is %u %u\n", \
+ #field, hello->field ##_offset, \
+ __builtin_offsetof(struct gsm_mncc, field)); \
+ mncc_fd_close(lcr_gsm, lfd); \
+ return 0; \
+ }
+
+ CHECK_OFFSET(hello, called, lcr_gsm, lfd);
+ CHECK_OFFSET(hello, signal, lcr_gsm, lfd);
+ CHECK_OFFSET(hello, emergency, lcr_gsm, lfd);
+ CHECK_OFFSET(hello, lchan_type, lcr_gsm, lfd);
+#undef CHECK_OFFSET
+
+ break;
+ }
+
+ /* Hand the MNCC message into LCR */
+ switch (lcr_gsm->type) {
+#ifdef WITH_GSM_BS
+ case LCR_GSM_TYPE_NETWORK:
+ return message_bsc(lcr_gsm, mncc_prim->msg_type, mncc_prim);
+#endif
+#ifdef WITH_GSM_MS
+ case LCR_GSM_TYPE_MS:
+ return message_ms(lcr_gsm, mncc_prim->msg_type, mncc_prim);
+#endif
+ default:
+ return 0;
+ }