summaryrefslogtreecommitdiffstats
path: root/Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex
diff options
context:
space:
mode:
Diffstat (limited to 'Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex')
-rw-r--r--Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex805
1 files changed, 805 insertions, 0 deletions
diff --git a/Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex b/Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex
new file mode 100644
index 0000000..704d123
--- /dev/null
+++ b/Replicant_contributors_meeting_27_28_July_2019_Paris_France/modems/Replicant_and_modems_Samsung-ipc.tex
@@ -0,0 +1,805 @@
+\documentclass{beamer}
+\usepackage[english]{babel}
+\usepackage{color}
+\usepackage{graphicx}
+\usepackage{ifthen}
+\usepackage[utf8]{inputenc}
+\usepackage{listings}
+\usepackage{pdfpages}
+
+\lstdefinestyle{terminal}{
+ backgroundcolor=\color{black},
+ basicstyle=\scriptsize\color{green},
+}
+
+%% Based on:
+%% https://tex.stackexchange.com/questions/136900/insert-a-full-page-image
+\newcommand{\pictureframe}[1] {
+ {
+ \begin{frame}
+ \noindent
+ \resizebox{\textwidth}{\textheight}
+ {\includegraphics{#1}}
+ \hspace*{-\textwidth}
+ \end{frame}
+ }
+}
+
+\usetheme{Singapore}
+
+\title{Samsung-ipc compatible modems in Replicant}
+\author{Denis 'GNUtoo' Carikli}
+\date{\today}
+
+\begin{document}
+
+\maketitle
+
+%% TODO: Using \itemize{} fails to compile
+%% TODO: convert \center frames to chapter title
+
+\begin{frame}
+ \center{Samsung IPC}
+\end{frame}
+
+\begin{frame}
+ \center{Samsung IPC: Hardware part}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$RAM$\rightarrow$SOC }
+ \begin{itemize}
+ \item Galaxy S (I9100)
+ \item Nexus S (I902x)
+ \item Galaxy Tab (unsupported)
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$RAM$\rightarrow$SOC: Isolation }
+ \begin{itemize}
+ \item Nexus S: The modem kernel driver shows that part of a RAM chip is shared between the modem and the SOC
+ \item No hardware guarantees that the modem cannot take control of the SOC
+ \item IOMMU:
+ \begin{itemize}
+ \item Requires a mainline kernel to trust the code
+ \item And the SOC documentation on that...
+ \item And people having analyzed its security...
+ \item And to be setup before the RAM is even initialized...
+ \item But the bootloader is not free software...
+ \end{itemize}
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$MIPI$\rightarrow$SOC }
+ \begin{itemize}
+ \item Galaxy Nexus (I9250)
+ \item Galaxy Tab II 7.0 (P3100)
+ \item Galaxy Tab II 10.1 (P5100)
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$MIPI$\rightarrow$SOC: Isolation }
+ \begin{itemize}
+ \item Not analyzed in depth but probably ok
+ \item Same interface than camera and screens
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$HSIC$\rightarrow$SOC }
+ \begin{itemize}
+ \item Galaxy S2 (I9100)
+ \item Galaxy SIII (I9300)
+ \item Galaxy Note (N7000)
+ \item Galaxy Note II (N7100)
+ \item Galaxy Note 8.0 (N5100)
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{XMM626 $\leftarrow$HSIC$\rightarrow$SOC: Isolation }
+ \begin{itemize}
+ \item No DMA to the SOC RAM
+ \item USB-like bus: USB without the PHY
+ \item The host need to reset the bus to get the devices re-enumerated
+ \item $\rightarrow$ More complicated for the modem to become a keyboard
+ \item Ideally (not looked into yet):
+ \begin{itemize}
+ \item usbguard
+ \item USB peripherals whitelist
+ \item $\rightarrow$ re-usable for USB modems (GTA04, PinePhone, Librem5, etc).
+ \end{itemize}
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{Samsung IPC}
+ \begin{itemize}
+ \item We will look more specifically at the case with the XMM626 connected through HSIC.
+ \item Other transports are similar: transports are abstracted by the kernel driver and libsamsung-ipc transport drivers.
+ \end{itemize}
+\end{frame}
+
+\pictureframe{output/xmm626_hsic.png}
+
+\begin{frame}
+ \center{The samsung-ipc protocol}
+ \begin{itemize}
+ \item Asyncronous
+ \item $\rightarrow$ You get asyncronous responses from the modem
+ \item $\rightarrow$ You match with the request (sequence numbers)
+ \item $\rightarrow$ libsamsung-ril needs receive asyncronous responses too
+ \item $\rightarrow$ More complex design with callbacks
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{libsamsung-ril}
+\end{frame}
+
+\begin{frame}
+ \center{The initialization not very different from the reference-ril}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ RIL_RadioFunctions ril_radio_functions = {
+ RIL_VERSION,
+ ril_on_request,
+ ril_on_state_request,
+ ril_supports,
+ ril_on_cancel,
+ ril_get_version
+ };
+ ...
+
+ const RIL_RadioFunctions *RIL_Init(
+ const struct RIL_Env *env, int argc,
+ char **argv) {
+ ...
+ return radio_functions;
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{The interface with rild is not that different either}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ void ril_on_request(int request, void *data,
+ size_t size, RIL_Token token) {
+ ...
+ ril_request_register(request, data, size, token);
+ ...
+}
+\end{lstlisting}
+
+\begin{frame}
+\center{ril\_request\_register}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ril_request_register(int request, void *data,
+ size_t size, RIL_Token token) {
+ ...
+ list_end = ril_data->requests;
+ ...
+ list = list_head_alloc(list_end, NULL,
+ (void *) ril_request);
+ ...
+ ril_data->requests = list;
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{Global ril\_Data variable}
+\end{frame}
+
+\lstset{language=bash}
+\begin{lstlisting}[style=terminal]
+$ git grep "extern.*ril_data;"
+samsung-ril.h:extern struct ril_data *ril_data;
+\end{lstlisting}
+
+\begin{frame}
+ \center{Why?}
+ \begin{itemize}
+ \item Asyncronous design $\rightarrow$ Faster
+ \item Enqueue the request and continue serving new requests.
+ \item We will see how getting the response works in a second time
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \begin{itemize}
+ \item We got a request to power on the phone from the RIL
+ \item We got rid of it by adding it to a list
+ \item Now what happens to it?
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{RIL\_Init}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ const RIL_RadioFunctions *RIL_Init(
+ const struct RIL_Env *env, int argc,
+ char **argv) {
+ ...
+ rc = pthread_create(&ril_data->request_thread,
+ &attr, ril_request_loop, NULL);
+ ...
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_request\_loop}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+
+void *ril_request_loop(void *data) { ...
+ while (1) {
+ do {
+ request = ril_request_find_status(
+ RIL_REQUEST_UNHANDLED);
+ ...
+ request->status = RIL_REQUEST_PENDING;
+ } while (request != NULL);
+ do {
+ ...
+ request = ril_request_find_status(
+ RIL_REQUEST_PENDING);
+ ...
+ rc = ril_request_dispatch(request);
+ } while (request != NULL);
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_request\_dispatch}
+\end{frame}
+
+\lstset{language=C}
+\begin{lstlisting}
+ int ril_request_dispatch(
+ struct ril_request *request) {
+ ...
+ for (i = 0; i < ril_request_handlers_count;
+ i++) {
+ ...
+ if (ril_request_handlers[i].request ==
+ request->request) {
+ status = ril_request_handlers[i].handler(
+ quest->data, request->size,
+ request->token);
+ ...
+ request->status = status; ...
+ } ...
+ } ...
+ return 0;
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_request\_handler}
+\end{frame}
+
+\lstset{language=C}
+\begin{lstlisting}
+ struct ril_request_handler
+ ril_request_handlers[] = {
+ /* Power */
+ {
+ .request = RIL_REQUEST_RADIO_POWER,
+ .handler = ril_request_radio_power,
+ }, ...
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_request\_radio\_power}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ril_request_radio_power(void *data,
+ size_t size, RIL_Token token) {
+ ...
+ power_state = *((int *)data);
+ ...
+ if (power_state > 0) {
+ request_data.state =
+ IPC_PWR_PHONE_STATE_REQUEST_NORMAL; ...
+ } else {
+ request_data.state =
+ IPC_PWR_PHONE_STATE_REQUEST_LPM; ...
+ } ...
+ rc = ipc_fmt_send(ipc_fmt_request_seq(token),
+ IPC_PWR_PHONE_STATE, IPC_TYPE_EXEC,
+ (void *) &request_data,
+ sizeof(request_data)); ...
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_fmt\_send}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_fmt_send(unsigned char mseq,
+ unsigned short command, unsigned char type,
+ const void *data, size_t size) {
+ ...
+ ipc_fmt_data = (struct ipc_fmt_data *)
+ client->data;
+ ...
+ rc = ipc_client_send(ipc_fmt_data->ipc_client,
+ mseq, command, type, data, size);
+ ...
+}
+
+\end{lstlisting}
+
+\begin{frame}
+ \center{The rest happens in libsamsung-ipc}
+\end{frame}
+
+\lstset{language=bash}
+\begin{lstlisting}[style=terminal]
+$ git grep IPC_PWR_PHONE_STATE_REQUEST_NORMAL
+ include/pwr.h:
+ #define IPC_PWR_PHONE_STATE_REQUEST_NORMAL 0x0202
+$ git grep ipc_client_send
+ include/samsung-ipc.h:
+ int ipc_client_send(struct ipc_client *client, ...
+ samsung-ipc/ipc.c:
+ int ipc_client_send(struct ipc_client *client, ...
+\end{lstlisting}
+
+
+\begin{frame}
+ \center{Now what happens with notifications from the modem?}
+\end{frame}
+
+\begin{frame}
+ \center{Again RIL\_Init}
+\end{frame}
+
+\lstset{language=C}
+\begin{lstlisting}
+ const RIL_RadioFunctions *RIL_Init(
+ const struct RIL_Env *env,
+ __attribute__((unused)) int argc,
+ __attribute__((unused)) char **argv) {
+ ...
+ rc = ril_client_loop(ril_clients[i]);
+ ...
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_clients[i]?}
+\end{frame}
+
+\begin{frame}
+ \center{ril\_clients}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ril_client *ril_clients[] = {
+ &ipc_fmt_client,
+ &ipc_rfs_client,
+ &srs_client,
+};
+\end{lstlisting}
+
+\begin{frame}
+ \center{IPC and RFS}
+ \begin{itemize}
+ \item RFS: Modem's remote filesystem (EFS)
+ \item SRS: For the audio part
+ \item IPC: The rest of the protocol
+ \end{itemize}
+\end{frame}
+
+\lstset{language=bash}
+\begin{lstlisting}[style=terminal]
+$ git grep XMM626_SEC_MODEM_IPC0_DEVICE
+ modems/xmm626/xmm626_sec_modem.c:
+ fd = open(XMM626_SEC_MODEM_IPC0_DEVICE,
+ O_RDWR | O_NOCTTY | O_NONBLOCK);
+ modems/xmm626/xmm626_sec_modem.h:
+ #define XMM626_SEC_MODEM_IPC0_DEVICE
+ "/dev/umts_ipc0"
+ $ git grep XMM626_SEC_MODEM_RFS0_DEVICE
+ modems/xmm626/xmm626_sec_modem.c:
+ fd = open(XMM626_SEC_MODEM_RFS0_DEVICE,
+ O_RDWR | O_NOCTTY | O_NONBLOCK);
+ modems/xmm626/xmm626_sec_modem.h:
+ #define XMM626_SEC_MODEM_RFS0_DEVICE
+ "/dev/umts_rfs0"
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_client\_loop}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ril_client_loop(struct ril_client *client) {
+ ...
+ rc = pthread_create(&client->thread, &attr,
+ ril_client_thread, (void *) client);
+ ...
+ }
+
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_client\_thread}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ void *ril_client_thread(void *data) {
+ ...
+ client = (struct ril_client *) data;
+ ...
+ rc = client->handlers->loop(client);
+ ...
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ril\_client}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ril_client ipc_fmt_client = {
+ .id = RIL_CLIENT_IPC_FMT,
+ .name = "IPC FMT",
+ .handlers = &ipc_fmt_handlers,
+ .callbacks = &ipc_fmt_callbacks,
+};
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_fmt\_handers}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ril_client_handlers ipc_fmt_handlers = {
+ .create = ipc_fmt_create,
+ .destroy = ipc_fmt_destroy,
+ .open = ipc_fmt_open,
+ .close = ipc_fmt_close,
+ .loop = ipc_fmt_loop,
+};
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_fmt\_loop}
+\end{frame}
+
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_fmt_loop(struct ril_client *client) {
+ ...
+ rc = ipc_client_recv(data->ipc_client,
+ &message);
+ ...
+ rc = ipc_fmt_dispatch(client, &message);
+ ...
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_fmt\_dispatch}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_fmt_dispatch(struct ril_client *client,
+ struct ipc_message *message) {
+ ...
+ for (i = 0;
+ i < ipc_fmt_dispatch_handlers_count; i++) {
+ ...
+ if (ipc_fmt_dispatch_handlers[i].command ==
+ message->command) {
+ ...
+ rc = ipc_fmt_dispatch_handlers[i].handler(
+ message);
+ ...
+ }
+ }
+ ...
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_fmt\_dispatch\_handlers}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ struct ipc_dispatch_handler
+ ipc_fmt_dispatch_handlers[] = {
+ /* Power */
+ {
+ .command = IPC_PWR_PHONE_PWR_UP,
+ .handler = ipc_pwr_phone_pwr_up,
+ },
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_pwr\_phone\_pwr\_up}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_pwr_phone_pwr_up(__attribute__((unused))
+ struct ipc_message *message) {
+ ril_radio_state_update(RADIO_STATE_OFF);
+
+ return 0;
+ }
+\end{lstlisting}
+
+\begin{frame}
+ \center{libsamsung-ipc}
+\end{frame}
+
+\begin{frame}
+ \center{ipc\_client\_send}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_client_send(struct ipc_client *client,
+ unsigned char mseq, unsigned short command,
+ unsigned char type, const void *data,
+ size_t size) {
+ ...
+ memset(&message, 0, sizeof(message));
+ message.mseq = mseq;
+ message.aseq = 0xff;
+ message.command = command;
+ message.type = type;
+ message.data = (void *) data;
+ message.size = size;
+
+ return client->ops->send(client, &message);
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{client$\rightarrow$ops$\rightarrow$send}
+\end{frame}
+
+
+\begin{frame}
+ \center{ipc\_client\_create}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ipc_client *ipc_client_create(int type) {
+ ...
+ rc = ipc_device_detect();
+ ...
+ switch (type) {
+ ...
+ case IPC_CLIENT_TYPE_FMT:
+ client->ops =
+ ipc_devices[device_index].fmt_ops;
+ break;
+ ...
+ }
+ ...
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_device\_detect}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int ipc_device_detect(void) {
+ ...
+ fd = open("/proc/cpuinfo", O_RDONLY);
+ ...
+ if (strncmp(line, "Hardware", 8) == 8) {
+ ...
+ if (... && strcmp(kernel_version,
+ ipc_devices[i].kernel_version) != 0)
+ ...
+}
+
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_devices}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ipc_device_desc ipc_devices[] = {
+ ...
+ {
+ .name = "i9300",
+ .board_name = "smdk4x12",
+ .kernel_version = NULL,
+ .fmt_ops = &i9300_fmt_ops,
+ .rfs_ops = &i9300_rfs_ops,
+ .handlers = &i9300_handlers,
+ .gprs_specs = &i9300_gprs_specs,
+ .nv_data_specs = &i9300_nv_data_specs,
+ },
+\end{lstlisting}
+
+\begin{frame}
+ \center{ipc\_client\_ops}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+struct ipc_client_ops i9300_fmt_ops = {
+ .boot = i9300_boot,
+ .send = xmm626_sec_modem_fmt_send,
+ .recv = xmm626_sec_modem_fmt_recv,
+};
+\end{lstlisting}
+
+\begin{frame}
+ \center{xmm626\_sec\_modem\_fmt\_send}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int xmm626_sec_modem_fmt_send(
+ struct ipc_client *client,
+ struct ipc_message *message) {
+ ...
+ rc = client->handlers->write(
+ client->handlers->transport_data,
+ p, length - count);
+ }
+\end{lstlisting}
+
+
+\begin{frame}
+ \center{i9300\_handlers}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ struct ipc_client_handlers i9300_handlers = {
+ .read = i9300_read,
+ .write = i9300_write,
+ .open = i9300_open,
+ .close = i9300_close,
+ .poll = i9300_poll,
+ .transport_data = NULL,
+ .power_on = i9300_power_on,
+ .power_off = i9300_power_off,
+ .power_data = NULL,
+ .gprs_activate = i9300_gprs_activate,
+ .gprs_deactivate = i9300_gprs_deactivate,
+ .gprs_data = NULL,
+ .data_create = i9300_data_create,
+ .data_destroy = i9300_data_destroy,
+ };
+\end{lstlisting}
+
+\begin{frame}
+ \center{i9300\_open}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+int i9300_open(void *data, int type)
+{
+ ...
+ transport_data->fd =
+ xmm626_sec_modem_open(type);
+ ...
+ return 0;
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{xmm626\_sec\_modem\_open}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+int xmm626_sec_modem_open(int type) {
+ switch (type) {
+ case IPC_CLIENT_TYPE_FMT:
+ fd = open(XMM626_SEC_MODEM_IPC0_DEVICE,
+ O_RDWR | O_NOCTTY | O_NONBLOCK);
+ break;
+ case IPC_CLIENT_TYPE_RFS:
+ fd = open(XMM626_SEC_MODEM_RFS0_DEVICE,
+ O_RDWR | O_NOCTTY | O_NONBLOCK);
+ break;
+ default:
+ return -1;
+ }
+ ...
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{xmm626\_sec\_modem\_fmt\_send}
+\end{frame}
+\lstset{language=bash}
+\begin{lstlisting}[style=terminal]
+ $ git grep XMM626_SEC_MODEM_IPC0_DEVICE
+ devices/xmm626/xmm626_sec_modem.h:
+ #define XMM626_SEC_MODEM_IPC0_DEVICE "/dev/umts_ipc0"
+ $ git grep XMM626_SEC_MODEM_RFS0_DEVICE
+ devices/xmm626/xmm626_sec_modem.h:
+ #define XMM626_SEC_MODEM_RFS0_DEVICE "/dev/umts_rfs0"
+\end{lstlisting}
+
+\begin{frame}
+ \center{i9300\_write}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int i9300_write(void *data,
+ const void *buffer, size_t length) {
+ ...
+ rc = xmm626_sec_modem_write(
+ transport_data->fd, buffer, length);
+
+ return rc;
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{xmm626\_modem\_write}
+\end{frame}
+\lstset{language=C}
+\begin{lstlisting}
+ int xmm626_sec_modem_write(int fd,
+ const void *buffer, size_t length) {
+ ...
+ status = ioctl(fd, IOCTL_MODEM_STATUS, 0);
+ if (status != STATE_ONLINE &&
+ status != STATE_BOOTING)
+ return -1;
+
+ rc = write(fd, buffer, length);
+
+ return rc;
+}
+\end{lstlisting}
+
+\begin{frame}
+ \center{Sharing the work with other Android distributions}
+ \begin{itemize}
+ \item Not complete enough to be merged in LineageOS
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{Sharing the work with GNU/Linux distributions}
+ \begin{itemize}
+ \item Freesmartphone.org
+ \item Ofono + rild + libamsung-ril + libsamsung-ipc
+ \item Ofono + libsamsung-ipc
+ \item PostmarketOS?
+ \item PureOS, Parabola?
+ \end{itemize}
+\end{frame}
+
+\begin{frame}
+ \center{Discussions:}
+ \begin{itemize}
+ \item Sharing code with other distributions?
+ \item USB Guard?
+ \item Device requirements: Require modem to be isolated ?
+ \item Other modems and GNU/Linux stack (PinePhone, GTA04)?
+ \item Using GNU/Linux modem stack as much as possible?
+ \item Automatic testing infrastructure and ofono?
+ \item GNU/Linux and testing?
+ \end{itemize}
+\end{frame}
+
+\begin{thebibliography}{99}
+\end{thebibliography}
+
+\end{document}