Merge "qmiwds: Add service and initial methods"
diff --git a/include/libqmi.h b/include/libqmi.h
index ca7cad4..b19b2eb 100644
--- a/include/libqmi.h
+++ b/include/libqmi.h
@@ -19,13 +19,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-typedef unsigned char byte;
-
-/*
- * Type of a QMI device.  May be created with qmidev_new_file and destroyed
- * with qmidev_destroy.
- */
-typedef struct qmidev qmidev;
+struct qmidev;
 
 /* GENERIC RESPONSE FUNCTIONS */
 
@@ -41,7 +35,7 @@
  * returning a response, the status will be a QMIDEV_ERR_* constant.
  */
 typedef void (*qmidev_response_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status);
 
@@ -53,7 +47,7 @@
  * See qmidev_response_fn for more details.
  */
 typedef void (*qmidev_int_response_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   int result);
@@ -68,14 +62,11 @@
  * See qmidev_response_fn for more details.
  */
 typedef void (*qmidev_string_response_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   const char *result);
 
-/* TODO */
-int qmidev_init(void);
-
 /*
  * Creates a new QMI device backed by a file (to which the QMI messages will
  * be written to and read from).  (This is likely to be a device file, such as
@@ -83,21 +74,21 @@
  *
  * Returns the allocated QMI device, or NULL if it could not be created.
  */
-qmidev *qmidev_new_file(const char *path);
+struct qmidev *qmidev_new_file(const char *path);
 
 /*
  * Sets the "private data" pointer for a QMI device.  This pointer is solely
  * for use by the caller, perhaps to tie the QMI device to an associated object
  * of its own.
  */
-void qmidev_set_priv(qmidev *dev, void *priv);
+void qmidev_set_priv(struct qmidev *qmidev, void *priv);
 
 /*
  * Returns the "private data" pointer for a QMI device.
  *
  * See qmidev_set_priv for more details.
  */
-void *qmidev_priv(qmidev *dev);
+void *qmidev_priv(struct qmidev *qmidev);
 
 /*
  * Returns the file descriptor upon which the caller should wait to determine
@@ -107,14 +98,14 @@
  *
  * This function may be called any time after creating the QMI device.
  */
-int qmidev_fd(qmidev *dev);
+int qmidev_fd(struct qmidev *qmidev);
 
 /*
  * Processes activity on the QMI device.  This function should be called when
  * select or epoll reveals that there is activity on the file descriptor
  * returned by qmidev_fd.
  */
-int qmidev_process(qmidev *dev);
+int qmidev_process(struct qmidev *qmidev);
 
 /*
  * Connects to the device.  This may involve several method calls to initialize
@@ -122,7 +113,8 @@
  * be called with the provided context when the library has finished connecting
  * to the device (or failed to connect).
  */
-int qmidev_connect(qmidev *dev, qmidev_response_fn callback, void *context);
+int qmidev_connect(struct qmidev *qmidev,
+                   qmidev_response_fn callback, void *context);
 
 /*
  * Disconnects from the device.  This may involve several method calls to tear
@@ -130,7 +122,8 @@
  * will be called with the provided context when the library has finished
  * disconnecting to the device (or failed to disconnect).
  */
-int qmidev_disconnect(qmidev *dev, qmidev_response_fn callback, void *context);
+int qmidev_disconnect(struct qmidev *qmidev,
+                      qmidev_response_fn callback, void *context);
 
 /* ENUMS */
 
@@ -297,7 +290,7 @@
   QMIDEV_SESSION_CONNECTED,
   QMIDEV_SESSION_SUSPENDED,
   QMIDEV_SESSION_AUTHENTICATING
