#include #include #include #include "v5x_internal.h" #include "v5x_protocol.h" #include "lapv5.h" #include "v5x_le_ctrl_fsm.h" #include "v5x_le_port_fsm.h" #include "v5x_le_pstn_fsm.h" #include "v5x_le_management.h" #include "logging.h" static LLIST_HEAD(v5_instances); struct v5x_instance *v5x_instance_alloc(void *ctx) { struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance); if (!v5i) return NULL; INIT_LLIST_HEAD(&v5i->interfaces); llist_add_tail(&v5i->list, &v5_instances); return v5i; } static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr) { v5ts->nr = nr; v5ts->link = v5l; v5ts->l3_address = l3_addr; } static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active) { v5cc->link = v5l; v5cc->ts = v5ts; v5cc->active = active; } static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id) { unsigned int i; v5l->id = id; v5l->interface = v5if; for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) { v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/); } /* primary c-channel must always be present */ v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true); } struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect, int (*ph_data_req_cb)(struct msgb *msg, void *cbdata)) { struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface); //struct v5x_link *v5l; struct v5x_c_channel *primary_c_chan; v5if->instance = v5i; v5if->dialect = dialect; /* primary link must alwasy be present */ v5x_link_init(&v5if->links[0], v5if, 0); v5if->primary_link = &v5if->links[0]; primary_c_chan = &v5if->primary_link->c_channel[0]; v5if->control.ctrl = v5x_le_ctrl_create(v5if, NULL, 0); if (!v5if->control.ctrl) return NULL; v5if->control.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "control"); v5if->control.c_chan = primary_c_chan; v5if->pstn.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "pstn"); v5if->pstn.c_chan = primary_c_chan; if (v5if->dialect == V5X_DIALECT_V52) { v5if->lcp.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "lcp"); v5if->lcp.c_chan = primary_c_chan; v5if->bcc.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "bcc"); v5if->bcc.c_chan = primary_c_chan; v5if->protection[0].li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "protection0"); v5if->protection[0].c_chan = primary_c_chan; //protection[1] ? } INIT_LLIST_HEAD(&v5if->user_ports); llist_add_tail(&v5if->list, &v5i->interfaces); return v5if; } void v5x_interface_free(struct v5x_interface *v5if) { struct v5x_user_port *v5up, *v5up2; llist_for_each_entry_safe(v5up, v5up2, &v5if->user_ports, list) v5x_user_port_destroy(v5up); if (v5if->control.ctrl) v5x_le_ctrl_destroy(v5if->control.ctrl); if (v5if->control.li) lapv5_instance_free(v5if->control.li); if (v5if->pstn.li) lapv5_instance_free(v5if->pstn.li); if (v5if->lcp.li) lapv5_instance_free(v5if->lcp.li); if (v5if->bcc.li) lapv5_instance_free(v5if->bcc.li); if (v5if->protection[0].li) lapv5_instance_free(v5if->protection[0].li); if (v5if->protection[1].li) lapv5_instance_free(v5if->protection[1].li); llist_del(&v5if->list); talloc_free(v5if); } struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type type, uint8_t ts1, uint8_t ts2) { struct v5x_user_port *v5up; int rc; if ((v5up = v5if->links[0].ts[ts1].v5up)) { LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts1); return NULL; } if (type == V5X_USER_TYPE_ISDN) { if ((v5up = v5if->links[0].ts[ts2].v5up)) { LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts2); return NULL; } } v5up = talloc_zero(v5if, struct v5x_user_port); if (!v5up) return NULL; llist_add_tail(&v5up->list, &v5if->user_ports); v5up->inst = v5if; v5up->nr = nr; v5up->type = type; /* assign ports */ if (v5if->dialect == V5X_DIALECT_V51) { OSMO_ASSERT(ts1 >= 1 && ts1 <= 31); v5if->links[0].ts[ts1].v5up = v5up; v5up->ts_nr[0] = ts1; if (type == V5X_USER_TYPE_ISDN) { OSMO_ASSERT(ts2 >= 1 && ts2 <= 31); v5if->links[0].ts[ts2].v5up = v5up; v5up->ts_nr[1] = ts2; } } v5up->ctrl = v5x_le_ctrl_create(NULL, v5up, nr); if (!v5up->ctrl) { LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n"); v5x_user_port_destroy(v5up); return NULL; } switch (type) { case V5X_USER_TYPE_ISDN: LOGP(DV5, LOGL_NOTICE, "Creating V5 ISDN user port with L3 addr %d\n", nr); sprintf(v5up->ifname, "isdn-%d", nr); OSMO_ASSERT(nr <= 8175); v5up->port_fi = v5x_le_port_isdn_create(v5up, nr); if (!v5up->port_fi) { LOGP(DV5, LOGL_ERROR, "Failed to create port control\n"); v5x_user_port_destroy(v5up); return NULL; } break; case V5X_USER_TYPE_PSTN: LOGP(DV5, LOGL_NOTICE, "Creating V5 PSTN user port with L3 addr %d\n", nr); sprintf(v5up->ifname, "pstn-%d", nr); OSMO_ASSERT(nr <= 32767); v5up->port_fi = v5x_le_port_pstn_create(v5up, nr); if (!v5up->port_fi) { LOGP(DV5, LOGL_ERROR, "Failed to create port control\n"); v5x_user_port_destroy(v5up); return NULL; } v5up->pstn.proto = v5x_le_pstn_create(v5up, nr); if (!v5up->pstn.proto) { LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n"); v5x_user_port_destroy(v5up); return NULL; } /* bring port into service */ v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked); break; } rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1); if (rc < 0) { LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n"); v5x_user_port_destroy(v5up); return NULL; } return v5up; } void v5x_user_port_destroy(struct v5x_user_port *v5up) { struct v5x_interface *v5if = v5up->inst; LOGP(DV5, LOGL_NOTICE, "Destroying V5 user port with L3 addr %d\n", v5up->nr); /* close fitst, because it sends messages */ ph_socket_exit(&v5up->ph_socket); /* unassign ports */ if (v5up->ts_nr[0]) v5if->links[v5up->link_nr[0]].ts[v5up->ts_nr[0]].v5up = NULL; if (v5up->ts_nr[1]) v5if->links[v5up->link_nr[1]].ts[v5up->ts_nr[1]].v5up = NULL; if (v5up->ctrl) v5x_le_ctrl_destroy(v5up->ctrl); if (v5up->port_fi) osmo_fsm_inst_free(v5up->port_fi); if (v5up->pstn.proto) v5x_le_pstn_destroy(v5up->pstn.proto); llist_del(&v5up->list); talloc_free(v5up); } struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn) { struct v5x_user_port *v5up; llist_for_each_entry(v5up, &v5if->user_ports, list) { if (v5up->nr == nr && is_isdn == (v5up->type == V5X_USER_TYPE_ISDN)) return v5up; } return NULL; }