* [PATCH 1/2] HID: sony: Add battery status reporting for the Sixaxis and Dualshock 4 controllers.
@ 2014-01-25 3:27 Frank Praznik
2014-01-25 3:27 ` [PATCH 2/2] HID: sony: Add output events for the multi-touch pad on the Dualshock 4 Frank Praznik
0 siblings, 1 reply; 2+ messages in thread
From: Frank Praznik @ 2014-01-25 3:27 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
Add battery status reporting for the Sixaxis and Dualshock 4 controllers.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 185 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 1235405..04fd611 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -30,6 +30,8 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/leds.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
#include "hid-ids.h"
@@ -42,6 +44,7 @@
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_USB)
#define MAX_LEDS 4
@@ -487,18 +490,30 @@ static const unsigned int buzz_keymap[] = {
[20] = BTN_TRIGGER_HAPPY20,
};
+static enum power_supply_property sony_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
struct sony_sc {
+ spinlock_t lock;
struct hid_device *hdev;
struct led_classdev *leds[MAX_LEDS];
struct hid_report *output_report;
unsigned long quirks;
struct work_struct state_worker;
+ struct power_supply battery;
#ifdef CONFIG_SONY_FF
__u8 left;
__u8 right;
#endif
+ __u8 cable_state;
+ __u8 battery_charging;
+ __u8 battery_capacity;
__u8 led_state[MAX_LEDS];
__u8 led_count;
};
@@ -599,6 +614,63 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc;
}
+static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+ static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
+ unsigned long flags;
+ __u8 cable_state, battery_capacity, battery_charging;
+
+ /* The sixaxis is charging if the battery value is 0xee
+ * and it is fully charged if the value is 0xef.
+ * It does not report the actual level while charging so it
+ * is set to 100% while charging is in progress.
+ */
+ if (rd[30] >= 0xee) {
+ battery_capacity = 100;
+ battery_charging = rd[30] & 0x01;
+ } else {
+ battery_capacity = sixaxis_battery_capacity[rd[30]];
+ battery_charging = 0;
+ }
+ cable_state = (rd[31] >> 4) & 0x01;
+
+ spin_lock_irqsave(&sc->lock, flags);
+ sc->cable_state = cable_state;
+ sc->battery_capacity = battery_capacity;
+ sc->battery_charging = battery_charging;
+ spin_unlock_irqrestore(&sc->lock, flags);
+}
+
+static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+ unsigned long flags;
+ __u8 cable_state, battery_capacity, battery_charging;
+
+ /* The lower 4 bits of byte 30 contain the battery level
+ * and the 5th bit contains the USB cable state.
+ */
+ cable_state = (rd[30] >> 4) & 0x01;
+ battery_capacity = rd[30] & 0x0F;
+
+ /* On USB the Dualshock 4 battery level goes from 0 to 11.
+ * A battery level of 11 means fully charged.
+ */
+ if (cable_state && battery_capacity == 11)
+ battery_charging = 0;
+ else
+ battery_charging = 1;
+
+ if (battery_capacity > 10)
+ battery_capacity--;
+ battery_capacity *= 10;
+
+ spin_lock_irqsave(&sc->lock, flags);
+ sc->cable_state = cable_state;
+ sc->battery_capacity = battery_capacity;
+ sc->battery_charging = battery_charging;
+ spin_unlock_irqrestore(&sc->lock, flags);
+}
+
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
__u8 *rd, int size)
{
@@ -613,6 +685,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
swap(rd[43], rd[44]);
swap(rd[45], rd[46]);
swap(rd[47], rd[48]);
+
+ sixaxis_parse_report(sc, rd, size);
+ } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+ size == 64) {
+ dualshock4_parse_report(sc, rd, size);
}
return 0;
@@ -1011,6 +1088,91 @@ static void sony_destroy_ff(struct hid_device *hdev)
}
#endif
+static int sony_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sony_sc *sc = container_of(psy, struct sony_sc, battery);
+ unsigned long flags;
+ int ret = 0;
+ u8 battery_charging, battery_capacity, cable_state;
+
+ spin_lock_irqsave(&sc->lock, flags);
+ battery_charging = sc->battery_charging;
+ battery_capacity = sc->battery_capacity;
+ cable_state = sc->cable_state;
+ spin_unlock_irqrestore(&sc->lock, flags);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = battery_capacity;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery_charging)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ if (battery_capacity == 100 && cable_state)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int sony_battery_probe(struct sony_sc *sc)
+{
+ static atomic_t power_id_seq = ATOMIC_INIT(0);
+ unsigned long power_id;
+ struct hid_device *hdev = sc->hdev;
+ int ret;
+
+ power_id = (unsigned long)atomic_inc_return(&power_id_seq);
+
+ sc->battery.properties = sony_battery_props;
+ sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
+ sc->battery.get_property = sony_battery_get_property;
+ sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ sc->battery.use_for_apm = 0;
+ sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
+ power_id);
+ if (!sc->battery.name)
+ return -ENOMEM;
+
+ ret = power_supply_register(&hdev->dev, &sc->battery);
+ if (ret) {
+ hid_err(hdev, "Unable to register battery device\n");
+ goto err_free;
+ }
+
+ power_supply_powers(&sc->battery, &hdev->dev);
+ return 0;
+
+err_free:
+ kfree(sc->battery.name);
+ sc->battery.name = NULL;
+ return ret;
+}
+
+static void sony_battery_remove(struct sony_sc *sc)
+{
+ if (!sc->battery.name)
+ return;
+
+ power_supply_unregister(&sc->battery);
+ kfree(sc->battery.name);
+ sc->battery.name = NULL;
+}
+
static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
{
struct list_head *head, *list;
@@ -1101,14 +1263,31 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_stop;
}
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ ret = sony_battery_probe(sc);
+ if (ret < 0)
+ goto err_stop;
+
+ /* Open the device to receive reports with battery info */
+ ret = hid_hw_open(hdev);
+ if (ret < 0) {
+ hid_err(hdev, "hw open failed\n");
+ goto err_stop;
+ }
+ }
+
ret = sony_init_ff(hdev);
if (ret < 0)
- goto err_stop;
+ goto err_close;
return 0;
+err_close:
+ hid_hw_close(hdev);
err_stop:
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev);
+ if (sc->quirks & SONY_BATTERY_SUPPORT)
+ sony_battery_remove(sc);
hid_hw_stop(hdev);
return ret;
}
@@ -1120,6 +1299,11 @@ static void sony_remove(struct hid_device *hdev)
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev);
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ hid_hw_close(hdev);
+ sony_battery_remove(sc);
+ }
+
sony_destroy_ff(hdev);
hid_hw_stop(hdev);
--
1.8.3.2
^ permalink raw reply related [flat|nested] 2+ messages in thread* [PATCH 2/2] HID: sony: Add output events for the multi-touch pad on the Dualshock 4.
2014-01-25 3:27 [PATCH 1/2] HID: sony: Add battery status reporting for the Sixaxis and Dualshock 4 controllers Frank Praznik
@ 2014-01-25 3:27 ` Frank Praznik
0 siblings, 0 replies; 2+ messages in thread
From: Frank Praznik @ 2014-01-25 3:27 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
Add output events for the multi-touch pad on the Dualshock 4.
The touchpad has a resolution of 1920x940 and is capable of 2 simultaneous
touches. A 'Type B' stateful slot protocol is implemented as defined in
Documentation/input/multi-touch-protocol.txt
Applications can use the touchpad data by processing the ABS_MT_SLOT,
ABS_MT_TRACKING_ID, ABS_MT_POSITION_X and ABS_MT_POSITION_Y events.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 04fd611..2bd3f13 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -32,6 +32,7 @@
#include <linux/leds.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
+#include <linux/input/mt.h>
#include "hid-ids.h"
@@ -643,7 +644,11 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
{
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+ struct hid_input, list);
+ struct input_dev *input_dev = hidinput->input;
unsigned long flags;
+ int n, offset = 35;
__u8 cable_state, battery_capacity, battery_charging;
/* The lower 4 bits of byte 30 contain the battery level
@@ -669,6 +674,28 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
sc->battery_capacity = battery_capacity;
sc->battery_charging = battery_charging;
spin_unlock_irqrestore(&sc->lock, flags);
+
+ /* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB.
+ * The first 7 bits of the first byte is a counter and bit 8 is a touch
+ * indicator that is 0 when pressed and 1 when not pressed.
+ * The next 3 bytes are two 12 bit touch coordinates, X and Y.
+ * The data for the second touch is in the same format and immediatly
+ * follows the data for the first.
+ */
+ for (n = 0; n < 2; n++) {
+ __u16 x, y;
+
+ x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
+ y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
+
+ input_mt_slot(input_dev, n);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ !(rd[offset] >> 7));
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+ offset += 4;
+ }
}
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
@@ -1200,6 +1227,26 @@ static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
return -EINVAL;
}
+static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
+ int w, int h)
+{
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+ struct hid_input, list);
+ struct input_dev *input_dev = hidinput->input;
+ int ret;
+
+ ret = input_mt_init_slots(input_dev, touch_count, 0);
+ if (ret < 0) {
+ hid_err(sc->hdev, "Unable to initialize multi-touch slots\n");
+ return ret;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
+
+ return 0;
+}
+
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
@@ -1249,6 +1296,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0)
goto err_stop;
+ /* The Dualshock 4 touchpad supports 2 touches and has a
+ * resolution of 1920x940.
+ */
+ ret = sony_register_touchpad(sc, 2, 1920, 940);
+ if (ret < 0)
+ goto err_stop;
+
INIT_WORK(&sc->state_worker, dualshock4_state_worker);
} else {
ret = 0;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-01-25 3:27 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-25 3:27 [PATCH 1/2] HID: sony: Add battery status reporting for the Sixaxis and Dualshock 4 controllers Frank Praznik
2014-01-25 3:27 ` [PATCH 2/2] HID: sony: Add output events for the multi-touch pad on the Dualshock 4 Frank Praznik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox