osmocom-bb/src/host/layer23/src/mobile/primitives.c

227 lines
5.8 KiB
C

/* (C) 2017 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <inttypes.h>
#include <osmocom/bb/mobile/gsm322.h>
#include <osmocom/bb/mobile/primitives.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
static LLIST_HEAD(s_prims);
struct timer_closure {
struct llist_head entry;
struct mobile_prim_intf *intf;
struct osmo_timer_list timer;
uint64_t id;
};
struct mobile_prim_intf *mobile_prim_intf_alloc(struct osmocom_ms *ms)
{
struct mobile_prim_intf *intf;
intf = talloc_zero(ms, struct mobile_prim_intf);
intf->ms = ms;
INIT_LLIST_HEAD(&intf->timers);
llist_add_tail(&intf->entry, &s_prims);
return intf;
}
void mobile_prim_intf_free(struct mobile_prim_intf *intf)
{
struct timer_closure *timer, *tmp;
llist_for_each_entry_safe(timer, tmp, &intf->timers, entry) {
osmo_timer_del(&timer->timer);
llist_del(&timer->entry);
talloc_free(timer);
}
llist_del(&intf->entry);
talloc_free(intf);
}
struct mobile_prim *mobile_prim_alloc(unsigned int primitive, enum osmo_prim_operation op)
{
struct msgb *msg = msgb_alloc(1024, "Mobile Primitive");
struct mobile_prim *prim = (struct mobile_prim *) msgb_put(msg, sizeof(*prim));
osmo_prim_init(&prim->hdr, 0, primitive, op, msg);
msg->l2h = msg->tail;
return prim;
}
static void timer_expired_cb(void *_closure)
{
struct timer_closure *closure = _closure;
struct mobile_prim_intf *intf;
struct mobile_prim *prim;
prim = mobile_prim_alloc(PRIM_MOB_TIMER, PRIM_OP_INDICATION);
intf = closure->intf;
prim->u.timer.timer_id = closure->id;
llist_del(&closure->entry);
talloc_free(closure);
intf->indication(intf, prim);
}
static int create_timer(struct mobile_prim_intf *intf, struct mobile_timer_param *param)
{
struct timer_closure *closure;
LOGP(DPRIM, LOGL_DEBUG, "Creating timer with reference: "
"%" PRIu64 "\n", param->timer_id);
closure = talloc_zero(intf, struct timer_closure);
closure->intf = intf;
closure->id = param->timer_id;
closure->timer.cb = timer_expired_cb;
closure->timer.data = closure;
llist_add_tail(&closure->entry, &intf->timers);
osmo_timer_schedule(&closure->timer, param->seconds, 0);
return 0;
}
static void dispatch(struct osmocom_ms *ms, struct mobile_prim *prim)
{
struct mobile_prim_intf *intf, *tmp;
llist_for_each_entry_safe(intf, tmp, &s_prims, entry) {
if (intf->ms == ms)
intf->indication(intf, prim);
}
msgb_free(prim->hdr.msg);
}
void mobile_prim_ntfy_started(struct osmocom_ms *ms, bool started)
{
struct mobile_prim *prim = mobile_prim_alloc(PRIM_MOB_STARTED, PRIM_OP_INDICATION);
prim->u.started.started = started;
dispatch(ms, prim);
}
void mobile_prim_ntfy_shutdown(struct osmocom_ms *ms, int old_state, int new_state)
{
struct mobile_prim *prim = mobile_prim_alloc(PRIM_MOB_SHUTDOWN, PRIM_OP_INDICATION);
prim->u.shutdown.old_state = old_state;
prim->u.shutdown.new_state = new_state;
dispatch(ms, prim);
}
void mobile_prim_ntfy_sms_new(struct osmocom_ms *ms, struct gsm_sms *sms)
{
struct mobile_prim *prim = mobile_prim_alloc(PRIM_MOB_SMS, PRIM_OP_INDICATION);
prim->u.sms.sms = *sms;
dispatch(ms, prim);
}
void mobile_prim_ntfy_sms_status(struct osmocom_ms *ms, struct gsm_sms *sms, uint8_t cause)
{
struct mobile_prim *prim = mobile_prim_alloc(PRIM_MOB_SMS, PRIM_OP_INDICATION);
prim->u.sms.sms = *sms;
prim->u.sms.cause_valid = true;
prim->u.sms.cause = cause;
dispatch(ms, prim);
}
void mobile_prim_ntfy_mm_status(struct osmocom_ms *ms, int state, int substate, int mr_substate)
{
struct mobile_prim *prim = mobile_prim_alloc(PRIM_MOB_MM, PRIM_OP_INDICATION);
prim->u.mm.state = state;
prim->u.mm.substate = substate;
prim->u.mm.prev_substate = mr_substate;
dispatch(ms, prim);
}
static int cancel_timer(struct mobile_prim_intf *intf, struct mobile_timer_param *param)
{
struct timer_closure *closure;
llist_for_each_entry(closure, &intf->timers, entry) {
if (closure->id != param->timer_id)
continue;
LOGP(DPRIM, LOGL_DEBUG, "Canceling timer with reference: "
"%" PRIu64 "\n", param->timer_id);
osmo_timer_del(&closure->timer);
llist_del(&closure->entry);
talloc_free(closure);
return 0;
}
return -1;
}
static int send_sms(struct mobile_prim_intf *intf, struct mobile_sms_param *param)
{
struct gsm_sms *sms;
sms = sms_alloc();
*sms = param->sms;
/* Force a NUL at the end of the string */
param->sca[sizeof(param->sca) - 1] = '\0';
return gsm411_tx_sms_submit(intf->ms, param->sca, sms);
}
static int network_reselect(struct mobile_prim_intf *intf)
{
struct msgb *nmsg;
nmsg = gsm322_msgb_alloc(GSM322_EVENT_USER_RESEL);
if (!nmsg)
return -1;
gsm322_plmn_sendmsg(intf->ms, nmsg);
return 0;
}
int mobile_prim_intf_req(struct mobile_prim_intf *intf, struct mobile_prim *prim)
{
int rc = 0;
switch (OSMO_PRIM_HDR(&prim->hdr)) {
case OSMO_PRIM(PRIM_MOB_TIMER, PRIM_OP_REQUEST):
rc = create_timer(intf, &prim->u.timer);
break;
case OSMO_PRIM(PRIM_MOB_TIMER_CANCEL, PRIM_OP_REQUEST):
rc = cancel_timer(intf, &prim->u.timer);
break;
case OSMO_PRIM(PRIM_MOB_SMS, PRIM_OP_REQUEST):
rc = send_sms(intf, &prim->u.sms);
break;
case OSMO_PRIM(PRIM_MOB_NETWORK_RESELECT, PRIM_OP_REQUEST):
rc = network_reselect(intf);
break;
default:
LOGP(DPRIM, LOGL_ERROR, "Unknown primitive: %d\n", OSMO_PRIM_HDR(&prim->hdr));
break;
}
msgb_free(prim->hdr.msg);
return rc;
}