Merge branch 'android11-5.4' into android11-5.4-lts
Sync up with android11-5.4 for the following commits:d0f21836e3
ANDROID: Revert "tracing/ring-buffer: Have polling block on watermark"5a8d108cc6
Merge "Merge tag 'android11-5.4.226_r00' into android11-5.4" into android11-5.4c429b23536
UPSTREAM: usb: gadget: f_hid: fix f_hidg lifetime vs cdev28b985f146
UPSTREAM: usb: gadget: f_hid: optional SETUP/SET_REPORT mode15a5e4ad4f
ANDROID: add TEST_MAPPING for net/, include/netf5b4a7be57
UPSTREAM: nfp: fix use-after-free in area_cache_get()5a9d35543f
UPSTREAM: proc: avoid integer type confusion in get_proc_longcffda199e1
UPSTREAM: proc: proc_skip_spaces() shouldn't think it is working on C stringse418b27c0c
ANDROID: usb: f_accessory: Check buffer size when initialised via composite98d8600199
Merge tag 'android11-5.4.226_r00' into android11-5.48912db2538
BACKPORT: mm: don't be stuck to rmap lock on reclaim patha69a8cd3c5
ANDROID: Add more hvc devices for virtio-console.7b7c361b98
UPSTREAM: HID: playstation: support updated DualSense rumble mode.ff79b92f34
UPSTREAM: HID: playstation: add initial DualSense Edge controller support9c127a4a06
UPSTREAM: HID: playstation: stop DualSense output work on remove.b32bdd3e88
UPSTREAM: HID: playstation: convert to use dev_groupsc1ac1f8001
UPSTREAM: HID: playstation: fix return from dualsense_player_led_set_brightness()d44545535e
UPSTREAM: HID: playstation: expose DualSense player LEDs through LED class.07dd46d289
BACKPORT: leds: add new LED_FUNCTION_PLAYER for player LEDs for game controllers.f91c45c176
UPSTREAM: HID: playstation: expose DualSense lightbar through a multi-color LED.749207d940
UPSTREAM: leds: flash: Fix multicolor no-ops registration by return 03e667a0854
UPSTREAM: leds: multicolor: Introduce a multicolor class definition0b5ee17e7d
ANDROID: GKI: enable mulitcolor-led0ce03d1655
BACKPORT: Kconfig.debug: provide a little extra FRAME_WARN leeway when KASAN is enabled20fb10aa4c
UPSTREAM: bpf: Ensure correct locking around vulnerable function find_vpid()0ec485223d
UPSTREAM: HID: roccat: Fix use-after-free in roccat_read()cbbd724281
ANDROID: arm64: mm: perform clean & invalidation in __dma_map_area Change-Id: I31bed9dd4eb36433c1028ab9073df4839f5a78f6 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
cc162d31ce
35
Documentation/ABI/testing/sysfs-class-led-multicolor
Normal file
35
Documentation/ABI/testing/sysfs-class-led-multicolor
Normal file
@ -0,0 +1,35 @@
|
||||
What: /sys/class/leds/<led>/brightness
|
||||
Date: March 2020
|
||||
KernelVersion: 5.9
|
||||
Contact: Dan Murphy <dmurphy@ti.com>
|
||||
Description: read/write
|
||||
Writing to this file will update all LEDs within the group to a
|
||||
calculated percentage of what each color LED intensity is set
|
||||
to. The percentage is calculated for each grouped LED via the
|
||||
equation below:
|
||||
|
||||
led_brightness = brightness * multi_intensity/max_brightness
|
||||
|
||||
For additional details please refer to
|
||||
Documentation/leds/leds-class-multicolor.rst.
|
||||
|
||||
The value of the LED is from 0 to
|
||||
/sys/class/leds/<led>/max_brightness.
|
||||
|
||||
What: /sys/class/leds/<led>/multi_index
|
||||
Date: March 2020
|
||||
KernelVersion: 5.9
|
||||
Contact: Dan Murphy <dmurphy@ti.com>
|
||||
Description: read
|
||||
The multi_index array, when read, will output the LED colors
|
||||
as an array of strings as they are indexed in the
|
||||
multi_intensity file.
|
||||
|
||||
What: /sys/class/leds/<led>/multi_intensity
|
||||
Date: March 2020
|
||||
KernelVersion: 5.9
|
||||
Contact: Dan Murphy <dmurphy@ti.com>
|
||||
Description: read/write
|
||||
This file contains array of integers. Order of components is
|
||||
described by the multi_index array. The maximum intensity should
|
||||
not exceed /sys/class/leds/<led>/max_brightness.
|
@ -9,6 +9,7 @@ LEDs
|
||||
|
||||
leds-class
|
||||
leds-class-flash
|
||||
leds-class-multicolor
|
||||
ledtrig-oneshot
|
||||
ledtrig-transient
|
||||
ledtrig-usbport
|
||||
|
86
Documentation/leds/leds-class-multicolor.rst
Normal file
86
Documentation/leds/leds-class-multicolor.rst
Normal file
@ -0,0 +1,86 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
====================================
|
||||
Multicolor LED handling under Linux
|
||||
====================================
|
||||
|
||||
Description
|
||||
===========
|
||||
The multicolor class groups monochrome LEDs and allows controlling two
|
||||
aspects of the final combined color: hue and lightness. The former is
|
||||
controlled via the multi_intensity array file and the latter is controlled
|
||||
via brightness file.
|
||||
|
||||
Multicolor Class Control
|
||||
========================
|
||||
The multicolor class presents files that groups the colors as indexes in an
|
||||
array. These files are children under the LED parent node created by the
|
||||
led_class framework. The led_class framework is documented in led-class.rst
|
||||
within this documentation directory.
|
||||
|
||||
Each colored LED will be indexed under the multi_* files. The order of the
|
||||
colors will be arbitrary. The multi_index file can be read to determine the
|
||||
color name to indexed value.
|
||||
|
||||
The multi_index file is an array that contains the string list of the colors as
|
||||
they are defined in each multi_* array file.
|
||||
|
||||
The multi_intensity is an array that can be read or written to for the
|
||||
individual color intensities. All elements within this array must be written in
|
||||
order for the color LED intensities to be updated.
|
||||
|
||||
Directory Layout Example
|
||||
========================
|
||||
root:/sys/class/leds/multicolor:status# ls -lR
|
||||
-rw-r--r-- 1 root root 4096 Oct 19 16:16 brightness
|
||||
-r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness
|
||||
-r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index
|
||||
-rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity
|
||||
|
||||
Multicolor Class Brightness Control
|
||||
===================================
|
||||
The brightness level for each LED is calculated based on the color LED
|
||||
intensity setting divided by the global max_brightness setting multiplied by
|
||||
the requested brightness.
|
||||
|
||||
led_brightness = brightness * multi_intensity/max_brightness
|
||||
|
||||
Example:
|
||||
A user first writes the multi_intensity file with the brightness levels
|
||||
for each LED that are necessary to achieve a certain color output from a
|
||||
multicolor LED group.
|
||||
|
||||
cat /sys/class/leds/multicolor:status/multi_index
|
||||
green blue red
|
||||
|
||||
echo 43 226 138 > /sys/class/leds/multicolor:status/multi_intensity
|
||||
|
||||
red -
|
||||
intensity = 138
|
||||
max_brightness = 255
|
||||
green -
|
||||
intensity = 43
|
||||
max_brightness = 255
|
||||
blue -
|
||||
intensity = 226
|
||||
max_brightness = 255
|
||||
|
||||
The user can control the brightness of that multicolor LED group by writing the
|
||||
global 'brightness' control. Assuming a max_brightness of 255 the user
|
||||
may want to dim the LED color group to half. The user would write a value of
|
||||
128 to the global brightness file then the values written to each LED will be
|
||||
adjusted base on this value.
|
||||
|
||||
cat /sys/class/leds/multicolor:status/max_brightness
|
||||
255
|
||||
echo 128 > /sys/class/leds/multicolor:status/brightness
|
||||
|
||||
adjusted_red_value = 128 * 138/255 = 69
|
||||
adjusted_green_value = 128 * 43/255 = 21
|
||||
adjusted_blue_value = 128 * 226/255 = 113
|
||||
|
||||
Reading the global brightness file will return the current brightness value of
|
||||
the color LED group.
|
||||
|
||||
cat /sys/class/leds/multicolor:status/brightness
|
||||
128
|
@ -434,6 +434,7 @@ CONFIG_MMC_CRYPTO=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_LEDS_CLASS_FLASH=y
|
||||
CONFIG_LEDS_CLASS_MULTICOLOR=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
CONFIG_EDAC=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
|
@ -228,6 +228,8 @@ ENDPIPROC(__dma_flush_area)
|
||||
* - dir - DMA direction
|
||||
*/
|
||||
ENTRY(__dma_map_area)
|
||||
cmp w2, #DMA_FROM_DEVICE
|
||||
b.eq __dma_flush_area
|
||||
b __dma_clean_area
|
||||
ENDPIPROC(__dma_map_area)
|
||||
|
||||
|
@ -381,6 +381,7 @@ CONFIG_MMC_CRYPTO=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_LEDS_CLASS_FLASH=y
|
||||
CONFIG_LEDS_CLASS_MULTICOLOR=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_DMADEVICES=y
|
||||
|
@ -1085,6 +1085,7 @@
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0
|
||||
#define USB_DEVICE_ID_SONY_PS5_CONTROLLER 0x0ce6
|
||||
#define USB_DEVICE_ID_SONY_PS5_CONTROLLER_2 0x0df2
|
||||
#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
|
||||
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
|
||||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <linux/hid.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
@ -38,11 +40,13 @@ struct ps_device {
|
||||
uint8_t battery_capacity;
|
||||
int battery_status;
|
||||
|
||||
const char *input_dev_name; /* Name of primary input device. */
|
||||
uint8_t mac_address[6]; /* Note: stored in little endian order. */
|
||||
uint32_t hw_version;
|
||||
uint32_t fw_version;
|
||||
|
||||
int (*parse_report)(struct ps_device *dev, struct hid_report *report, u8 *data, int size);
|
||||
void (*remove)(struct ps_device *dev);
|
||||
};
|
||||
|
||||
/* Calibration data for playstation motion sensors. */
|
||||
@ -53,6 +57,13 @@ struct ps_calibration_data {
|
||||
int sens_denom;
|
||||
};
|
||||
|
||||
struct ps_led_info {
|
||||
const char *name;
|
||||
const char *color;
|
||||
enum led_brightness (*brightness_get)(struct led_classdev *cdev);
|
||||
int (*brightness_set)(struct led_classdev *cdev, enum led_brightness);
|
||||
};
|
||||
|
||||
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
|
||||
#define PS_INPUT_CRC32_SEED 0xA1
|
||||
#define PS_OUTPUT_CRC32_SEED 0xA2
|
||||
@ -97,6 +108,9 @@ struct ps_calibration_data {
|
||||
#define DS_STATUS_CHARGING GENMASK(7, 4)
|
||||
#define DS_STATUS_CHARGING_SHIFT 4
|
||||
|
||||
/* Feature version from DualSense Firmware Info report. */
|
||||
#define DS_FEATURE_VERSION(major, minor) ((major & 0xff) << 8 | (minor & 0xff))
|
||||
|
||||
/*
|
||||
* Status of a DualSense touch point contact.
|
||||
* Contact IDs, with highest bit set are 'inactive'
|
||||
@ -115,6 +129,7 @@ struct ps_calibration_data {
|
||||
#define DS_OUTPUT_VALID_FLAG1_RELEASE_LEDS BIT(3)
|
||||
#define DS_OUTPUT_VALID_FLAG1_PLAYER_INDICATOR_CONTROL_ENABLE BIT(4)
|
||||
#define DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE BIT(1)
|
||||
#define DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2 BIT(2)
|
||||
#define DS_OUTPUT_POWER_SAVE_CONTROL_MIC_MUTE BIT(4)
|
||||
#define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT BIT(1)
|
||||
|
||||
@ -132,6 +147,9 @@ struct dualsense {
|
||||
struct input_dev *sensors;
|
||||
struct input_dev *touchpad;
|
||||
|
||||
/* Update version is used as a feature/capability version. */
|
||||
uint16_t update_version;
|
||||
|
||||
/* Calibration data for accelerometer and gyroscope. */
|
||||
struct ps_calibration_data accel_calib_data[3];
|
||||
struct ps_calibration_data gyro_calib_data[3];
|
||||
@ -142,11 +160,13 @@ struct dualsense {
|
||||
uint32_t sensor_timestamp_us;
|
||||
|
||||
/* Compatible rumble state */
|
||||
bool use_vibration_v2;
|
||||
bool update_rumble;
|
||||
uint8_t motor_left;
|
||||
uint8_t motor_right;
|
||||
|
||||
/* RGB lightbar */
|
||||
struct led_classdev_mc lightbar;
|
||||
bool update_lightbar;
|
||||
uint8_t lightbar_red;
|
||||
uint8_t lightbar_green;
|
||||
@ -163,6 +183,7 @@ struct dualsense {
|
||||
struct led_classdev player_leds[5];
|
||||
|
||||
struct work_struct output_worker;
|
||||
bool output_worker_initialized;
|
||||
void *output_report_dmabuf;
|
||||
uint8_t output_seq; /* Sequence number for output report. */
|
||||
};
|
||||
@ -288,6 +309,9 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
static inline void dualsense_schedule_work(struct dualsense *ds);
|
||||
static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
/*
|
||||
* Add a new ps_device to ps_devices if it doesn't exist.
|
||||
* Return error on duplicate device, which can happen if the same
|
||||
@ -525,6 +549,71 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led,
|
||||
const struct ps_led_info *led_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL,
|
||||
"%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name);
|
||||
|
||||
if (!led->name)
|
||||
return -ENOMEM;
|
||||
|
||||
led->brightness = 0;
|
||||
led->max_brightness = 1;
|
||||
led->flags = LED_CORE_SUSPENDRESUME;
|
||||
led->brightness_get = led_info->brightness_get;
|
||||
led->brightness_set_blocking = led_info->brightness_set;
|
||||
|
||||
ret = devm_led_classdev_register(&ps_dev->hdev->dev, led);
|
||||
if (ret) {
|
||||
hid_err(ps_dev->hdev, "Failed to register LED %s: %d\n", led_info->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */
|
||||
static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev,
|
||||
int (*brightness_set)(struct led_classdev *, enum led_brightness))
|
||||
{
|
||||
struct hid_device *hdev = ps_dev->hdev;
|
||||
struct mc_subled *mc_led_info;
|
||||
struct led_classdev *led_cdev;
|
||||
int ret;
|
||||
|
||||
mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info),
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!mc_led_info)
|
||||
return -ENOMEM;
|
||||
|
||||
mc_led_info[0].color_index = LED_COLOR_ID_RED;
|
||||
mc_led_info[1].color_index = LED_COLOR_ID_GREEN;
|
||||
mc_led_info[2].color_index = LED_COLOR_ID_BLUE;
|
||||
|
||||
lightbar_mc_dev->subled_info = mc_led_info;
|
||||
lightbar_mc_dev->num_colors = 3;
|
||||
|
||||
led_cdev = &lightbar_mc_dev->led_cdev;
|
||||
led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s:rgb:indicator",
|
||||
ps_dev->input_dev_name);
|
||||
if (!led_cdev->name)
|
||||
return -ENOMEM;
|
||||
led_cdev->brightness = 255;
|
||||
led_cdev->max_brightness = 255;
|
||||
led_cdev->brightness_set_blocking = brightness_set;
|
||||
|
||||
ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "Cannot register multicolor LED device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, int accel_res,
|
||||
int gyro_range, int gyro_res)
|
||||
{
|
||||
@ -614,15 +703,12 @@ static ssize_t hardware_version_show(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR_RO(hardware_version);
|
||||
|
||||
static struct attribute *ps_device_attributes[] = {
|
||||
static struct attribute *ps_device_attrs[] = {
|
||||
&dev_attr_firmware_version.attr,
|
||||
&dev_attr_hardware_version.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ps_device_attribute_group = {
|
||||
.attrs = ps_device_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ps_device);
|
||||
|
||||
static int dualsense_get_calibration_data(struct dualsense *ds)
|
||||
{
|
||||
@ -714,6 +800,7 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int dualsense_get_firmware_info(struct dualsense *ds)
|
||||
{
|
||||
uint8_t *buf;
|
||||
@ -733,6 +820,15 @@ static int dualsense_get_firmware_info(struct dualsense *ds)
|
||||
ds->base.hw_version = get_unaligned_le32(&buf[24]);
|
||||
ds->base.fw_version = get_unaligned_le32(&buf[28]);
|
||||
|
||||
/* Update version is some kind of feature version. It is distinct from
|
||||
* the firmware version as there can be many different variations of a
|
||||
* controller over time with the same physical shell, but with different
|
||||
* PCBs and other internal changes. The update version (internal name) is
|
||||
* used as a means to detect what features are available and change behavior.
|
||||
* Note: the version is different between DualSense and DualSense Edge.
|
||||
*/
|
||||
ds->update_version = get_unaligned_le16(&buf[44]);
|
||||
|
||||
err_free:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
@ -761,6 +857,53 @@ static int dualsense_get_mac_address(struct dualsense *ds)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dualsense_lightbar_set_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
|
||||
struct dualsense *ds = container_of(mc_cdev, struct dualsense, lightbar);
|
||||
uint8_t red, green, blue;
|
||||
|
||||
led_mc_calc_color_components(mc_cdev, brightness);
|
||||
red = mc_cdev->subled_info[0].brightness;
|
||||
green = mc_cdev->subled_info[1].brightness;
|
||||
blue = mc_cdev->subled_info[2].brightness;
|
||||
|
||||
dualsense_set_lightbar(ds, red, green, blue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum led_brightness dualsense_player_led_get_brightness(struct led_classdev *led)
|
||||
{
|
||||
struct hid_device *hdev = to_hid_device(led->dev->parent);
|
||||
struct dualsense *ds = hid_get_drvdata(hdev);
|
||||
|
||||
return !!(ds->player_leds_state & BIT(led - ds->player_leds));
|
||||
}
|
||||
|
||||
static int dualsense_player_led_set_brightness(struct led_classdev *led, enum led_brightness value)
|
||||
{
|
||||
struct hid_device *hdev = to_hid_device(led->dev->parent);
|
||||
struct dualsense *ds = hid_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
unsigned int led_index;
|
||||
|
||||
spin_lock_irqsave(&ds->base.lock, flags);
|
||||
|
||||
led_index = led - ds->player_leds;
|
||||
if (value == LED_OFF)
|
||||
ds->player_leds_state &= ~BIT(led_index);
|
||||
else
|
||||
ds->player_leds_state |= BIT(led_index);
|
||||
|
||||
ds->update_player_leds = true;
|
||||
spin_unlock_irqrestore(&ds->base.lock, flags);
|
||||
|
||||
dualsense_schedule_work(ds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp,
|
||||
void *buf)
|
||||
{
|
||||
@ -800,6 +943,16 @@ static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dualsense_schedule_work(struct dualsense *ds)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ds->base.lock, flags);
|
||||
if (ds->output_worker_initialized)
|
||||
schedule_work(&ds->output_worker);
|
||||
spin_unlock_irqrestore(&ds->base.lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to send DualSense output reports. Applies a CRC at the end of a report
|
||||
* for Bluetooth reports.
|
||||
@ -838,7 +991,10 @@ static void dualsense_output_worker(struct work_struct *work)
|
||||
if (ds->update_rumble) {
|
||||
/* Select classic rumble style haptics and enable it. */
|
||||
common->valid_flag0 |= DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT;
|
||||
common->valid_flag0 |= DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION;
|
||||
if (ds->use_vibration_v2)
|
||||
common->valid_flag2 |= DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2;
|
||||
else
|
||||
common->valid_flag0 |= DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION;
|
||||
common->motor_left = ds->motor_left;
|
||||
common->motor_right = ds->motor_right;
|
||||
ds->update_rumble = false;
|
||||
@ -960,7 +1116,7 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r
|
||||
spin_unlock_irqrestore(&ps_dev->lock, flags);
|
||||
|
||||
/* Schedule updating of microphone state at hardware level. */
|
||||
schedule_work(&ds->output_worker);
|
||||
dualsense_schedule_work(ds);
|
||||
}
|
||||
ds->last_btn_mic_state = btn_mic_state;
|
||||
|
||||
@ -1075,10 +1231,22 @@ static int dualsense_play_effect(struct input_dev *dev, void *data, struct ff_ef
|
||||
ds->motor_right = effect->u.rumble.weak_magnitude / 256;
|
||||
spin_unlock_irqrestore(&ds->base.lock, flags);
|
||||
|
||||
schedule_work(&ds->output_worker);
|
||||
dualsense_schedule_work(ds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dualsense_remove(struct ps_device *ps_dev)
|
||||
{
|
||||
struct dualsense *ds = container_of(ps_dev, struct dualsense, base);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ds->base.lock, flags);
|
||||
ds->output_worker_initialized = false;
|
||||
spin_unlock_irqrestore(&ds->base.lock, flags);
|
||||
|
||||
cancel_work_sync(&ds->output_worker);
|
||||
}
|
||||
|
||||
static int dualsense_reset_leds(struct dualsense *ds)
|
||||
{
|
||||
struct dualsense_output_report report;
|
||||
@ -1106,12 +1274,16 @@ static int dualsense_reset_leds(struct dualsense *ds)
|
||||
|
||||
static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ds->base.lock, flags);
|
||||
ds->update_lightbar = true;
|
||||
ds->lightbar_red = red;
|
||||
ds->lightbar_green = green;
|
||||
ds->lightbar_blue = blue;
|
||||
spin_unlock_irqrestore(&ds->base.lock, flags);
|
||||
|
||||
schedule_work(&ds->output_worker);
|
||||
dualsense_schedule_work(ds);
|
||||
}
|
||||
|
||||
static void dualsense_set_player_leds(struct dualsense *ds)
|
||||
@ -1134,7 +1306,7 @@ static void dualsense_set_player_leds(struct dualsense *ds)
|
||||
|
||||
ds->update_player_leds = true;
|
||||
ds->player_leds_state = player_ids[player_id];
|
||||
schedule_work(&ds->output_worker);
|
||||
dualsense_schedule_work(ds);
|
||||
}
|
||||
|
||||
static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
@ -1142,7 +1314,20 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
struct dualsense *ds;
|
||||
struct ps_device *ps_dev;
|
||||
uint8_t max_output_report_size;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
static const struct ps_led_info player_leds_info[] = {
|
||||
{ LED_FUNCTION_PLAYER1, "white", dualsense_player_led_get_brightness,
|
||||
dualsense_player_led_set_brightness },
|
||||
{ LED_FUNCTION_PLAYER2, "white", dualsense_player_led_get_brightness,
|
||||
dualsense_player_led_set_brightness },
|
||||
{ LED_FUNCTION_PLAYER3, "white", dualsense_player_led_get_brightness,
|
||||
dualsense_player_led_set_brightness },
|
||||
{ LED_FUNCTION_PLAYER4, "white", dualsense_player_led_get_brightness,
|
||||
dualsense_player_led_set_brightness },
|
||||
{ LED_FUNCTION_PLAYER5, "white", dualsense_player_led_get_brightness,
|
||||
dualsense_player_led_set_brightness }
|
||||
};
|
||||
|
||||
ds = devm_kzalloc(&hdev->dev, sizeof(*ds), GFP_KERNEL);
|
||||
if (!ds)
|
||||
@ -1160,7 +1345,9 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
ps_dev->battery_capacity = 100; /* initial value until parse_report. */
|
||||
ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
ps_dev->parse_report = dualsense_parse_report;
|
||||
ps_dev->remove = dualsense_remove;
|
||||
INIT_WORK(&ds->output_worker, dualsense_output_worker);
|
||||
ds->output_worker_initialized = true;
|
||||
hid_set_drvdata(hdev, ds);
|
||||
|
||||
max_output_report_size = sizeof(struct dualsense_output_report_bt);
|
||||
@ -1181,6 +1368,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Original DualSense firmware simulated classic controller rumble through
|
||||
* its new haptics hardware. It felt different from classic rumble users
|
||||
* were used to. Since then new firmwares were introduced to change behavior
|
||||
* and make this new 'v2' behavior default on PlayStation and other platforms.
|
||||
* The original DualSense requires a new enough firmware as bundled with PS5
|
||||
* software released in 2021. DualSense edge supports it out of the box.
|
||||
* Both devices also support the old mode, but it is not really used.
|
||||
*/
|
||||
if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER) {
|
||||
/* Feature version 2.21 introduced new vibration method. */
|
||||
ds->use_vibration_v2 = ds->update_version >= DS_FEATURE_VERSION(2, 21);
|
||||
} else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) {
|
||||
ds->use_vibration_v2 = true;
|
||||
}
|
||||
|
||||
ret = ps_devices_list_add(ps_dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
@ -1196,6 +1398,8 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
ret = PTR_ERR(ds->gamepad);
|
||||
goto err;
|
||||
}
|
||||
/* Use gamepad input device name as primary device name for e.g. LEDs */
|
||||
ps_dev->input_dev_name = dev_name(&ds->gamepad->dev);
|
||||
|
||||
ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G,
|
||||
DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S);
|
||||
@ -1223,8 +1427,21 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Set default lightbar color. */
|
||||
dualsense_set_lightbar(ds, 0, 0, 128); /* blue */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(player_leds_info); i++) {
|
||||
const struct ps_led_info *led_info = &player_leds_info[i];
|
||||
|
||||
ret = ps_led_register(ps_dev, &ds->player_leds[i], led_info);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = ps_device_set_player_id(ps_dev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed to assign player id for DualSense: %d\n", ret);
|
||||
@ -1282,7 +1499,8 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER) {
|
||||
if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER ||
|
||||
hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) {
|
||||
dev = dualsense_create(hdev);
|
||||
if (IS_ERR(dev)) {
|
||||
hid_err(hdev, "Failed to create dualsense.\n");
|
||||
@ -1291,12 +1509,6 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_device_add_group(&hdev->dev, &ps_device_attribute_group);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed to register sysfs nodes.\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_close:
|
||||
@ -1313,6 +1525,9 @@ static void ps_remove(struct hid_device *hdev)
|
||||
ps_devices_list_remove(dev);
|
||||
ps_device_release_player_id(dev);
|
||||
|
||||
if (dev->remove)
|
||||
dev->remove(dev);
|
||||
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
@ -1320,6 +1535,8 @@ static void ps_remove(struct hid_device *hdev)
|
||||
static const struct hid_device_id ps_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ps_devices);
|
||||
@ -1330,6 +1547,9 @@ static struct hid_driver ps_driver = {
|
||||
.probe = ps_probe,
|
||||
.remove = ps_remove,
|
||||
.raw_event = ps_raw_event,
|
||||
.driver = {
|
||||
.dev_groups = ps_device_groups,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ps_init(void)
|
||||
|
@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH
|
||||
for the flash related features of a LED device. It can be built
|
||||
as a module.
|
||||
|
||||
config LEDS_CLASS_MULTICOLOR
|
||||
tristate "LED Multicolor Class Support"
|
||||
depends on LEDS_CLASS
|
||||
help
|
||||
This option enables the multicolor LED sysfs class in /sys/class/leds.
|
||||
It wraps LED class and adds multicolor LED specific sysfs attributes
|
||||
and kernel internal API to it. You'll need this to provide support
|
||||
for multicolor LEDs that are grouped together. This class is not
|
||||
intended for single color LEDs. It can be built as a module.
|
||||
|
||||
config LEDS_BRIGHTNESS_HW_CHANGED
|
||||
bool "LED Class brightness_hw_changed attribute support"
|
||||
depends on LEDS_CLASS
|
||||
|
@ -4,6 +4,7 @@
|
||||
obj-$(CONFIG_NEW_LEDS) += led-core.o
|
||||
obj-$(CONFIG_LEDS_CLASS) += led-class.o
|
||||
obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
|
||||
obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += led-class-multicolor.o
|
||||
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
|
||||
|
||||
# LED Platform Drivers
|
||||
|
203
drivers/leds/led-class-multicolor.c
Normal file
203
drivers/leds/led-class-multicolor.c
Normal file
@ -0,0 +1,203 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// LED Multicolor class interface
|
||||
// Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
|
||||
// Author: Dan Murphy <dmurphy@ti.com>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/led-class-multicolor.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "leds.h"
|
||||
|
||||
int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++)
|
||||
mcled_cdev->subled_info[i].brightness = brightness *
|
||||
mcled_cdev->subled_info[i].intensity /
|
||||
led_cdev->max_brightness;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
|
||||
|
||||
static ssize_t multi_intensity_store(struct device *dev,
|
||||
struct device_attribute *intensity_attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
|
||||
int nrchars, offset = 0;
|
||||
int intensity_value[LED_COLOR_ID_MAX];
|
||||
int i;
|
||||
ssize_t ret;
|
||||
|
||||
mutex_lock(&led_cdev->led_access);
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++) {
|
||||
ret = sscanf(buf + offset, "%i%n",
|
||||
&intensity_value[i], &nrchars);
|
||||
if (ret != 1) {
|
||||
ret = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
offset += nrchars;
|
||||
}
|
||||
|
||||
offset++;
|
||||
if (offset < size) {
|
||||
ret = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++)
|
||||
mcled_cdev->subled_info[i].intensity = intensity_value[i];
|
||||
|
||||
led_set_brightness(led_cdev, led_cdev->brightness);
|
||||
ret = size;
|
||||
err_out:
|
||||
mutex_unlock(&led_cdev->led_access);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t multi_intensity_show(struct device *dev,
|
||||
struct device_attribute *intensity_attr,
|
||||
char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++) {
|
||||
len += sprintf(buf + len, "%d",
|
||||
mcled_cdev->subled_info[i].intensity);
|
||||
if (i < mcled_cdev->num_colors - 1)
|
||||
len += sprintf(buf + len, " ");
|
||||
}
|
||||
|
||||
buf[len++] = '\n';
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RW(multi_intensity);
|
||||
|
||||
static ssize_t multi_index_show(struct device *dev,
|
||||
struct device_attribute *multi_index_attr,
|
||||
char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
|
||||
int len = 0;
|
||||
int index;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mcled_cdev->num_colors; i++) {
|
||||
index = mcled_cdev->subled_info[i].color_index;
|
||||
len += sprintf(buf + len, "%s", led_colors[index]);
|
||||
if (i < mcled_cdev->num_colors - 1)
|
||||
len += sprintf(buf + len, " ");
|
||||
}
|
||||
|
||||
buf[len++] = '\n';
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RO(multi_index);
|
||||
|
||||
static struct attribute *led_multicolor_attrs[] = {
|
||||
&dev_attr_multi_intensity.attr,
|
||||
&dev_attr_multi_index.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(led_multicolor);
|
||||
|
||||
int led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data)
|
||||
{
|
||||
struct led_classdev *led_cdev;
|
||||
|
||||
if (!mcled_cdev)
|
||||
return -EINVAL;
|
||||
|
||||
if (mcled_cdev->num_colors <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
led_cdev = &mcled_cdev->led_cdev;
|
||||
mcled_cdev->led_cdev.groups = led_multicolor_groups;
|
||||
|
||||
return led_classdev_register_ext(parent, led_cdev, init_data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
|
||||
|
||||
void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
|
||||
{
|
||||
if (!mcled_cdev)
|
||||
return;
|
||||
|
||||
led_classdev_unregister(&mcled_cdev->led_cdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
|
||||
|
||||
static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
|
||||
{
|
||||
led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
|
||||
}
|
||||
|
||||
int devm_led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data)
|
||||
{
|
||||
struct led_classdev_mc **dr;
|
||||
int ret;
|
||||
|
||||
dr = devres_alloc(devm_led_classdev_multicolor_release,
|
||||
sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
|
||||
init_data);
|
||||
if (ret) {
|
||||
devres_free(dr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*dr = mcled_cdev;
|
||||
devres_add(parent, dr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
|
||||
|
||||
static int devm_led_classdev_multicolor_match(struct device *dev,
|
||||
void *res, void *data)
|
||||
{
|
||||
struct led_classdev_mc **p = res;
|
||||
|
||||
if (WARN_ON(!p || !*p))
|
||||
return 0;
|
||||
|
||||
return *p == data;
|
||||
}
|
||||
|
||||
void devm_led_classdev_multicolor_unregister(struct device *dev,
|
||||
struct led_classdev_mc *mcled_cdev)
|
||||
{
|
||||
WARN_ON(devres_release(dev,
|
||||
devm_led_classdev_multicolor_release,
|
||||
devm_led_classdev_multicolor_match, mcled_cdev));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
|
||||
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
MODULE_DESCRIPTION("Multicolor LED class interface");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -16,7 +16,7 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_F_ACC
|
||||
extern int acc_ctrlrequest(struct usb_composite_dev *cdev,
|
||||
extern int acc_ctrlrequest_composite(struct usb_composite_dev *cdev,
|
||||
const struct usb_ctrlrequest *ctrl);
|
||||
void acc_disconnect(void);
|
||||
#endif
|
||||
@ -1559,7 +1559,7 @@ static int android_setup(struct usb_gadget *gadget,
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_F_ACC
|
||||
if (value < 0)
|
||||
value = acc_ctrlrequest(cdev, c);
|
||||
value = acc_ctrlrequest_composite(cdev, c);
|
||||
#endif
|
||||
|
||||
if (value < 0)
|
||||
|
@ -1006,6 +1006,26 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acc_ctrlrequest);
|
||||
|
||||
int acc_ctrlrequest_composite(struct usb_composite_dev *cdev,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
if (w_length > USB_COMP_EP0_BUFSIZ) {
|
||||
if (ctrl->bRequestType & USB_DIR_IN) {
|
||||
/* Cast away the const, we are going to overwrite on purpose. */
|
||||
__le16 *temp = (__le16 *)&ctrl->wLength;
|
||||
|
||||
*temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ);
|
||||
w_length = USB_COMP_EP0_BUFSIZ;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return acc_ctrlrequest(cdev, ctrl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acc_ctrlrequest_composite);
|
||||
|
||||
static int
|
||||
__acc_function_bind(struct usb_configuration *c,
|
||||
struct usb_function *f, bool configfs)
|
||||
|
@ -58,6 +58,11 @@
|
||||
#define LED_FUNCTION_MUTE "mute"
|
||||
#define LED_FUNCTION_NUMLOCK "numlock"
|
||||
#define LED_FUNCTION_PANIC "panic"
|
||||
#define LED_FUNCTION_PLAYER1 "player-1"
|
||||
#define LED_FUNCTION_PLAYER2 "player-2"
|
||||
#define LED_FUNCTION_PLAYER3 "player-3"
|
||||
#define LED_FUNCTION_PLAYER4 "player-4"
|
||||
#define LED_FUNCTION_PLAYER5 "player-5"
|
||||
#define LED_FUNCTION_PROGRAMMING "programming"
|
||||
#define LED_FUNCTION_POWER "power"
|
||||
#define LED_FUNCTION_RX "rx"
|
||||
|
@ -554,6 +554,11 @@ static inline void i_mmap_unlock_write(struct address_space *mapping)
|
||||
up_write(&mapping->i_mmap_rwsem);
|
||||
}
|
||||
|
||||
static inline int i_mmap_trylock_read(struct address_space *mapping)
|
||||
{
|
||||
return down_read_trylock(&mapping->i_mmap_rwsem);
|
||||
}
|
||||
|
||||
static inline void i_mmap_lock_read(struct address_space *mapping)
|
||||
{
|
||||
down_read(&mapping->i_mmap_rwsem);
|
||||
|
109
include/linux/led-class-multicolor.h
Normal file
109
include/linux/led-class-multicolor.h
Normal file
@ -0,0 +1,109 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* LED Multicolor class interface
|
||||
* Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_MULTICOLOR_LEDS_H_INCLUDED
|
||||
#define _LINUX_MULTICOLOR_LEDS_H_INCLUDED
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
struct mc_subled {
|
||||
unsigned int color_index;
|
||||
unsigned int brightness;
|
||||
unsigned int intensity;
|
||||
unsigned int channel;
|
||||
};
|
||||
|
||||
struct led_classdev_mc {
|
||||
/* led class device */
|
||||
struct led_classdev led_cdev;
|
||||
unsigned int num_colors;
|
||||
|
||||
struct mc_subled *subled_info;
|
||||
};
|
||||
|
||||
static inline struct led_classdev_mc *lcdev_to_mccdev(
|
||||
struct led_classdev *led_cdev)
|
||||
{
|
||||
return container_of(led_cdev, struct led_classdev_mc, led_cdev);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
|
||||
/**
|
||||
* led_classdev_multicolor_register_ext - register a new object of led_classdev
|
||||
* class with support for multicolor LEDs
|
||||
* @parent: the multicolor LED to register
|
||||
* @mcled_cdev: the led_classdev_mc structure for this device
|
||||
* @init_data: the LED class multicolor device initialization data
|
||||
*
|
||||
* Returns: 0 on success or negative error value on failure
|
||||
*/
|
||||
int led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data);
|
||||
|
||||
/**
|
||||
* led_classdev_multicolor_unregister - unregisters an object of led_classdev
|
||||
* class with support for multicolor LEDs
|
||||
* @mcled_cdev: the multicolor LED to unregister
|
||||
*
|
||||
* Unregister a previously registered via led_classdev_multicolor_register
|
||||
* object
|
||||
*/
|
||||
void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev);
|
||||
|
||||
/* Calculate brightness for the monochrome LED cluster */
|
||||
int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
|
||||
enum led_brightness brightness);
|
||||
|
||||
int devm_led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data);
|
||||
|
||||
void devm_led_classdev_multicolor_unregister(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev);
|
||||
#else
|
||||
|
||||
static inline int led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev) {};
|
||||
static inline int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int devm_led_classdev_multicolor_register_ext(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev,
|
||||
struct led_init_data *init_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void devm_led_classdev_multicolor_unregister(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev)
|
||||
{};
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR) */
|
||||
|
||||
static inline int led_classdev_multicolor_register(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev)
|
||||
{
|
||||
return led_classdev_multicolor_register_ext(parent, mcled_cdev, NULL);
|
||||
}
|
||||
|
||||
static inline int devm_led_classdev_multicolor_register(struct device *parent,
|
||||
struct led_classdev_mc *mcled_cdev)
|
||||
{
|
||||
return devm_led_classdev_multicolor_register_ext(parent, mcled_cdev,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_MULTICOLOR_LEDS_H_INCLUDED */
|
@ -131,6 +131,11 @@ static inline void anon_vma_lock_read(struct anon_vma *anon_vma)
|
||||
down_read(&anon_vma->root->rwsem);
|
||||
}
|
||||
|
||||
static inline int anon_vma_trylock_read(struct anon_vma *anon_vma)
|
||||
{
|
||||
return down_read_trylock(&anon_vma->root->rwsem);
|
||||
}
|
||||
|
||||
static inline void anon_vma_unlock_read(struct anon_vma *anon_vma)
|
||||
{
|
||||
up_read(&anon_vma->root->rwsem);
|
||||
@ -245,17 +250,14 @@ void try_to_munlock(struct page *);
|
||||
|
||||
void remove_migration_ptes(struct page *old, struct page *new, bool locked);
|
||||
|
||||
/*
|
||||
* Called by memory-failure.c to kill processes.
|
||||
*/
|
||||
struct anon_vma *page_lock_anon_vma_read(struct page *page);
|
||||
void page_unlock_anon_vma_read(struct anon_vma *anon_vma);
|
||||
int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma);
|
||||
|
||||
/*
|
||||
* rmap_walk_control: To control rmap traversing for specific needs
|
||||
*
|
||||
* arg: passed to rmap_one() and invalid_vma()
|
||||
* try_lock: bail out if the rmap lock is contended
|
||||
* contended: indicate the rmap traversal bailed out due to lock contention
|
||||
* rmap_one: executed on each vma where page is mapped
|
||||
* done: for checking traversing termination condition
|
||||
* anon_lock: for getting anon_lock by optimized way rather than default
|
||||
@ -263,6 +265,8 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma);
|
||||
*/
|
||||
struct rmap_walk_control {
|
||||
void *arg;
|
||||
bool try_lock;
|
||||
bool contended;
|
||||
/*
|
||||
* Return false if page table scanning in rmap_walk should be stopped.
|
||||
* Otherwise, return true.
|
||||
@ -270,13 +274,21 @@ struct rmap_walk_control {
|
||||
bool (*rmap_one)(struct page *page, struct vm_area_struct *vma,
|
||||
unsigned long addr, void *arg);
|
||||
int (*done)(struct page *page);
|
||||
struct anon_vma *(*anon_lock)(struct page *page);
|
||||
struct anon_vma *(*anon_lock)(struct page *page,
|
||||
struct rmap_walk_control *rwc);
|
||||
bool (*invalid_vma)(struct vm_area_struct *vma, void *arg);
|
||||
};
|
||||
|
||||
void rmap_walk(struct page *page, struct rmap_walk_control *rwc);
|
||||
void rmap_walk_locked(struct page *page, struct rmap_walk_control *rwc);
|
||||
|
||||
/*
|
||||
* Called by memory-failure.c to kill processes.
|
||||
*/
|
||||
struct anon_vma *page_lock_anon_vma_read(struct page *page,
|
||||
struct rmap_walk_control *rwc);
|
||||
void page_unlock_anon_vma_read(struct anon_vma *anon_vma);
|
||||
|
||||
#else /* !CONFIG_MMU */
|
||||
|
||||
#define anon_vma_init() do {} while (0)
|
||||
|
7
include/net/TEST_MAPPING
Normal file
7
include/net/TEST_MAPPING
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsNetTestCases"
|
||||
}
|
||||
]
|
||||
}
|
@ -1606,7 +1606,7 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t pmd)
|
||||
*/
|
||||
get_page(page);
|
||||
spin_unlock(vmf->ptl);
|
||||
anon_vma = page_lock_anon_vma_read(page);
|
||||
anon_vma = page_lock_anon_vma_read(page, NULL);
|
||||
|
||||
/* Confirm the PMD did not change while page_table_lock was released */
|
||||
spin_lock(vmf->ptl);
|
||||
|
8
mm/ksm.c
8
mm/ksm.c
@ -2620,7 +2620,13 @@ void rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc)
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
cond_resched();
|
||||
anon_vma_lock_read(anon_vma);
|
||||
if (!anon_vma_trylock_read(anon_vma)) {
|
||||
if (rwc->try_lock) {
|
||||
rwc->contended = true;
|
||||
return;
|
||||
}
|
||||
anon_vma_lock_read(anon_vma);
|
||||
}
|
||||
anon_vma_interval_tree_foreach(vmac, &anon_vma->rb_root,
|
||||
0, ULONG_MAX) {
|
||||
unsigned long addr;
|
||||
|
@ -443,7 +443,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
|
||||
struct anon_vma *av;
|
||||
pgoff_t pgoff;
|
||||
|
||||
av = page_lock_anon_vma_read(page);
|
||||
av = page_lock_anon_vma_read(page, NULL);
|
||||
if (av == NULL) /* Not actually mapped anymore */
|
||||
return;
|
||||
|
||||
|
@ -95,10 +95,10 @@ static bool page_idle_clear_pte_refs_one(struct page *page,
|
||||
static void page_idle_clear_pte_refs(struct page *page)
|
||||
{
|
||||
/*
|
||||
* Since rwc.arg is unused, rwc is effectively immutable, so we
|
||||
* can make it static const to save some cycles and stack.
|
||||
* Since rwc.try_lock is unused, rwc is effectively immutable, so we
|
||||
* can make it static to save some cycles and stack.
|
||||
*/
|
||||
static const struct rmap_walk_control rwc = {
|
||||
static struct rmap_walk_control rwc = {
|
||||
.rmap_one = page_idle_clear_pte_refs_one,
|
||||
.anon_lock = page_lock_anon_vma_read,
|
||||
};
|
||||
|
45
mm/rmap.c
45
mm/rmap.c
@ -503,9 +503,11 @@ struct anon_vma *page_get_anon_vma(struct page *page)
|
||||
*
|
||||
* Its a little more complex as it tries to keep the fast path to a single
|
||||
* atomic op -- the trylock. If we fail the trylock, we fall back to getting a
|
||||
* reference like with page_get_anon_vma() and then block on the mutex.
|
||||
* reference like with page_get_anon_vma() and then block on the mutex
|
||||
* on !rwc->try_lock case.
|
||||
*/
|
||||
struct anon_vma *page_lock_anon_vma_read(struct page *page)
|
||||
struct anon_vma *page_lock_anon_vma_read(struct page *page,
|
||||
struct rmap_walk_control *rwc)
|
||||
{
|
||||
struct anon_vma *anon_vma = NULL;
|
||||
struct anon_vma *root_anon_vma;
|
||||
@ -533,6 +535,12 @@ struct anon_vma *page_lock_anon_vma_read(struct page *page)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rwc && rwc->try_lock) {
|
||||
anon_vma = NULL;
|
||||
rwc->contended = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* trylock failed, we got to sleep */
|
||||
if (!atomic_inc_not_zero(&anon_vma->refcount)) {
|
||||
anon_vma = NULL;
|
||||
@ -828,8 +836,10 @@ static bool invalid_page_referenced_vma(struct vm_area_struct *vma, void *arg)
|
||||
* @memcg: target memory cgroup
|
||||
* @vm_flags: collect encountered vma->vm_flags who actually referenced the page
|
||||
*
|
||||
* Quick test_and_clear_referenced for all mappings to a page,
|
||||
* returns the number of ptes which referenced the page.
|
||||
* Quick test_and_clear_referenced for all mappings of a page,
|
||||
*
|
||||
* Return: The number of mappings which referenced the page. Return -1 if
|
||||
* the function bailed out due to rmap lock contention.
|
||||
*/
|
||||
int page_referenced(struct page *page,
|
||||
int is_locked,
|
||||
@ -845,6 +855,7 @@ int page_referenced(struct page *page,
|
||||
.rmap_one = page_referenced_one,
|
||||
.arg = (void *)&pra,
|
||||
.anon_lock = page_lock_anon_vma_read,
|
||||
.try_lock = true,
|
||||
};
|
||||
|
||||
*vm_flags = 0;
|
||||
@ -875,7 +886,7 @@ int page_referenced(struct page *page,
|
||||
if (we_locked)
|
||||
unlock_page(page);
|
||||
|
||||
return pra.referenced;
|
||||
return rwc.contended ? -1 : pra.referenced;
|
||||
}
|
||||
|
||||
static bool page_mkclean_one(struct page *page, struct vm_area_struct *vma,
|
||||
@ -1810,7 +1821,7 @@ static struct anon_vma *rmap_walk_anon_lock(struct page *page,
|
||||
struct anon_vma *anon_vma;
|
||||
|
||||
if (rwc->anon_lock)
|
||||
return rwc->anon_lock(page);
|
||||
return rwc->anon_lock(page, rwc);
|
||||
|
||||
/*
|
||||
* Note: remove_migration_ptes() cannot use page_lock_anon_vma_read()
|
||||
@ -1822,7 +1833,17 @@ static struct anon_vma *rmap_walk_anon_lock(struct page *page,
|
||||
if (!anon_vma)
|
||||
return NULL;
|
||||
|
||||
if (anon_vma_trylock_read(anon_vma))
|
||||
goto out;
|
||||
|
||||
if (rwc->try_lock) {
|
||||
anon_vma = NULL;
|
||||
rwc->contended = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
anon_vma_lock_read(anon_vma);
|
||||
out:
|
||||
return anon_vma;
|
||||
}
|
||||
|
||||
@ -1913,8 +1934,18 @@ static void rmap_walk_file(struct page *page, struct rmap_walk_control *rwc,
|
||||
|
||||
pgoff_start = page_to_pgoff(page);
|
||||
pgoff_end = pgoff_start + hpage_nr_pages(page) - 1;
|
||||
if (!locked)
|
||||
if (!locked) {
|
||||
if (i_mmap_trylock_read(mapping))
|
||||
goto lookup;
|
||||
|
||||
if (rwc->try_lock) {
|
||||
rwc->contended = true;
|
||||
return;
|
||||
}
|
||||
|
||||
i_mmap_lock_read(mapping);
|
||||
}
|
||||
lookup:
|
||||
vma_interval_tree_foreach(vma, &mapping->i_mmap,
|
||||
pgoff_start, pgoff_end) {
|
||||
unsigned long address = vma_address(page, vma);
|
||||
|
@ -1048,6 +1048,10 @@ static enum page_references page_check_references(struct page *page,
|
||||
if (vm_flags & VM_LOCKED)
|
||||
return PAGEREF_RECLAIM;
|
||||
|
||||
/* rmap lock contention: rotate */
|
||||
if (referenced_ptes == -1)
|
||||
return PAGEREF_KEEP;
|
||||
|
||||
if (referenced_ptes) {
|
||||
if (PageSwapBacked(page))
|
||||
return PAGEREF_ACTIVATE;
|
||||
@ -2094,9 +2098,9 @@ static void shrink_active_list(unsigned long nr_to_scan,
|
||||
}
|
||||
}
|
||||
|
||||
/* Referenced or rmap lock contention: rotate */
|
||||
if (page_referenced(page, 0, sc->target_mem_cgroup,
|
||||
&vm_flags)) {
|
||||
nr_rotated += hpage_nr_pages(page);
|
||||
&vm_flags) != 0) {
|
||||
/*
|
||||
* Identify referenced, file-backed active pages and
|
||||
* give them one more trip around the active list. So
|
||||
@ -2107,6 +2111,7 @@ static void shrink_active_list(unsigned long nr_to_scan,
|
||||
* so we ignore them here.
|
||||
*/
|
||||
if ((vm_flags & VM_EXEC) && page_is_file_cache(page)) {
|
||||
nr_rotated += hpage_nr_pages(page);
|
||||
list_add(&page->lru, &l_active);
|
||||
continue;
|
||||
}
|
||||
|
7
net/TEST_MAPPING
Normal file
7
net/TEST_MAPPING
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsNetTestCases"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user