blob: 45854097c22765473bc161b16c15aeb1d2fa11fa [file] [log] [blame]
/*
*
* neard - Near Field Communication manager
*
* Copyright (C) 2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <gdbus.h>
#include "near.h"
/* We check for the tag being present every 2 seconds */
#define CHECK_PRESENCE_PERIOD 2
static DBusConnection *connection = NULL;
static GHashTable *adapter_hash;
enum near_adapter_rf_mode {
NEAR_ADAPTER_RF_MODE_IDLE = 0,
NEAR_ADAPTER_RF_MODE_INITIATOR = 1,
NEAR_ADAPTER_RF_MODE_TARGET = 2
};
#define NEAR_ADAPTER_MODE_INITIATOR 0x1
#define NEAR_ADAPTER_MODE_TARGET 0x2
#define NEAR_ADAPTER_MODE_DUAL 0x3
struct near_adapter {
char *path;
char *name;
uint32_t idx;
uint32_t protocols;
uint32_t poll_mode;
enum near_adapter_rf_mode rf_mode;
bool powered;
bool polling;
bool constant_poll;
bool dep_up;
GHashTable *tags;
struct near_tag *tag_link;
int tag_sock;
GHashTable *devices;
struct near_device *device_link;
int device_sock;
GIOChannel *channel;
guint watch;
GList *ioreq_list;
guint presence_timeout;
guint dep_timer;
};
struct near_adapter_ioreq {
uint32_t target_idx;
near_recv cb;
unsigned char buf[1024];
size_t len;
void *data;
};
/* HACK HACK */
#ifndef AF_NFC
#define AF_NFC 39
#endif
static void free_adapter(gpointer data)
{
struct near_adapter *adapter = data;
if (adapter->presence_timeout > 0)
g_source_remove(adapter->presence_timeout);
if (adapter->dep_timer > 0)
g_source_remove(adapter->dep_timer);
g_free(adapter->name);
g_free(adapter->path);
g_hash_table_destroy(adapter->tags);
g_hash_table_destroy(adapter->devices);
g_free(adapter);
}
static void free_tag(gpointer data)
{
struct near_tag *tag = data;
__near_tag_remove(tag);
}
static void free_device(gpointer data)
{
struct near_device *device = data;
__near_device_remove(device);
}
static char *rf_mode_to_string(struct near_adapter *adapter)
{
switch (adapter->rf_mode) {
case NEAR_ADAPTER_RF_MODE_IDLE:
return "Idle";
case NEAR_ADAPTER_RF_MODE_INITIATOR:
return "Initiator";
case NEAR_ADAPTER_RF_MODE_TARGET:
return "Target";
}
return NULL;
}
static void polling_changed(struct near_adapter *adapter)
{
g_dbus_emit_property_changed(connection, adapter->path,
NFC_ADAPTER_INTERFACE, "Polling");
}
static void rf_mode_changed(struct near_adapter *adapter)
{
g_dbus_emit_property_changed(connection, adapter->path,
NFC_ADAPTER_INTERFACE, "Mode");
}
static int adapter_start_poll(struct near_adapter *adapter)
{
int err;
uint32_t im_protos, tm_protos;
if (g_hash_table_size(adapter->tags) > 0) {
DBG("Clearing tags");
g_hash_table_remove_all(adapter->tags);
}
if (g_hash_table_size(adapter->devices) > 0) {
DBG("Clearing devices");
g_hash_table_remove_all(adapter->devices);
}
DBG("Poll mode 0x%x", adapter->poll_mode);
im_protos = tm_protos = 0;
if (adapter->poll_mode & NEAR_ADAPTER_MODE_INITIATOR)
im_protos = adapter->protocols;
if (adapter->poll_mode & NEAR_ADAPTER_MODE_TARGET)
tm_protos = adapter->protocols;
err = __near_netlink_start_poll(adapter->idx, im_protos, tm_protos);
if (err < 0)
return err;
adapter->polling = true;
polling_changed(adapter);
return 0;
}
static gboolean property_get_mode(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct near_adapter *adapter = user_data;
const char *rf_mode;
rf_mode = rf_mode_to_string(adapter);
if (!rf_mode)
return FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &rf_mode);
return TRUE;
}
static gboolean property_get_polling(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct near_adapter *adapter = user_data;
dbus_bool_t val;
val = adapter->polling;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
return TRUE;
}
static gboolean property_get_powered(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct near_adapter *adapter = user_data;
dbus_bool_t val;
val = adapter->powered;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
return TRUE;
}
static void set_powered(GDBusPendingPropertySet id, dbus_bool_t powered,
void *data)
{
struct near_adapter *adapter = data;
int err;
err = __near_netlink_adapter_enable(adapter->idx, powered);
if (err < 0) {
if (err == -EALREADY) {
if (powered)
g_dbus_pending_property_error(id,
NFC_ERROR_INTERFACE ".Failed",
"Device already enabled");
else
g_dbus_pending_property_error(id,
NFC_ERROR_INTERFACE ".Failed",
"Device already disabled");
}
g_dbus_pending_property_error(id,
NFC_ERROR_INTERFACE ".Failed",
strerror(err));
return;
}
g_dbus_pending_property_success(id);
adapter->powered = powered;
g_dbus_emit_property_changed(connection, adapter->path,
NFC_ADAPTER_INTERFACE, "Powered");
}
static void property_set_powered(const GDBusPropertyTable *property,
DBusMessageIter *value,
GDBusPendingPropertySet id, void *data)
{
dbus_bool_t powered;
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
g_dbus_pending_property_error(id,
NFC_ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call");
return;
}
dbus_message_iter_get_basic(value, &powered);
set_powered(id, powered, data);
}
static void append_protocols(DBusMessageIter *iter,
struct near_adapter *adapter)
{
const char *str;
DBG("protocols 0x%x", adapter->protocols);
if (adapter->protocols & NFC_PROTO_FELICA_MASK) {
str = "Felica";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
if (adapter->protocols & NFC_PROTO_MIFARE_MASK) {
str = "MIFARE";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
if (adapter->protocols & NFC_PROTO_JEWEL_MASK) {
str = "Jewel";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
if ((adapter->protocols & NFC_PROTO_ISO14443_MASK) ||
(adapter->protocols & NFC_PROTO_ISO14443_B_MASK)) {
str = "ISO-DEP";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
if (adapter->protocols & NFC_PROTO_NFC_DEP_MASK) {
str = "NFC-DEP";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
if (adapter->protocols & NFC_PROTO_ISO15693_MASK) {
str = "ISO-15693";
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
}
}
static gboolean property_get_protocols(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct near_adapter *adapter = user_data;
DBusMessageIter dict;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &dict);
append_protocols(&dict, adapter);
dbus_message_iter_close_container(iter, &dict);
return TRUE;
}
static DBusMessage *start_poll_loop(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct near_adapter *adapter = data;
const char *dbus_mode;
int err;
DBG("conn %p", conn);
if (!adapter->powered) {
near_error("Adapter is down, can not start polling");
return __near_error_failed(msg, ENODEV);
}
dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dbus_mode,
DBUS_TYPE_INVALID);
DBG("Mode %s", dbus_mode);
if (g_strcmp0(dbus_mode, "Initiator") == 0)
adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR;
else if (g_strcmp0(dbus_mode, "Target") == 0)
adapter->poll_mode = NEAR_ADAPTER_MODE_TARGET;
else if (g_strcmp0(dbus_mode, "Dual") == 0)
adapter->poll_mode = NEAR_ADAPTER_MODE_DUAL;
else
adapter->poll_mode = NEAR_ADAPTER_MODE_INITIATOR;
err = adapter_start_poll(adapter);
if (err < 0)
return __near_error_failed(msg, -err);
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static DBusMessage *stop_poll_loop(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct near_adapter *adapter = data;
int err;
DBG("conn %p", conn);
if (!adapter->polling)
return __near_error_not_polling(msg);
err = __near_netlink_stop_poll(adapter->idx);
if (err < 0)
return __near_error_failed(msg, -err);
adapter->polling = false;
polling_changed(adapter);
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx,
int status);
static gboolean check_presence(gpointer user_data)
{
struct near_adapter *adapter = user_data;
struct near_tag *tag;
int err;
DBG("");
if (!adapter)
return FALSE;
tag = adapter->tag_link;
if (!tag)
goto out_err;
err = __near_tag_check_presence(tag, tag_present_cb);
if (err < 0) {
DBG("Could not check target presence");
goto out_err;
}
return FALSE;
out_err:
near_adapter_disconnect(adapter->idx);
if (adapter->constant_poll)
adapter_start_poll(adapter);
return FALSE;
}
static gboolean dep_timer(gpointer user_data)
{
struct near_adapter *adapter = user_data;
DBG("");
if (!adapter)
return FALSE;
adapter_start_poll(adapter);
return FALSE;
}
static void tag_present_cb(uint32_t adapter_idx, uint32_t target_idx,
int status)
{
struct near_adapter *adapter;
DBG("");
adapter = g_hash_table_lookup(adapter_hash,
GINT_TO_POINTER(adapter_idx));
if (!adapter)
return;
if (status < 0) {
DBG("Tag is gone");
near_adapter_disconnect(adapter->idx);
if (adapter->constant_poll)
adapter_start_poll(adapter);
return;
}
adapter->presence_timeout =
g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
check_presence, adapter);
}
void __near_adapter_start_check_presence(uint32_t adapter_idx,
uint32_t target_idx)
{
struct near_adapter *adapter;
DBG("");
adapter = g_hash_table_lookup(adapter_hash,
GINT_TO_POINTER(adapter_idx));
if (!adapter)
return;
adapter->presence_timeout =
g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
check_presence, adapter);
}
void __near_adapter_stop_check_presence(uint32_t adapter_idx,
uint32_t target_idx)
{
struct near_adapter *adapter;
DBG("");
adapter = g_hash_table_lookup(adapter_hash,
GINT_TO_POINTER(adapter_idx));
if (!adapter)
return;
if (adapter->presence_timeout > 0)
g_source_remove(adapter->presence_timeout);
}
static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("StartPollLoop", GDBUS_ARGS({"name", "s"}), NULL,
start_poll_loop) },
{ GDBUS_METHOD("StopPollLoop", NULL, NULL, stop_poll_loop) },
{ },
};
static const GDBusPropertyTable adapter_properties[] = {
{ "Mode", "s", property_get_mode },
{ "Powered", "b", property_get_powered, property_set_powered },
{ "Polling", "b", property_get_polling },
{ "Protocols", "as", property_get_protocols },
{ }
};
struct near_adapter *__near_adapter_create(uint32_t idx,
const char *name, uint32_t protocols, bool powered)
{
struct near_adapter *adapter;
bool powered_setting;
adapter = g_try_malloc0(sizeof(struct near_adapter));
if (!adapter)
return NULL;
adapter->name = g_strdup(name);
if (!adapter->name) {
g_free(adapter);
return NULL;
}
powered_setting = near_setting_get_bool("DefaultPowered");
if (powered_setting && !powered &&
!__near_netlink_adapter_enable(idx, powered_setting))
powered = true;
DBG("Powered %d", powered);
adapter->idx = idx;
adapter->protocols = protocols;
adapter->powered = powered;
adapter->constant_poll = near_setting_get_bool("ConstantPoll");
adapter->dep_up = false;
adapter->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, free_tag);
adapter->tag_sock = -1;
adapter->devices = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, free_device);
adapter->device_sock = -1;
adapter->path = g_strdup_printf("%s/nfc%d", NFC_PATH, idx);
return adapter;
}
void __near_adapter_destroy(struct near_adapter *adapter)
{
DBG("");
free_adapter(adapter);
}
const char *__near_adapter_get_path(struct near_adapter *adapter)
{
return adapter->path;
}
struct near_adapter *__near_adapter_get(uint32_t idx)
{
return g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
}
int __near_adapter_set_dep_state(uint32_t idx, bool dep)
{
struct near_adapter *adapter;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
adapter->dep_up = dep;
if (!dep && adapter->constant_poll) {
/*
* The immediate polling may fail if the adapter is busy in
* that very moment. In this case we need to try polling later
* again, so constant polling will work properly.
*/
if(adapter_start_poll(adapter) == -EBUSY) {
near_error("Adapter is busy, retry polling later");
g_timeout_add_seconds(1, dep_timer, adapter);
}
}
if (!dep) {
uint32_t target_idx;
target_idx = __neard_device_get_idx(adapter->device_link);
__near_adapter_remove_target(idx, target_idx);
} else {
if (adapter->dep_timer > 0)
g_source_remove(adapter->dep_timer);
if (!__near_device_register_interface(adapter->device_link))
return -ENODEV;
}
return 0;
}
bool __near_adapter_get_dep_state(uint32_t idx)
{
struct near_adapter *adapter;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return false;
return adapter->dep_up;
}
int __near_adapter_add(struct near_adapter *adapter)
{
uint32_t idx = adapter->idx;
DBG("%s", adapter->path);
if (g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx)))
return -EEXIST;
g_hash_table_insert(adapter_hash, GINT_TO_POINTER(idx), adapter);
DBG("connection %p", connection);
g_dbus_register_interface(connection, adapter->path,
NFC_ADAPTER_INTERFACE,
adapter_methods, NULL,
adapter_properties, adapter, NULL);
return 0;
}
void __near_adapter_remove(struct near_adapter *adapter)
{
DBG("%s", adapter->path);
g_dbus_unregister_interface(connection, adapter->path,
NFC_ADAPTER_INTERFACE);
g_hash_table_remove(adapter_hash, GINT_TO_POINTER(adapter->idx));
}
static void tag_read_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
{
struct near_adapter *adapter;
DBG("status %d", status);
adapter = g_hash_table_lookup(adapter_hash,
GINT_TO_POINTER(adapter_idx));
if (!adapter)
return;
if (status < 0) {
near_adapter_disconnect(adapter->idx);
if (adapter->constant_poll)
adapter_start_poll(adapter);
return;
}
adapter->presence_timeout =
g_timeout_add_seconds(CHECK_PRESENCE_PERIOD,
check_presence, adapter);
}
static void device_read_cb(uint32_t adapter_idx, uint32_t target_idx,
int status)
{
struct near_adapter *adapter;
DBG("status %d", status);
adapter = g_hash_table_lookup(adapter_hash,
GINT_TO_POINTER(adapter_idx));
if (!adapter)
return;
if (status < 0) {
if (adapter->device_link) {
__near_netlink_dep_link_down(adapter->idx);
adapter->device_link = NULL;
}
if (adapter->constant_poll)
adapter_start_poll(adapter);
return;
}
}
static int adapter_add_tag(struct near_adapter *adapter, uint32_t target_idx,
uint32_t protocols,
uint16_t sens_res, uint8_t sel_res,
uint8_t *nfcid, uint8_t nfcid_len,
uint8_t iso15693_dsfid,
uint8_t iso15693_uid_len, uint8_t *iso15693_uid)
{
struct near_tag *tag;
uint32_t tag_type;
int err;
tag = __near_tag_add(adapter->idx, target_idx, protocols,
sens_res, sel_res,
nfcid, nfcid_len,
iso15693_dsfid, iso15693_uid_len, iso15693_uid);
if (!tag)
return -ENODEV;
g_hash_table_insert(adapter->tags, GINT_TO_POINTER(target_idx), tag);
tag_type = __near_tag_get_type(tag);
err = near_adapter_connect(adapter->idx, target_idx, tag_type);
if (err < 0) {
near_error("Could not connect");
return err;
}
err = __near_tag_read(tag, tag_read_cb);
if (err < 0) {
near_error("Could not read the tag");
near_adapter_disconnect(adapter->idx);
__near_adapter_remove_target(adapter->idx, target_idx);
}
return err;
}
static int adapter_add_device(struct near_adapter *adapter,
uint32_t target_idx,
uint8_t *nfcid, uint8_t nfcid_len)
{
struct near_device *device;
int err;
DBG();
device = __near_device_add(adapter->idx, target_idx, nfcid, nfcid_len);
if (!device)
return -ENODEV;
g_hash_table_insert(adapter->devices, GINT_TO_POINTER(target_idx),
device);
/* For p2p, reading is listening for an incoming connection */
err = __near_device_listen(device, device_read_cb);
if (err < 0) {
near_error("Could not read device");
return err;
}
adapter->device_link = device;
if (adapter->dep_up) {
if (!__near_device_register_interface(device))
return -ENODEV;
return 0;
}
err = __near_netlink_dep_link_up(adapter->idx, target_idx,
NFC_COMM_ACTIVE, NFC_RF_INITIATOR);
if (err < 0)
adapter->device_link = NULL;
DBG("Starting DEP timer");
adapter->dep_timer = g_timeout_add_seconds(1, dep_timer, adapter);
return err;
}
int __near_adapter_add_target(uint32_t idx, uint32_t target_idx,
uint32_t protocols, uint16_t sens_res, uint8_t sel_res,
uint8_t *nfcid, uint8_t nfcid_len,
uint8_t iso15693_dsfid,
uint8_t iso15693_uid_len, uint8_t *iso15693_uid)
{
struct near_adapter *adapter;
int ret;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
adapter->polling = false;
polling_changed(adapter);
adapter->rf_mode = NEAR_ADAPTER_RF_MODE_INITIATOR;
rf_mode_changed(adapter);
if (protocols & NFC_PROTO_NFC_DEP_MASK)
ret = adapter_add_device(adapter, target_idx,
nfcid, nfcid_len);
else
ret = adapter_add_tag(adapter, target_idx, protocols,
sens_res, sel_res, nfcid, nfcid_len,
iso15693_dsfid,
iso15693_uid_len, iso15693_uid);
if (ret < 0 && adapter->constant_poll)
adapter_start_poll(adapter);
return ret;
}
int __near_adapter_remove_target(uint32_t idx, uint32_t target_idx)
{
struct near_adapter *adapter;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE;
rf_mode_changed(adapter);
if (g_hash_table_remove(adapter->tags, GINT_TO_POINTER(target_idx)))
return 0;
if (g_hash_table_remove(adapter->devices, GINT_TO_POINTER(target_idx)))
return 0;
return 0;
}
static gboolean poll_error(gpointer user_data)
{
struct near_adapter *adapter = user_data;
bool reset;
DBG("adapter %d", adapter->idx);
reset = near_setting_get_bool("ResetOnError");
if (reset) {
near_error("Resetting nfc%d", adapter->idx);
__near_netlink_adapter_enable(adapter->idx, false);
__near_netlink_adapter_enable(adapter->idx, true);
}
adapter_start_poll(adapter);
return FALSE;
}
int __near_adapter_get_targets_done(uint32_t idx)
{
struct near_adapter *adapter;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
if (g_hash_table_size(adapter->devices) > 0)
return 0;
if (g_hash_table_size(adapter->tags) > 0)
return 0;
near_error("No targets found - Polling error");
adapter->polling = false;
polling_changed(adapter);
g_idle_add(poll_error, adapter);
return 0;
}
int __near_adapter_add_device(uint32_t idx, uint8_t *nfcid, uint8_t nfcid_len)
{
struct near_adapter *adapter;
int ret;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
adapter->polling = false;
adapter->dep_up = true;
adapter->rf_mode = NEAR_ADAPTER_RF_MODE_TARGET;
polling_changed(adapter);
rf_mode_changed(adapter);
ret = adapter_add_device(adapter, 0, nfcid, nfcid_len);
if (ret < 0)
return ret;
return 0;
}
int __near_adapter_remove_device(uint32_t idx)
{
struct near_adapter *adapter;
uint32_t device_idx = 0;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
if (!g_hash_table_remove(adapter->devices, GINT_TO_POINTER(device_idx)))
return 0;
adapter->rf_mode = NEAR_ADAPTER_RF_MODE_IDLE;
rf_mode_changed(adapter);
adapter->dep_up = false;
if (adapter->constant_poll)
adapter_start_poll(adapter);
return 0;
}
static void adapter_flush_rx(struct near_adapter *adapter, int error)
{
GList *list;
for (list = adapter->ioreq_list; list; list = list->next) {
struct near_adapter_ioreq *req = list->data;
if (!req)
continue;
req->cb(NULL, error, req->data);
g_free(req);
}
g_list_free(adapter->ioreq_list);
adapter->ioreq_list = NULL;
}
static gboolean execute_recv_cb(gpointer user_data)
{
struct near_adapter_ioreq *req = user_data;
DBG("data %p", req->data);
req->cb(req->buf, req->len, req->data);
g_free(req);
return FALSE;
}
static gboolean adapter_recv_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
struct near_adapter *adapter = user_data;
struct near_adapter_ioreq *req;
GList *first;
int sk;
DBG("condition 0x%x", condition);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
near_error("Error while reading NFC bytes");
adapter_flush_rx(adapter, -EIO);
near_adapter_disconnect(adapter->idx);
return FALSE;
}
sk = g_io_channel_unix_get_fd(channel);
first = g_list_first(adapter->ioreq_list);
if (!first)
return TRUE;
req = first->data;
req->len = recv(sk, req->buf, sizeof(req->buf), 0);
adapter->ioreq_list = g_list_remove(adapter->ioreq_list, req);
g_idle_add(execute_recv_cb, req);
return TRUE;
}
int near_adapter_connect(uint32_t idx, uint32_t target_idx, uint8_t protocol)
{
struct near_adapter *adapter;
struct near_tag *tag;
struct sockaddr_nfc addr;
int err, sock;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
if (adapter->tag_sock != -1)
return -EALREADY;
tag = g_hash_table_lookup(adapter->tags,
GINT_TO_POINTER(target_idx));
if (!tag)
return -ENOLINK;
sock = socket(AF_NFC, SOCK_SEQPACKET, NFC_SOCKPROTO_RAW);
if (sock == -1)
return sock;
addr.sa_family = AF_NFC;
addr.dev_idx = idx;
addr.target_idx = target_idx;
addr.nfc_protocol = protocol;
err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err) {
close(sock);
return err;
}
adapter->tag_sock = sock;
adapter->tag_link = tag;
if (!adapter->channel)
adapter->channel = g_io_channel_unix_new(adapter->tag_sock);
g_io_channel_set_flags(adapter->channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_channel_set_close_on_unref(adapter->channel, TRUE);
if (adapter->watch == 0)
adapter->watch = g_io_add_watch(adapter->channel,
G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
adapter_recv_event, adapter);
return 0;
}
int near_adapter_disconnect(uint32_t idx)
{
struct near_adapter *adapter;
uint32_t target_idx;
uint16_t tag_type;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter)
return -ENODEV;
DBG("link %p", adapter->tag_link);
if (!adapter->tag_link)
return -ENOLINK;
tag_type = __near_tag_get_type(adapter->tag_link);
target_idx = near_tag_get_target_idx(adapter->tag_link);
DBG("tag type %d", tag_type);
__near_adapter_remove_target(adapter->idx, target_idx);
if (adapter->tag_sock == -1)
return -ENOLINK;
if (adapter->watch > 0) {
g_source_remove(adapter->watch);
adapter->watch = 0;
}
g_io_channel_unref(adapter->channel);
adapter->channel = NULL;
adapter->tag_sock = -1;
adapter->tag_link = NULL;
return 0;
}
int near_adapter_send(uint32_t idx, uint8_t *buf, size_t length,
near_recv cb, void *data, near_release data_rel)
{
struct near_adapter *adapter;
struct near_adapter_ioreq *req = NULL;
int err;
DBG("idx %d", idx);
adapter = g_hash_table_lookup(adapter_hash, GINT_TO_POINTER(idx));
if (!adapter) {
err = -ENODEV;
goto out_err;
}
if (adapter->tag_sock == -1 || !adapter->tag_link) {
err = -ENOLINK;
goto out_err;
}
if (cb && adapter->watch != 0) {
req = g_try_malloc0(sizeof(*req));
if (!req) {
err = -ENOMEM;
goto out_err;
}
DBG("req %p cb %p data %p", req, cb, data);
req->target_idx = near_tag_get_target_idx(adapter->tag_link);
req->cb = cb;
req->data = data;
adapter->ioreq_list =
g_list_append(adapter->ioreq_list, req);
}
err = send(adapter->tag_sock, buf, length, 0);
if (err < 0)
goto out_err;
return err;
out_err:
if (req) {
GList *last = g_list_last(adapter->ioreq_list);
g_free(req);
adapter->ioreq_list =
g_list_delete_link(adapter->ioreq_list, last);
}
if (data_rel)
return (*data_rel)(err, data);
return err;
}
static void adapter_listen(gpointer key, gpointer value, gpointer user_data)
{
struct near_adapter *adapter = value;
struct near_device_driver *driver = user_data;
DBG("%s", adapter->path);
if (!adapter->path)
return;
driver->listen(adapter->idx, device_read_cb);
}
void __near_adapter_listen(struct near_device_driver *driver)
{
g_hash_table_foreach(adapter_hash, adapter_listen, driver);
}
int __near_adapter_init(void)
{
DBG("");
connection = near_dbus_get_connection();
adapter_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, free_adapter);
return 0;
}
void __near_adapter_cleanup(void)
{
DBG("");
g_hash_table_destroy(adapter_hash);
adapter_hash = NULL;
}