-} qmidev_session_state;
+} qmidev_packet_status;
 
 typedef enum {
   QMIDEV_BEARER_CDMA2000 = 1,
@@ -524,260 +517,19 @@
 
 /* METHODS */
 
-/* Performs automatic service activation. */
-int qmidev_activate_automatic(
-  qmidev *dev,
-  const char *activation_code,
-  qmidev_response_fn callback,
-  void *context);
-
-/**
- * Performs manual service activation and then resets the device.
- * @spc: Service Programming Code (six digit string)
- * @sid: System identification number
- * @mdn: Mobile Directory Number (string or NULL)
- * @min: Mobile Identification Number (string or NULL)
- * @prl: PRL file (byte array or NULL),
- * @prl_len: length of PRL file, if provided
- * @mnha: MN-HA (string or NULL)
- * @mnaaa: MN-AAA (string or NULL)
- */
-int qmidev_activate_manual(
-  qmidev *dev,
-  const char *spc,
-  int sid,
-  const char *mdn,
-  const char *min,
-  const char *prl,
-  size_t prl_len,
-  const char *mnha,
-  const char *mnaaa,
-  qmidev_response_fn callback,
-  void *context);
-
-/*
- * Deletes one or more SMS messages from the device.
- * storage_type is the storage type of the messages to delete.
- *
- * If idx and tag are both NULL, all the messages in the given storage
- * location are deleted.
- *
- * If idx is NULL but tag is not, all the messages in the given storage
- * location with the given tag are deleted.
- *
- * If idx is not NULL buttag is, the message with the given index in
- * the given storage location is deleted.
- *
- * If idx and tag are both non-NULL, an error is returned.
- */
-int qmidev_sms_delete(
-  qmidev *dev,
-  qmidev_sms_storage storage_type,
-  uint8_t *idx,
-  qmidev_sms_tag *tag,
-  qmidev_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_get_activation_state.  state is the activation
- * state of the device.
- */
-typedef void (*qmidev_activation_state_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  qmidev_activation_state state);
-
-/* Gets the activation state of the device. */
-int qmidev_get_activation_state(
-  qmidev *dev,
-  qmidev_activation_state_response_fn callback,
-  void *context);
-
-/* Gets the active mobile IP profile of the device. */
-int qmidev_get_active_mobile_ip_profile(
-  qmidev *dev,
-  qmidev_int_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_get_data_bearer_technology.  tech is the data
- * bearer technology.
- */
-typedef void (*qmidev_data_bearer_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  qmidev_data_bearer tech);
-
-/*
- * Gets the current data bearer technology of the device.  Only valid when
- * connected to a network.
- */
-int qmidev_get_data_bearer_technology(
-  qmidev *dev,
-  qmidev_data_bearer_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_device_capabilities.  */
-typedef void (*qmidev_device_capabilities_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  const qmidev_device_caps *caps);
-
-/* Gets the device capabilities.  TODO. */
-int qmidev_get_device_capabilities(
-  qmidev *dev,
-  qmidev_device_capabilities_response_fn callback,
-  void *context);
-
-/* TODO: enums */
-
-/* Callback type for qmidev_get_firmware_info. */
-typedef void (*qmidev_firmware_info_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int firmware_id,
-  int technology,
-  int carrier,
-  int region,
-  int gps_capability);
-
-/* Gets information about the current device firmware. */
-int qmidev_get_firmware_info(
-  qmidev *dev,
-  qmidev_firmware_info_response_fn callback,
-  void *context);
-
-/* Gets the device firmware revision. */
-int qmidev_get_firmware_revision(
-  qmidev *dev,
-  qmidev_string_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_firmware_revisions. */
-typedef void (*qmidev_firmware_revisions_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  const char *amss_version,
-  const char *boot_version,
-  const char *pri_version);
-
-/* Gets the revisions of the current device AMSS, boot, and PRI firmware. */
-int qmidev_get_firmware_revisions(
-  qmidev *dev,
-  qmidev_firmware_revisions_response_fn callback,
-  void *context);
-
-/* Gets the hardware revision of the device. */
-int qmidev_get_hardware_revision(
-  qmidev *dev,
-  qmidev_string_response_fn callback,
-  void *context);
-
-/**
- * Callback type for qmidev_get_home_network.
- * @mcc: Mobile Country Code
- * @mnc: Mobile Network Code
- * @name: network name
- * @sid: System ID (of home network)
- * @nid: Network ID (of home network)
- */
-typedef void (*qmidev_home_network_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int mcc,
-  int mnc,
-  const char *name,
-  int sid,
-  int nid);
-
-/* Gets information about the home network of the device. */
-int qmidev_get_home_network(
-  qmidev *dev,
-  qmidev_home_network_response_fn callback,
-  void *context);
-
-/* Gets the IMSI of the device. */
-int qmidev_get_imsi(
-  qmidev *dev,
-  qmidev_string_response_fn callback,
-  void *context);
-
-/* Gets the manufacturer of the device. */
-int qmidev_get_manufacturer(
-  qmidev *dev,
-  qmidev_string_response_fn callback,
-  void *context);
-
-/* Gets the model ID of the device. */
-int qmidev_get_model_id(
-  qmidev *dev,
-  qmidev_string_response_fn callback,
-  void *context);
-
-/**
- * Callback type for qmidev_get_plmn_name.
- * @name_is_ucs2: 1 if the name is in UCS-2, 0 if TODO
- * @name_len: the length of name in bytes.
- * @name: the name itself
- */
-typedef void (*qmidev_plmn_name_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  char name_is_ucs2,
-  size_t name_len,
-  const char *name);
-
-/**
- * Gets PLMN information for a given network.
- * @mcc: Mobile Country Code
- * @mnc: Mobile Network Code
- */
-int qmidev_get_plmn_name(
-  qmidev *dev,
-  int mcc,
-  int mnc,
-  qmidev_plmn_name_response_fn callback,
-  void *context);
-
 /* Callback type for qmidev_get_power.  state is the power state. */
 typedef void (*qmidev_power_state_response_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_power_state state);
 
 /* Gets the power state of the device. */
 int qmidev_get_power(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_power_state_response_fn callback,
   void *context);
 
-/* Gets the PRL version on the device. */
-int qmidev_get_prl_version(
-  qmidev *dev,
-  qmidev_int_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_rf_info.  info is the info structure. */
-typedef void (*qmidev_rf_info_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  const qmidev_rf_info info);
-
-/* Gets the current RF information from the device. */
-int qmidev_get_rf_info(
-  qmidev *dev,
-  qmidev_rf_info_response_fn callback,
-  void *context);
-
 /**
  * Callback type for qmidev_get_ids.
  * @esn: Electronic Serial Number,
@@ -785,7 +537,7 @@
  * @meid: Mobile Equipment Identifier
  */
 typedef void (*qmidev_get_ids_response_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   const char *esn,
@@ -794,403 +546,58 @@
 
 /* Gets the serial numbers of the device. */
 int qmidev_get_ids(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_get_ids_response_fn callback,
   void *context);
 
-/* TODO: enums */
-
-/* Callback type for qmidev_get_serving_network. */
-typedef void (*qmidev_serving_network_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int reg_state,
-  int cs_status,
-  int ps_status,
-  int radio_access_network,
-  size_t num_interfaces,
-  const qmidev_radio_interface *interfaces,
-  int roaming,
-  int mcc,
-  int mnc,
-  size_t name_len,
-  const char *name);
-
-/* Gets information about the serving network. */
-int qmidev_get_serving_network(
-  qmidev *dev,
-  qmidev_serving_network_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_serving_network_caps. */
-typedef void (*qmidev_serving_network_caps_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  size_t caps_len,
-  const qmidev_data_cap *caps);
-
-/* Gets the capabilities of the serving network. */
-int qmidev_get_serving_network_caps(
-  qmidev *dev,
-  qmidev_serving_network_caps_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_get_signal_strengths.  num_strengths is the number
- * of signal strengths; strengths is an array of num_strengths items, each
- * containing the strength for one radio interface.
- */
-typedef void (*qmidev_signal_strengths_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int num_strengths,
-  qmidev_signal_strength strengths[]);
-
-/* Gets the signal strength(s) of the device. */
-int qmidev_get_signal_strengths(
-  qmidev *dev,
-  qmidev_signal_strengths_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_sms. */
-typedef void (*qmidev_sms_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  qmidev_sms_tag tag,
-  qmidev_sms_format format,
-  size_t message_len,
-  const byte *message);
-
-/* Gets an SMS from the device, given the storage location and idx. */
-int qmidev_get_sms(
-  qmidev *dev,
-  qmidev_sms_storage storage,
-  uint8_t idx,
-  qmidev_sms_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_get_smsc_address. */
-typedef void (*qmidev_smsc_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  const char *smsc_address,
-  const char *smsc_type);
-
-/* Gets the SMSC address (and type). */
-int qmidev_get_smsc_address(
-  qmidev *dev,
-  qmidev_smsc_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_get_sms_list.  num_entries is the number of entries
- * in the SMS list, and entries is an array of num_entries entries.
- */
-typedef void (*qmidev_sms_list_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  size_t num_entries,
-  const qmidev_sms_list_entry *entries);
-
-/* Gets a list of SMS messages, given a storage location and (optional) tag. */
-int qmidev_get_sms_list(
-  qmidev *dev,
-  qmidev_sms_storage storage,
-  qmidev_sms_tag *tag,
-  qmidev_sms_list_response_fn callback,
-  void *context);
-
-/**
- * Callback type for qmidev_get_voice_number.
- *
- * @voice_number: the device's voice number,
- * @min: the device's Mobile Identification Number
- */
-typedef void (*qmidev_voice_number_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  const char *voice_number,
-  const char *min);
-
-/* Gets the voice number (and MIN) of the device. */
-int qmidev_get_voice_number(
-  qmidev *dev,
-  qmidev_voice_number_response_fn callback,
-  void *context);
-
-/* Initiates network registration. */
-int qmidev_initiate_network_registration(
-  qmidev *dev,
-  qmidev_reg_type registration_type,
-  int mcc,
-  int mnc,
-  qmidev_radio_access_tech radio_access_type,
-  qmidev_response_fn callback,
-  void *context);
-
-/* Callback type for qmidev_omadm_get_session_info.  TODO. */
-typedef void (*qmidev_omadm_session_info_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  /* TODO: enums */
-  int state,
-  int type,
-  int failure_reason,
-  int retry_count,
-  int session_pause,
-  int time_remaining);
-
-/*
- * Gets information related to the current (or previous, if no session is
- * active) OMA-DM session.
- */
-int qmidev_omadm_get_session_info(
-  qmidev *dev,
-  qmidev_omadm_session_info_response_fn callback,
-  void *context);
-
-/* Starts an OMA-DM session.  type is the type of session to initiate. */
-int qmidev_omadm_start_session(
-  qmidev *dev,
-  /* TODO: enum */
-  int type,
-  qmidev_response_fn callback,
-  void *context);
-
-typedef struct qmidev_network_info qmidev_network_info;
-
-struct qmidev_network_info {
-  int mcc;
-  int mnc;
-  /* flags */
-  char in_use;
-  char roaming;
-  char forbidden;
-  char preferred;
-};
-
-/*
- * Callback type for qmidev_perform_network_scan.  num_networks is the number
- * of networks found; networks is an array of num_networks network info
- * structures.
- */
-typedef void (*qmidev_network_scan_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  size_t num_networks;
-  qmidev_network_info *networks;
-);
-
-/* Performs a scan for available networks. */
-int qmidev_perform_network_scan(
-  qmidev *dev,
-  qmidev_network_scan_response_fn *callback,
-  void *context);
-
-/**
- * Resets the device's configuration to factory defaults and then resets the
- * device.
- * @spc: Service Programming Code.
- */
-int qmidev_reset_to_factory_defaults(
-  qmidev *dev,
-  const char *spc,
-  qmidev_response_fn callback,
-  void *context);
-
-/* Enables or disables port automatic tracking for the NMEA port. */
-int qmidev_set_port_automatic_tracking(
-  qmidev *dev,
-  int enabled,
-  qmidev_response_fn callback,
-  void *context);
-
 /* Sets the operating mode of the device. */
 int qmidev_set_power(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_power_state state,
   qmidev_response_fn callback,
   void *context);
 
-/* Enables or disables service automatic tracking. */
-int qmidev_set_service_automatic_tracking(
-  qmidev *dev,
-  int enabled,
-  qmidev_response_fn callback,
-  void *context);
+typedef uint32_t qmidev_session_id;
 
-/* Sets the SMSC address (and type). */
-int qmidev_set_smsc_address(
-  qmidev *dev,
-  const char *smsc_address,
-  const char *smsc_type,
-  qmidev_response_fn callback,
+typedef void (*qmidev_packet_status_response_fn)(
+  struct qmidev *qmidev,
+  void *context,
+  int status,
+  qmidev_packet_status packet_status);
+
+int qmidev_get_packet_status(
+  struct qmidev *qmidev,
+  qmidev_packet_status_response_fn callback,
   void *context);
 
 /*
- * Callback type for qmidev_start_data_session.  If successful, session_id
+ * Callback type for qmidev_start_network.  If successful, session_id
  * is the id of the newly-started session.  If unsuccessful, failure_reason
  * is the reason the session did not start.
  */
-typedef void (*qmidev_start_data_session_response_fn)(
-  qmidev *dev,
+typedef void (*qmidev_start_network_response_fn)(
+  struct qmidev *qmidev,
   void *context,
   int status,
-  int session_id, /* TODO: Really int? */
-  qmidev_oma_failure_reason failure_reason);
+  qmidev_session_id session_id, /* TODO: Really int? */
+  uint16_t call_end_reason);
 
 /* Starts a packet data session.  TODO: Document absurd arguments. */
-int qmidev_start_data_session(
-  qmidev *dev,
-  qmidev_session_tech *tech,
-  uint32_t *pri_dns,
-  uint32_t *sec_dns,
-  uint32_t *pri_nbns,
-  uint32_t *sec_nbns,
-  const char *apn_name,
-  uint32_t *ip_addr,
-  qmidev_session_auth *auth,
-  const char *username,
-  const char *password,
-  qmidev_start_data_session_response_fn callback,
+int qmidev_start_network(
+  struct qmidev *qmidev,
+  qmidev_start_network_response_fn callback,
   void *context);
 
 /*
  * Stops a packet data session.  session_id is the session id, as passed to
  * the callback for qmidev_start_data_session when the session started.
  */
-int qmidev_stop_data_session(
-  qmidev *dev,
-  int session_id,
+int qmidev_stop_network(
+  struct qmidev *qmidev,
+  qmidev_session_id session_id,
   qmidev_response_fn callback,
   void *context);
 
-/*
- * Callback type for qmidev_uim_change_pin.  If unsuccessful,
- * verify_retries_left and unblock_retries_left will contain the number of
- * retries available to verify the PIN and unblock the PIN, respectively.
- */
-typedef void (*qmidev_uim_change_pin_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int verify_retries_left,
-  int unblock_retries_left);
-
-/*
- * Changes a PIN on the UIM.  pin_id is which pin (1 for PIN, 2 for PIN2);
- * old_value and new_value are the old and new values for the PIN.
- */
-int qmidev_uim_change_pin(
-  qmidev *dev,
-  qmidev_pin_id pin_id,
-  const char *old_value,
-  const char *new_value,
-  qmidev_uim_change_pin_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_uim_get_pin_status.  pin_status is the PIN status;
- * verify_retries_left and unblock_retries_left are the number of retries
- * available to verify the PIN and unblock the PIN, respectively.
- */
-typedef void (*qmidev_uim_get_pin_status_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  qmidev_pin_status pin_status,
-  int verify_retries_left,
-  int unblock_retries_left);
-
-/*
- * Gets the status of one of the PINs on the UIM.  pin_id is which pin (1 for
- * PIN, 2 for PIN2).
- */
-int qmidev_uim_get_pin_status(
-  qmidev *dev,
-  qmidev_pin_id pin_id,
-  qmidev_uim_get_pin_status_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_uim_set_pin_protection.  If unsuccessful,
- * verify_retries_left is the number of retries available to verify the PIN.
- */
-typedef void (*qmidev_uim_set_pin_protection_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int verify_retries_left);
-
-/**
- * Enables or disables PIN protection for one of the PINs on the UIM.
- * @pin_id: which pin (1 for PIN, 2 for PIN2)
- * @enabled: whether to enable (1) or disable (0) the PIN protection
- * @value: the value of the PIN.
- */
-int qmidev_uim_set_pin_protection(
-  qmidev *dev,
-  qmidev_pin_id pin_id,
-  int enabled,
-  const char *value,
-  qmidev_uim_set_pin_protection_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_uim_unblock_pin.  If unsuccessful,
- * verify_retries_left and unblock_retries_left are the number of retries
- * available to verify the PIN and unblock the PIN, respectively.
- */
-typedef void (*qmidev_uim_unblock_pin_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int verify_retries_left,
-  int unblock_retries_left);
-
-/*
- * Unblocks one of the PINs on the UIM.  pin_id is which pin (1 = PIN, 2 =
- * PIN2), puk_value is the value of the corresponding PUK (PUK or PUK2), and
- * new_value is the value to which to reset the PIN to.
- */
-int qmidev_uim_unblock_pin(
-  qmidev *dev,
-  qmidev_pin_id pin_id,
-  const char *puk_value,
-  const char *new_value,
-  qmidev_uim_unblock_pin_response_fn callback,
-  void *context);
-
-/*
- * Callback type for qmidev_uim_verify_pin.  If unsuccessful,
- * verify_retries_left is the number of retries available to verify the PIN.
- */
-typedef void (*qmidev_uim_verify_pin_response_fn)(
-  qmidev *dev,
-  void *context,
-  int status,
-  int verify_retries_left);
-
-/*
- * Verifies one of the PINs on the UIM.  pin_id is which pin (1 = PIN, 2 =
- * PIN2).  value is the value of that PIN.
- */
-int qmidev_uim_verify_pin(
-  qmidev *dev,
-  qmidev_pin_id pin_id,
-  const char *value,
-  qmidev_uim_verify_pin_response_fn callback,
-  void *context);
-
 /* CALLBACKS */
 
 /*
@@ -1221,14 +628,14 @@
 
 /* Activation status callback type.  state is the activation state. */
 typedef void (*qmidev_activation_status_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_activation_state state);
 
 /* Sets the activation status callback. */
 int qmidev_set_activation_status_changed_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_activation_status_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1236,7 +643,7 @@
 
 /* Byte totals callback type. */
 typedef void (*qmidev_byte_totals_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   uint64_t tx_bytes,
@@ -1244,7 +651,7 @@
 
 /* Sets the byte totals callback. */
 int qmidev_set_byte_totals_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_byte_totals_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1252,14 +659,14 @@
 
 /* Data bearer callback type.  tech is the new data bearer technology. */
 typedef void (*qmidev_data_bearer_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_data_bearer tech);
 
 /* Sets the data bearer callback. */
 int qmidev_set_data_bearer_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_data_bearer_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1267,7 +674,7 @@
 
 /* Data capabilities callback type.  TODO. */
 typedef void (*qmidev_data_capabilities_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   size_t num_caps,
@@ -1275,7 +682,7 @@
 
 /* Sets the data capabilities callback. */
 int qmidev_set_data_capabilities_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_data_capabilities_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1283,14 +690,14 @@
 
 /* Dormancy status callback type.  dormancy is the dormancy status. */
 typedef void (*qmidev_dormancy_status_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_dormancy_status dormancy);
 
 /* Sets the dormancy status callback. */
 int qmidev_set_dormancy_status_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_dormancy_status_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1301,7 +708,7 @@
  * for rejection.
  */
 typedef void (*qmidev_lu_reject_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_lu_domain domain,
@@ -1309,7 +716,7 @@
 
 /* Sets the LU reject callback. */
 int qmidev_set_lu_reject_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_lu_reject_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1317,14 +724,14 @@
 
 /* Mobile IP status callback type.  ip_status is the new status. */
 typedef void (*qmidev_mobile_ip_status_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_mobile_ip_status ip_status);
 
 /* Sets the mobile IP status callback. */
 int qmidev_set_mobile_ip_status_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_mobile_ip_status_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1335,7 +742,7 @@
  * idx is the message index within that storage location.
  */
 typedef void (*qmidev_new_sms_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_sms_storage storage,
@@ -1343,7 +750,7 @@
 
 /* Sets the new SMS callback. */
 int qmidev_set_new_sms_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_new_sms_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1358,7 +765,7 @@
  * alert.
  */
 typedef void (*qmidev_omadm_alert_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   int type,
@@ -1366,7 +773,7 @@
 
 /* Sets the OMA-DM alert callback. */
 int qmidev_set_omadm_alert_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_omadm_alert_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1374,14 +781,14 @@
 
 /* OMA-DM state callback type.  state is the new OMA-DM state. */
 typedef void (*qmidev_omadm_state_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_omadm_state state);
 
 /* Sets the OMA-DM state callback. */
 int qmidev_set_omadm_state_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_omadm_state_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1394,7 +801,7 @@
  * state.
  */
 typedef void (*qmidev_pds_state_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   int enabled,
@@ -1402,7 +809,7 @@
 
 /* Sets the PDS state callback. */
 int qmidev_set_pds_state_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_pds_state_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1410,14 +817,14 @@
 
 /* Power state callback type.  state is the new power state of the device. */
 typedef void (*qmidev_power_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_power_state state);
 
 /* Sets the power state callback. */
 int qmidev_set_power_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_power_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1425,14 +832,14 @@
 
 /* RF info callback type.  info is the RF info. */
 typedef void (*qmidev_rf_info_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   const qmidev_rf_info info);
 
 /* Sets the RF info callback. */
 int qmidev_set_rf_info_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_rf_info_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
@@ -1443,30 +850,30 @@
  * indicator.
  */
 typedef void (*qmidev_roaming_indicator_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_roaming_indicator indicator);
 
 /* Sets the roaming indicator callback. */
 int qmidev_set_roaming_indicator_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_roaming_indicator_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
   void *set_context);
 
-/* (Packet data) session state callback type.  state is the new state. */
-typedef void (*qmidev_session_state_callback_fn)(
-  qmidev *dev,
+/* packet_status callback type.  state is the new state. */
+typedef void (*qmidev_packet_status_callback_fn)(
+  struct qmidev *qmidev,
   void *context,
   int status,
-  qmidev_session_state state);
+  qmidev_packet_status packet_status);
 
-/* Sets the session state callback. */
-int qmidev_set_session_state_callback(
-  qmidev *dev,
-  qmidev_session_state_callback_fn callback,
+/* Sets the packet status callback. */
+int qmidev_set_packet_status_callback(
+  struct qmidev *qmidev,
+  qmidev_packet_status_callback_fn callback,
   void *context,
   qmidev_response_fn set_callback,
   void *set_context);
@@ -1476,7 +883,7 @@
  * strength has changed, and strength is the new strength.
  */
 typedef void (*qmidev_signal_strength_callback_fn)(
-  qmidev *dev,
+  struct qmidev *qmidev,
   void *context,
   int status,
   qmidev_radio_interface interface,
@@ -1488,7 +895,7 @@
  * num_thresholds signal strength thresholds.
  */
 int qmidev_set_signal_strength_callback(
-  qmidev *dev,
+  struct qmidev *qmidev,
   qmidev_signal_strength_callback_fn callback,
   void *context,
   size_t num_thresholds,
diff --git a/src/Makefile b/src/Makefile
index 7919a50..ae08818 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -16,6 +16,7 @@
 	qmidev.c \
 	qmidms.c \
 	qmimsg.c \
+	qmiwds.c \
 	util.c
 
 UNITTESTS = \
@@ -23,8 +24,9 @@
 TESTS = \
 	connect_disconnect_test \
 	get_cid_stress_test \
-	power_test \
-	qmiinfo
+	power_stress_test \
+	qmiinfo \
+	start_network_stress_test
 
 PC_CFLAGS := $(shell pkg-config --cflags glib-2.0)
 CFLAGS += -fpic -I ../include $(PC_CFLAGS)
@@ -54,20 +56,24 @@
 get_cid_stress_test: get_cid_stress_test.o | libqmi.so
 	$(LINK.c)
 
-power_test: power_test.o | libqmi.so
+power_stress_test: power_stress_test.o | libqmi.so
 	$(LINK.c)
 
 qmiinfo: qmiinfo.o | libqmi.so
 	$(LINK.c)
 
+start_network_stress_test: start_network_stress_test.o | libqmi.so
+	$(LINK.c)
+
 install:
 	install -d $(DESTDIR)$(LIBDIR)
 	install -m755 libqmi.so $(DESTDIR)$(LIBDIR)
 	install -d $(DESTDIR)$(SBINDIR)
 	install -m755 connect_disconnect_test $(DESTDIR)$(SBINDIR)
 	install -m755 get_cid_stress_test $(DESTDIR)$(SBINDIR)
-	install -m755 power_test $(DESTDIR)$(SBINDIR)
+	install -m755 power_stress_test $(DESTDIR)$(SBINDIR)
 	install -m755 qmiinfo $(DESTDIR)$(SBINDIR)
+	install -m755 start_network_stress_test $(DESTDIR)$(SBINDIR)
 
 clean:
 	rm -f *.o libqmi.so $(UNITTESTS) $(TESTS)
diff --git a/src/power_stress_test.c b/src/power_stress_test.c
new file mode 100644
index 0000000..23c022b
--- /dev/null
+++ b/src/power_stress_test.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libqmi.h>
+
+struct context {
+  qmidev_power_state last_state;
+  int done;
+};
+
+static void set_power(struct qmidev *qmidev, void *data, int status)
+{
+  assert(status == 0);
+  printf("set_power: status = %d\n", status);
+  qmidev = qmidev;
+  data = data;
+}
+
+static void set_next_state(struct qmidev *qmidev, struct context *context)
+{
+  qmidev_power_state next_state = 1 - context->last_state;
+  int result = qmidev_set_power(qmidev, next_state, &set_power, context);
+  assert(result == 0);
+}
+
+static void power_callback(struct qmidev *qmidev, void *data, int status,
+                           qmidev_power_state state)
+{
+  struct context *context = data;
+
+  assert(status == 0);
+  printf("power_callback: status = %d state = %d\n", status, state);
+  context->last_state = state;
+  set_next_state(qmidev, context);
+}
+
+static void got_power(struct qmidev *qmidev, void *data, int status,
+                      qmidev_power_state state)
+{
+  struct context *context = data;
+
+  assert(status == 0);
+  printf("got_power: status = %d state = %d\n", status, state);
+  context->last_state = state;
+  set_next_state(qmidev, context);
+}
+
+static void set_power_callback(struct qmidev *qmidev, void *data, int status)
+{
+  assert(status == 0);
+  printf("set_power_callback: status %d\n", status);
+  int result = qmidev_get_power(qmidev, &got_power, data);
+  assert(result == 0);
+}
+
+static void connected(struct qmidev *qmidev, void *data, int status)
+{
+  assert(status == 0);
+  int result = qmidev_set_power_callback(qmidev,
+                                         &power_callback, data,
+                                         &set_power_callback, data);
+  assert(result == 0);
+}
+
+int main(void)
+{
+  struct context context;
+  int result;
+
+  context.done = 0;
+
+  struct qmidev *qmidev = qmidev_new_file("/dev/cdc-wdm0");
+  assert(qmidev);
+
+  result = qmidev_connect(qmidev, &connected, &context);
+  assert(result == 0);
+
+  while (!context.done) {
+    result = qmidev_process(qmidev);
+    assert(result == 0);
+  }
+
+  return 0;
+}
diff --git a/src/qmi.h b/src/qmi.h
index fcf0555..188fa40 100644
--- a/src/qmi.h
+++ b/src/qmi.h
@@ -96,6 +96,12 @@
   QMIDMS_EVENT_WIRELESS_DISABLE_STATE
 };
 
+enum {
+  QMIWDS_MSG_EVENT         = 0x0001,
+  QMIWDS_MSG_START_NETWORK = 0x0020,
+  QMIWDS_MSG_STOP_NETWORK  = 0x0021,
+  QMIWDS_MSG_PACKET_STATUS = 0x0022
+};
 
 /**
  * Gets the result from a TLV message.
diff --git a/src/qmidev.c b/src/qmidev.c
index 23ca917..246ee7b 100644
--- a/src/qmidev.c
+++ b/src/qmidev.c
@@ -28,6 +28,7 @@
 #include "qmidev.h"
 #include "qmidms.h"
 #include "qmimsg.h"
+#include "qmiwds.h"
 #include "util.h"
 
 static const int qmidev_log_unhandled_messages = 0;
@@ -47,48 +48,27 @@
   int connected;
 };
 
-enum {
-  MATCH_SERVICE     = 0x01,
-  MATCH_CLIENT      = 0x02,
-  MATCH_QMI_FLAGS   = 0x04,
-  MATCH_TRANSACTION = 0x08,
-  MATCH_MESSAGE     = 0x10
-};
-
-/**
- * Callback type for qmidev_listen.
- *
- * Will be called when a message matching the listen criteria is received.
- *
- * @qmidev: the QMI device on which a message was received
- * @context: the context passed to qmidev_listen
- * @message: the message received
- *
- * The callback should return zero to continue listening for such messages,
- * or non-zero to remove the listener.
- */
-typedef int (*qmidev_listen_callback_fn)(struct qmidev *qmidev,
-                                         void *context,
-                                         struct qmimsg *message);
-
-struct listen_criteria {
-  int flags;
-  uint8_t service;
-  uint8_t client;
-  uint8_t qmi_flags;
-  uint16_t transaction;
-  uint16_t message;
+struct callback {
+  callback_fn func;
+  void *context;
 };
 
 struct listener {
+  struct client *client;
   struct listen_criteria criteria;
   qmidev_listen_callback_fn callback;
   void *context;
 };
 
-struct callback {
-  callback_fn func;
-  void *context;
+struct client {
+  struct service *service;
+
+  uint8_t client_id;
+  uint16_t next_transaction_id;
+  struct listener *event_listener;
+  GList *events;
+
+  void *priv;
 };
 
 static int service_is_control(struct service *service)
@@ -171,6 +151,7 @@
 }
 
 struct listener *qmidev_listen(struct qmidev *qmidev,
+                               struct client *client,
                                struct listen_criteria *criteria,
                                qmidev_listen_callback_fn callback,
                                void *context)
@@ -181,6 +162,7 @@
 
   struct listener *listener = g_slice_new(struct listener);
 
+  listener->client = client;
   listener->criteria = *criteria;
   listener->callback = callback;
   listener->context = context;
@@ -217,23 +199,22 @@
   struct listen_criteria *crit = &listener->criteria;
   int flags = crit->flags;
 
-  if ((flags & MATCH_SERVICE) &&
-      (crit->service != context->msg_service))
+  if (listener->client->service->service_id != context->msg_service)
     return;
 
-  if ((flags & MATCH_CLIENT) &&
-      (crit->client != context->msg_client))
+  if ((context->msg_client != QMI_CID_BROADCAST) &&
+      (listener->client->client_id != context->msg_client))
     return;
 
-  if ((flags & MATCH_QMI_FLAGS) &&
+  if ((flags & LISTEN_MATCH_QMI_FLAGS) &&
       (crit->qmi_flags != context->msg_qmi_flags))
     return;
 
-  if ((flags & MATCH_TRANSACTION) &&
+  if ((flags & LISTEN_MATCH_TRANSACTION) &&
       (crit->transaction != context->msg_transaction))
     return;
 
-  if ((flags & MATCH_MESSAGE) &&
+  if ((flags & LISTEN_MATCH_MESSAGE) &&
       (crit->message != context->msg_message))
     return;
 
@@ -267,17 +248,6 @@
 
 
 
-struct client {
-  struct service *service;
-
-  uint8_t client_id;
-  uint16_t next_transaction_id;
-  struct listener *event_listener;
-  GList *events;
-
-  void *priv;
-};
-
 static struct client *make_ctl_client(struct qmidev *qmidev)
 {
   struct client *client = g_slice_new(struct client);
@@ -380,16 +350,15 @@
   if (client->service->create_fn)
     client->service->create_fn(qmidev, client);
 
-  event_criteria.flags = MATCH_SERVICE | MATCH_CLIENT | MATCH_QMI_FLAGS |
-                         MATCH_MESSAGE;
-  event_criteria.service = client->service->service_id;
-  event_criteria.client = client->client_id;
+  event_criteria.flags = LISTEN_MATCH_QMI_FLAGS |
+                         LISTEN_MATCH_MESSAGE;
   if (service_is_control(client->service))
     event_criteria.qmi_flags = QMI_FLAG_CTL_INDICATION;
   else
     event_criteria.qmi_flags = QMI_FLAG_SVC_INDICATION;
   event_criteria.message = client->service->event_msg_id;
-  client->event_listener = qmidev_listen(qmidev,
+
+  client->event_listener = qmidev_listen(qmidev, client,
                                          &event_criteria,
                                          &event_message_received,
                                          client);
@@ -586,10 +555,9 @@
       goto fail;
   }
 
-  response_criteria.flags = MATCH_SERVICE | MATCH_CLIENT | MATCH_TRANSACTION |
-                            MATCH_QMI_FLAGS | MATCH_MESSAGE;
-  response_criteria.service = client->service->service_id;
-  response_criteria.client = client->client_id;
+  response_criteria.flags = LISTEN_MATCH_TRANSACTION |
+                            LISTEN_MATCH_QMI_FLAGS |
+                            LISTEN_MATCH_MESSAGE;
   if (service_is_control(client->service))
     response_criteria.qmi_flags = QMI_FLAG_CTL_RESPONSE;
   else
@@ -598,7 +566,7 @@
   response_criteria.message = msg_id;
 
   transaction->response_listener =
-          qmidev_listen(qmidev,
+          qmidev_listen(qmidev, client,
                         &response_criteria,
                         &response_received,
                         transaction);
@@ -851,6 +819,7 @@
   context->pending_services = g_queue_new();
 
   g_queue_push_tail(context->pending_services, &qmidms_service);
+  g_queue_push_tail(context->pending_services, &qmiwds_service);
 
   connect_next_service(qmidev, context);
 
@@ -1049,33 +1018,3 @@
   g_list_free(qmidev->callbacks);
   qmidev->callbacks = NULL;
 }
-
-
-
-
-/**
- * Starts listening for QMI messages matching certain criteria.
- *
- * @qmidev: the QMI device
- * @criteria: the listen criteria (see struct listen_criteria)
- * @callback: the callback to call when a matching message is received
- * @context: the context with which to call the callback
- *
- * Returns a pointer to a struct listener that can be passed to
- * qmidev_cancel_listen to stop listening.
- */
-
-struct listener *qmidev_listen(struct qmidev *qmidev,
-                               struct listen_criteria *criteria,
-                               qmidev_listen_callback_fn callback,
-                               void *context);
-
-/**
- * Stops listening for QMI messages matching certain criteria.
- *
- * @qmidev: the QMI device
- * @listener: the listener returned by qmidev_listen
- */
-void qmidev_cancel_listen(struct qmidev *qmidev, struct listener *listener);
-
-
diff --git a/src/qmidev.h b/src/qmidev.h
index 74e3656..4d3f368 100644
--- a/src/qmidev.h
+++ b/src/qmidev.h
@@ -233,6 +233,43 @@
                                   int status,
                                   struct qmimsg *resp_msg);
 
+enum {
+  LISTEN_MATCH_QMI_FLAGS   = 0x1,
+  LISTEN_MATCH_TRANSACTION = 0x2,
+  LISTEN_MATCH_MESSAGE     = 0x4
+};
+
+struct listen_criteria {
+  int flags;
+  uint8_t qmi_flags;
+  uint16_t transaction;
+  uint16_t message;
+};
+
+/**
+ * Callback type for qmidev_listen.
+ *
+ * Will be called when a message matching the listen criteria is received.
+ *
+ * @qmidev: the QMI device on which a message was received
+ * @context: the context passed to qmidev_listen
+ * @message: the message received
+ *
+ * The callback should return zero to continue listening for such messages,
+ * or non-zero to remove the listener.
+ */
+typedef int (*qmidev_listen_callback_fn)(struct qmidev *qmidev,
+                                         void *context,
+                                         struct qmimsg *message);
+
+struct listener *qmidev_listen(struct qmidev *qmidev,
+                               struct client *client,
+                               struct listen_criteria *criteria,
+                               qmidev_listen_callback_fn callback,
+                               void *context);
+
+void qmidev_cancel_listen(struct qmidev *qmidev, struct listener *listener);
+
 /**
  * Sends a QMI message.
  * Returns zero on success, errno on failure.
diff --git a/src/qmiwds.c b/src/qmiwds.c
new file mode 100644
index 0000000..d34de71
--- /dev/null
+++ b/src/qmiwds.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * qmiwds: QMI calls on the WDS (Wireless Data Service) interface
+ *
+ * Sources used in writing this file (see README for links):
+ * [GobiNet]/QMI.c
+ * [GobiNet]/QMIDevice.c
+ * [cros-kerne]/drivers/net/usb/gobi/qmi.c
+ * [cros-kerne]/drivers/net/usb/gobi/qmidevice.c
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <libqmi.h>
+
+#include "error.h"
+#include "qmi.h"
+#include "qmidev.h"
+#include "qmimsg.h"
+
+struct qmiwds_priv {
+  struct listener *packet_status_listener;
+  qmidev_packet_status_callback_fn packet_status_callback;
+  void *packet_status_context;
+};
+
+static int packet_status_changed(struct qmidev *qmidev,
+                                 void *data,
+                                 struct qmimsg *message)
+{
+  struct client *client = data;
+  struct qmiwds_priv *priv = client_priv(client);
+  uint16_t state = 0xfe;
+  int result;
+
+  /* unused */
+  qmidev = qmidev;
+  client = client;
+
+  result = qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(state), &state);
+
+  if (priv->packet_status_callback)
+    priv->packet_status_callback(qmidev,
+                                 priv->packet_status_context,
+                                 result,
+                                 state);
+
+  return 0;
+}
+
+static void client_create(struct qmidev *qmidev, struct client *client)
+{
+  struct listen_criteria criteria;
+
+  qmidev = qmidev;
+  struct qmiwds_priv *priv = g_slice_new(struct qmiwds_priv);
+
+  criteria.flags = LISTEN_MATCH_QMI_FLAGS |
+                   LISTEN_MATCH_MESSAGE;
+  criteria.qmi_flags = QMI_FLAG_SVC_INDICATION;
+  criteria.message = QMIWDS_MSG_PACKET_STATUS;
+
+  priv->packet_status_listener = qmidev_listen(qmidev,
+                                               client,
+                                               &criteria,
+                                               &packet_status_changed,
+                                               client);
+
+  priv->packet_status_callback = NULL;
+  priv->packet_status_context = NULL;
+  client_set_priv(client, priv);
+}
+
+static void client_destroy(struct qmidev *qmidev, struct client *client)
+{
+  qmidev = qmidev;
+  struct qmiwds_priv *priv = client_priv(client);
+  g_slice_free(struct qmiwds_priv, priv);
+}
+
+struct service qmiwds_service = {
+  .service_id   = QMI_SVC_WDS,
+  .event_msg_id = QMIWDS_MSG_EVENT,
+  .create_fn    = &client_create,
+  .destroy_fn   = &client_destroy
+};
+
+struct get_packet_status_context {
+  qmidev_packet_status_response_fn callback;
+  void *context;
+};
+
+static void get_packet_status_response(struct qmidev *qmidev,
+                                       void *data,
+                                       int result,
+                                       struct qmimsg *message)
+{
+  struct get_packet_status_context *context = data;
+  uint32_t state = 0;
+
+  assert(qmidev);
+  assert(data);
+
+  if (result)
+    goto out;
+
+  result = qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(state), &state);
+
+out:
+  context->callback(qmidev, context->context, result, state);
+  g_slice_free(struct get_packet_status_context, context);
+}
+
+int qmidev_get_packet_status(struct qmidev *qmidev,
+                             qmidev_packet_status_response_fn caller_callback,
+                             void *caller_context)
+{
+  struct get_packet_status_context *context;
+  int result;
+
+  context = g_slice_new(struct get_packet_status_context);
+  context->callback = caller_callback;
+  context->context  = caller_context;
+
+  result = qmidev_request(qmidev, &qmiwds_service,
+                          QMIWDS_MSG_PACKET_STATUS,
+                          NULL, NULL,
+                          &get_packet_status_response, context);
+  if (result)
+    g_slice_free(struct get_packet_status_context, context);
+  return result;
+}
+
+int qmidev_set_packet_status_callback(
+    struct qmidev *qmidev,
+    qmidev_packet_status_callback_fn callback,
+    void *context,
+    qmidev_response_fn set_callback,
+    void *set_context)
+{
+  struct client *client = qmidev_default_client(qmidev, &qmiwds_service);
+  struct qmiwds_priv *priv = client_priv(client);
+  priv->packet_status_callback = callback;
+  priv->packet_status_context = context;
+  /* TODO: Call later. */
+  if (set_callback)
+    set_callback(qmidev, set_context, 0);
+  return 0;
+}
+
+struct start_network_context {
+  qmidev_start_network_response_fn callback;
+  void *context;
+};
+
+enum {
+  QMIWDS_TLV_START_NETWORK_CALL_END_REASON = 0x10,
+  QMIWDS_TLV_START_NETWORK_VERBOSE_CALL_END_REASON = 0x11
+};
+
+static void start_network_response(struct qmidev *qmidev,
+                                   void *data,
+                                   int result,
+                                   struct qmimsg *message)
+{
+  struct start_network_context *context = data;
+  /* Arbitrary recognizable values, to debug whether the TLV is present. */
+  qmidev_session_id session_id = 0xfefefefe;
+  uint16_t call_end_type = 0xfefe;
+
+  qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(session_id), &session_id);
+  qmimsg_tlv_get(message, QMIWDS_TLV_START_NETWORK_CALL_END_REASON,
+                 sizeof(call_end_type), &call_end_type);
+
+  /* TODO: Verbose call end reason? */
+
+  context->callback(qmidev, context->context, result,
+                    session_id, call_end_type);
+  g_slice_free(struct start_network_context, context);
+}
+
+int qmidev_start_network(struct qmidev *qmidev,
+                         qmidev_start_network_response_fn caller_callback,
+                         void *caller_context)
+{
+  struct start_network_context *context;
+  int result;
+
+  context = g_slice_new(struct start_network_context);
+  context->callback = caller_callback;
+  context->context = caller_context;
+
+  result = qmidev_request(qmidev, &qmiwds_service,
+                          QMIWDS_MSG_START_NETWORK,
+                          NULL, NULL,
+                          &start_network_response, context);
+  if (result)
+    g_slice_free(struct start_network_context, context);
+  return result;
+}
+
+static int stop_network_request(void *data,
+                                struct qmimsg *message)
+{
+  qmidev_session_id *session_id = data;
+  return qmimsg_tlv_add(message,
+                        QMI_TLV_VALUE,
+                        sizeof(*session_id),
+                        session_id);
+}
+
+int qmidev_stop_network(struct qmidev *qmidev,
+                        qmidev_session_id session_id,
+                        qmidev_response_fn caller_callback,
+                        void *caller_context)
+{
+  return qmidev_void_request(qmidev, &qmiwds_service,
+                             QMIWDS_MSG_STOP_NETWORK,
+                             &stop_network_request, &session_id,
+                             caller_callback, caller_context);
+}
diff --git a/src/qmiwds.h b/src/qmiwds.h
new file mode 100644
index 0000000..2e652cf
--- /dev/null
+++ b/src/qmiwds.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * qmiwds: QMI calls on WDS (Wireless Data Service)
+ *
+ * (See qmiwds.c for sources.)
+ */
+
+#ifndef LIBQMI_QMIWDS_H
+#define LIBQMI_QMIWDS_H
+
+#include <stdint.h>
+
+#include "qmidev.h"
+
+struct service qmiwds_service;
+
+#endif /* LIBQMI_QMIWDS_H */
diff --git a/src/start_network_stress_test.c b/src/start_network_stress_test.c
new file mode 100644
index 0000000..db7a125
--- /dev/null
+++ b/src/start_network_stress_test.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <libqmi.h>
+
+struct context {
+  uint32_t session_id;
+};
+
+static void stopped(struct qmidev *qmidev, void *data, int status);
+static void started(struct qmidev *qmidev, void *data, int status,
+                    qmidev_session_id session_id, uint16_t end);
+
+static void start(struct qmidev *qmidev, struct context *context)
+{
+  assert(!context->session_id);
+  int result = qmidev_start_network(qmidev, &started, context);
+  assert(result == 0);
+}
+
+static void stop(struct qmidev *qmidev, struct context *context)
+{
+  assert(context->session_id);
+  int result = qmidev_stop_network(qmidev, context->session_id,
+                                   &stopped, context);
+  assert(result == 0);
+}
+
+static void started(struct qmidev *qmidev, void *data, int status,
+                    qmidev_session_id session_id, uint16_t end)
+{
+  /* unused */
+  qmidev = qmidev;
+
+  struct context *context = data;
+  printf("started: status = %d session_id = 0x%08x call_end_reason = %d\n",
+         status, session_id, end);
+  assert(status == 0);
+  context->session_id = session_id;
+  stop(qmidev, context);
+}
+
+static void stopped(struct qmidev *qmidev, void *data, int status)
+{
+  struct context *context = data;
+  printf("stopped: status = %d\n", status);
+  assert(status == 0);
+  context->session_id = 0;
+  start(qmidev, context);
+}
+
+static void packet_status_changed(struct qmidev *qmidev,
+                                  void *data,
+                                  int status,
+                                  qmidev_packet_status state)
+{
+  printf("packet_status_changed: status = %d state = %d\n", status, state);
+  qmidev = qmidev;
+  data = data;
+}
+
+static void connected(struct qmidev *qmidev, void *data, int status)
+{
+  assert(status == 0);
+  int result = qmidev_set_packet_status_callback(qmidev,
+                                                 &packet_status_changed, data,
+                                                 NULL, NULL);
+  assert(result == 0);
+  start(qmidev, data);
+}
+
+int main(void)
+{
+  struct context context;
+  int result;
+
+  context.session_id = 0;
+
+  struct qmidev *qmidev = qmidev_new_file("/dev/cdc-wdm0");
+  assert(qmidev);
+
+  result = qmidev_connect(qmidev, &connected, &context);
+  assert(result == 0);
+
+  while (1) {
+    result = qmidev_process(qmidev);
+    assert(result == 0);
+  }
+
+  return 0;
+}
diff --git a/src/util.c b/src/util.c
index a2b792f..266d4ba 100644
--- a/src/util.c
+++ b/src/util.c
@@ -17,7 +17,7 @@
   for (i = 0; i < len; i++) {
     if (i > 0 && (i % 32 == 0))
       fprintf(stderr, "\n");
-    fprintf(stderr, " %02x", buf[i]);
+    fprintf(stderr, " %02x", buf[i] & 0xff);
   }
   fprintf(stderr, "\n");
 }