aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-01-18 18:01:49 +0000
committerHarald Welte <laforge@gnumonks.org>2009-01-18 18:01:49 +0000
commit4724f99d194a8f0f46c368f5b4e86613daace67e (patch)
treee052ea9595ea19237517ceef3730ef57d5df8a3a /src
parent978cb42911750915e8501b9b51661da7278188ea (diff)
Implementation of GSM12.21 LOAD SOFTWARE (BS11 dialect thereof)
Diffstat (limited to 'src')
-rw-r--r--src/abis_nm.c314
1 files changed, 300 insertions, 14 deletions
diff --git a/src/abis_nm.c b/src/abis_nm.c
index 25c5f1c7e..ea08489fc 100644
--- a/src/abis_nm.c
+++ b/src/abis_nm.c
@@ -1,7 +1,7 @@
/* GSM Network Management (OML) messages on the A-bis interface
* 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
-/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -23,8 +23,12 @@
#include <errno.h>
+#include <unistd.h>
#include <stdio.h>
+#include <fcntl.h>
+
#include <sys/types.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <openbsc/gsm_data.h>
@@ -52,6 +56,20 @@ static const enum abis_nm_msgtype no_ack_nack[] = {
NM_MT_START_MEAS,
};
+/* Messages related to software load */
+static const enum abis_nm_msgtype sw_load_msgs[] = {
+ NM_MT_LOAD_INIT_ACK,
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_SEG_ACK,
+ NM_MT_LOAD_ABORT,
+ NM_MT_LOAD_END_ACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ,
+ NM_MT_ACTIVATE_SW_ACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_SW_ACTIVATED_REP,
+};
+
/* Attributes that the BSC can set, not only get, according to Section 9.4 */
static const enum abis_nm_attr nm_att_settable[] = {
NM_ATT_ADD_INFO,
@@ -144,6 +162,8 @@ int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
return _abis_nm_sendmsg(msg);
}
+static int abis_nm_rcvmsg_sw(struct msgb *mb);
+
/* Receive a OML NM Message from BTS */
static int abis_nm_rcvmsg_fom(struct msgb *mb)
{
@@ -157,6 +177,9 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
return 0;
}
+ if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs)))
+ return abis_nm_rcvmsg_sw(mb);
+
#if 0
/* check if last message is to be acked */
if (is_ack_nack(nmh->last_msgtype)) {
@@ -256,33 +279,296 @@ void abis_nm_fini(struct abis_nm_h *nmh)
* based state machine.
*/
-/* 6.2 Software Load: FIXME */
-
+/* 6.2 Software Load: */
+enum sw_state {
+ SW_STATE_NONE,
+ SW_STATE_WAIT_INITACK,
+ SW_STATE_WAIT_SEGACK,
+ SW_STATE_WAIT_ENDACK,
+ SW_STATE_WAIT_ACTACK,
+ SW_STATE_ERROR,
+};
-#if 0
struct abis_nm_sw {
+ struct gsm_bts *bts;
/* this will become part of the SW LOAD INITIATE */
u_int8_t obj_class;
u_int8_t obj_instance[3];
- u_int8_t sw_description[255];
- u_int16_t window_size;
- /* the actual data that is to be sent subsequently */
- unsigned char *sw;
- unsigned int sw_len;
+
+ u_int8_t file_id[255];
+ u_int8_t file_id_len;
+
+ u_int8_t file_version[255];
+ u_int8_t file_version_len;
+
+ u_int8_t window_size;
+ u_int8_t seg_in_window;
+
+ int fd;
+ FILE *stream;
+ enum sw_state state;
};
-/* Load the specified software into the BTS */
-int abis_nm_sw_load(struct abis_nm_h *h, struct abis_nm_sw *sw);
+static struct abis_nm_sw g_sw;
+
+/* 6.2.1 / 8.3.1: Load Data Initiate */
+static int sw_load_init(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 3*2 + sw->file_id_len + sw->file_version_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+ msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+/* 6.2.2 / 8.3.2 Load Data Segment */
+static int sw_load_segment(struct abis_nm_sw *sw)
{
- /* FIXME: Implementation */
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ char seg_buf[256];
+ char *line_buf = seg_buf+2;
+ u_int8_t len;
+ int rc;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ /* FIXME: this is BS11 specific format */
+ rc = fscanf(sw->stream, "%s\r\n", line_buf);
+ if (rc < 1) {
+ perror("fscanf reading segment");
+ return -EINVAL;
+ }
+ seg_buf[0] = 0x00;
+ seg_buf[1] = sw->seg_in_window++;
+
+ msgb_tlv_put(msg, NM_ATT_FILE_DATA, 2+strlen(line_buf),
+ (u_int8_t *)seg_buf);
+ /* BS11 wants CR + LF in excess of the TLV length !?! */
+ msgb_tv_put(msg, 0x0d, 0x0a);
+
+ /* we only now know the exact length for the OM hdr */
+ len = 2+strlen(line_buf)+2;
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ return abis_nm_sendmsg(sw->bts, msg);
}
+/* 6.2.4 / 8.3.4 Load Data End */
+static int sw_load_end(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
/* Activate the specified software into the BTS */
-int abis_nm_sw_activate(struct abis_nm_h *h)
+static int sw_activate(struct abis_nm_sw *sw)
{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
+{
+ char file_id[12+1];
+ char file_version[80+1];
+ int rc;
+
+ sw->fd = open(fname, O_RDONLY);
+ if (sw->fd < 0)
+ return sw->fd;
+
+ switch (sw->bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ sw->stream = fdopen(sw->fd, "r");
+ if (!sw->stream) {
+ perror("fdopen");
+ return -1;
+ }
+ /* read first line and parse file ID and VERSION */
+ rc = fscanf(sw->stream, "@(@)%12s:%80s\r\n",
+ file_id, file_version);
+ if (rc != 2) {
+ perror("parsing header line of software file");
+ return -1;
+ }
+ strcpy((char *)sw->file_id, file_id);
+ sw->file_id_len = strlen(file_id);
+ strcpy((char *)sw->file_version, file_version);
+ sw->file_version_len = strlen(file_version);
+ /* rewind to start of file */
+ fseek(sw->stream, 0, SEEK_SET);
+ break;
+ default:
+ /* We don't know how to treat them yet */
+ close(sw->fd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sw_close_file(struct abis_nm_sw *sw)
+{
+ switch (sw->bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ fclose(sw->stream);
+ break;
+ default:
+ close(sw->fd);
+ break;
+ }
+}
+
+/* Fill the window */
+static int sw_fill_window(struct abis_nm_sw *sw)
+{
+ int rc;
+
+ while (sw->seg_in_window < sw->window_size) {
+ rc = sw_load_segment(sw);
+ if (rc < 0)
+ return rc;
+ if (rc == 1) {
+ sw->state = SW_STATE_WAIT_ENDACK;
+ return sw_load_end(sw);
+ }
+ }
+ return 0;
+}
+
+/* callback function from abis_nm_rcvmsg() handler */
+static int abis_nm_rcvmsg_sw(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ int rc = -1;
+ struct abis_nm_sw *sw = &g_sw;
+ enum sw_state old_state = sw->state;
+
+ DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type);
+
+ switch (sw->state) {
+ case SW_STATE_WAIT_INITACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_INIT_ACK:
+ /* fill window with segments */
+ rc = sw_fill_window(sw);
+ sw->state = SW_STATE_WAIT_SEGACK;
+ break;
+ case NM_MT_LOAD_INIT_NACK:
+ sw->state = SW_STATE_ERROR;
+ break;
+ }
+ break;
+ case SW_STATE_WAIT_SEGACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_SEG_ACK:
+ sw->seg_in_window = 0;
+ /* fill window with more segments */
+ rc = sw_fill_window(sw);
+ sw->state = SW_STATE_WAIT_SEGACK;
+ break;
+ }
+ break;
+ case SW_STATE_WAIT_ENDACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_END_ACK:
+ sw_close_file(sw);
+ /* send activate request */
+ sw->state = SW_STATE_WAIT_ACTACK;
+ rc = sw_activate(sw);
+ break;
+ case NM_MT_LOAD_END_NACK:
+ sw->state = SW_STATE_ERROR;
+ break;
+ }
+ case SW_STATE_WAIT_ACTACK:
+ switch (foh->msg_type) {
+ case NM_MT_ACTIVATE_SW_ACK:
+ /* we're done */
+ sw->state = SW_STATE_NONE;
+ rc = 0;
+ DEBUGP(DMM, "DONE!\n");
+ break;
+ case NM_MT_ACTIVATE_SW_NACK:
+ sw->state = SW_STATE_ERROR;
+ break;
+ }
+ case SW_STATE_NONE:
+ case SW_STATE_ERROR:
+ break;
+ }
+
+ if (rc)
+ fprintf(stderr, "unexpected NM MT 0x%02x in state %u -> %u\n",
+ foh->msg_type, old_state, sw->state);
+
+ return rc;
+}
+
+/* Load the specified software into the BTS */
+int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+ u_int8_t win_size)
+{
+ struct abis_nm_sw *sw = &g_sw;
+ int rc;
+
+ if (sw->state != SW_STATE_NONE)
+ return -EBUSY;
+
+ sw->bts = bts;
+ sw->obj_class = NM_OC_SITE_MANAGER;
+ sw->obj_instance[0] = 0xff;
+ sw->obj_instance[1] = 0xff;
+ sw->obj_instance[2] = 0xff;
+ sw->window_size = win_size;
+ sw->state = SW_STATE_WAIT_INITACK;
+
+ rc = sw_open_file(sw, fname);
+ if (rc < 0) {
+ sw->state = SW_STATE_NONE;
+ return rc;
+ }
+
+ return sw_load_init(sw);
}
-#endif
static void fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port,
u_int8_t ts_nr, u_int8_t subslot_nr)