Store classmark1/2/3 in equipment SQL table

For further evaluation/analysis, this patch stores the classmark 1, 2 and 3
values of every equipment in the SQL database.  We can use this non-volatile
data to determine the supported features for each handset that we've ever
seen on our network.
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index dc4f6d7..4a23115 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -11,6 +11,18 @@
 #define GSM_NAME_LENGTH 128
 #define GSM_EXTENSION_LENGTH 128
 
+struct gsm_equipment {
+	long long unsigned int id;
+	char imei[GSM_IMEI_LENGTH];
+	char name[GSM_NAME_LENGTH];
+
+	struct gsm48_classmark1 classmark1;
+	u_int8_t classmark2_len;
+	u_int8_t classmark2[3];
+	u_int8_t classmark3_len;
+	u_int8_t classmark3[14];
+};
+
 struct gsm_subscriber {
 	struct gsm_network *net;
 	long long unsigned int id;
@@ -21,18 +33,14 @@
 	char extension[GSM_EXTENSION_LENGTH];
 	int authorized;
 
+	/* Every user can only have one equipment in use at any given
+	 * point in time */
+	struct gsm_equipment equipment;
+
 	/* for internal management */
 	int use_count;
 	struct llist_head entry;
 
-	/* those are properties of the equipment, but they
-	 * are applicable to the subscriber at the moment */
-	struct gsm48_classmark1 classmark1;
-	u_int8_t classmark2_len;
-	u_int8_t classmark2[3];
-	u_int8_t classmark3_len;
-	u_int8_t classmark3[14];
-
 	/* pending requests */
 	int in_callback;
 	struct llist_head requests;
@@ -47,6 +55,7 @@
 enum gsm_subscriber_update_reason {
 	GSM_SUBSCRIBER_UPDATE_ATTACHED,
 	GSM_SUBSCRIBER_UPDATE_DETACHED,
+	GSM_SUBSCRIBER_UPDATE_EQUIPMENT,
 };
 
 struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 600699a..543f44c 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -1,6 +1,7 @@
 /* Simple HLR/VLR database backend using dbi */
 /* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
  * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -59,6 +60,9 @@
 		"created TIMESTAMP NOT NULL, "
 		"updated TIMESTAMP NOT NULL, "
 		"name TEXT, "
+		"classmark1 NUMERIC, "
+		"classmark2 BLOB, "
+		"classmark3 BLOB, "
 		"imei NUMERIC UNIQUE NOT NULL"
 		")",
 	"CREATE TABLE IF NOT EXISTS EquipmentWatch ("
@@ -288,6 +292,37 @@
 	return 0;
 }
 
+int db_sync_equipment(struct gsm_equipment *equip)
+{
+	dbi_result result;
+	unsigned char *cm2, *cm3;
+
+	dbi_conn_quote_binary_copy(conn, equip->classmark2,
+				   equip->classmark2_len, &cm2);
+	dbi_conn_quote_binary_copy(conn, equip->classmark3,
+				   equip->classmark3_len, &cm3);
+
+	result = dbi_conn_queryf(conn,
+		"UPDATE Equipment SET "
+			"updated = datetime('now'), "
+			"classmark1 = %u, "
+			"classmark2 = %s, "
+			"classmark3 = %s "
+		"WHERE imei = '%s' ",
+		equip->classmark1, cm2, cm3, equip->imei);
+
+	free(cm2);
+	free(cm3);
+
+	if (!result) {
+		printf("DB: Failed to update Equipment\n");
+		return -EIO;
+	}
+
+	dbi_result_free(result);
+	return 0;
+}
+
 int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
 	dbi_result result=NULL;
 	char* tmsi_quoted;
@@ -322,6 +357,9 @@
 	u_int64_t equipment_id, watch_id;
 	dbi_result result;
 
+	strncpy(subscriber->equipment.imei, imei,
+		sizeof(subscriber->equipment.imei)-1),
+
 	result = dbi_conn_queryf(conn,
 		"INSERT OR IGNORE INTO Equipment "
 		"(imei, created, updated) "
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 2312e8a..3e2d171 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1469,8 +1469,9 @@
 		subscr_put(subscr);
 	}
 
-	subscr->classmark2_len = classmark2_len;
-	memcpy(subscr->classmark2, classmark2, classmark2_len);
+	subscr->equipment.classmark2_len = classmark2_len;
+	memcpy(subscr->equipment.classmark2, classmark2, classmark2_len);
+	db_sync_equipment(&subscr->equipment);
 
 	return gsm48_tx_mm_serv_ack(msg->lchan);
 }
@@ -1602,8 +1603,9 @@
 	DEBUGP(DRR, "<- Channel was requested by %s\n",
 		subscr->name ? subscr->name : subscr->imsi);
 
-	subscr->classmark2_len = *classmark2_lv;
-	memcpy(subscr->classmark2, classmark2_lv+1, *classmark2_lv);
+	subscr->equipment.classmark2_len = *classmark2_lv;
+	memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv);
+	db_sync_equipment(&subscr->equipment);
 
 	if (!msg->lchan->subscr) {
 		msg->lchan->subscr = subscr;
@@ -1667,16 +1669,15 @@
 		DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len);
 	}
 	if (subscr) {
-		subscr->classmark2_len = cm2_len;
-		memcpy(subscr->classmark2, cm2, cm2_len);
+		subscr->equipment.classmark2_len = cm2_len;
+		memcpy(subscr->equipment.classmark2, cm2, cm2_len);
 		if (cm3) {
-			subscr->classmark3_len = cm3_len;
-			memcpy(subscr->classmark3, cm3, cm3_len);
+			subscr->equipment.classmark3_len = cm3_len;
+			memcpy(subscr->equipment.classmark3, cm3, cm3_len);
 		}
+		db_sync_equipment(&subscr->equipment);
 	}
 
-	/* FIXME: store the classmark2/3 values with the equipment register */
-
 	return 0;
 }