Generalize SIM switching code and allow local/remote switching via USB
diff --git a/firmware/libboard/common/include/sim_switch.h b/firmware/libboard/common/include/sim_switch.h
new file mode 100644
index 0000000..16c5aa3
--- /dev/null
+++ b/firmware/libboard/common/include/sim_switch.h
@@ -0,0 +1,4 @@
+#pragma once
+
+int sim_switch_use_physical(unsigned int nr, int physical);
+int sim_switch_init(void);
diff --git a/firmware/libboard/common/source/sim_switch.c b/firmware/libboard/common/source/sim_switch.c
new file mode 100644
index 0000000..49e6cea
--- /dev/null
+++ b/firmware/libboard/common/source/sim_switch.c
@@ -0,0 +1,64 @@
+/* Code to switch between local (physical) and remote (emulated) SIM */
+
+#include "board.h"
+#include "trace.h"
+#include "sim_switch.h"
+
+#ifdef PIN_SIM_SWITCH1
+static const Pin pin_conn_usim1 = {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
+#endif
+#ifdef PIN_SIM_SWITCH2
+static const Pin pin_conn_usim2 = {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
+#endif
+
+static int initialized = 0;
+
+int sim_switch_use_physical(unsigned int nr, int physical)
+{
+	const Pin *pin;
+
+	if (!initialized) {
+		TRACE_ERROR("Somebody forgot to call sim_switch_init()\r\n");
+		sim_switch_init();
+	}
+
+	TRACE_INFO("Modem %d: %s SIM\n\r", nr,
+		   physical ? "physical" : "virtual");
+
+	switch (nr) {
+#ifdef PIN_SIM_SWITCH1
+	case 1:
+		pin = &pin_conn_usim1;
+		break;
+#endif
+#ifdef PIN_SIM_SWITCH2
+	case 2:
+		pin = &pin_conn_usim2;
+		break;
+#endif
+	default:
+		TRACE_ERROR("Invalid SIM%u\n\r", nr);
+		return -1;
+	}
+
+	if (physical)
+		PIO_Clear(pin);
+	else
+		PIO_Set(pin);
+
+	return 0;
+}
+
+int sim_switch_init(void)
+{
+	int num_switch = 0;
+#ifdef PIN_SIM_SWITCH1
+	PIO_Configure(&pin_conn_usim1, 1);
+	num_switch++;
+#endif
+#ifdef PIN_SIM_SWITCH2
+	PIO_Configure(&pin_conn_usim2, 1);
+	num_switch++;
+#endif
+	return num_switch;
+}
diff --git a/firmware/libboard/qmod/include/board.h b/firmware/libboard/qmod/include/board.h
index 082d254..d49ff9d 100644
--- a/firmware/libboard/qmod/include/board.h
+++ b/firmware/libboard/qmod/include/board.h
@@ -56,6 +56,10 @@
 
 #define PIN_VERSION_DET		{PIO_PA19, PIOA, ID_PIOA, PIO_PERIPH_D, PIO_DEFAULT}
 
+/* GPIO towards SPDT switches between real SIM and SAM3 */
+#define PIN_SIM_SWITCH1 {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
+#define PIN_SIM_SWITCH2 {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
+
 #define BOARD_USB_BMATTRIBUTES	USBConfigurationDescriptor_SELFPOWERED_NORWAKEUP
 
 #define BOARD_USB_VENDOR_ID	USB_VENDOR_OPENMOKO
diff --git a/firmware/libboard/qmod/source/board_qmod.c b/firmware/libboard/qmod/source/board_qmod.c
index 889d31e..54983d7 100644
--- a/firmware/libboard/qmod/source/board_qmod.c
+++ b/firmware/libboard/qmod/source/board_qmod.c
@@ -6,6 +6,7 @@
 #include "utils.h"
 #include "wwan_led.h"
 #include "wwan_perst.h"
+#include "sim_switch.h"
 #include "boardver_adc.h"
 #include "card_pres.h"
 #include "osmocom/core/timer.h"
@@ -17,37 +18,9 @@
 static const Pin pin_peer_rst = {PIO_PA0, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
 static const Pin pin_peer_erase = {PIO_PA11, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
 
-static const Pin pin_conn_usim1 = {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
-static const Pin pin_conn_usim2 = {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
-
 /* array of generated USB Strings */
 extern unsigned char *usb_strings[];
 
-static void qmod_use_physical_sim(unsigned int nr, int physical)
-{
-	const Pin *pin;
-
-	TRACE_INFO("Modem %d: %s SIM\n\r", nr,
-		   physical ? "physical" : "virtual");
-
-	switch (nr) {
-	case 1:
-		pin = &pin_conn_usim1;
-		break;
-	case 2:
-		pin = &pin_conn_usim2;
-		break;
-	default:
-		TRACE_ERROR("Invalid SIM%u\n\r", nr);
-		return;
-	}
-
-	if (physical)
-		PIO_Clear(pin);
-	else
-		PIO_Set(pin);
-}
-
 static int qmod_sam3_is_12(void)
 {
 	if (PIO_Get(&pin_1234_detect) == 0)
@@ -218,10 +191,10 @@
 		wwan_perst_do_reset_pulse(2, 300);
 		break;
 	case '!':
-		qmod_use_physical_sim(1, 0);
+		sim_switch_use_physical(1, 0);
 		break;
 	case '@':
-		qmod_use_physical_sim(2, 0);
+		sim_switch_use_physical(2, 0);
 		break;
 	default:
 		if (!qmod_sam3_is_12())
@@ -238,6 +211,7 @@
 
 	wwan_led_init();
 	wwan_perst_init();
+	sim_switch_init();
 
 	/* make sure we can detect whether running in ST12 or ST34 */
 	PIO_Configure(&pin_1234_detect, 1);
@@ -250,8 +224,6 @@
 	}
 	PIO_Configure(&pin_peer_rst, 1);
 	PIO_Configure(&pin_peer_erase, 1);
-	PIO_Configure(&pin_conn_usim1, 1);
-	PIO_Configure(&pin_conn_usim2, 1);
 	i2c_pin_init();
 
 	if (qmod_sam3_is_12()) {
diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c
index 2e50134..4f1c3dd 100644
--- a/firmware/libcommon/source/mode_cardemu.c
+++ b/firmware/libcommon/source/mode_cardemu.c
@@ -12,6 +12,7 @@
 #include "usb_buf.h"
 #include "simtrace_prot.h"
 #include "wwan_perst.h"
+#include "sim_switch.h"
 
 #define TRACE_ENTRY()	TRACE_DEBUG("%s entering\r\n", __func__)
 
@@ -521,6 +522,21 @@
 	return 0;
 }
 
+static int usb_command_sim_select(struct msgb *msg, struct cardem_inst *ci)
+{
+	struct st_modem_sim_select *mss = msg->l2h;
+
+	if (msgb_l2len(msg) < sizeof(*mss))
+		return -1;
+
+	if (mss->remote_sim)
+		sim_switch_use_physical(ci->num, 0);
+	else
+		sim_switch_use_physical(ci->num, 1);
+
+	return 0;
+}
+
 /* handle a single USB command as received from the USB host */
 static void dispatch_usb_command_modem(struct msgb *msg, struct cardem_inst *ci)
 {
@@ -532,6 +548,7 @@
 		usb_command_modem_reset(msg, ci);
 		break;
 	case SIMTRACE_MSGT_DT_MODEM_SIM_SELECT:
+		usb_command_sim_select(msg, ci);
 		break;
 	case SIMTRACE_MSGT_BD_MODEM_STATUS:
 		break;