};
#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
+/* Version of the Greybus battery protocol we support */
+#define GB_BATTERY_VERSION_MAJOR 0x00
+#define GB_BATTERY_VERSION_MINOR 0x01
+
+/* Greybus battery request types */
+#define GB_BATTERY_TYPE_INVALID 0x00
+#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01
+#define GB_BATTERY_TYPE_TECHNOLOGY 0x02
+#define GB_BATTERY_TYPE_STATUS 0x03
+#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04
+#define GB_BATTERY_TYPE_CAPACITY 0x05
+#define GB_BATTERY_TYPE_TEMPERATURE 0x06
+#define GB_BATTERY_TYPE_VOLTAGE 0x07
+
+struct gb_battery_proto_version_response {
+ __u8 status;
+ __u8 major;
+ __u8 minor;
+};
+
+/* Should match up with battery types in linux/power_supply.h */
+#define GB_BATTERY_TECH_UNKNOWN 0x0000
+#define GB_BATTERY_TECH_NiMH 0x0001
+#define GB_BATTERY_TECH_LION 0x0002
+#define GB_BATTERY_TECH_LIPO 0x0003
+#define GB_BATTERY_TECH_LiFe 0x0004
+#define GB_BATTERY_TECH_NiCd 0x0005
+#define GB_BATTERY_TECH_LiMn 0x0006
+
+struct gb_battery_technology_request {
+ __u8 status;
+ __le32 technology;
+};
+
+/* Should match up with battery status in linux/power_supply.h */
+#define GB_BATTERY_STATUS_UNKNOWN 0x0000
+#define GB_BATTERY_STATUS_CHARGING 0x0001
+#define GB_BATTERY_STATUS_DISCHARGING 0x0002
+#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003
+#define GB_BATTERY_STATUS_FULL 0x0004
+
+struct gb_battery_status_request {
+ __u8 status;
+ __le16 battery_status;
+};
+
+struct gb_battery_max_voltage_request {
+ __u8 status;
+ __le32 max_voltage;
+};
+
+struct gb_battery_capacity_request {
+ __u8 status;
+ __le32 capacity;
+};
+
+struct gb_battery_temperature_request {
+ __u8 status;
+ __le32 temperature;
+};
+
+struct gb_battery_voltage_request {
+ __u8 status;
+ __le32 voltage;
+};
+
+
static const struct greybus_module_id id_table[] = {
{ GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */
{ }, /* terminating NULL entry */
};
+static int battery_operation(struct gb_battery *gb, int type,
+ void *response, int response_size)
+{
+ struct gb_connection *connection = gb->connection;
+ struct gb_operation *operation;
+ struct gb_battery_technology_request *fake_request;
+ u8 *local_response;
+ int ret;
+
+ local_response = kmalloc(response_size, GFP_KERNEL);
+ if (!local_response)
+ return -ENOMEM;
+
+ operation = gb_operation_create(connection, type, 0, response_size);
+ if (!operation) {
+ kfree(local_response);
+ return -ENOMEM;
+ }
+
+ /* Synchronous operation--no callback */
+ ret = gb_operation_request_send(operation, NULL);
+ if (ret) {
+ pr_err("version operation failed (%d)\n", ret);
+ goto out;
+ }
+
+ /*
+ * We only want to look at the status, and all requests have the same
+ * layout for where the status is, so cast this to a random request so
+ * we can see the status easier.
+ */
+ fake_request = (struct gb_battery_technology_request *)local_response;
+ if (fake_request->status) {
+ gb_connection_err(connection, "version response %hhu",
+ fake_request->status);
+ ret = -EIO;
+ } else {
+ /* Good request, so copy to the caller's buffer */
+ memcpy(response, local_response, response_size);
+ }
+out:
+ gb_operation_destroy(operation);
+
+ return ret;
+}
+
+/*
+ * This request only uses the connection field, and if successful,
+ * fills in the major and minor protocol version of the target.
+ */
+static int get_version(struct gb_battery *gb)
+{
+ struct gb_battery_proto_version_response version_request;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION,
+ &version_request, sizeof(version_request));
+ if (retval)
+ return retval;
+
+ if (version_request.major > GB_BATTERY_VERSION_MAJOR) {
+ pr_err("unsupported major version (%hhu > %hhu)\n",
+ version_request.major, GB_BATTERY_VERSION_MAJOR);
+ return -ENOTSUPP;
+ }
+
+ gb->version_major = version_request.major;
+ gb->version_minor = version_request.minor;
+ return 0;
+}
+
static int get_tech(struct gb_battery *gb)
{
- // FIXME - guess!
- return POWER_SUPPLY_TECHNOLOGY_NiMH;
+ struct gb_battery_technology_request tech_request;
+ u32 technology;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY,
+ &tech_request, sizeof(tech_request));
+ if (retval)
+ return retval;
+
+ /*
+ * We have a one-to-one mapping of tech types to power_supply
+ * status, so just return that value.
+ */
+ technology = le32_to_cpu(tech_request.technology);
+ return technology;
}
static int get_status(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_status_request status_request;
+ u16 battery_status;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS,
+ &status_request, sizeof(status_request));
+ if (retval)
+ return retval;
+
+ /*
+ * We have a one-to-one mapping of battery status to power_supply
+ * status, so just return that value.
+ */
+ battery_status = le16_to_cpu(status_request.battery_status);
+ return battery_status;
}
static int get_max_voltage(struct gb_battery *gb)
{
- // FIXME!!!
- return 4700000;
+ struct gb_battery_max_voltage_request volt_request;
+ u32 max_voltage;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE,
+ &volt_request, sizeof(volt_request));
+ if (retval)
+ return retval;
+
+ max_voltage = le32_to_cpu(volt_request.max_voltage);
+ return max_voltage;
}
static int get_capacity(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_capacity_request capacity_request;
+ u32 capacity;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY,
+ &capacity_request, sizeof(capacity_request));
+ if (retval)
+ return retval;
+
+ capacity = le32_to_cpu(capacity_request.capacity);
+ return capacity;
}
static int get_temp(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_temperature_request temp_request;
+ u32 temperature;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE,
+ &temp_request, sizeof(temp_request));
+ if (retval)
+ return retval;
+
+ temperature = le32_to_cpu(temp_request.temperature);
+ return temperature;
}
static int get_voltage(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_voltage_request voltage_request;
+ u32 voltage;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE,
+ &voltage_request, sizeof(voltage_request));
+ if (retval)
+ return retval;
+
+ voltage = le32_to_cpu(voltage_request.voltage);
+ return voltage;
}
static int get_property(struct power_supply *b,
gb->connection = connection; // FIXME refcount!
+ /* Check the version */
+ retval = get_version(gb);
+ if (retval) {
+ kfree(gb);
+ return retval;
+ }
+
b = &gb->bat;
// FIXME - get a better (i.e. unique) name
// FIXME - anything else needs to be set?