/*
* SVC Greybus driver.
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/input.h>
#include <linux/workqueue.h>
#include "greybus.h"
#define CPORT_FLAGS_E2EFC BIT(0)
#define CPORT_FLAGS_CSD_N BIT(1)
#define CPORT_FLAGS_CSV_N BIT(2)
#define SVC_KEY_ARA_BUTTON KEY_A
struct gb_svc_deferred_request {
struct work_struct work;
struct gb_operation *operation;
};
static ssize_t endo_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_svc *svc = to_gb_svc(dev);
return sprintf(buf, "0x%04x\n", svc->endo_id);
}
static DEVICE_ATTR_RO(endo_id);
static ssize_t ap_intf_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_svc *svc = to_gb_svc(dev);
return sprintf(buf, "%u\n", svc->ap_intf_id);
}
static DEVICE_ATTR_RO(ap_intf_id);
// FIXME
// This is a hack, we need to do this "right" and clean the interface up
// properly, not just forcibly yank the thing out of the system and hope for the
// best. But for now, people want their modules to come out without having to
// throw the thing to the ground or get out a screwdriver.
static ssize_t intf_eject_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
struct gb_svc *svc = to_gb_svc(dev);
unsigned short intf_id;
int ret;
ret = kstrtou16(buf, 10, &intf_id);
if (ret < 0)
return ret;
dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id);
ret = gb_svc_intf_eject(svc, intf_id);
if (ret < 0)
return ret;
return len;
}
static DEVICE_ATTR_WO(intf_eject);
static ssize_t watchdog_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct gb_svc *svc = to_gb_svc(dev);
return sprintf(buf, "%s\n",
gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled");
}
static ssize_t watchdog_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
struct gb_svc *svc = to_gb_svc(dev);
int retval;
bool user_request;
retval = strtobool(buf, &user_request);
if (retval)
return retval;
if (user_request)
retval = gb_svc_watchdog_enable(svc);
else
retval = gb_svc_watchdog_disable(svc);
if (retval)
return retval;
return len;
}
static DEVICE_ATTR_RW(watchdog);
static struct attribute *svc_attrs[] = {
&dev_attr_endo_id.attr,
&dev_attr_ap_intf_id.attr,
&dev_attr_intf_eject.attr,
&dev_attr_watchdog.attr,
NULL,
};
ATTRIBUTE_GROUPS(svc);
static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
{
struct gb_svc_intf_device_id_request request;
request.intf_id = intf_id;
request.device_id = device_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
&request, sizeof(request), NULL, 0);
}
int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
{
struct gb_svc_intf_reset_request request;
request.intf_id = intf_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
&request, sizeof(request), NULL, 0);
}
EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id)
{
struct gb_svc_intf_eject_request request;
request.intf_id = intf_id;
/*
* The pulse width for module release in svc is long so we need to
* increase the timeout so the operation will not return to soon.
*/
return gb_operation_sync_timeout(svc->connection,
GB_SVC_TYPE_INTF_EJECT, &request,
sizeof(request), NULL, 0,
GB_SVC_EJECT_TIME);
}
EXPORT_SYMBOL_GPL(gb_svc_intf_eject);
int gb_svc_dme_peer_get(struct gb_svc *svc, u8