mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:09:30 +02:00
Merge tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc/IIO/etc driver updates from Greg KH:
"Here is the big set of char, misc, iio, counter, fpga, and other small
driver subsystems for 7.3-rc1.
Overall, due to some driver removals we only added a bit more code
than removed, which was a nice change. Highlights in this merge
request are:
- Loads of IIO driver updates and additions
- binder driver updates (more on that below...)
- Removal of the SGI XP and GRU drivers as they are not used anymore
and turn out to be pretty insecure overall
- Removal of the obsolete ibmasm driver as it's not being used
anymore
- Coresight driver updates and additions
- Mei driver udpates
- Counter driver updates
- FPGA driver updates
- ICC driver updates
- lots and lots of other tiny driver updates to resolve reported
issues
All of these have been in linux-next for a while"
* tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (513 commits)
iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF
iio: adc: pac1921: fix wrong channel used in trigger handler read
iio: light: gp2ap002: re-enable irq if runtime suspend fails
iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes
iio: light: apds9306: fix PM reference leak in apds9306_read_data()
iio: gyro: mpu3050: fix sign of raw angular velocity readings
iio: srf04: fix pm_runtime handling on probe error path
iio: adc: ad4080: configure backend data size
iio: adc: adi-axi-adc: add data size support for AD408X backend
iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable
iio: dac: ad5446: fix OF module device table
iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask
iio: light: opt4001: Reject integration times with a non-zero seconds part
iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem()
iio: light: opt4001: Fix power down clearing bits of the wrong register
iio: light: opt4060: Fix incorrect register name in threshold read error message
iio: light: opt4060: Fix pointer type passed to div_u64_rem()
iio: light: opt4060: Reject integration times with a non-zero seconds part
iio: light: ltrf216a: fix runtime PM reference leak in error path
iio: pressure: dps310: fix NULL pointer dereference on ACPI probe
...
This commit is contained in:
@@ -611,6 +611,7 @@ Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@s-opensource.com>
|
||||
Maxime Ripard <mripard@kernel.org> <maxime@cerno.tech>
|
||||
Maxime Ripard <mripard@kernel.org> <maxime.ripard@bootlin.com>
|
||||
Maxime Ripard <mripard@kernel.org> <maxime.ripard@free-electrons.com>
|
||||
Maxwell Doose <maxwell@maxwelld.cc> <m32285159@gmail.com>
|
||||
Maya Erez <quic_merez@quicinc.com> <merez@codeaurora.org>
|
||||
Mayuresh Janorkar <mayur@ti.com>
|
||||
Md Sadre Alam <quic_mdalam@quicinc.com> <mdalam@codeaurora.org>
|
||||
@@ -899,6 +900,7 @@ Tobias Klauser <tklauser@distanz.ch> <klto@zhaw.ch>
|
||||
Tobias Klauser <tklauser@distanz.ch> <tklauser@nuerscht.ch>
|
||||
Tobias Klauser <tklauser@distanz.ch> <tklauser@xenon.tklauser.home>
|
||||
Todor Tomov <todor.too@gmail.com> <todor.tomov@linaro.org>
|
||||
Tomasz Duszynski <tduszyns@gmail.com> <tomasz.duszynski@octakon.com>
|
||||
Tomasz Jeznach <tomasz.jeznach@linux.dev> <tjeznach@rivosinc.com>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Trilok Soni <quic_tsoni@quicinc.com> <tsoni@codeaurora.org>
|
||||
|
||||
@@ -162,10 +162,11 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled) differential voltage measurement equivalent to
|
||||
channel Y - channel Z where these channel numbers apply to the
|
||||
physically equivalent inputs when non differential readings are
|
||||
separately available. In differential only parts, then all that
|
||||
is required is a consistent labeling. Units after application
|
||||
of scale and offset are millivolts.
|
||||
physically equivalent inputs when non-differential readings are
|
||||
separately available. Even though differential-only parts had
|
||||
been merely required to have consistent channel labeling, this
|
||||
ABI is preferred over the non-differential one for those chips.
|
||||
Units after application of scale and offset are millivolts.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw
|
||||
KernelVersion: 6.18
|
||||
@@ -507,6 +508,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@@ -770,6 +773,7 @@ Description:
|
||||
3.85kohm_to_gnd: connected to ground via a 3.85kOhm resistor,
|
||||
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
|
||||
7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor,
|
||||
10kohm_to_gnd: connected to ground via a 10kOhm resistor,
|
||||
16kohm_to_gnd: connected to ground via a 16kOhm resistor,
|
||||
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
|
||||
32kohm_to_gnd: connected to ground via a 32kOhm resistor,
|
||||
@@ -917,7 +921,7 @@ Description:
|
||||
|
||||
Note the driver will assume the last p events requested are
|
||||
to be enabled where p is how many it supports (which may vary
|
||||
depending on the exact set requested. So if you want to be
|
||||
depending on the exact set requested). So if you want to be
|
||||
sure you have set what you think you have, check the contents of
|
||||
these attributes after everything is configured. Drivers may
|
||||
have to buffer any parameters so that they are consistent when
|
||||
@@ -973,7 +977,7 @@ Description:
|
||||
|
||||
Note the driver will assume the last p events requested are
|
||||
to be enabled where p is however many it supports (which may
|
||||
vary depending on the exact set requested. So if you want to be
|
||||
vary depending on the exact set requested). So if you want to be
|
||||
sure you have set what you think you have, check the contents of
|
||||
these attributes after everything is configured. Drivers may
|
||||
have to buffer any parameters so that they are consistent when
|
||||
@@ -1528,9 +1532,9 @@ Description:
|
||||
the unused bits, so to get a clean value the bits value must be
|
||||
used to mask the buffer output value appropriately. The storagebits
|
||||
value also specifies the data alignment. So s48/64>>2 will be a
|
||||
signed 48 bit integer stored in a 64 bit location aligned to a 64
|
||||
bit boundary. To obtain the clean value, shift right 2 and apply a
|
||||
mask to zero the top 16 bits of the result.
|
||||
signed 48-bit integer stored in a 64-bit location aligned to a
|
||||
64-bit boundary. To obtain the clean value, shift right 2 and apply
|
||||
a mask to zero the top 16 bits of the result.
|
||||
For other storage combinations this attribute will be extended
|
||||
appropriately.
|
||||
|
||||
@@ -2496,3 +2500,12 @@ Description:
|
||||
seconds, expressed as:
|
||||
|
||||
- a range specified as "[min step max]"
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_raw
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled) volumetric flow rate reading from the channel.
|
||||
To convert to standard units (cubic metres per second, m^3/s)
|
||||
apply the channel's _scale (and _offset, when present).
|
||||
|
||||
@@ -16,7 +16,7 @@ Description:
|
||||
In bridge applications, such as strain gauges and load cells,
|
||||
the bridge itself consumes the majority of the current in the
|
||||
system. To minimize the current consumption of the system,
|
||||
the bridge can be disconnected (when it is not being used
|
||||
the bridge can be disconnected (when it is not being used)
|
||||
using the bridge_switch_en attribute.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
The calibration medium the flow sensor uses to convert its
|
||||
raw reading into a volumetric flow rate. Liquid flow sensors
|
||||
are factory-calibrated per medium, so the selected medium has
|
||||
to match the fluid actually flowing through the sensor for the
|
||||
reported flow rate to be correct.
|
||||
|
||||
Reading returns the currently active medium; writing one of the
|
||||
strings listed in in_volumeflow_medium_available selects it.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_medium_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_medium_available
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Space separated list of the calibration media supported by the
|
||||
device, e.g. "water ipa".
|
||||
@@ -0,0 +1,11 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
|
||||
KernelVersion: 3.4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Stores channel Y frequency resolution/channel spacing in Hz for PLL
|
||||
devices. The given value directly influences the operating mode when
|
||||
fractional-N synthesis is required, as it derives values for
|
||||
configurable modulus parameters used in the calculation of the output
|
||||
frequency. It is assumed that the algorithm used to compute the various
|
||||
dividers is able to generate proper values for multiples of the channel
|
||||
spacing.
|
||||
@@ -1,13 +1,3 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
|
||||
KernelVersion: 3.4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Stores channel Y frequency resolution/channel spacing in Hz.
|
||||
The value given directly influences the MODULUS used by
|
||||
the fractional-N PLL. It is assumed that the algorithm
|
||||
that is used to compute the various dividers, is able to
|
||||
generate proper values for multiples of channel spacing.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency
|
||||
KernelVersion: 3.4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
What: /sys/class/issei/
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description:
|
||||
The issei/ class sub-directory belongs to issei device class
|
||||
|
||||
What: /sys/class/issei/issei<N>/
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description:
|
||||
The /sys/class/issei/isseiN directory is created for
|
||||
each probed issei device
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_ver
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: Display the ISSE firmware version.
|
||||
|
||||
The version of the ISSE firmware is in format:
|
||||
<major>.<minor>.<milestone>.<build_no>.
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description:
|
||||
The fw_clients directory stores all firmware clients on the
|
||||
probed issei device
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients/<M>
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description:
|
||||
The /sys/class/issei/isseiN/fw_client/M directory is created for
|
||||
each firmware client on the probed issei device where M is the
|
||||
id of firmware client.
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients/<M>/id
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: Displays id of the firmware client
|
||||
|
||||
The id of firmware client is it's number in client enumeration order,
|
||||
starting from 1.
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients/<M>/uuid
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: Displays uuid of the firmware client
|
||||
|
||||
The universally unique identifier of the firmware client
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients/<M>/mtu
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: Displays maximum transmission unit of the firmware client
|
||||
|
||||
The maximum transmission unit (in bytes) used by the firmware client.
|
||||
|
||||
What: /sys/class/issei/issei<N>/fw_clients/<M>/ver
|
||||
Date: June 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
Description: Displays version of the firmware client
|
||||
|
||||
The version of the firmware client
|
||||
@@ -172,3 +172,61 @@ Contact: Richard Gong <richard.gong@linux.intel.com>
|
||||
Description:
|
||||
(RO) max retry parameter is stored in the firmware
|
||||
decision IO section, as a byte located at offset 0x18c.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size0
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Flash size in bytes for QSPI device 0 (32-bit hex, one
|
||||
line). EIO if unavailable.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size1
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 1.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size2
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 2.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size3
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 3.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size0
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Erase size in bytes for QSPI device 0 (32-bit hex, one
|
||||
line). EIO if unavailable.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size1
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 1.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size2
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 2.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size3
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 3.
|
||||
|
||||
@@ -84,7 +84,7 @@ Description: Read-Write. Read this file to get errors detected on port and
|
||||
to clear errors. Write fails with -EINVAL if input parsing
|
||||
fails or input error code doesn't match. Write fails with
|
||||
-EBUSY or -ETIMEDOUT if error can't be cleared as hardware
|
||||
in low power state (-EBUSY) or not respoding (-ETIMEDOUT).
|
||||
in low power state (-EBUSY) or not responding (-ETIMEDOUT).
|
||||
|
||||
What: /sys/bus/platform/devices/dfl-port.0/errors/first_error
|
||||
Date: August 2019
|
||||
|
||||
@@ -73,6 +73,47 @@ properties:
|
||||
device design and can interact with other characteristics such as
|
||||
settling time.
|
||||
|
||||
reference-sources:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
description:
|
||||
Indicates the voltage reference source or sources for this channel. Some
|
||||
ADCs usually allow choosing between internal reference sources or a pair
|
||||
of external pins.
|
||||
|
||||
input-chopping:
|
||||
type: boolean
|
||||
description:
|
||||
If set, the positive and negative input channels are swapped on every
|
||||
other conversion to reduce offset error. This generally comes at the
|
||||
expense of added settling time between conversions, so should be omitted
|
||||
on systems with high-frequency signals or high data rate requirements.
|
||||
|
||||
excitation-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description:
|
||||
Excitation current sources provide current to resistive temperature
|
||||
devices (RTDs), thermistors, diodes and other resistive sensors that
|
||||
require constant current biasing.
|
||||
|
||||
This array describes the mux configuration of the excitation current
|
||||
sources. The values correspond to pins similar to the diff-channels and
|
||||
single-channel properties.
|
||||
|
||||
excitation-current-nanoamp:
|
||||
description:
|
||||
Excitation current sources provide current to resistive temperature
|
||||
devices (RTDs), thermistors, diodes and other resistive sensors that
|
||||
require constant current biasing.
|
||||
|
||||
This array describes the current configuration of the excitation current
|
||||
sources. The index in the array corresponds to the same index in the
|
||||
excitation-channels array.
|
||||
|
||||
burn-out-current-nanoamp:
|
||||
description:
|
||||
Burn-out current sources provide current to the channel's input pins for
|
||||
open-circuit or short-circuit detection.
|
||||
|
||||
anyOf:
|
||||
- oneOf:
|
||||
- required:
|
||||
|
||||
@@ -39,6 +39,7 @@ properties:
|
||||
- adi,ad4087
|
||||
- adi,ad4088
|
||||
- adi,ad4880
|
||||
- adi,ad4883
|
||||
- adi,ad4884
|
||||
|
||||
reg:
|
||||
@@ -102,6 +103,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad4880
|
||||
- adi,ad4883
|
||||
- adi,ad4884
|
||||
then:
|
||||
properties:
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices LTC2378 and similar Analog to Digital Converters
|
||||
|
||||
maintainers:
|
||||
- Marcelo Schmitt <marcelo.schmitt@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices LTC2378 series of ADCs.
|
||||
Specifications can be found at:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
# Single compatible string match.
|
||||
- enum:
|
||||
- adi,ltc2338-18
|
||||
- adi,ltc2364-16
|
||||
- adi,ltc2364-18
|
||||
- adi,ltc2376-16
|
||||
- adi,ltc2376-18
|
||||
- adi,ltc2376-20
|
||||
|
||||
# Low sample rate fallback for 16-bit unipolar sensors.
|
||||
- items:
|
||||
- enum:
|
||||
- adi,ltc2370-16 # 2 MSPS
|
||||
- adi,ltc2368-16 # 1 MSPS
|
||||
- adi,ltc2367-16 # 500 kSPS
|
||||
- const: adi,ltc2364-16 # fallback (250 kSPS)
|
||||
|
||||
# Low sample rate fallback for 18-bit unipolar sensors.
|
||||
- items:
|
||||
- enum:
|
||||
- adi,ltc2369-18 # 1.6 MSPS
|
||||
- adi,ltc2368-18 # 1 MSPS
|
||||
- adi,ltc2367-18 # 500 kSPS
|
||||
- const: adi,ltc2364-18 # fallback (250 kSPS)
|
||||
|
||||
# Low sample rate fallback for 16-bit bipolar sensors.
|
||||
- items:
|
||||
- enum:
|
||||
- adi,ltc2380-16 # 2 MSPS
|
||||
- adi,ltc2378-16 # 1 MSPS
|
||||
- adi,ltc2377-16 # 500 kSPS
|
||||
- const: adi,ltc2376-16 # fallback (250 kSPS)
|
||||
|
||||
# Low sample rate fallback for 18-bit bipolar sensors.
|
||||
- items:
|
||||
- enum:
|
||||
- adi,ltc2379-18 # 1.6 MSPS
|
||||
- adi,ltc2378-18 # 1 MSPS
|
||||
- adi,ltc2377-18 # 500 kSPS
|
||||
- const: adi,ltc2376-18 # fallback (250 kSPS)
|
||||
|
||||
# Low sample rate fallback for 20-bit bipolar sensors.
|
||||
- items:
|
||||
- enum:
|
||||
- adi,ltc2378-20 # 1 MSPS
|
||||
- adi,ltc2377-20 # 500 kSPS
|
||||
- const: adi,ltc2376-20 # fallback (250 kSPS)
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 100000000
|
||||
|
||||
vdd-supply:
|
||||
description: A 2.5V supply that powers the chip (VDD).
|
||||
|
||||
ovdd-supply:
|
||||
description:
|
||||
A 1.71V to 5.25V supply that sets the logic level for digital interface.
|
||||
|
||||
ref-supply:
|
||||
description:
|
||||
Voltage reference input that determines the scale of ADC conversions.
|
||||
|
||||
refin-supply:
|
||||
description:
|
||||
Alternative voltage reference input.
|
||||
|
||||
cnv-gpios:
|
||||
description:
|
||||
When provided, this property indicates the GPIO that is connected to the
|
||||
CNV pin.
|
||||
maxItems: 1
|
||||
|
||||
pwms:
|
||||
description: PWM signal connected to the CNV pin.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
Interrupt for signaling the completion of conversion results. The active
|
||||
low signal provided on the BUSY pin asserts when ADC conversions finish.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- ovdd-supply
|
||||
|
||||
allOf:
|
||||
# Except for LTC2338, all designs require a voltage reference input
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ltc2338-18
|
||||
then:
|
||||
required:
|
||||
- ref-supply
|
||||
properties:
|
||||
refin-supply: false
|
||||
else:
|
||||
properties:
|
||||
ref-supply: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@0 {
|
||||
compatible = "adi,ltc2378-20", "adi,ltc2376-20";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <71000000>;
|
||||
vdd-supply = <&supply_2_5V>;
|
||||
ovdd-supply = <&supply_3_3V>;
|
||||
ref-supply = <&supply_5V>;
|
||||
cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
};
|
||||
};
|
||||
@@ -33,6 +33,26 @@ properties:
|
||||
description:
|
||||
Analog supply voltage (AVDD).
|
||||
|
||||
vsup-supply:
|
||||
description:
|
||||
Supply voltage for the on-chip regulator (VSUP).
|
||||
|
||||
dvdd-supply:
|
||||
description:
|
||||
Digital supply voltage (DVDD). On the HX711, DVDD is a
|
||||
board-supplied digital rail; it was missing from the original
|
||||
binding. On the HX710B, DVDD must be greater than or equal to
|
||||
AVDD; when DVDD is a battery rail and AVDD is a regulated supply,
|
||||
one channel monitors the DVDD-AVDD difference for battery level
|
||||
detection.
|
||||
|
||||
rate-gpios:
|
||||
description:
|
||||
GPIO connected to the RATE pin. When driven low the output data
|
||||
rate is 10 SPS; when driven high it is 80 SPS. If omitted the
|
||||
RATE pin state is determined by the board wiring.
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Controls the SCK bit-bang timing. The value is used to derive the
|
||||
@@ -57,6 +77,7 @@ examples:
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
rate-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
|
||||
avdd-supply = <&avdd>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
$id: http://devicetree.org/schemas/iio/adc/mediatek,mt6359-auxadc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek MT6350 series PMIC AUXADC
|
||||
title: MediaTek MT6350 series and similar PMIC AUXADC
|
||||
|
||||
maintainers:
|
||||
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
|
||||
@@ -20,6 +20,7 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- mediatek,mt6323-auxadc
|
||||
- mediatek,mt6357-auxadc
|
||||
- mediatek,mt6358-auxadc
|
||||
- mediatek,mt6359-auxadc
|
||||
|
||||
@@ -22,7 +22,9 @@ properties:
|
||||
- const: rockchip,rk3562-saradc
|
||||
- const: rockchip,rk3588-saradc
|
||||
- items:
|
||||
- const: rockchip,rk3576-saradc
|
||||
- enum:
|
||||
- rockchip,rk3576-saradc
|
||||
- rockchip,rv1106-saradc
|
||||
- const: rockchip,rk3588-saradc
|
||||
- items:
|
||||
- enum:
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/ti,ads112c14.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments' ADS112C14 and similar ADC chips
|
||||
|
||||
description: |
|
||||
Supports the following Texas Instruments' ADC chips:
|
||||
- ADS112C14 (16-bit)
|
||||
- ADS122C14 (24-bit)
|
||||
|
||||
https://www.ti.com/lit/ds/symlink/ads122c14.pdf
|
||||
|
||||
These chips are primarily designed for use with resistive sensors such as
|
||||
RTDs, thermocouples, Wheatstone bridges, etc. The channel bindings reflect
|
||||
this in that each channel represents the conditions required to make a
|
||||
measurement rather than strictly just the physical input channels.
|
||||
|
||||
maintainers:
|
||||
- David Lechner <dlechner@baylibre.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,ads112c14
|
||||
- ti,ads122c14
|
||||
|
||||
reg:
|
||||
items:
|
||||
- minimum: 0x40
|
||||
maximum: 0x47
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: Optional external clock connected to GPIO3 pin.
|
||||
|
||||
avdd-supply: true
|
||||
dvdd-supply: true
|
||||
|
||||
refp-supply: true
|
||||
refn-supply: true
|
||||
|
||||
ti,refp-refn-resistor-ohms:
|
||||
description:
|
||||
The resistance of the external resistor between REFP and REFN when using
|
||||
resistor bridge driven by current outputs for RTD measurements.
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: /FAULT interrupt (GPIO2 pin)
|
||||
- description: /DRDY interrupt (GPIO3 pin)
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum: [fault, drdy]
|
||||
|
||||
gpio-controller: true
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
^channel@[0-9a-f]$:
|
||||
$ref: adc.yaml
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- maximum: 15 # arbitrary limit, channel@ can be any combination of AIN0-AIN7
|
||||
|
||||
single-channel:
|
||||
maximum: 7
|
||||
|
||||
diff-channels:
|
||||
items:
|
||||
maximum: 7
|
||||
|
||||
bipolar: true
|
||||
|
||||
input-chopping: true
|
||||
|
||||
excitation-channels:
|
||||
maxItems: 2
|
||||
items:
|
||||
maximum: 7
|
||||
|
||||
excitation-current-nanoamp:
|
||||
maxItems: 2
|
||||
items:
|
||||
enum: [1000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000,
|
||||
90000, 100000, 200000, 300000, 400000, 500000, 600000, 700000,
|
||||
800000, 900000, 1000000]
|
||||
|
||||
burn-out-current-nanoamp:
|
||||
items:
|
||||
- enum: [200, 1000, 10000]
|
||||
|
||||
reference-sources:
|
||||
items:
|
||||
- enum: [internal-2.5v, internal-1.25v, external, avdd]
|
||||
default: internal-2.5v
|
||||
|
||||
dependencies:
|
||||
excitation-channels: [ excitation-current-nanoamp ]
|
||||
excitation-current-nanoamp: [ excitation-channels ]
|
||||
|
||||
oneOf:
|
||||
- required: [ single-channel ]
|
||||
- required: [ diff-channels ]
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
|
||||
dependencies:
|
||||
refn-supply: [ refp-supply ]
|
||||
|
||||
oneOf:
|
||||
- required: [ refp-supply ]
|
||||
- required: [ "ti,refp-refn-resistor-ohms" ]
|
||||
- properties:
|
||||
refp-supply: false
|
||||
refn-supply: false
|
||||
ti,refp-refn-resistor-ohms: false
|
||||
|
||||
# /DRDY out and CLK in use the same AIN7/GPIO3 pin.
|
||||
if:
|
||||
required:
|
||||
- interrupt-names
|
||||
properties:
|
||||
interrupt-names:
|
||||
contains:
|
||||
const: drdy
|
||||
then:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@40 {
|
||||
compatible = "ti,ads112c14";
|
||||
reg = <0x40>;
|
||||
|
||||
avdd-supply = <&avdd>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
|
||||
/* 3-Wire RTD: Two IDACs, One Measurement (AIN1-AIN2) */
|
||||
|
||||
ti,refp-refn-resistor-ohms = <500>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <1>, <2>;
|
||||
input-chopping;
|
||||
excitation-channels = <0>, <3>;
|
||||
excitation-current-nanoamp = <500000>, <500000>;
|
||||
reference-sources = "external";
|
||||
label = "rtd";
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@40 {
|
||||
compatible = "ti,ads112c14";
|
||||
reg = <0x40>;
|
||||
|
||||
avdd-supply = <&avdd>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
|
||||
/* Resistive Bridge Measurement With a Thermistor for Temperature Compensation */
|
||||
|
||||
refp-supply = <&avdd>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <6>, <7>;
|
||||
bipolar;
|
||||
reference-sources = "external";
|
||||
label = "bridge";
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
diff-channels = <1>, <2>;
|
||||
reference-sources = "internal-2.5v";
|
||||
label = "thermistor";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/xlnx,versal-sysmon.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: AMD/Xilinx Versal System Monitor
|
||||
|
||||
maintainers:
|
||||
- Salih Erim <salih.erim@amd.com>
|
||||
|
||||
description:
|
||||
The AMD/Xilinx Versal System Monitor (SysMon) is the successor to the
|
||||
Zynq UltraScale+ AMS block. It provides on-chip voltage and temperature
|
||||
monitoring with up to 160 voltage measurement points and up to
|
||||
64 temperature satellites distributed across the SoC. The hardware
|
||||
supports configurable threshold alarms and oversampling. The device
|
||||
can be accessed via memory-mapped I/O or via an I2C interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: xlnx,versal-sysmon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#io-channel-cells':
|
||||
const: 1
|
||||
|
||||
voltage-channels:
|
||||
type: object
|
||||
description:
|
||||
Container for voltage measurement channels.
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^channel@([0-9a-f]|[1-9][0-9a-f])$':
|
||||
$ref: adc.yaml
|
||||
|
||||
description:
|
||||
Measures a voltage rail. The register index and rail
|
||||
name are assigned by the hardware design tool (Vivado).
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 159
|
||||
description:
|
||||
Voltage measurement register index assigned by the hardware
|
||||
design tool.
|
||||
|
||||
required:
|
||||
- reg
|
||||
- label
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
temperature-channels:
|
||||
type: object
|
||||
description:
|
||||
Container for temperature satellite measurement channels.
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^channel@([1-9a-f]|[1-3][0-9a-f]|40)$':
|
||||
$ref: adc.yaml
|
||||
|
||||
description:
|
||||
Reads a temperature satellite sensor. Each satellite monitors
|
||||
a specific region of the SoC die.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 64
|
||||
description:
|
||||
Temperature satellite number (1-based hardware index).
|
||||
|
||||
required:
|
||||
- reg
|
||||
- label
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
sysmon@f1270000 {
|
||||
compatible = "xlnx,versal-sysmon";
|
||||
reg = <0xf1270000 0x4000>;
|
||||
interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
voltage-channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
label = "vccaux";
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
label = "vcc_ram";
|
||||
bipolar;
|
||||
};
|
||||
};
|
||||
|
||||
temperature-channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@a {
|
||||
reg = <10>;
|
||||
label = "aie-temp-ch1";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: Sensirion SCD30 carbon dioxide sensor
|
||||
|
||||
maintainers:
|
||||
- Maxwell Doose <m32285159@gmail.com>
|
||||
- Maxwell Doose <maxwell@maxwelld.cc>
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Air quality sensor capable of measuring co2 concentration, temperature
|
||||
|
||||
@@ -10,15 +10,17 @@ maintainers:
|
||||
- Kim Seer Paller <kimseer.paller@analog.com>
|
||||
|
||||
description: |
|
||||
The AD3530/AD3530R (8-channel) and AD3531/AD3531R (4-channel) are low-power,
|
||||
16-bit, buffered voltage output digital-to-analog converters (DACs) with
|
||||
software-programmable gain controls, providing full-scale output spans of 2.5V
|
||||
or 5V for reference voltages of 2.5V. These devices operate from a single 2.7V
|
||||
to 5.5V supply and are guaranteed monotonic by design. The "R" variants
|
||||
include a 2.5V, 5ppm/°C internal reference, which is disabled by default.
|
||||
The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), and AD3532/AD3532R
|
||||
(16-channel) are low-power, 16-bit, buffered voltage output digital-to-analog
|
||||
converters (DACs) with software-programmable gain controls, providing
|
||||
full-scale output spans of 2.5V or 5V for reference voltages of 2.5V. These
|
||||
devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic
|
||||
by design. The "R" variants include a 2.5V, 5ppm/°C internal reference, which
|
||||
is disabled by default.
|
||||
Datasheet can be found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3530_ad530r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3531-ad3531r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3532r.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -27,6 +29,8 @@ properties:
|
||||
- adi,ad3530r
|
||||
- adi,ad3531
|
||||
- adi,ad3531r
|
||||
- adi,ad3532
|
||||
- adi,ad3532r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,ad5686.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD5360 and similar SPI DACs
|
||||
title: Analog Devices AD5686 and similar SPI DACs
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
@@ -14,10 +14,14 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad5310r
|
||||
- adi,ad5313r
|
||||
- adi,ad5317r
|
||||
- adi,ad5672r
|
||||
- adi,ad5674
|
||||
- adi,ad5674r
|
||||
- adi,ad5676
|
||||
- adi,ad5676r
|
||||
- adi,ad5679
|
||||
- adi,ad5679r
|
||||
- adi,ad5681r
|
||||
- adi,ad5682r
|
||||
@@ -28,31 +32,107 @@ properties:
|
||||
- adi,ad5685r
|
||||
- adi,ad5686
|
||||
- adi,ad5686r
|
||||
- adi,ad5687
|
||||
- adi,ad5687r
|
||||
- adi,ad5689
|
||||
- adi,ad5689r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Input power supply.
|
||||
|
||||
vlogic-supply:
|
||||
description:
|
||||
Digital power supply. On some tiny package variants for single-channel
|
||||
devices, this supply is internally connected to vdd; in that case, specify
|
||||
this property with the same regulator as vdd.
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
Reference voltage supply. If not supplied the internal reference is used.
|
||||
|
||||
vcc-supply:
|
||||
description: If not supplied the internal reference is used.
|
||||
deprecated: true
|
||||
description: Use vref-supply instead.
|
||||
|
||||
reset-gpios:
|
||||
description: Active-low RESET pin to reset the device.
|
||||
maxItems: 1
|
||||
|
||||
ldac-gpios:
|
||||
description:
|
||||
Active-low LDAC pin used to asynchronously update the DAC channels.
|
||||
maxItems: 1
|
||||
|
||||
gain-gpios:
|
||||
description:
|
||||
GAIN pin that sets a multiplier for the DAC output voltage. When high,
|
||||
the DAC output voltage is multiplied by 2, otherwise it is unchanged.
|
||||
maxItems: 1
|
||||
|
||||
adi,range-double:
|
||||
description:
|
||||
Sets the initial voltage output range from 0 to 2xVREF. On devices that
|
||||
have a GAIN pin and no gain-gpios property is set, this indicates the pin
|
||||
is hardwired high.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vlogic-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5674
|
||||
- adi,ad5676
|
||||
- adi,ad5679
|
||||
- adi,ad5683
|
||||
- adi,ad5684
|
||||
- adi,ad5686
|
||||
- adi,ad5687
|
||||
- adi,ad5689
|
||||
then:
|
||||
required:
|
||||
- vref-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5310r
|
||||
- adi,ad5681r
|
||||
- adi,ad5682r
|
||||
- adi,ad5683
|
||||
- adi,ad5683r
|
||||
then:
|
||||
properties:
|
||||
gain-gpios: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dac@0 {
|
||||
reg = <0>;
|
||||
compatible = "adi,ad5310r";
|
||||
vcc-supply = <&dac_vref0>;
|
||||
vdd-supply = <&dac_vdd>;
|
||||
vlogic-supply = <&dac_vlogic>;
|
||||
vref-supply = <&dac_vref>;
|
||||
reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
|
||||
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
||||
@@ -16,10 +16,14 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad5311r
|
||||
- adi,ad5316r
|
||||
- adi,ad5337r
|
||||
- adi,ad5338r
|
||||
- adi,ad5671r
|
||||
- adi,ad5673r
|
||||
- adi,ad5675
|
||||
- adi,ad5675r
|
||||
- adi,ad5677r
|
||||
- adi,ad5691r
|
||||
- adi,ad5692r
|
||||
- adi,ad5693
|
||||
@@ -29,22 +33,88 @@ properties:
|
||||
- adi,ad5695r
|
||||
- adi,ad5696
|
||||
- adi,ad5696r
|
||||
- adi,ad5697r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Input power supply.
|
||||
|
||||
vlogic-supply:
|
||||
description:
|
||||
Digital power supply. On some tiny package variants for single-channel
|
||||
devices, this supply is internally connected to vdd; in that case, specify
|
||||
this property with the same regulator as vdd.
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
Reference voltage supply. If not supplied the internal reference is used.
|
||||
|
||||
vcc-supply:
|
||||
description: |
|
||||
The regulator supply for DAC reference voltage.
|
||||
deprecated: true
|
||||
description: Use vref-supply instead.
|
||||
|
||||
reset-gpios:
|
||||
description: Active-low RESET pin to reset the device.
|
||||
maxItems: 1
|
||||
|
||||
ldac-gpios:
|
||||
description:
|
||||
Active-low LDAC pin used to asynchronously update the DAC channels.
|
||||
maxItems: 1
|
||||
|
||||
gain-gpios:
|
||||
description:
|
||||
GAIN pin that sets a multiplier for the DAC output voltage. When high,
|
||||
the DAC output voltage is multiplied by 2, otherwise it is unchanged.
|
||||
maxItems: 1
|
||||
|
||||
adi,range-double:
|
||||
description:
|
||||
Sets the initial voltage output range from 0 to 2xVREF. On devices that
|
||||
have a GAIN pin and no gain-gpios property is set, this indicates the pin
|
||||
is hardwired high.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vlogic-supply
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5675
|
||||
- adi,ad5693
|
||||
- adi,ad5694
|
||||
- adi,ad5696
|
||||
then:
|
||||
required:
|
||||
- vref-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5311r
|
||||
- adi,ad5691r
|
||||
- adi,ad5692r
|
||||
- adi,ad5693
|
||||
- adi,ad5693r
|
||||
then:
|
||||
properties:
|
||||
gain-gpios: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@@ -52,7 +122,10 @@ examples:
|
||||
ad5696: dac@0 {
|
||||
compatible = "adi,ad5696";
|
||||
reg = <0>;
|
||||
vcc-supply = <&dac_vref>;
|
||||
vdd-supply = <&dac_vdd>;
|
||||
vlogic-supply = <&dac_vlogic>;
|
||||
vref-supply = <&dac_vref>;
|
||||
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
||||
@@ -36,11 +36,11 @@ properties:
|
||||
|
||||
vrefp-supply:
|
||||
description:
|
||||
Positive referance input voltage range. From 5v to (vdd - 2.5)
|
||||
Positive reference input voltage range. From 5v to (vdd - 2.5)
|
||||
|
||||
vrefn-supply:
|
||||
description:
|
||||
Negative referance input voltage range. From (vss + 2.5) to 0.
|
||||
Negative reference input voltage range. From (vss + 2.5) to 0.
|
||||
|
||||
adi,rbuf-gain2-en:
|
||||
description: Specify to allow an external amplifier to be connected in a
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47a1.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip MCP47A1 DAC
|
||||
|
||||
maintainers:
|
||||
- Joshua Crofts <joshua.crofts1@gmail.com>
|
||||
|
||||
description: |
|
||||
The Microchip MCP47A1 is a 6-bit single output volatile DAC.
|
||||
This device can have different IDs (0x2e and 0x3e).
|
||||
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,mcp47a1
|
||||
|
||||
reg:
|
||||
items:
|
||||
- enum: [0x2e, 0x3e]
|
||||
|
||||
vref-supply: true
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vref-supply
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@2e {
|
||||
compatible = "microchip,mcp47a1";
|
||||
reg = <0x2e>;
|
||||
vref-supply = <&vref_regulator>;
|
||||
vdd-supply = <&vdd_regulator>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/flow/sensirion,slf3s.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sensirion SLF3S liquid flow sensor
|
||||
|
||||
maintainers:
|
||||
- Wadim Mueller <wafgo01@gmail.com>
|
||||
|
||||
description:
|
||||
Family of digital liquid-flow sensors from Sensirion with I2C interface. All
|
||||
family members share the same register map; sub-types differ only in the flow
|
||||
scale factor and the calibrated measurement range. The sub-type can be
|
||||
identified from the product-information register.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensirion,slf3s-0600f
|
||||
- sensirion,slf3s-1300f
|
||||
- sensirion,slf3s-4000b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flow-sensor@8 {
|
||||
compatible = "sensirion,slf3s-0600f";
|
||||
reg = <0x08>;
|
||||
vdd-supply = <®_3v3>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,227 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADF41513 PLL Frequency Synthesizer
|
||||
|
||||
maintainers:
|
||||
- Rodrigo Alencar <rodrigo.alencar@analog.com>
|
||||
|
||||
description:
|
||||
The ADF41513 is an ultralow noise frequency synthesizer that can be used to
|
||||
implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
|
||||
downconversion sections of wireless receivers and transmitters. The ADF41510
|
||||
supports frequencies up to 10 GHz.
|
||||
|
||||
https://www.analog.com/en/products/adf41510.html
|
||||
https://www.analog.com/en/products/adf41513.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adf41510
|
||||
- adi,adf41513
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 25000000
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: Clock that provides the reference input frequency.
|
||||
|
||||
avdd1-supply:
|
||||
description: PFD and Up and Down Digital Driver Power Supply (3.3 V)
|
||||
|
||||
avdd2-supply:
|
||||
description: RF Buffer and Prescaler Power Supply (3.3 V)
|
||||
|
||||
avdd3-supply:
|
||||
description: N Divider Power Supply (3.3 V)
|
||||
|
||||
avdd4-supply:
|
||||
description: R Divider and Lock Detector Power Supply (3.3 V)
|
||||
|
||||
avdd5-supply:
|
||||
description: Sigma-Delta Modulator and SPI Power Supply (3.3 V)
|
||||
|
||||
vp-supply:
|
||||
description: Charge Pump Power Supply (3.3 V)
|
||||
|
||||
enable-gpios:
|
||||
description:
|
||||
GPIO that controls the chip enable pin. A logic low on this pin
|
||||
powers down the device and puts the charge pump output into
|
||||
three-state mode.
|
||||
maxItems: 1
|
||||
|
||||
lock-detect-gpios:
|
||||
description:
|
||||
GPIO for lock detect functionality. When configured for digital lock
|
||||
detect, this pin will output a logic high when the PLL is locked.
|
||||
maxItems: 1
|
||||
|
||||
adi,power-up-frequency-mhz:
|
||||
minimum: 1000
|
||||
maximum: 26500
|
||||
default: 10000
|
||||
description:
|
||||
The PLL tunes to this frequency during the initialization sequence.
|
||||
This property should be set to a frequency supported by the loop filter
|
||||
and VCO used in the design. Range is 1 GHz to 26.5 GHz for ADF41513,
|
||||
and 1 GHz to 10 GHz for ADF41510.
|
||||
|
||||
adi,reference-div-factor:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 32
|
||||
default: 1
|
||||
description:
|
||||
Value for the reference division factor (R Counter). The driver will
|
||||
increment R Counter as needed to achieve a PFD frequency within the
|
||||
allowed range. High R counter values will reduce the PFD frequency, which
|
||||
lowers the frequency resolution, and affects phase noise performance.
|
||||
As it affects the PFD frequency, this value depends on the loop filter
|
||||
design.
|
||||
|
||||
adi,reference-doubler-enable:
|
||||
description:
|
||||
Enables the reference doubler when deriving the PFD frequency.
|
||||
The maximum reference frequency when the doubler is enabled is 225 MHz.
|
||||
As it affects the PFD frequency, this value depends on the loop filter
|
||||
design.
|
||||
type: boolean
|
||||
|
||||
adi,reference-div2-enable:
|
||||
description:
|
||||
Enables the reference divide-by-2 function when deriving the PFD
|
||||
frequency. As it affects the PFD frequency, this value depends on the
|
||||
loop filter design.
|
||||
type: boolean
|
||||
|
||||
adi,charge-pump-resistor-ohms:
|
||||
minimum: 1800
|
||||
maximum: 10000
|
||||
default: 2700
|
||||
description:
|
||||
External charge pump resistor (R_SET) value in ohms. This sets the maximum
|
||||
charge pump current along with the charge pump current setting.
|
||||
|
||||
adi,charge-pump-current-microamp:
|
||||
minimum: 81
|
||||
maximum: 7200
|
||||
description:
|
||||
Charge pump current (I_CP) in microamps. The value will be rounded to the
|
||||
nearest supported value. Range of acceptable values depends on the
|
||||
charge pump resistor value, such that 810 mV <= I_CP * R_SET <= 12960 mV.
|
||||
This value depends on the loop filter and the VCO design.
|
||||
|
||||
adi,logic-level-1v8-enable:
|
||||
description:
|
||||
Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V.
|
||||
type: boolean
|
||||
|
||||
adi,phase-detector-polarity-positive-enable:
|
||||
description:
|
||||
Set phase detector polarity to positive. Default is negative.
|
||||
Use positive polarity with non-inverting loop filter and VCO with
|
||||
positive tuning slope, or with inverting loop filter and VCO with
|
||||
negative tuning slope.
|
||||
type: boolean
|
||||
|
||||
adi,lock-detector-count:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 64
|
||||
description:
|
||||
Sets the value for Lock Detector count of the PLL, which determines the
|
||||
number of consecutive phase detector cycles that must be within the lock
|
||||
detector window before lock is declared. Lower values increase the lock
|
||||
detection sensitivity, while higher values provides a more stable lock
|
||||
detection. Applications that consume the lock detect signal may require
|
||||
different settings based on system requirements.
|
||||
enum: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]
|
||||
|
||||
adi,phase-resync-period-ns:
|
||||
default: 0
|
||||
description:
|
||||
When this value is non-zero, enable phase resync functionality, which
|
||||
produces a consistent output phase offset with respect to the input
|
||||
reference. The value specifies the resync period in nanoseconds, used
|
||||
to configure clock dividers with respect to the PFD frequency. This value
|
||||
should be set to a value that is at least as long as the worst case lock
|
||||
time, i.e., it depends mostly on the loop filter design.
|
||||
|
||||
adi,le-sync-enable:
|
||||
description:
|
||||
Synchronizes Load Enable (LE) transitions with the reference signal to
|
||||
avoid asynchronous glitches in the output. This is recommended when using
|
||||
the PLL as a frequency synthesizer, where the reference signal will always
|
||||
be present while the device is being configured. When using the PLL as a
|
||||
frequency tracker, where the reference signal may be absent, LE sync
|
||||
should be left disabled.
|
||||
type: boolean
|
||||
|
||||
dependencies:
|
||||
adi,charge-pump-resistor-ohms: [ 'adi,charge-pump-current-microamp' ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- avdd1-supply
|
||||
- avdd2-supply
|
||||
- avdd3-supply
|
||||
- avdd4-supply
|
||||
- avdd5-supply
|
||||
- vp-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,adf41510
|
||||
then:
|
||||
properties:
|
||||
adi,power-up-frequency-mhz:
|
||||
maximum: 10000
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pll@0 {
|
||||
compatible = "adi,adf41513";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
clocks = <&ref_clk>;
|
||||
avdd1-supply = <&avdd1_3v3>;
|
||||
avdd2-supply = <&avdd2_3v3>;
|
||||
avdd3-supply = <&avdd3_3v3>;
|
||||
avdd4-supply = <&avdd4_3v3>;
|
||||
avdd5-supply = <&avdd5_3v3>;
|
||||
vp-supply = <&vp_3v3>;
|
||||
enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
|
||||
lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
adi,power-up-frequency-mhz = <15500>;
|
||||
adi,charge-pump-current-microamp = <3600>;
|
||||
adi,charge-pump-resistor-ohms = <2700>;
|
||||
adi,reference-doubler-enable;
|
||||
adi,lock-detector-count = <64>;
|
||||
adi,phase-resync-period-ns = <0>;
|
||||
adi,phase-detector-polarity-positive-enable;
|
||||
adi,le-sync-enable;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -30,6 +30,8 @@ properties:
|
||||
- invensense,icm42600
|
||||
- invensense,icm42602
|
||||
- invensense,icm42605
|
||||
- invensense,icm42607
|
||||
- invensense,icm42607p
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42686
|
||||
@@ -53,6 +55,8 @@ properties:
|
||||
drive-open-drain:
|
||||
type: boolean
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
@@ -65,10 +69,24 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,icm42600
|
||||
- invensense,icm42602
|
||||
- invensense,icm42605
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42686
|
||||
- invensense,icm42688
|
||||
then:
|
||||
required:
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883l.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: QST QMC5883L 3-Axis Magnetic Sensor
|
||||
|
||||
maintainers:
|
||||
- Siratul Islam <siratul.islam@linux.dev>
|
||||
|
||||
description: |
|
||||
QST QMC5883L 3-Axis Magnetic Sensor on I2C bus.
|
||||
https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qstcorp,qmc5883l
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
vddio-supply: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@d {
|
||||
compatible = "qstcorp,qmc5883l";
|
||||
reg = <0x0d>;
|
||||
vdd-supply = <&vdd_3v3_reg>;
|
||||
vddio-supply = <&vdd_3v3_reg>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc6308.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: QST QMC6308 3-Axis Magnetic Sensor
|
||||
|
||||
maintainers:
|
||||
- Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||
|
||||
description:
|
||||
QST QMC6308 3-axis anisotropic magnetoresistive (AMR) magnetic sensor
|
||||
on I2C bus, with a single supply and no interrupt pin.
|
||||
https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qstcorp,qmc6308
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@2c {
|
||||
compatible = "qstcorp,qmc6308";
|
||||
reg = <0x2c>;
|
||||
vdd-supply = <&vdd_1v8_reg>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -8,7 +8,7 @@ title: ST VL53L0X/VL53L1X ToF ranging sensor
|
||||
|
||||
maintainers:
|
||||
- Song Qiang <songqiang1304521@gmail.com>
|
||||
- Siratul Islam <email@sirat.me>
|
||||
- Siratul Islam <siratul.islam@linux.dev>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
||||
@@ -7,19 +7,25 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: TI TMP117 - Digital temperature sensor with integrated NV memory
|
||||
|
||||
description: |
|
||||
TI TMP116/117 - Digital temperature sensor with integrated NV memory that
|
||||
supports I2C interface.
|
||||
TI TMP116/117/119 - Digital temperature sensor with integrated NV memory
|
||||
that supports I2C interface.
|
||||
https://www.ti.com/lit/gpn/tmp116
|
||||
https://www.ti.com/lit/gpn/tmp117
|
||||
https://www.ti.com/lit/gpn/tmp119
|
||||
|
||||
maintainers:
|
||||
- Puranjay Mohan <puranjay12@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,tmp116
|
||||
- ti,tmp117
|
||||
oneOf:
|
||||
- enum:
|
||||
- ti,tmp116
|
||||
- ti,tmp117
|
||||
- items:
|
||||
- enum:
|
||||
- ti,tmp119
|
||||
- const: ti,tmp117
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interconnect/qcom,maili-rpmh.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm RPMh Network-On-Chip Interconnect on Maili SoC
|
||||
|
||||
maintainers:
|
||||
- Raviteja Laggyshetty <raviteja.laggyshetty@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
RPMh interconnect providers support system bandwidth requirements through
|
||||
RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is
|
||||
able to communicate with the BCM through the Resource State Coordinator (RSC)
|
||||
associated with each execution environment. Provider nodes must point to at
|
||||
least one RPMh device child node pertaining to their RSC and each provider
|
||||
can map to multiple RPMh resources.
|
||||
|
||||
See also: include/dt-bindings/interconnect/qcom,maili-rpmh.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
- qcom,maili-clk-virt
|
||||
- qcom,maili-cnoc-main
|
||||
- qcom,maili-gem-noc
|
||||
- qcom,maili-llclpi-noc
|
||||
- qcom,maili-lpass-ag-noc
|
||||
- qcom,maili-lpass-lpiaon-noc
|
||||
- qcom,maili-lpass-lpicx-noc
|
||||
- qcom,maili-mc-virt
|
||||
- qcom,maili-mmss-noc
|
||||
- qcom,maili-nsp-noc
|
||||
- qcom,maili-pcie-anoc
|
||||
- qcom,maili-stdst-cfg
|
||||
- qcom,maili-stdst-main
|
||||
- qcom,maili-system-noc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,rpmh-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-clk-virt
|
||||
- qcom,maili-mc-virt
|
||||
then:
|
||||
properties:
|
||||
reg: false
|
||||
else:
|
||||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: RPMH CC IPA clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-pcie-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre-NOC PCIe AXI clock
|
||||
- description: cfg-NOC PCIe a-NOC AHB clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
- qcom,maili-pcie-anoc
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
else:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
gem_noc: interconnect@31100000 {
|
||||
compatible = "qcom,maili-gem-noc";
|
||||
reg = <0x31100000 0x160200>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
aggre_noc: interconnect@f00000 {
|
||||
compatible = "qcom,maili-aggre-noc";
|
||||
reg = <0x00f00000 0x56200>;
|
||||
#interconnect-cells = <2>;
|
||||
clocks = <&gcc_phy_axi_clk>,
|
||||
<&gcc_prim_axi_clk>,
|
||||
<&rpmhcc_ipa_clk>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
@@ -17,6 +17,8 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,shikra-epss-l3
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sc7180-osm-l3
|
||||
|
||||
@@ -32,18 +32,115 @@ properties:
|
||||
- qcom,sc8280xp-nspb-noc
|
||||
- qcom,sc8280xp-system-noc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 7
|
||||
maxItems: 8
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,rpmh-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-clk-virt
|
||||
- qcom,sc8280xp-mc-virt
|
||||
then:
|
||||
properties:
|
||||
reg: false
|
||||
clocks: false
|
||||
else:
|
||||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre1-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: aggre USB3 SEC AXI clock
|
||||
- description: aggre USB3 MP AXI clock
|
||||
- description: aggre USB4 AXI clock
|
||||
- description: aggre USB4 1 AXI clock
|
||||
- description: aggre USB NOC SOUTH AXI clock
|
||||
- description: RPMH CC IPA clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre2-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre NOC PCIE0 tunnel AXI clock
|
||||
- description: aggre NOC PCIE1 tunnel AXI clock
|
||||
- description: aggre NOC PCIE 4 AXI clock
|
||||
- description: aggre NOC PCIE SOUTH SF AXI clock
|
||||
- description: aggre UFS CARD AXI clock
|
||||
- description: DDRSS GPU AXI clock
|
||||
- description: DDRSS PCIE SF TBU clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre1-noc
|
||||
- qcom,sc8280xp-aggre2-noc
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
else:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
interconnect-0 {
|
||||
compatible = "qcom,sc8280xp-aggre1-noc";
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
|
||||
interconnect {
|
||||
compatible = "qcom,sc8280xp-clk-virt";
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
interconnect@9100000 {
|
||||
compatible = "qcom,sc8280xp-gem-noc";
|
||||
reg = <0x9100000 0xb8400>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
interconnect@16c0000 {
|
||||
compatible = "qcom,sc8280xp-aggre1-noc";
|
||||
reg = <0x16c0000 0x3af80>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_MP_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB4_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB4_1_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB_NOC_SOUTH_AXI_CLK>,
|
||||
<&rpmhcc RPMH_IPA_CLK>;
|
||||
};
|
||||
|
||||
@@ -46,6 +46,10 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 6
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@@ -65,6 +69,73 @@ allOf:
|
||||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-aggre1-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-usb-north-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre USB2 PRIM AXI clock
|
||||
- description: aggre USB3 MP AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-usb-south-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: aggre USB3 SEC AXI clock
|
||||
- description: aggre USB3 TERT AXI clock
|
||||
- description: aggre USB4_0 AXI clock
|
||||
- description: aggre USB4_1 AXI clock
|
||||
- description: aggre USB4_2 AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-aggre2-noc
|
||||
- qcom,x1e80100-clk-virt
|
||||
- qcom,x1e80100-cnoc-cfg
|
||||
- qcom,x1e80100-cnoc-main
|
||||
- qcom,x1e80100-gem-noc
|
||||
- qcom,x1e80100-lpass-ag-noc
|
||||
- qcom,x1e80100-lpass-lpiaon-noc
|
||||
- qcom,x1e80100-lpass-lpicx-noc
|
||||
- qcom,x1e80100-mc-virt
|
||||
- qcom,x1e80100-mmss-noc
|
||||
- qcom,x1e80100-nsp-noc
|
||||
- qcom,x1e80100-pcie-center-anoc
|
||||
- qcom,x1e80100-pcie-north-anoc
|
||||
- qcom,x1e80100-pcie-south-anoc
|
||||
- qcom,x1e80100-system-noc
|
||||
- qcom,x1e80100-usb-center-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
@@ -80,4 +151,5 @@ examples:
|
||||
reg = <0x016e0000 0x14400>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
clocks = <&gcc_aggre_ufs_phy_axi_clk>;
|
||||
};
|
||||
|
||||
@@ -26,6 +26,8 @@ properties:
|
||||
- enum:
|
||||
- qcom,glymur-fastrpc
|
||||
- qcom,hawi-fastrpc
|
||||
- qcom,maili-fastrpc
|
||||
- qcom,nord-fastrpc
|
||||
- const: qcom,kaanapali-fastrpc
|
||||
|
||||
label:
|
||||
@@ -35,6 +37,8 @@ properties:
|
||||
- sdsp
|
||||
- cdsp
|
||||
- cdsp1
|
||||
- cdsp2
|
||||
- cdsp3
|
||||
- gdsp0
|
||||
- gdsp1
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/nvmem/airoha,smc-efuses.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Airoha SMC eFuses
|
||||
|
||||
description: |
|
||||
Airoha new SoC AN7581 expose banks of eFuse accessible
|
||||
via specific SMC commands.
|
||||
|
||||
2 different bank of eFuse or 64 cells of 32 bit are exposed
|
||||
read-only used to give information on HW Revision, PHY Calibration,
|
||||
Device Model, Private Key...
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- airoha,an7581-efuses
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^efuse-bank@[0-1]$':
|
||||
type: object
|
||||
|
||||
allOf:
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Identify the eFuse bank.
|
||||
enum: [0, 1]
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
efuse {
|
||||
compatible = "airoha,an7581-efuses";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
efuse-bank@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- const: microchip,lan9662-otpc
|
||||
- enum:
|
||||
- microchip,lan9662-otpc
|
||||
- microchip,lan9691-otpc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -19,14 +19,19 @@ properties:
|
||||
- enum:
|
||||
- qcom,apq8064-qfprom
|
||||
- qcom,apq8084-qfprom
|
||||
- qcom,glymur-qfprom
|
||||
- qcom,hawi-qfprom
|
||||
- qcom,ipq5018-qfprom
|
||||
- qcom,ipq5210-qfprom
|
||||
- qcom,ipq5332-qfprom
|
||||
- qcom,ipq5424-qfprom
|
||||
- qcom,ipq6018-qfprom
|
||||
- qcom,ipq8064-qfprom
|
||||
- qcom,ipq8074-qfprom
|
||||
- qcom,ipq9574-qfprom
|
||||
- qcom,ipq9650-qfprom
|
||||
- qcom,kaanapali-qfprom
|
||||
- qcom,milos-qfprom
|
||||
- qcom,msm8226-qfprom
|
||||
- qcom,msm8916-qfprom
|
||||
- qcom,msm8917-qfprom
|
||||
@@ -48,6 +53,7 @@ properties:
|
||||
- qcom,sdm630-qfprom
|
||||
- qcom,sdm670-qfprom
|
||||
- qcom,sdm845-qfprom
|
||||
- qcom,shikra-qfprom
|
||||
- qcom,sm6115-qfprom
|
||||
- qcom,sm6350-qfprom
|
||||
- qcom,sm6375-qfprom
|
||||
|
||||
@@ -1387,6 +1387,8 @@ patternProperties:
|
||||
description: Shenzhen QiShenglong Industrialist Co., Ltd.
|
||||
"^qnap,.*":
|
||||
description: QNAP Systems, Inc.
|
||||
"^qstcorp,.*":
|
||||
description: QST Corporation Limited
|
||||
"^quanta,.*":
|
||||
description: Quanta Computer Inc.
|
||||
"^radxa,.*":
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
* OMAP HDQ One wire bus master controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "ti,omap3-1w" or "ti,am4372-hdq"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts : interrupt line.
|
||||
- ti,hwmods : "hdq1w"
|
||||
|
||||
Optional properties:
|
||||
- ti,mode: should be "hdq": HDQ mode "1w": one-wire mode.
|
||||
If not specified HDQ mode is implied.
|
||||
|
||||
Example:
|
||||
|
||||
- From omap3.dtsi
|
||||
hdqw1w: 1w@480b2000 {
|
||||
compatible = "ti,omap3-1w";
|
||||
reg = <0x480b2000 0x1000>;
|
||||
interrupts = <58>;
|
||||
ti,hwmods = "hdq1w";
|
||||
ti,mode = "hdq";
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/w1/ti,hdq.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments OMAP HDQ One Wire Bus Master Controller
|
||||
|
||||
maintainers:
|
||||
- Eduard Bostina <egbostina@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,omap3-1w
|
||||
- ti,am4372-hdq
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: fck
|
||||
|
||||
ti,hwmods:
|
||||
description: Name of the hwmod associated to the hdq
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
const: hdq1w
|
||||
|
||||
ti,mode:
|
||||
description: |
|
||||
Operation mode. "hdq" for HDQ mode, "1w" for One-Wire mode.
|
||||
If not specified, HDQ mode is implied.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum:
|
||||
- hdq
|
||||
- 1w
|
||||
default: hdq
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
onewire@0 {
|
||||
compatible = "ti,am4372-hdq";
|
||||
reg = <0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&func_12m_clk>;
|
||||
clock-names = "fck";
|
||||
};
|
||||
- |
|
||||
onewire@480b2000 {
|
||||
compatible = "ti,omap3-1w";
|
||||
reg = <0x480b2000 0x1000>;
|
||||
interrupts = <58>;
|
||||
ti,hwmods = "hdq1w";
|
||||
ti,mode = "hdq";
|
||||
};
|
||||
@@ -105,6 +105,7 @@ Subsystem-specific APIs
|
||||
interconnect
|
||||
ipmb
|
||||
ipmi
|
||||
issei/index
|
||||
libata
|
||||
mailbox
|
||||
md/index
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
=========================================================
|
||||
The Intel Silicon Security Engine Interface (Intel SSEI)
|
||||
=========================================================
|
||||
|
||||
**Copyright** |copy| 2026 Intel Corporation
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of Contents
|
||||
:maxdepth: 3
|
||||
|
||||
issei
|
||||
@@ -0,0 +1,135 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The Intel Silicon Security Engine (Intel SSE) is an isolated and
|
||||
protected computing resource (Co-processor) residing inside
|
||||
Intel client chipsets released in 2024 (Lunar Lake) or later.
|
||||
The Intel SSE provide security support and platform boot orchestration.
|
||||
The actual feature set depends on the Intel chipset SKU.
|
||||
|
||||
The Intel Silicon Security Engine Interface (Intel SSEI)
|
||||
is the interface between the Host and Intel SSE.
|
||||
This interface is exposed to the host as one or more PCI devices.
|
||||
The Intel SSEI Driver is in charge of the communication channel between
|
||||
a host application and the Intel SSE features.
|
||||
|
||||
Each Intel SSE feature, or Intel SSE Client is addressed by a unique UUID and
|
||||
each client has its own protocol. The protocol is message-based with a
|
||||
header and payload up to maximal number of bytes advertised by the client,
|
||||
upon connection.
|
||||
|
||||
Intel SSEI Driver
|
||||
=================
|
||||
|
||||
The driver exposes a character device with device nodes /dev/isseiX.
|
||||
|
||||
An application maintains communication with an Intel SSE feature while
|
||||
/dev/isseiX is open. The binding to a specific feature is performed by calling
|
||||
:c:macro:`IOCTL_ISSEI_CONNECT_CLIENT`, which passes the desired UUID.
|
||||
The number of instances of an Intel SSE feature that can be opened
|
||||
at the same time is limited to single instance.
|
||||
|
||||
The driver is transparent to data that are passed between firmware feature
|
||||
and host application.
|
||||
|
||||
Because some of the Intel SSE features can change the system
|
||||
configuration, the driver by default allows only a privileged
|
||||
user to access it.
|
||||
|
||||
The connection termination is performed by calling
|
||||
:c:macro:`IOCTL_ISSEI_DISCONNECT_CLIENT`.
|
||||
|
||||
The session is terminated calling :c:expr:`close(fd)`.
|
||||
|
||||
A code snippet for an application communicating with SPDM client:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct issei_connect_client_data data = {.in_client_uuid =
|
||||
{0xe8, 0x51, 0x49, 0xdf, 0x94, 0x47, 0x4C,
|
||||
0x9A, 0x83, 0x67, 0xC4, 0xE3, 0x34, 0x64, 0xF1, 0xB4}};
|
||||
__u8 req_data[] = {0x10, 0x84, 0x00, 0x00}; /* SPDM Get Version */
|
||||
size_t req_data_len = sizeof(req_data);
|
||||
__u8 res_data[256];
|
||||
size_t res_data_len = sizeof(res_data);
|
||||
int fd = open("/dev/issei0", O_RDWR);
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &data);
|
||||
|
||||
printf("Ver=%d, MaxLen=%u, Flags=0x%08X\n",
|
||||
data.out_client_properties.protocol_version,
|
||||
data.out_client_properties.max_msg_length,
|
||||
data.out_client_properties.flags);
|
||||
|
||||
[...]
|
||||
|
||||
write(fd, req_data, req_data_len);
|
||||
|
||||
[...]
|
||||
|
||||
read(fd, res_data, res_data_len);
|
||||
|
||||
printf("SPDM version count %u, version[0]=%02X%02X\n",
|
||||
res_data[5], res_data[6], res_data[7]);
|
||||
|
||||
[...]
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, &data);
|
||||
|
||||
[...]
|
||||
|
||||
close(fd);
|
||||
|
||||
|
||||
User space API ioctl
|
||||
====================
|
||||
|
||||
The Intel SSEI Driver supports the following ioctl commands:
|
||||
|
||||
IOCTL_ISSEI_CONNECT_CLIENT
|
||||
--------------------------
|
||||
Connect to firmware Feature/Client.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Usage:
|
||||
|
||||
struct issei_connect_client_data client_data;
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &client_data);
|
||||
|
||||
struct issei_connect_client_data - contain the following
|
||||
Inputs:
|
||||
in_client_uuid - UUID of the FW Feature that needs to connect to.
|
||||
Outputs:
|
||||
out_client_properties - Client Properties: MTU, Protocol Version and Flags.
|
||||
|
||||
Error returns:
|
||||
ENOTTY No such client (i.e. wrong UUID) or connection is not allowed.
|
||||
EINVAL Wrong IOCTL Number
|
||||
ENODEV Device or Connection is not initialized or ready.
|
||||
ENOMEM Unable to allocate memory to client internal data.
|
||||
EFAULT Fatal Error (e.g. Unable to access user input data)
|
||||
EBUSY Connection Already Open
|
||||
|
||||
:Note:
|
||||
max_msg_length (MTU) in client properties describes the maximum
|
||||
data that can be sent or received. (e.g. with MTU=2K, can send
|
||||
requests up to bytes 2k and received responses up to 2k bytes).
|
||||
|
||||
IOCTL_ISSEI_DISCONNECT_CLIENT
|
||||
-----------------------------
|
||||
Disconnect from firmware Feature/Client.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Usage:
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, NULL);
|
||||
|
||||
Error returns:
|
||||
EINVAL Wrong IOCTL Number
|
||||
ENODEV Device or Connection is not initialized or ready.
|
||||
ENOTCONN Feature/Client is not connected.
|
||||
@@ -254,6 +254,7 @@ Hardware Monitoring Kernel Drivers
|
||||
smsc47b397
|
||||
smsc47m192
|
||||
smsc47m1
|
||||
socfpga-hwmon
|
||||
sparx5-temp
|
||||
spd5118
|
||||
sq24860
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver socfpga-hwmon
|
||||
=============================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Altera Stratix 10 SoC FPGA
|
||||
* Altera Agilex SoC FPGA
|
||||
|
||||
Authors:
|
||||
- Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
|
||||
- Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Altera SoC
|
||||
FPGA devices through the Secure Device Manager and Stratix 10 service layer.
|
||||
|
||||
The following sensor types are supported:
|
||||
|
||||
* temperature
|
||||
* voltage
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
The stratix10-svc driver registers a socfpga-hwmon platform device when
|
||||
hardware monitor support is enabled. Sensor channels are selected in the
|
||||
driver based on the service layer compatible string:
|
||||
|
||||
* intel,stratix10-svc
|
||||
* intel,agilex-svc
|
||||
@@ -0,0 +1,199 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===============
|
||||
ADF41513 driver
|
||||
===============
|
||||
|
||||
This driver supports Analog Devices' ADF41513 and similar SPI PLL frequency
|
||||
synthesizers.
|
||||
|
||||
1. Supported devices
|
||||
====================
|
||||
|
||||
* `ADF41510 <https://www.analog.com/ADF41510>`_
|
||||
* `ADF41513 <https://www.analog.com/ADF41513>`_
|
||||
|
||||
The ADF41513 is an ultralow noise frequency synthesizer that can be used to
|
||||
implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
|
||||
downconversion sections of wireless receivers and transmitters. The ADF41510
|
||||
is a similar device that supports frequencies up to 10 GHz.
|
||||
|
||||
Both devices support integer-N and fractional-N operation modes, providing
|
||||
excellent phase noise performance and flexible frequency generation
|
||||
capabilities.
|
||||
|
||||
Key Features:
|
||||
|
||||
- **ADF41510**: 1 GHz to 10 GHz frequency range
|
||||
- **ADF41513**: 1 GHz to 26.5 GHz frequency range
|
||||
- Integer-N and fractional-N operation modes
|
||||
- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
|
||||
- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
|
||||
- 25-bit fixed modulus or 49-bit variable modulus fractional modes
|
||||
- Programmable charge pump currents with 16x range
|
||||
- Digital lock detect functionality
|
||||
- Phase resync capability for consistent output phase
|
||||
|
||||
2. Device attributes
|
||||
====================
|
||||
|
||||
The ADF41513 driver provides the following IIO extended attributes for
|
||||
frequency control and monitoring:
|
||||
|
||||
Each IIO device has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
|
||||
where X is the IIO index of the device. Under these folders reside a set of
|
||||
device files that provide access to the synthesizer's functionality.
|
||||
|
||||
The following table shows the ADF41513 related device files:
|
||||
|
||||
+--------------------------------------+-------------------------------------------------------+
|
||||
| Device file | Description |
|
||||
+======================================+=======================================================+
|
||||
| out_altvoltage0_frequency | RF output frequency control and readback (Hz) |
|
||||
+--------------------------------------+-------------------------------------------------------+
|
||||
| out_altvoltage0_frequency_resolution | Target frequency resolution control (Hz) |
|
||||
+--------------------------------------+-------------------------------------------------------+
|
||||
| out_altvoltage0_powerdown | Power management control (0=active, 1=power down) |
|
||||
+--------------------------------------+-------------------------------------------------------+
|
||||
| out_altvoltage0_phase | RF output phase adjustment and readback (radians) |
|
||||
+--------------------------------------+-------------------------------------------------------+
|
||||
|
||||
2.1 Frequency Control
|
||||
----------------------
|
||||
|
||||
The ``out_altvoltage0_frequency`` attribute controls the RF output frequency
|
||||
with sub-Hz precision. The driver automatically selects between integer-N and
|
||||
fractional-N modes to achieve the requested frequency with the best possible
|
||||
phase noise performance.
|
||||
|
||||
**Supported ranges:**
|
||||
|
||||
- **ADF41510**: 1,000,000,000 Hz to 10,000,000,000 Hz (1 GHz to 10 GHz)
|
||||
- **ADF41513**: 1,000,000,000 Hz to 26,500,000,000 Hz (1 GHz to 26.5 GHz)
|
||||
|
||||
The frequency is specified in Hz, for sub-Hz precision use decimal notation.
|
||||
For example, 12.102 GHz would be written as "12102000000.000000".
|
||||
|
||||
2.2 Frequency Resolution Control
|
||||
--------------------------------
|
||||
|
||||
The ``out_altvoltage0_frequency_resolution`` attribute controls the target
|
||||
frequency resolution that the driver attempts to achieve. This affects the
|
||||
choice between integer-N and fractional-N modes, including fixed modulus
|
||||
(25-bit) and variable modulus (49-bit) fractional-N modes:
|
||||
|
||||
- **Integer-N**: Resolution = :math:`f_{PFD}` (same as PFD frequency)
|
||||
- **Fixed modulus**: Resolution = :math:`f_{PFD} / 2^{25}` (~3 Hz with 100 MHz PFD)
|
||||
- **Variable modulus**: Resolution = :math:`f_{PFD} / 2^{49}` (µHz resolution possible)
|
||||
|
||||
Default resolution is 1 Hz (1,000,000 µHz).
|
||||
|
||||
2.3 Phase adjustment
|
||||
--------------------
|
||||
|
||||
The ``out_altvoltage0_phase`` attribute allows adjustment of the output phase
|
||||
in radians. Setting this attribute enables phase adjustment. It can be set
|
||||
from 0 to :math:`2\pi` radians. Reading this attribute returns the current
|
||||
phase offset of the output signal. To create a consistent phase relationship
|
||||
with the reference signal, the phase resync feature needs to be enabled by
|
||||
setting a non-zero value to the ``adi,phase-resync-period-ns`` device property,
|
||||
which triggers a phase resynchronization after locking is achieved.
|
||||
|
||||
3. Operating modes
|
||||
==================
|
||||
|
||||
3.1 Integer-N Mode
|
||||
------------------
|
||||
|
||||
When the requested frequency can be achieved as an integer multiple of the PFD
|
||||
frequency (within the specified resolution tolerance), the driver automatically
|
||||
selects integer-N mode for optimal phase noise performance.
|
||||
|
||||
In integer-N mode:
|
||||
|
||||
- Phase noise: -235 dBc/Hz normalized floor
|
||||
- Frequency resolution: :math:`f_{PFD}` (same as PFD frequency)
|
||||
- Maximum PFD frequency: 250 MHz
|
||||
- Bleed current: Disabled
|
||||
|
||||
3.2 Fractional-N Mode
|
||||
---------------------
|
||||
|
||||
When sub-integer frequency steps are required, the driver automatically selects
|
||||
fractional-N mode using either fixed or variable modulus.
|
||||
|
||||
**Fixed Modulus (25-bit)**:
|
||||
|
||||
- Used when variable modulus is not required
|
||||
- Resolution: :math:`f_{PFD} / 2^{25}`
|
||||
- Simpler implementation, faster settling
|
||||
|
||||
**Variable Modulus (49-bit)**:
|
||||
|
||||
- Used for maximum resolution requirements
|
||||
- Resolution: :math:`f_{PFD} / 2^{49}` (theoretical)
|
||||
- Exact frequency synthesis capability
|
||||
|
||||
In fractional-N mode:
|
||||
|
||||
- Phase noise: -231 dBc/Hz normalized floor
|
||||
- Maximum PFD frequency: 125 MHz
|
||||
- Bleed current: Automatically enabled and optimized
|
||||
- Dithering: Enabled to reduce fractional spurs
|
||||
|
||||
3.3 Automatic Mode Selection
|
||||
----------------------------
|
||||
|
||||
The driver automatically selects the optimal operating mode based on:
|
||||
|
||||
1. **Frequency accuracy requirements**: Determined by ``frequency_resolution`` setting
|
||||
2. **Phase noise optimization**: Integer-N preferred when possible
|
||||
3. **PFD frequency constraints**: Different limits for integer vs fractional modes
|
||||
4. **Prescaler selection**: Automatic 4/5 vs 8/9 prescaler selection based on frequency
|
||||
|
||||
4. Usage examples
|
||||
=================
|
||||
|
||||
4.1 Basic Frequency Setting
|
||||
----------------------------
|
||||
|
||||
Set output frequency to 12.102 GHz:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency
|
||||
|
||||
Read current frequency:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
|
||||
12101999999.582767
|
||||
|
||||
4.2 High Resolution Frequency Control
|
||||
-------------------------------------
|
||||
|
||||
Configure for sub-Hz resolution and set a precise frequency:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Set resolution to 0.1 Hz (100,000 µHz)
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 0.1 > out_altvoltage0_frequency_resolution
|
||||
|
||||
# Set frequency to 12.102 GHz (1 µHz precision)
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency
|
||||
root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
|
||||
12101999999.980131
|
||||
|
||||
4.3 Monitor Lock Status
|
||||
-----------------------
|
||||
|
||||
When lock detect GPIO is configured, check if PLL is locked:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Read frequency - will return error if not locked
|
||||
root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
|
||||
|
||||
If the PLL is not locked, the frequency read will return ``-EBUSY`` (Device or
|
||||
resource busy).
|
||||
@@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers
|
||||
ad7625
|
||||
ad7944
|
||||
ade9000
|
||||
adf41513
|
||||
adis16475
|
||||
adis16480
|
||||
adis16550
|
||||
|
||||
@@ -48,6 +48,66 @@ Access restrictions:
|
||||
* APML Mailbox messages and Register xfer access are read-write,
|
||||
* CPUID and MCA_MSR access is read-only.
|
||||
|
||||
SBTSI device
|
||||
============
|
||||
|
||||
sbtsi driver under the drivers/misc/amd-sbi creates miscdevice
|
||||
/dev/sbtsi-* to let user space programs run APML TSI register transfer
|
||||
commands.
|
||||
|
||||
The driver supports both I2C and I3C transports for SB-TSI targets.
|
||||
The transport is selected by the bus where the device is enumerated.
|
||||
|
||||
Misc device:
|
||||
* In 1P socket 0: /dev/sbtsi-4c
|
||||
* In 2P socket 0: /dev/sbtsi-4c, socket 1: /dev/sbtsi-48
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ls -al /dev/sbtsi-4c
|
||||
crw------- 1 root root 10, 116 Apr 2 05:22 /dev/sbtsi-4c
|
||||
|
||||
|
||||
Access restrictions:
|
||||
* Only root user is allowed to open the file.
|
||||
* APML TSI Register transfer access is read-write.
|
||||
|
||||
SBTSI hwmon interface
|
||||
=====================
|
||||
|
||||
The sbtsi_temp auxiliary driver binds to the auxiliary device published
|
||||
by the core sbtsi driver on the auxiliary bus. The auxiliary device is
|
||||
named amd-sbtsi.temp-sensor.<id>, where <id> is the device's transfer
|
||||
address: the client address for I2C, or the assigned-address for I3C.
|
||||
|
||||
Note that the auxiliary bus formats <id> in decimal, whereas the
|
||||
/dev/sbtsi-* misc node formats its address in hex. The two therefore
|
||||
differ for the same device: an I2C/I3C sensor at address 0x4c appears as the
|
||||
misc node /dev/sbtsi-4c and the auxiliary device
|
||||
amd-sbtsi.temp-sensor.76.
|
||||
|
||||
It registers a hwmon device, providing a standard Linux hwmon interface
|
||||
for reading CPU temperature and managing temperature limits.
|
||||
|
||||
The hwmon device appears under ``/sys/class/hwmon/`` when both ``sbtsi.ko``
|
||||
and ``sbtsi_temp.ko`` are loaded.
|
||||
|
||||
Verify auxiliary bus device::
|
||||
|
||||
ls /sys/bus/auxiliary/devices/
|
||||
# e.g. amd-sbtsi.temp-sensor.76 for an I2C/I3C sensor at address 0x4c
|
||||
|
||||
Example usage::
|
||||
|
||||
# Read current temperature
|
||||
cat /sys/class/hwmon/hwmon<N>/temp1_input
|
||||
|
||||
# Set high temperature limit to 70 °C
|
||||
echo 70000 > /sys/class/hwmon/hwmon<N>/temp1_max
|
||||
|
||||
# Verify
|
||||
cat /sys/class/hwmon/hwmon<N>/temp1_max
|
||||
|
||||
Driver IOCTLs
|
||||
=============
|
||||
|
||||
@@ -63,6 +123,9 @@ Driver IOCTLs
|
||||
.. c:macro:: SBRMI_IOCTL_REG_XFER_CMD
|
||||
.. kernel-doc:: include/uapi/misc/amd-apml.h
|
||||
:doc: SBRMI_IOCTL_REG_XFER_CMD
|
||||
.. c:macro:: SBTSI_IOCTL_REG_XFER_CMD
|
||||
.. kernel-doc:: include/uapi/misc/amd-apml.h
|
||||
:doc: SBTSI_IOCTL_REG_XFER_CMD
|
||||
|
||||
User-space usage
|
||||
================
|
||||
@@ -85,6 +148,16 @@ Next thing, open the device file, as follows::
|
||||
exit(1);
|
||||
}
|
||||
|
||||
To open SB-TSI device::
|
||||
|
||||
int file;
|
||||
|
||||
file = open("/dev/sbtsi-4c", O_RDWR);
|
||||
if (file < 0) {
|
||||
/* ERROR HANDLING */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
The following IOCTLs are defined:
|
||||
|
||||
``#define SB_BASE_IOCTL_NR 0xF9``
|
||||
@@ -92,6 +165,7 @@ The following IOCTLs are defined:
|
||||
``#define SBRMI_IOCTL_CPUID_CMD _IOWR(SB_BASE_IOCTL_NR, 1, struct apml_cpuid_msg)``
|
||||
``#define SBRMI_IOCTL_MCAMSR_CMD _IOWR(SB_BASE_IOCTL_NR, 2, struct apml_mcamsr_msg)``
|
||||
``#define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg)``
|
||||
``#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)``
|
||||
|
||||
|
||||
User space C-APIs are made available by esmi_oob_library, hosted at
|
||||
|
||||
@@ -83,18 +83,18 @@ structures used in ioctl calls::
|
||||
};
|
||||
|
||||
struct ntsync_event_args {
|
||||
__u32 signaled;
|
||||
__u32 manual;
|
||||
__u32 signaled;
|
||||
};
|
||||
|
||||
struct ntsync_wait_args {
|
||||
__u64 timeout;
|
||||
__u64 objs;
|
||||
__u32 count;
|
||||
__u32 owner;
|
||||
__u32 index;
|
||||
__u32 alert;
|
||||
__u32 flags;
|
||||
__u32 owner;
|
||||
__u32 alert;
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
@@ -152,7 +152,7 @@ The ioctls on the device file are as follows:
|
||||
|
||||
The ioctls on the individual objects are as follows:
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_SEM_POST
|
||||
.. c:macro:: NTSYNC_IOC_SEM_RELEASE
|
||||
|
||||
Post to a semaphore object. Takes a pointer to a 32-bit integer,
|
||||
which on input holds the count to be added to the semaphore, and on
|
||||
@@ -186,7 +186,7 @@ The ioctls on the individual objects are as follows:
|
||||
unowned and signaled, and eligible threads waiting on it will be
|
||||
woken as appropriate.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_SET_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_SET
|
||||
|
||||
Signal an event object. Takes a pointer to a 32-bit integer, which on
|
||||
output contains the previous state of the event.
|
||||
@@ -194,12 +194,12 @@ The ioctls on the individual objects are as follows:
|
||||
Eligible threads will be woken, and auto-reset events will be
|
||||
designaled appropriately.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_RESET_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_RESET
|
||||
|
||||
Designal an event object. Takes a pointer to a 32-bit integer, which
|
||||
on output contains the previous state of the event.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_PULSE_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_PULSE
|
||||
|
||||
Wake threads waiting on an event object while leaving it in an
|
||||
unsignaled state. Takes a pointer to a 32-bit integer, which on
|
||||
@@ -213,7 +213,7 @@ The ioctls on the individual objects are as follows:
|
||||
afterwards, and a simultaneous read operation will always report the
|
||||
event as unsignaled.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_SEM
|
||||
.. c:macro:: NTSYNC_IOC_SEM_READ
|
||||
|
||||
Read the current state of a semaphore object. Takes a pointer to
|
||||
struct :c:type:`ntsync_sem_args`, which is used as follows:
|
||||
@@ -225,7 +225,7 @@ The ioctls on the individual objects are as follows:
|
||||
* - ``max``
|
||||
- On output, contains the maximum count of the semaphore.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_MUTEX
|
||||
.. c:macro:: NTSYNC_IOC_MUTEX_READ
|
||||
|
||||
Read the current state of a mutex object. Takes a pointer to struct
|
||||
:c:type:`ntsync_mutex_args`, which is used as follows:
|
||||
@@ -242,7 +242,7 @@ The ioctls on the individual objects are as follows:
|
||||
``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
|
||||
zero.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_READ
|
||||
|
||||
Read the current state of an event object. Takes a pointer to struct
|
||||
:c:type:`ntsync_event_args`, which is used as follows:
|
||||
@@ -255,7 +255,7 @@ The ioctls on the individual objects are as follows:
|
||||
- On output, contains 1 if the event is a manual-reset event,
|
||||
and 0 otherwise.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_KILL_OWNER
|
||||
.. c:macro:: NTSYNC_IOC_MUTEX_KILL
|
||||
|
||||
Mark a mutex as unowned and abandoned if it is owned by the given
|
||||
owner. Takes an input-only pointer to a 32-bit integer denoting the
|
||||
@@ -276,11 +276,12 @@ The ioctls on the individual objects are as follows:
|
||||
* - ``timeout``
|
||||
- Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
|
||||
is set, the timeout is measured against the REALTIME clock;
|
||||
otherwise it is measured against the MONOTONIC clock. If the
|
||||
timeout is equal to or earlier than the current time, the
|
||||
function returns immediately without sleeping. If ``timeout``
|
||||
is U64_MAX, the function will sleep until an object is
|
||||
signaled, and will not fail with ``ETIMEDOUT``.
|
||||
otherwise it is measured against the MONOTONIC clock in the
|
||||
caller's time namespace. If the timeout is equal to or earlier
|
||||
than the current time, the function returns immediately
|
||||
without sleeping. If ``timeout`` is U64_MAX, the function will
|
||||
sleep until an object is signaled, and will not fail with
|
||||
``ETIMEDOUT``.
|
||||
* - ``objs``
|
||||
- Pointer to an array of ``count`` file descriptors
|
||||
(specified as an integer so that the structure has the same
|
||||
|
||||
@@ -50,5 +50,5 @@ The driver also supports 1-wire mode. In this mode, there is no need to
|
||||
pass slave ID as parameter. The driver will auto-detect slaves connected
|
||||
to the bus using SEARCH_ROM procedure. 1-wire mode can be selected by
|
||||
setting "ti,mode" property to "1w" in DT (see
|
||||
Documentation/devicetree/bindings/w1/omap-hdq.txt for more details).
|
||||
Documentation/devicetree/bindings/w1/ti,hdq.yaml for more details).
|
||||
By default driver is in HDQ mode.
|
||||
|
||||
+94
-25
@@ -964,6 +964,14 @@ L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-altera.c
|
||||
|
||||
ALTERA SoC FPGA HWMON DRIVER
|
||||
M: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
|
||||
M: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/socfpga-hwmon.rst
|
||||
F: drivers/hwmon/socfpga-hwmon.c
|
||||
|
||||
ALTERA TRIPLE SPEED ETHERNET DRIVER
|
||||
M: Boon Khai Ng <boon.khai.ng@altera.com>
|
||||
L: netdev@vger.kernel.org
|
||||
@@ -1718,6 +1726,15 @@ W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
|
||||
F: drivers/iio/adc/ade9000.c
|
||||
|
||||
ANALOG DEVICES INC ADF41513 DRIVER
|
||||
M: Rodrigo Alencar <rodrigo.alencar@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
|
||||
F: Documentation/iio/adf41513.rst
|
||||
F: drivers/iio/frequency/adf41513.c
|
||||
|
||||
ANALOG DEVICES INC ADF4377 DRIVER
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@@ -4184,11 +4201,11 @@ F: include/linux/async_tx.h
|
||||
|
||||
AT24 EEPROM DRIVER
|
||||
M: Bartosz Golaszewski <brgl@kernel.org>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
|
||||
F: Documentation/devicetree/bindings/eeprom/at24.yaml
|
||||
F: drivers/misc/eeprom/at24.c
|
||||
F: drivers/nvmem/at24.c
|
||||
|
||||
ATA OVER ETHERNET (AOE) DRIVER
|
||||
M: "Justin Sanders" <justin@coraid.com>
|
||||
@@ -11713,12 +11730,6 @@ W: http://www.highpoint-tech.com
|
||||
F: Documentation/scsi/hptiop.rst
|
||||
F: drivers/scsi/hptiop.c
|
||||
|
||||
HIKEY960 ONBOARD USB GPIO HUB DRIVER
|
||||
M: John Stultz <jstultz@google.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/misc/hisi_hikey_usb.c
|
||||
|
||||
HIMAX HX83112B TOUCHSCREEN SUPPORT
|
||||
M: Job Noorman <job@noorman.info>
|
||||
L: linux-input@vger.kernel.org
|
||||
@@ -11832,8 +11843,7 @@ F: drivers/perf/hisilicon
|
||||
F: tools/perf/pmu-events/arch/arm64/hisilicon/
|
||||
|
||||
HISILICON PTT DRIVER
|
||||
M: Yicong Yang <yangyicong@hisilicon.com>
|
||||
M: Jonathan Cameron <jonathan.cameron@huawei.com>
|
||||
M: Sizhe Liu <liusizhe5@huawei.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
|
||||
@@ -13430,6 +13440,14 @@ F: drivers/platform/x86/intel/sdsi.c
|
||||
F: tools/arch/x86/intel_sdsi/
|
||||
F: tools/testing/selftests/drivers/sdsi/
|
||||
|
||||
INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI)
|
||||
M: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
S: Supported
|
||||
F: Documentation/ABI/testing/sysfs-class-issei
|
||||
F: Documentation/driver-api/issei/issei.rst
|
||||
F: drivers/misc/issei/
|
||||
F: include/uapi/linux/issei.h
|
||||
|
||||
INTEL SGX
|
||||
M: Jarkko Sakkinen <jarkko@kernel.org>
|
||||
R: Dave Hansen <dave.hansen@linux.intel.com>
|
||||
@@ -15435,6 +15453,14 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
|
||||
F: drivers/iio/dac/ltc1660.c
|
||||
|
||||
LTC2378 IIO ADC DRIVER
|
||||
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
|
||||
F: drivers/iio/adc/ltc2378.c
|
||||
|
||||
LTC2664 IIO DAC DRIVER
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Kim Seer Paller <kimseer.paller@analog.com>
|
||||
@@ -16657,6 +16683,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml
|
||||
F: drivers/mmc/host/mtk-sd.c
|
||||
|
||||
MEDIATEK MT6323 PMIC AUXADC DRIVER
|
||||
M: Roman Vivchar <rva333@protonmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/adc/mt6323-auxadc.c
|
||||
F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h
|
||||
|
||||
MEDIATEK MT6735 CLOCK & RESET DRIVERS
|
||||
M: Yassine Oudjana <y.oudjana@protonmail.com>
|
||||
L: linux-clk@vger.kernel.org
|
||||
@@ -17567,6 +17600,7 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.*
|
||||
|
||||
MHI BUS
|
||||
M: Manivannan Sadhasivam <mani@kernel.org>
|
||||
R: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
|
||||
L: mhi@lists.linux.dev
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
@@ -17788,6 +17822,12 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml
|
||||
F: drivers/iio/adc/mcp3911.c
|
||||
|
||||
MICROCHIP MCP47A1 DAC DRIVER
|
||||
M: Joshua Crofts <joshua.crofts1@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml
|
||||
F: drivers/iio/dac/mcp47a1.c
|
||||
|
||||
MICROCHIP MCP9982 TEMPERATURE DRIVER
|
||||
M: Victor Duicu <victor.duicu@microchip.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@@ -18982,6 +19022,8 @@ F: include/uapi/linux/sctp.h
|
||||
F: lib/net_utils.c
|
||||
F: lib/random32.c
|
||||
F: net/
|
||||
F: rust/helpers/net/
|
||||
F: rust/kernel/net/
|
||||
F: samples/pktgen/
|
||||
F: tools/net/
|
||||
F: tools/testing/selftests/net/
|
||||
@@ -22188,6 +22230,20 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst
|
||||
F: drivers/bus/fsl-mc/
|
||||
F: include/uapi/linux/fsl_mc.h
|
||||
|
||||
QST QMC5883L 3-Axis Magnetic Sensor
|
||||
M: Siratul Islam <siratul.islam@linux.dev>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
|
||||
F: drivers/iio/magnetometer/qmc5883l.c
|
||||
|
||||
QST QMC6308 3-AXIS MAGNETIC SENSOR
|
||||
M: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml
|
||||
F: drivers/iio/magnetometer/qmc6308.c
|
||||
|
||||
QT1010 MEDIA DRIVER
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Orphan
|
||||
@@ -22346,7 +22402,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
|
||||
|
||||
QUALCOMM FASTRPC DRIVER
|
||||
M: Srinivas Kandagatla <srini@kernel.org>
|
||||
M: Amol Maheshwari <amahesh@qti.qualcomm.com>
|
||||
R: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
@@ -24711,7 +24767,8 @@ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml
|
||||
F: drivers/iio/chemical/sunrise_co2.c
|
||||
|
||||
SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
|
||||
M: Maxwell Doose <m32285159@gmail.com>
|
||||
M: Maxwell Doose <maxwell@maxwelld.cc>
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
|
||||
F: drivers/iio/chemical/scd30.h
|
||||
@@ -24737,6 +24794,14 @@ S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40
|
||||
F: drivers/iio/chemical/sgp40.c
|
||||
|
||||
SENSIRION SLF3S LIQUID FLOW SENSOR DRIVER
|
||||
M: Wadim Mueller <wafgo01@gmail.com>
|
||||
R: Maxwell Doose <maxwell@maxwelld.cc>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
|
||||
F: drivers/iio/flow/slf3s.c
|
||||
|
||||
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
@@ -24800,17 +24865,6 @@ F: include/linux/phylink.h
|
||||
F: include/linux/sfp.h
|
||||
K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
|
||||
|
||||
SGI GRU DRIVER
|
||||
M: Dimitri Sivanich <dimitri.sivanich@hpe.com>
|
||||
S: Maintained
|
||||
F: drivers/misc/sgi-gru/
|
||||
|
||||
SGI XP/XPC/XPNET DRIVER
|
||||
M: Robin Holt <robinmholt@gmail.com>
|
||||
M: Steve Wahl <steve.wahl@hpe.com>
|
||||
S: Maintained
|
||||
F: drivers/misc/sgi-xp/
|
||||
|
||||
SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
|
||||
M: D. Wythe <alibuda@linux.alibaba.com>
|
||||
M: Dust Li <dust.li@linux.alibaba.com>
|
||||
@@ -25891,7 +25945,7 @@ F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml
|
||||
F: drivers/iio/proximity/vl53l0x-i2c.c
|
||||
|
||||
ST VL53L1X ToF RANGER(I2C) IIO DRIVER
|
||||
M: Siratul Islam <email@sirat.me>
|
||||
M: Siratul Islam <siratul.islam@linux.dev>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml
|
||||
@@ -27148,6 +27202,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml
|
||||
F: drivers/iio/adc/ti-ads1119.c
|
||||
|
||||
TI ADS112C14 ADC DRIVER
|
||||
M: David Lechner <dlechner@baylibre.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml
|
||||
F: drivers/iio/adc/ti-ads112c14.c
|
||||
|
||||
TI ADS1018 ADC DRIVER
|
||||
M: Kurt Borja <kuurtb@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@@ -29738,7 +29799,7 @@ F: include/uapi/linux/dqblk_xfs.h
|
||||
F: include/uapi/linux/fsmap.h
|
||||
|
||||
XILINX AMS DRIVER
|
||||
M: Salih Erim <salih.erim@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
M: Conall O'Griofa <conall.ogriofa@amd.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
@@ -29837,6 +29898,14 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.y
|
||||
F: drivers/edac/versalnet_edac.c
|
||||
F: include/linux/cdx/edac_cdx_pcol.h
|
||||
|
||||
XILINX VERSAL SYSMON DRIVER
|
||||
M: Salih Erim <salih.erim@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
|
||||
F: drivers/iio/adc/versal-sysmon*
|
||||
|
||||
XILINX WATCHDOG DRIVER
|
||||
M: Srinivas Neeli <srinivas.neeli@amd.com>
|
||||
R: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
|
||||
@@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void)
|
||||
input_sync(virt_keyboard);
|
||||
__this_cpu_write(reporting_keystroke, false);
|
||||
|
||||
/* reenable preemption */
|
||||
/* re-enable preemption */
|
||||
preempt_enable();
|
||||
/* reenable keyboard interrupts */
|
||||
/* re-enable keyboard interrupts */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ main(int argc, char *argv[])
|
||||
|
||||
open_input(NULL, argv[1]);
|
||||
while (fgets(buffer, sizeof(buffer), infile)) {
|
||||
lc++;
|
||||
value = shift_state = 0;
|
||||
|
||||
cp = strtok(buffer, delims);
|
||||
@@ -153,7 +152,7 @@ main(int argc, char *argv[])
|
||||
continue;
|
||||
printf("\n\t%d,", lc);
|
||||
for (i = 0; i < max_states; i++)
|
||||
printf(" %d,", (unsigned int)kp[i]);
|
||||
printf(" %u,", (unsigned int)kp[i]);
|
||||
}
|
||||
printf("\n\t0, %d\n", map_ver);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = {
|
||||
[MSG_CURSORING_OFF] = "cursoring off",
|
||||
[MSG_CURSORING_ON] = "cursoring on",
|
||||
[MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
|
||||
[MSG_READ_WINDOW] = "read windo",
|
||||
[MSG_READ_WINDOW] = "read window",
|
||||
[MSG_READ_ALL] = "read all",
|
||||
[MSG_EDIT_DONE] = "edit done",
|
||||
[MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/keyboard.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "spk_priv.h"
|
||||
#include "speakup.h"
|
||||
|
||||
#define MAXFUNCS 130
|
||||
#define MAXKEYS 256
|
||||
static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1;
|
||||
static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS];
|
||||
static u_short masks[] = { 32, 16, 8, 4, 2, 1 };
|
||||
static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS];
|
||||
static u16 masks[] = { 32, 16, 8, 4, 2, 1 };
|
||||
|
||||
static short letter_offsets[26] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
@@ -49,7 +50,7 @@ static int cur_item, nstates;
|
||||
static void build_key_data(void)
|
||||
{
|
||||
u_char *kp, counters[MAXFUNCS], ch, ch1;
|
||||
u_short *p_key, key;
|
||||
u16 *p_key, key;
|
||||
int i, offset = 1;
|
||||
|
||||
nstates = (int)(state_tbl[-1]);
|
||||
@@ -111,7 +112,7 @@ static void say_key(int key)
|
||||
spk_msg_get(MSG_KEYNAMES_START + (key - 1)));
|
||||
}
|
||||
|
||||
static int help_init(void)
|
||||
static void help_init(void)
|
||||
{
|
||||
char start = SPACE;
|
||||
int i;
|
||||
@@ -120,21 +121,27 @@ static int help_init(void)
|
||||
state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2;
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i);
|
||||
char first_letter;
|
||||
|
||||
if (start == *cur_funcname)
|
||||
first_letter = tolower(*cur_funcname);
|
||||
|
||||
/* Accept only 'a'..'z' to index letter_offsets[] safely */
|
||||
if (first_letter < 'a' || first_letter > 'z')
|
||||
continue;
|
||||
start = *cur_funcname;
|
||||
|
||||
if (start == first_letter)
|
||||
continue;
|
||||
start = first_letter;
|
||||
letter_offsets[(start & 31) - 1] = i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
int i, n;
|
||||
char *name;
|
||||
u_char func, *kp;
|
||||
u_short *p_keys, val;
|
||||
u16 *p_keys, val;
|
||||
|
||||
if (letter_offsets[0] == -1)
|
||||
help_init();
|
||||
@@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP));
|
||||
return 1;
|
||||
}
|
||||
ch |= 32; /* lower case */
|
||||
ch = tolower(ch);
|
||||
if (ch < 'a' || ch > 'z')
|
||||
return -1;
|
||||
if (letter_offsets[ch - 'a'] == -1) {
|
||||
|
||||
@@ -92,13 +92,13 @@ static void report_char_chartab_status(int reset, int received, int used,
|
||||
if (reset) {
|
||||
pr_info("%s reset to defaults\n", object_type[do_characters]);
|
||||
} else if (received) {
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
" updated %d of %d %s\n",
|
||||
used, received, object_type[do_characters]);
|
||||
len = scnprintf(buf, sizeof(buf),
|
||||
" updated %d of %d %s\n",
|
||||
used, received, object_type[do_characters]);
|
||||
if (rejected)
|
||||
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
|
||||
" with %d reject%s\n",
|
||||
rejected, rejected > 1 ? "s" : "");
|
||||
rejected, str_plural(rejected));
|
||||
pr_info("%s", buf);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj,
|
||||
ssize_t retval = count;
|
||||
unsigned long flags;
|
||||
unsigned long index = 0;
|
||||
int charclass = 0;
|
||||
u16 charclass = 0;
|
||||
int received = 0;
|
||||
int used = 0;
|
||||
int rejected = 0;
|
||||
@@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
struct st_var_header *p_header;
|
||||
struct punc_var_t *var;
|
||||
struct st_bits_data *pb;
|
||||
short mask;
|
||||
u16 mask;
|
||||
unsigned long flags;
|
||||
|
||||
p_header = spk_var_header_by_name(attr->attr.name);
|
||||
@@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used,
|
||||
if (rejected)
|
||||
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
|
||||
" with %d reject%s\n",
|
||||
rejected, rejected > 1 ? "s" : "");
|
||||
rejected, str_plural(rejected));
|
||||
pr_info("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
|
||||
int spk_no_intr, spk_spell_delay;
|
||||
int spk_key_echo, spk_say_word_ctl;
|
||||
int spk_say_ctrl, spk_bell_pos;
|
||||
short spk_punc_mask;
|
||||
u16 spk_punc_mask;
|
||||
int spk_punc_level, spk_reading_punc;
|
||||
int spk_cur_phonetic;
|
||||
char spk_str_caps_start[MAXVARLEN + 1] = "\0";
|
||||
@@ -183,13 +183,13 @@ char *spk_default_chars[256] = {
|
||||
/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
|
||||
};
|
||||
|
||||
/* array of 256 u_short (one for each character)
|
||||
/* array of 256 u16 (one for each character)
|
||||
* initialized to default_chartab and user selectable via
|
||||
* /sys/module/speakup/parameters/chartab
|
||||
*/
|
||||
u_short spk_chartab[256];
|
||||
u16 spk_chartab[256];
|
||||
|
||||
static u_short default_chartab[256] = {
|
||||
static u16 default_chartab[256] = {
|
||||
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
|
||||
B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
|
||||
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
|
||||
@@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc)
|
||||
spk_y = spk_cy = vc->state.y;
|
||||
spk_pos = spk_cp = vc->vc_pos;
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)spk_pos);
|
||||
spk_attr = get_attributes(vc, (u16 *)spk_pos);
|
||||
}
|
||||
|
||||
static void bleep(u_short val)
|
||||
static void bleep(u16 val)
|
||||
{
|
||||
static const short vals[] = {
|
||||
350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
|
||||
@@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc)
|
||||
|
||||
if (!mark_cut_flag) {
|
||||
mark_cut_flag = 1;
|
||||
spk_xs = (u_short)spk_x;
|
||||
spk_ys = (u_short)spk_y;
|
||||
spk_xs = (u16)spk_x;
|
||||
spk_ys = (u16)spk_y;
|
||||
spk_sel_cons = vc;
|
||||
synth_printf("%s\n", spk_msg_get(MSG_MARK));
|
||||
return;
|
||||
}
|
||||
spk_xe = (u_short)spk_x;
|
||||
spk_ye = (u_short)spk_y;
|
||||
spk_xe = (u16)spk_x;
|
||||
spk_ye = (u16)spk_y;
|
||||
mark_cut_flag = 0;
|
||||
synth_printf("%s\n", spk_msg_get(MSG_CUT));
|
||||
|
||||
@@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc)
|
||||
u16 ch;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
|
||||
if (spk_attr != spk_old_attr) {
|
||||
if (spk_attrib_bleep & 1)
|
||||
bleep(spk_y);
|
||||
@@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc)
|
||||
u16 ch;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
|
||||
if (ch <= 0x7f && isalpha(ch)) {
|
||||
ch &= 0x1f;
|
||||
synth_printf("%s\n", phonetic[--ch]);
|
||||
@@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc)
|
||||
u_char temp;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)tmp_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos, &temp);
|
||||
|
||||
/* decided to take out the sayword if on a space (mis-information */
|
||||
if (spk_say_word_ctl && ch == SPACE) {
|
||||
@@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc)
|
||||
return 0;
|
||||
} else if (tmpx < vc->vc_cols - 2 &&
|
||||
(ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
|
||||
get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
|
||||
get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) {
|
||||
tmp_pos += 2;
|
||||
tmpx++;
|
||||
} else {
|
||||
while (tmpx > 0) {
|
||||
ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos - 1, &temp);
|
||||
if ((ch == SPACE || ch == 0 ||
|
||||
(ch < 0x100 && IS_WDLM(ch))) &&
|
||||
get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
|
||||
get_char(vc, (u16 *)tmp_pos, &temp) > SPACE)
|
||||
break;
|
||||
tmp_pos -= 2;
|
||||
tmpx--;
|
||||
}
|
||||
}
|
||||
attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
|
||||
attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr);
|
||||
buf[cnt++] = attr_ch;
|
||||
while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) {
|
||||
tmp_pos += 2;
|
||||
tmpx++;
|
||||
ch = get_char(vc, (u_short *)tmp_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos, &temp);
|
||||
if (ch == SPACE || ch == 0 ||
|
||||
(buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
|
||||
ch > SPACE))
|
||||
@@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc)
|
||||
static void say_word(struct vc_data *vc)
|
||||
{
|
||||
u_long cnt = get_word(vc);
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
if (cnt == 0)
|
||||
return;
|
||||
@@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc)
|
||||
u_char temp;
|
||||
u16 ch;
|
||||
enum edge edge_said = edge_none;
|
||||
u_short last_state = 0, state = 0;
|
||||
u16 last_state = 0, state = 0;
|
||||
|
||||
spk_parked |= 0x01;
|
||||
|
||||
@@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc)
|
||||
spk_x--;
|
||||
}
|
||||
spk_pos -= 2;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &temp);
|
||||
if (ch == SPACE || ch == 0)
|
||||
state = 0;
|
||||
else if (ch < 0x100 && IS_WDLM(ch))
|
||||
@@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc)
|
||||
u_char temp;
|
||||
u16 ch;
|
||||
enum edge edge_said = edge_none;
|
||||
u_short last_state = 2, state = 0;
|
||||
u16 last_state = 2, state = 0;
|
||||
|
||||
spk_parked |= 0x01;
|
||||
if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
|
||||
@@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc)
|
||||
return;
|
||||
}
|
||||
while (1) {
|
||||
ch = get_char(vc, (u_short *)spk_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &temp);
|
||||
if (ch == SPACE || ch == 0)
|
||||
state = 0;
|
||||
else if (ch < 0x100 && IS_WDLM(ch))
|
||||
@@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc)
|
||||
u_char tmp2;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)spk_pos);
|
||||
spk_attr = get_attributes(vc, (u16 *)spk_pos);
|
||||
for (i = 0; i < vc->vc_cols; i++) {
|
||||
buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
|
||||
buf[i] = get_char(vc, (u16 *)tmp, &tmp2);
|
||||
tmp += 2;
|
||||
}
|
||||
for (--i; i >= 0; i--)
|
||||
@@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc)
|
||||
{
|
||||
int i = get_line(vc);
|
||||
u16 *cp;
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
if (i == 0) {
|
||||
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
|
||||
@@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to,
|
||||
{
|
||||
int i = 0;
|
||||
u_char tmp;
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)from);
|
||||
spk_attr = get_attributes(vc, (u16 *)from);
|
||||
while (from < to) {
|
||||
buf[i++] = get_char(vc, (u_short *)from, &tmp);
|
||||
buf[i++] = get_char(vc, (u16 *)from, &tmp);
|
||||
from += 2;
|
||||
if (i >= vc->vc_size_row)
|
||||
break;
|
||||
@@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc)
|
||||
sentmarks[bn][0] = &sentbuf[bn][0];
|
||||
i = 0;
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)start);
|
||||
spk_attr = get_attributes(vc, (u16 *)start);
|
||||
|
||||
while (start < end) {
|
||||
sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
|
||||
sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp);
|
||||
if (i > 0) {
|
||||
if (sentbuf[bn][i] == SPACE &&
|
||||
sentbuf[bn][i - 1] == '.' &&
|
||||
@@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc)
|
||||
static void say_char_num(struct vc_data *vc)
|
||||
{
|
||||
u_char tmp;
|
||||
u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
|
||||
u16 ch = get_char(vc, (u16 *)spk_pos, &tmp);
|
||||
|
||||
synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count)
|
||||
{
|
||||
static int rep_count;
|
||||
static u16 ch = '\0', old_ch = '\0';
|
||||
static u_short char_type, last_type;
|
||||
static u16 char_type, last_type;
|
||||
int in_count = count;
|
||||
|
||||
spk_keydown = 0;
|
||||
@@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void)
|
||||
|
||||
static const struct st_bits_data *pb_edit;
|
||||
|
||||
static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
short mask = pb_edit->mask, ch_type = spk_chartab[ch];
|
||||
u16 mask = pb_edit->mask, ch_type = spk_chartab[ch];
|
||||
|
||||
if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
|
||||
return -1;
|
||||
@@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc)
|
||||
spk_special_handler = edit_bits;
|
||||
}
|
||||
|
||||
static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
static u_char goto_buf[8];
|
||||
static int num;
|
||||
@@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value)
|
||||
static const char *pad_chars = "0123456789+-*/\015,.?()";
|
||||
|
||||
static int
|
||||
speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
|
||||
speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym,
|
||||
int up_flag)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -2444,6 +2444,7 @@ error_kbdnotifier:
|
||||
mutex_lock(&spk_mutex);
|
||||
synth_release();
|
||||
mutex_unlock(&spk_mutex);
|
||||
spk_ttyio_unregister_ldisc();
|
||||
speakup_kobj_exit();
|
||||
|
||||
error_kobjects:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "speakup.h"
|
||||
|
||||
unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
struct vc_data *spk_sel_cons;
|
||||
|
||||
struct speakup_selection_work {
|
||||
|
||||
@@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how);
|
||||
int spk_set_string_var(const char *page, struct st_var_header *var, int len);
|
||||
int spk_set_mask_bits(const char *input, const int which, const int how);
|
||||
extern special_func spk_special_handler;
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key);
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key);
|
||||
int synth_init(char *name);
|
||||
void synth_release(void);
|
||||
|
||||
@@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count);
|
||||
int synth_supports_indexing(void);
|
||||
|
||||
extern struct vc_data *spk_sel_cons;
|
||||
extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
|
||||
extern wait_queue_head_t speakup_event;
|
||||
extern struct kobject *speakup_kobj;
|
||||
@@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[];
|
||||
extern struct spk_synth *synth;
|
||||
extern char spk_pitch_buff[];
|
||||
extern u_char *spk_our_keys[];
|
||||
extern short spk_punc_masks[];
|
||||
extern u16 spk_punc_masks[];
|
||||
extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[];
|
||||
extern bool spk_paused;
|
||||
extern const struct st_bits_data spk_punc_info[];
|
||||
extern u_char spk_key_buf[600];
|
||||
extern char *spk_characters[];
|
||||
extern char *spk_default_chars[];
|
||||
extern u_short spk_chartab[];
|
||||
extern u16 spk_chartab[];
|
||||
extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level;
|
||||
extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps;
|
||||
extern int spk_bleep_time, spk_bell_pos;
|
||||
extern int spk_spell_delay, spk_key_echo;
|
||||
extern int spk_cur_phonetic;
|
||||
extern short spk_punc_mask;
|
||||
extern u16 spk_punc_mask;
|
||||
extern short spk_pitch_shift, synth_flags;
|
||||
extern bool spk_quiet_boot;
|
||||
extern char *synth_name;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* speakup_acntpc.h - header file for speakups Accent-PC driver. */
|
||||
#ifndef _SPEAKUP_ACNT_H
|
||||
#define _SPEAKUP_ACNT_H
|
||||
|
||||
#define SYNTH_IO_EXTENT 0x02
|
||||
|
||||
@@ -17,3 +19,4 @@
|
||||
#define SYNTH_FULL 'F' /* synth is full. */
|
||||
#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */
|
||||
#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#define STAT_new_index 0x0040 /* new last index ready */
|
||||
#define STAT_new_status 0x0080 /* new status posted */
|
||||
#define STAT_dma_state 0x0100 /* dma state toggle */
|
||||
#define STAT_index_valid 0x0200 /* indexs are valid */
|
||||
#define STAT_index_valid 0x0200 /* indexes are valid */
|
||||
#define STAT_flushing 0x0400 /* flush in progress */
|
||||
#define STAT_self_test 0x0800 /* module in self test */
|
||||
#define MODE_ready 0xc000 /* module ready for next phase */
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */
|
||||
#ifndef _SPEAKUP_DTLK_H
|
||||
#define _SPEAKUP_DTLK_H
|
||||
|
||||
#define SYNTH_IO_EXTENT 0x02
|
||||
#define SYNTH_CLEAR 0x18 /* stops speech */
|
||||
@@ -39,7 +41,7 @@
|
||||
|
||||
/* data returned by Interrogate command */
|
||||
struct synth_settings {
|
||||
u_short serial_number; /* 0-7Fh:0-7Fh */
|
||||
u16 serial_number; /* 0-7Fh:0-7Fh */
|
||||
u_char rom_version[24]; /* null terminated string */
|
||||
u_char mode; /* 0=Character; 1=Phoneme; 2=Text */
|
||||
u_char punc_level; /* nB; 0-7 */
|
||||
@@ -61,3 +63,4 @@ struct synth_settings {
|
||||
*/
|
||||
u_char has_indexing; /* nonzero if indexing is implemented */
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -446,8 +446,9 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var)
|
||||
if (var->var_id != PUNC_LEVEL)
|
||||
return 0;
|
||||
|
||||
/* We want to set the the speech synthesis punctuation level
|
||||
* accordingly, so it properly tunes speaking A_PUNC characters */
|
||||
/* We want to set the speech synthesis punctuation level
|
||||
* accordingly, so it properly tunes speaking A_PUNC characters
|
||||
*/
|
||||
var_data = var->data;
|
||||
if (!var_data)
|
||||
return 0;
|
||||
|
||||
@@ -53,7 +53,7 @@ enum var_id_t {
|
||||
};
|
||||
|
||||
typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch,
|
||||
u_short key);
|
||||
u16 key);
|
||||
|
||||
#define COLOR_BUFFER_SIZE 160
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth)
|
||||
/* restart */
|
||||
synth->alive = 1;
|
||||
synth_printf("%s", synth->init);
|
||||
return 2; /* reenabled */
|
||||
return 2; /* re-enabled */
|
||||
}
|
||||
pr_warn("%s: can't restart synth\n", synth->long_name);
|
||||
return 0;
|
||||
@@ -574,4 +574,4 @@ struct spk_synth *synth_current(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synth_current);
|
||||
|
||||
short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
|
||||
u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
|
||||
|
||||
@@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name)
|
||||
else
|
||||
snprintf(filename, sizeof(filename), "%s", name);
|
||||
infile = fopen(filename, "r");
|
||||
if (infile == 0) {
|
||||
if (!infile) {
|
||||
fprintf(stderr, "can't open %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC
|
||||
|
||||
config ANDROID_BINDER_IPC_RUST
|
||||
bool "Rust version of Android Binder IPC Driver"
|
||||
depends on RUST && MMU && !ANDROID_BINDER_IPC
|
||||
depends on RUST && MMU && NET && !ANDROID_BINDER_IPC
|
||||
help
|
||||
This enables the Rust implementation of the Binder driver.
|
||||
|
||||
|
||||
@@ -384,8 +384,8 @@ impl<'a> AllocationView<'a> {
|
||||
BINDER_TYPE_WEAK_BINDER
|
||||
};
|
||||
newobj.flags = obj.flags;
|
||||
newobj.__bindgen_anon_1.binder = ptr as _;
|
||||
newobj.cookie = cookie as _;
|
||||
newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t;
|
||||
newobj.cookie = cookie as uapi::binder_uintptr_t;
|
||||
self.write(offset, &newobj)?;
|
||||
// Increment the user ref count on the node. It will be decremented as part of the
|
||||
// destruction of the buffer, when we see a binder or weak-binder object.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2026 Google LLC.
|
||||
|
||||
//! Binder debugging helpers.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use kernel::bits::bit_u32;
|
||||
use kernel::sync::atomic::Atomic;
|
||||
|
||||
kernel::impl_flags!(
|
||||
/// Represents multiple debug mask flags.
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
|
||||
pub struct DebugMasks(u32);
|
||||
|
||||
/// Represents a single debug mask category.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DebugMask {
|
||||
UserError = bit_u32(0),
|
||||
FailedTransaction = bit_u32(1),
|
||||
DeadTransaction = bit_u32(2),
|
||||
OpenClose = bit_u32(3),
|
||||
DeadBinder = bit_u32(4),
|
||||
DeathNotification = bit_u32(5),
|
||||
ReadWrite = bit_u32(6),
|
||||
UserRefs = bit_u32(7),
|
||||
Threads = bit_u32(8),
|
||||
Transaction = bit_u32(9),
|
||||
TransactionComplete = bit_u32(10),
|
||||
FreeBuffer = bit_u32(11),
|
||||
InternalRefs = bit_u32(12),
|
||||
PriorityCap = bit_u32(13),
|
||||
Spinlocks = bit_u32(14),
|
||||
}
|
||||
);
|
||||
|
||||
#[no_mangle]
|
||||
pub(crate) static rust_binder_debug_mask: Atomic<u32> = Atomic::new(
|
||||
(DebugMask::UserError as u32)
|
||||
| (DebugMask::FailedTransaction as u32)
|
||||
| (DebugMask::DeadTransaction as u32),
|
||||
);
|
||||
|
||||
/// Checks if the given debug logging category is enabled in the mask.
|
||||
pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool {
|
||||
let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed);
|
||||
DebugMasks(current_mask).contains(mask)
|
||||
}
|
||||
|
||||
/// Prints a debug log if the specified mask category is enabled.
|
||||
#[macro_export]
|
||||
macro_rules! binder_debug {
|
||||
// Rule to explicitly specify a PID (used in kworkers).
|
||||
(pid=$pid:expr, $mask:ident, $($arg:tt)*) => {
|
||||
if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
|
||||
kernel::pr_info!(
|
||||
"{}: {}\n",
|
||||
$pid,
|
||||
kernel::prelude::fmt!($($arg)*)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Default rule (automatically prepends "PID:TID" of the current calling thread).
|
||||
($mask:ident, $($arg:tt)*) => {
|
||||
if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
|
||||
let thread = kernel::current!();
|
||||
kernel::pr_info!(
|
||||
"{}:{} {}\n",
|
||||
thread.tgid(),
|
||||
thread.pid(),
|
||||
kernel::prelude::fmt!($($arg)*)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr;
|
||||
|
||||
use kernel::{
|
||||
transmute::{AsBytes, FromBytes},
|
||||
uapi::{self, *},
|
||||
@@ -146,7 +148,7 @@ decl_wrapper!(ExtendedError, uapi::binder_extended_error);
|
||||
impl BinderVersion {
|
||||
pub(crate) fn current() -> Self {
|
||||
Self(MaybeUninit::new(uapi::binder_version {
|
||||
protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _,
|
||||
protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -165,8 +167,8 @@ impl BinderTransactionDataSecctx {
|
||||
pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData {
|
||||
// SAFETY: Transparent wrapper is safe to transmute.
|
||||
unsafe {
|
||||
&mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data
|
||||
as *mut BinderTransactionData)
|
||||
&mut *(ptr::from_mut::<uapi::binder_transaction_data>(&mut self.transaction_data)
|
||||
.cast::<BinderTransactionData>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
|
||||
/// Represents a notification that the freeze state has changed.
|
||||
pub(crate) struct FreezeMessage {
|
||||
cookie: FreezeCookie,
|
||||
pid: i32,
|
||||
}
|
||||
|
||||
kernel::list::impl_list_arc_safe! {
|
||||
@@ -73,8 +74,8 @@ impl FreezeMessage {
|
||||
UniqueArc::new_uninit(flags)
|
||||
}
|
||||
|
||||
fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
|
||||
match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
|
||||
fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> {
|
||||
match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) {
|
||||
Ok(msg) => ListArc::from(msg),
|
||||
Err(err) => match err {},
|
||||
}
|
||||
@@ -127,7 +128,7 @@ impl DeliverToRead for FreezeMessage {
|
||||
}
|
||||
|
||||
let mut state_info = BinderFrozenStateInfo::default();
|
||||
state_info.is_frozen = is_frozen as u32;
|
||||
state_info.is_frozen = u32::from(is_frozen);
|
||||
state_info.cookie = freeze.cookie.0;
|
||||
freeze.is_pending = true;
|
||||
freeze.last_is_frozen = Some(is_frozen);
|
||||
@@ -140,7 +141,14 @@ impl DeliverToRead for FreezeMessage {
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(self: DArc<Self>) {}
|
||||
fn cancel(self: DArc<Self>) {
|
||||
binder_debug!(
|
||||
pid = self.pid,
|
||||
DeadTransaction,
|
||||
"undelivered freeze notification, {:016x}",
|
||||
self.cookie.0
|
||||
);
|
||||
}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
false
|
||||
@@ -180,36 +188,61 @@ impl Process {
|
||||
let msg = FreezeMessage::new(GFP_KERNEL)?;
|
||||
let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
|
||||
|
||||
let mut afl_vec_alloc = KVVec::new();
|
||||
let mut info;
|
||||
let mut freeze_entry;
|
||||
let mut node_refs_guard = self.node_refs.lock();
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
|
||||
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
if info.freeze().is_some() {
|
||||
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let node_ref = info.node_ref();
|
||||
let freeze_entry = node_refs.freeze_listeners.entry(cookie);
|
||||
|
||||
if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
|
||||
if !dupe.get().allow_duplicate(&node_ref.node) {
|
||||
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
|
||||
loop {
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
info = match node_refs.by_handle.get_mut(&handle) {
|
||||
Some(info) => info,
|
||||
None => {
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}"
|
||||
);
|
||||
return Err(EINVAL);
|
||||
}
|
||||
};
|
||||
if info.freeze().is_some() {
|
||||
binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
}
|
||||
let node_ref = info.node_ref();
|
||||
freeze_entry = node_refs.freeze_listeners.entry(cookie);
|
||||
|
||||
// All failure paths must come before this call, and all modifications must come after this
|
||||
// call.
|
||||
node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
|
||||
if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
|
||||
if !dupe.get().allow_duplicate(&node_ref.node) {
|
||||
binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we add to the node's freeze listener list, with retry and re-allocate if the
|
||||
// vector is full.
|
||||
//
|
||||
// To ensure that the node is added atomically, this is the first time we modify any
|
||||
// state. When this call succeeds, all other modifications must occur without the
|
||||
// possibility for any failure paths.
|
||||
match node_ref
|
||||
.node
|
||||
.add_freeze_listener(self, &mut afl_vec_alloc)?
|
||||
{
|
||||
Ok(()) => break,
|
||||
Err(resize_target) => {
|
||||
drop(node_refs_guard);
|
||||
afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?;
|
||||
node_refs_guard = self.node_refs.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match freeze_entry {
|
||||
rbtree::Entry::Vacant(entry) => {
|
||||
entry.insert(
|
||||
FreezeListener {
|
||||
cookie,
|
||||
node: node_ref.node.clone(),
|
||||
node: info.node_ref().node.clone(),
|
||||
last_is_frozen: None,
|
||||
is_pending: false,
|
||||
is_clearing: false,
|
||||
@@ -233,7 +266,7 @@ impl Process {
|
||||
}
|
||||
|
||||
*info.freeze() = Some(cookie);
|
||||
let msg = FreezeMessage::init(msg, cookie);
|
||||
let msg = FreezeMessage::init(msg, cookie, self.task.pid());
|
||||
drop(node_refs_guard);
|
||||
let _ = self.push_work(msg);
|
||||
Ok(())
|
||||
@@ -245,18 +278,23 @@ impl Process {
|
||||
let mut node_refs_guard = self.node_refs.lock();
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
|
||||
pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_FREEZE_NOTIFICATION_DONE {:016x} not found",
|
||||
cookie.0
|
||||
);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
let mut clear_msg = None;
|
||||
if freeze.num_pending_duplicates > 0 {
|
||||
clear_msg = Some(FreezeMessage::init(alloc, cookie));
|
||||
clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
|
||||
freeze.num_pending_duplicates -= 1;
|
||||
freeze.num_cleared_duplicates += 1;
|
||||
} else {
|
||||
if !freeze.is_pending {
|
||||
pr_warn!(
|
||||
"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending",
|
||||
cookie.0
|
||||
);
|
||||
return Err(EINVAL);
|
||||
@@ -264,7 +302,7 @@ impl Process {
|
||||
let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
|
||||
if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
|
||||
// Immediately send another FreezeMessage.
|
||||
clear_msg = Some(FreezeMessage::init(alloc, cookie));
|
||||
clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
|
||||
}
|
||||
freeze.is_pending = false;
|
||||
}
|
||||
@@ -280,31 +318,44 @@ impl Process {
|
||||
let handle = hc.handle;
|
||||
let cookie = FreezeCookie(hc.cookie);
|
||||
|
||||
let _to_free_fl;
|
||||
let alloc = FreezeMessage::new(GFP_KERNEL)?;
|
||||
let mut node_refs_guard = self.node_refs.lock();
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}"
|
||||
);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
let Some(info_cookie) = info.freeze() else {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active"
|
||||
);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
if *info_cookie != cookie {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch"
|
||||
);
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}"
|
||||
);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
listener.is_clearing = true;
|
||||
listener.node.remove_freeze_listener(self);
|
||||
_to_free_fl = listener.node.remove_freeze_listener(self);
|
||||
*info.freeze() = None;
|
||||
let mut msg = None;
|
||||
if !listener.is_pending {
|
||||
msg = Some(FreezeMessage::init(alloc, cookie));
|
||||
msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
|
||||
}
|
||||
drop(node_refs_guard);
|
||||
|
||||
@@ -384,7 +435,7 @@ impl Process {
|
||||
continue;
|
||||
};
|
||||
let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
|
||||
let msg = FreezeMessage::init(msg_alloc, cookie);
|
||||
let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid());
|
||||
batch.push((proc, msg), GFP_KERNEL)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/* Based on: Documentation/netlink/specs/binder.yaml */
|
||||
|
||||
#![allow(unreachable_pub, clippy::wrong_self_convention)]
|
||||
use kernel::{
|
||||
net::netlink::{
|
||||
Family,
|
||||
GenlMsg,
|
||||
MulticastGroup,
|
||||
NetlinkSkBuff, //
|
||||
},
|
||||
prelude::*, //
|
||||
};
|
||||
|
||||
pub static BINDER_NL_FAMILY: Family = Family::const_new(
|
||||
kernel::module::this_module::<crate::LocalModule>(),
|
||||
kernel::uapi::BINDER_FAMILY_NAME,
|
||||
kernel::uapi::BINDER_FAMILY_VERSION,
|
||||
&BINDER_NL_FAMILY_MCGRPS,
|
||||
);
|
||||
|
||||
static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")];
|
||||
|
||||
/// A multicast event sent to userspace subscribers to notify them about
|
||||
/// binder transaction failures. The generated report provides the full
|
||||
/// details of the specific transaction that failed. The intention is for
|
||||
/// programs to monitor these events and react to the failures as needed.
|
||||
pub struct Report {
|
||||
skb: GenlMsg,
|
||||
}
|
||||
|
||||
impl Report {
|
||||
/// Create a new multicast message.
|
||||
pub fn new(
|
||||
size: usize,
|
||||
portid: u32,
|
||||
seq: u32,
|
||||
flags: kernel::alloc::Flags,
|
||||
) -> Result<Self, kernel::alloc::AllocError> {
|
||||
const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
|
||||
let skb = NetlinkSkBuff::new(size, flags)?;
|
||||
let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?;
|
||||
Ok(Self { skb })
|
||||
}
|
||||
|
||||
/// Broadcast this message.
|
||||
pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result {
|
||||
self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags)
|
||||
}
|
||||
|
||||
/// Check if this message type has listeners.
|
||||
pub fn has_listeners() -> bool {
|
||||
BINDER_NL_FAMILY.has_listeners(0)
|
||||
}
|
||||
|
||||
/// The enum binder_driver_return_protocol returned to the sender.
|
||||
pub fn error(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_ERROR, val)
|
||||
}
|
||||
|
||||
/// The binder context where the transaction occurred.
|
||||
pub fn context(&mut self, val: &CStr) -> Result {
|
||||
const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
|
||||
self.skb.put_string(BINDER_A_REPORT_CONTEXT, val)
|
||||
}
|
||||
|
||||
/// The PID of the sender process.
|
||||
pub fn from_pid(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val)
|
||||
}
|
||||
|
||||
/// The TID of the sender thread.
|
||||
pub fn from_tid(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val)
|
||||
}
|
||||
|
||||
/// The PID of the recipient process. This attribute may not be present
|
||||
/// if the target could not be determined.
|
||||
pub fn to_pid(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_TO_PID, val)
|
||||
}
|
||||
|
||||
/// The TID of the recipient thread. This attribute may not be present
|
||||
/// if the target could not be determined.
|
||||
pub fn to_tid(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_TO_TID, val)
|
||||
}
|
||||
|
||||
/// When present, indicates the failed transaction is a reply.
|
||||
pub fn is_reply(&mut self) -> Result {
|
||||
const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
|
||||
self.skb.put_flag(BINDER_A_REPORT_IS_REPLY)
|
||||
}
|
||||
|
||||
/// The bitmask of enum transaction_flags from the transaction.
|
||||
pub fn flags(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_FLAGS, val)
|
||||
}
|
||||
|
||||
/// The application-defined code from the transaction.
|
||||
pub fn code(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_CODE, val)
|
||||
}
|
||||
|
||||
/// The transaction payload size in bytes.
|
||||
pub fn data_size(&mut self, val: u32) -> Result {
|
||||
const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
|
||||
self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use kernel::{
|
||||
seq_print,
|
||||
sync::lock::{spinlock::SpinLockBackend, Guard},
|
||||
sync::{Arc, LockedBy, SpinLock},
|
||||
uapi,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -21,6 +22,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
|
||||
mod wrapper;
|
||||
pub(crate) use self::wrapper::CritIncrWrapper;
|
||||
@@ -321,7 +323,7 @@ impl Node {
|
||||
/// An id that is unique across all binder nodes on the system. Used as the key in the
|
||||
/// `by_node` map.
|
||||
pub(crate) fn global_id(&self) -> usize {
|
||||
self as *const Node as usize
|
||||
ptr::from_ref(self).addr()
|
||||
}
|
||||
|
||||
pub(crate) fn get_id(&self) -> (u64, u64) {
|
||||
@@ -333,6 +335,10 @@ impl Node {
|
||||
death: ListArc<DTRWrap<NodeDeath>, 1>,
|
||||
guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
|
||||
) {
|
||||
assert!(
|
||||
core::ptr::eq(self, &**death.node),
|
||||
"attempt to add NodeDeath to the wrong death list"
|
||||
);
|
||||
self.inner.access_mut(guard).death_list.push_back(death);
|
||||
}
|
||||
|
||||
@@ -343,7 +349,7 @@ impl Node {
|
||||
) -> Option<DLArc<Node>> {
|
||||
let inner = self.inner.access_mut(owner_inner);
|
||||
if inner.active_inc_refs == 0 {
|
||||
pr_err!("inc_ref_done called when no active inc_refs");
|
||||
binder_debug!(UserError, "inc_ref_done called when no active inc_refs");
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -464,7 +470,7 @@ impl Node {
|
||||
owner_inner: &mut ProcessInner,
|
||||
) -> Option<DLArc<dyn DeliverToRead>> {
|
||||
match self.incr_refcount_allow_zero2one(strong, owner_inner) {
|
||||
Ok(Some(node)) => Some(node as _),
|
||||
Ok(Some(node)) => Some(node as DLArc<dyn DeliverToRead>),
|
||||
Ok(None) => None,
|
||||
Err(CouldNotDeliverCriticalIncrement) => {
|
||||
assert!(strong);
|
||||
@@ -489,8 +495,8 @@ impl Node {
|
||||
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
|
||||
) {
|
||||
let inner = self.inner.access(guard);
|
||||
out.strong_count = inner.strong.count as _;
|
||||
out.weak_count = inner.weak.count as _;
|
||||
out.strong_count = inner.strong.count as u32;
|
||||
out.weak_count = inner.weak.count as u32;
|
||||
}
|
||||
|
||||
pub(crate) fn populate_debug_info(
|
||||
@@ -498,8 +504,8 @@ impl Node {
|
||||
out: &mut BinderNodeDebugInfo,
|
||||
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
|
||||
) {
|
||||
out.ptr = self.ptr as _;
|
||||
out.cookie = self.cookie as _;
|
||||
out.ptr = self.ptr as uapi::binder_uintptr_t;
|
||||
out.cookie = self.cookie as uapi::binder_uintptr_t;
|
||||
let inner = self.inner.access(guard);
|
||||
if inner.strong.has_count {
|
||||
out.has_strong_ref = 1;
|
||||
@@ -536,7 +542,7 @@ impl Node {
|
||||
inner.oneway_todo.push_back(transaction);
|
||||
} else {
|
||||
inner.has_oneway_transaction = true;
|
||||
guard.push_work(transaction)?;
|
||||
guard.push_work(&self.owner, transaction)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -568,7 +574,7 @@ impl Node {
|
||||
let transaction = inner.oneway_todo.pop_front();
|
||||
inner.has_oneway_transaction = transaction.is_some();
|
||||
if let Some(transaction) = transaction {
|
||||
match guard.push_work(transaction) {
|
||||
match guard.push_work(&self.owner, transaction) {
|
||||
Ok(()) => {}
|
||||
Err((_err, work)) => {
|
||||
// Process is dead.
|
||||
@@ -657,29 +663,26 @@ impl Node {
|
||||
pub(crate) fn add_freeze_listener(
|
||||
&self,
|
||||
process: &Arc<Process>,
|
||||
flags: kernel::alloc::Flags,
|
||||
) -> Result {
|
||||
let mut vec_alloc = KVVec::<Arc<Process>>::new();
|
||||
loop {
|
||||
let mut guard = self.owner.inner.lock();
|
||||
// Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
|
||||
// listener, no the target.
|
||||
let inner = self.inner.access_mut(&mut guard);
|
||||
let len = inner.freeze_list.len();
|
||||
if len >= inner.freeze_list.capacity() {
|
||||
if len >= vec_alloc.capacity() {
|
||||
drop(guard);
|
||||
vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?;
|
||||
continue;
|
||||
}
|
||||
mem::swap(&mut inner.freeze_list, &mut vec_alloc);
|
||||
for elem in vec_alloc.drain_all() {
|
||||
inner.freeze_list.push_within_capacity(elem)?;
|
||||
}
|
||||
// If the vector needs to be resized, it's done via this argument.
|
||||
vec_alloc: &mut KVVec<Arc<Process>>,
|
||||
) -> Result<Result<(), usize>> {
|
||||
let mut guard = self.owner.inner.lock();
|
||||
// Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
|
||||
// listener, not the target.
|
||||
let inner = self.inner.access_mut(&mut guard);
|
||||
let len = inner.freeze_list.len();
|
||||
if len == inner.freeze_list.capacity() {
|
||||
if len >= vec_alloc.capacity() {
|
||||
// Request the caller to reallocate.
|
||||
return Ok(Err((1 + len).next_power_of_two()));
|
||||
}
|
||||
mem::swap(&mut inner.freeze_list, vec_alloc);
|
||||
for elem in vec_alloc.drain_all() {
|
||||
inner.freeze_list.push_within_capacity(elem)?;
|
||||
}
|
||||
inner.freeze_list.push_within_capacity(process.clone())?;
|
||||
return Ok(());
|
||||
}
|
||||
inner.freeze_list.push_within_capacity(process.clone())?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> {
|
||||
@@ -695,6 +698,8 @@ impl Node {
|
||||
p.pid_in_current_ns()
|
||||
);
|
||||
}
|
||||
// If the vector is empty it needs to be freed. However, we can't free it here because that
|
||||
// might sleep, so return it to the caller.
|
||||
if inner.freeze_list.is_empty() {
|
||||
return mem::take(&mut inner.freeze_list);
|
||||
}
|
||||
@@ -820,6 +825,7 @@ impl NodeRef {
|
||||
|
||||
pub(crate) fn clone(&self, strong: bool) -> Result<NodeRef> {
|
||||
if strong && self.strong_count == 0 {
|
||||
binder_debug!(UserError, "tried to use weak ref as strong ref");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
Ok(self
|
||||
@@ -860,9 +866,10 @@ impl NodeRef {
|
||||
*count += 1;
|
||||
} else {
|
||||
if *count == 0 {
|
||||
pr_warn!(
|
||||
"pid {} performed invalid decrement on ref\n",
|
||||
kernel::current!().pid()
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"performed invalid {} decrement on ref",
|
||||
if strong { "strong" } else { "weak" }
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -1104,6 +1111,11 @@ impl DeliverToRead for NodeDeath {
|
||||
// We're still holding the inner lock, so it cannot be aborted while we insert it into
|
||||
// the delivered list.
|
||||
process_inner.death_delivered(self.clone());
|
||||
binder_debug!(
|
||||
DeathNotification,
|
||||
"sending death notification, cookie {:016x}",
|
||||
cookie
|
||||
);
|
||||
BR_DEAD_BINDER
|
||||
};
|
||||
|
||||
@@ -1114,7 +1126,14 @@ impl DeliverToRead for NodeDeath {
|
||||
Ok(cmd != BR_DEAD_BINDER)
|
||||
}
|
||||
|
||||
fn cancel(self: DArc<Self>) {}
|
||||
fn cancel(self: DArc<Self>) {
|
||||
binder_debug!(
|
||||
pid = self.process.task.pid(),
|
||||
DeadTransaction,
|
||||
"undelivered death notification, {:016x}",
|
||||
self.cookie
|
||||
);
|
||||
}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
false
|
||||
|
||||
@@ -21,7 +21,7 @@ impl CritIncrWrapper {
|
||||
|
||||
pub(super) fn init(self, node: DArc<Node>) -> DLArc<dyn DeliverToRead> {
|
||||
match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) {
|
||||
Ok(initialized) => ListArc::from(initialized) as _,
|
||||
Ok(initialized) => ListArc::from(initialized) as DLArc<dyn DeliverToRead>,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ impl ShrinkablePageRange {
|
||||
|
||||
// SAFETY: This just initializes the pages array.
|
||||
unsafe {
|
||||
let self_ptr = self as *const ShrinkablePageRange;
|
||||
let self_ptr = ptr::from_ref(self);
|
||||
for i in 0..num_pages {
|
||||
let info = pages.as_mut_ptr().add(i);
|
||||
(&raw mut (*info).range).write(self_ptr);
|
||||
@@ -571,7 +571,7 @@ impl ShrinkablePageRange {
|
||||
unsafe {
|
||||
self.iterate(offset, size_of::<T>(), |page, offset, to_copy| {
|
||||
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
|
||||
let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset);
|
||||
let obj_ptr = out.as_mut_ptr().cast::<u8>().add(out_offset);
|
||||
// SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid.
|
||||
page.read_raw(obj_ptr, offset, to_copy)?;
|
||||
out_offset += to_copy;
|
||||
@@ -593,7 +593,7 @@ impl ShrinkablePageRange {
|
||||
unsafe {
|
||||
self.iterate(offset, size_of_val(obj), |page, offset, to_copy| {
|
||||
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
|
||||
let obj_ptr = (obj as *const T as *const u8).add(obj_offset);
|
||||
let obj_ptr = ptr::from_ref(obj).cast::<u8>().add(obj_offset);
|
||||
// SAFETY: We have a reference to the object, so the pointer is valid.
|
||||
page.write_raw(obj_ptr, offset, to_copy)?;
|
||||
obj_offset += to_copy;
|
||||
@@ -712,7 +712,7 @@ unsafe extern "C" fn rust_shrink_free_page(
|
||||
|
||||
{
|
||||
// CAST: The `list_head` field is first in `PageInfo`.
|
||||
let info = item as *mut PageInfo;
|
||||
let info = item.cast::<PageInfo>();
|
||||
// SAFETY: The `range` field of `PageInfo` is immutable.
|
||||
range_ptr = unsafe { (*info).range };
|
||||
// SAFETY: The `range` outlives its `PageInfo` values.
|
||||
|
||||
@@ -16,6 +16,7 @@ use core::mem::take;
|
||||
|
||||
use kernel::{
|
||||
bindings,
|
||||
bits::bit_u8,
|
||||
cred::Credential,
|
||||
error::Error,
|
||||
fs::file::{self, File},
|
||||
@@ -30,9 +31,10 @@ use kernel::{
|
||||
sync::{
|
||||
aref::ARef,
|
||||
lock::{spinlock::SpinLockBackend, Guard},
|
||||
Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
|
||||
poll::PollCondVarBox,
|
||||
Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc,
|
||||
},
|
||||
task::Task,
|
||||
task::{Pid, Task},
|
||||
uaccess::{UserSlice, UserSliceReader},
|
||||
uapi,
|
||||
workqueue::{self, Work},
|
||||
@@ -70,9 +72,18 @@ impl Mapping {
|
||||
}
|
||||
}
|
||||
|
||||
// bitflags for defer_work.
|
||||
const PROC_DEFER_FLUSH: u8 = 1;
|
||||
const PROC_DEFER_RELEASE: u8 = 2;
|
||||
kernel::impl_flags!(
|
||||
/// Represents multiple deferred work flags.
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
|
||||
pub struct DeferWorks(u8);
|
||||
|
||||
/// Represents a single deferred work category.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DeferWork {
|
||||
Flush = bit_u8(0),
|
||||
Release = bit_u8(1),
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum IsFrozen {
|
||||
@@ -121,7 +132,7 @@ pub(crate) struct ProcessInner {
|
||||
started_thread_count: u32,
|
||||
|
||||
/// Bitmap of deferred work to do.
|
||||
defer_work: u8,
|
||||
defer_work: DeferWorks,
|
||||
|
||||
/// Number of transactions to be transmitted before processes in freeze_wait
|
||||
/// are woken up.
|
||||
@@ -151,7 +162,7 @@ impl ProcessInner {
|
||||
requested_thread_count: 0,
|
||||
max_threads: 0,
|
||||
started_thread_count: 0,
|
||||
defer_work: 0,
|
||||
defer_work: DeferWorks::default(),
|
||||
outstanding_txns: 0,
|
||||
is_frozen: IsFrozen::No,
|
||||
sync_recv: false,
|
||||
@@ -172,21 +183,26 @@ impl ProcessInner {
|
||||
/// taken while holding the inner process lock.
|
||||
pub(crate) fn push_work(
|
||||
&mut self,
|
||||
proc: &Process,
|
||||
work: DLArc<dyn DeliverToRead>,
|
||||
) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
|
||||
let sync = work.should_sync_wakeup();
|
||||
|
||||
// Try to find a ready thread to which to push the work.
|
||||
if let Some(thread) = self.ready_threads.pop_front() {
|
||||
// Push to thread while holding state lock. This prevents the thread from giving up
|
||||
// (for example, because of a signal) when we're about to deliver work.
|
||||
match thread.push_work(work) {
|
||||
match thread.push_work_inner(work, sync) {
|
||||
PushWorkRes::Ok => Ok(()),
|
||||
PushWorkRes::OkNotifyPoll => {
|
||||
proc.notify_poll(sync);
|
||||
Ok(())
|
||||
}
|
||||
PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)),
|
||||
}
|
||||
} else if self.is_dead {
|
||||
Err((BinderError::new_dead(), work))
|
||||
} else {
|
||||
let sync = work.should_sync_wakeup();
|
||||
|
||||
// Didn't find a thread waiting for proc work; this can happen
|
||||
// in two scenarios:
|
||||
// 1. All threads are busy handling transactions
|
||||
@@ -194,17 +210,12 @@ impl ProcessInner {
|
||||
// the kernel driver soon and pick up this work.
|
||||
// 2. Threads are using the (e)poll interface, in which case
|
||||
// they may be blocked on the waitqueue without having been
|
||||
// added to waiting_threads. For this case, we just iterate
|
||||
// over all threads not handling transaction work, and
|
||||
// wake them all up. We wake all because we don't know whether
|
||||
// a thread that called into (e)poll is handling non-binder
|
||||
// work currently.
|
||||
// added to waiting_threads. For this case, we wake it up
|
||||
// directly.
|
||||
self.work.push_back(work);
|
||||
|
||||
// Wake up polling threads, if any.
|
||||
for thread in self.threads.values() {
|
||||
thread.notify_if_poll_ready(sync);
|
||||
}
|
||||
proc.notify_poll(sync);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -227,11 +238,11 @@ impl ProcessInner {
|
||||
|
||||
// If we decided that we need to push work, push either to the process or to a thread if
|
||||
// one is specified.
|
||||
if let Some(node) = push {
|
||||
if let Some(pnode) = push {
|
||||
if let Some(thread) = othread {
|
||||
thread.push_work_deferred(node);
|
||||
thread.push_work_deferred(pnode);
|
||||
} else {
|
||||
let _ = self.push_work(node);
|
||||
let _ = self.push_work(&node.owner, pnode);
|
||||
// Nothing to do: `push_work` may fail if the process is dead, but that's ok as in
|
||||
// that case, it doesn't care about the notification.
|
||||
}
|
||||
@@ -259,7 +270,7 @@ impl ProcessInner {
|
||||
let push = match wrapper {
|
||||
None => node
|
||||
.incr_refcount_allow_zero2one(strong, self)?
|
||||
.map(|node| node as _),
|
||||
.map(|node| node as DLArc<dyn DeliverToRead>),
|
||||
Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self),
|
||||
};
|
||||
if let Some(node) = push {
|
||||
@@ -455,7 +466,13 @@ pub(crate) struct Process {
|
||||
// Node references are in a different lock to avoid recursive acquisition when
|
||||
// incrementing/decrementing a node in another process.
|
||||
#[pin]
|
||||
node_refs: Mutex<ProcessNodeRefs>,
|
||||
node_refs: SpinLock<ProcessNodeRefs>,
|
||||
|
||||
// Synchronizes `register_wait` calls to the `PollCondVarBox`.
|
||||
//
|
||||
// The `PollCondVarBox` is not stored here because synchronization is
|
||||
// done for `register_wait` only. Wakeups do not take this lock.
|
||||
poll: SetOnce<PollCondVarBox>,
|
||||
|
||||
// Work node for deferred work item.
|
||||
#[pin]
|
||||
@@ -489,13 +506,13 @@ impl workqueue::WorkItem for Process {
|
||||
{
|
||||
let mut inner = me.inner.lock();
|
||||
defer = inner.defer_work;
|
||||
inner.defer_work = 0;
|
||||
inner.defer_work = DeferWorks::default();
|
||||
}
|
||||
|
||||
if defer & PROC_DEFER_FLUSH != 0 {
|
||||
if defer.contains(DeferWork::Flush) {
|
||||
me.deferred_flush();
|
||||
}
|
||||
if defer & PROC_DEFER_RELEASE != 0 {
|
||||
if defer.contains(DeferWork::Release) {
|
||||
me.deferred_release();
|
||||
}
|
||||
}
|
||||
@@ -510,12 +527,13 @@ impl Process {
|
||||
cred,
|
||||
inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
|
||||
pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER),
|
||||
node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
|
||||
node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"),
|
||||
freeze_wait <- kernel::new_condvar!("Process::freeze_wait"),
|
||||
task: current.group_leader().into(),
|
||||
defer_work <- kernel::new_work!("Process::defer_work"),
|
||||
links <- ListLinks::new(),
|
||||
stats: BinderStats::new(),
|
||||
poll: SetOnce::new(),
|
||||
}),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
@@ -715,7 +733,7 @@ impl Process {
|
||||
|
||||
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
|
||||
// If push_work fails, drop the work item outside the lock.
|
||||
let res = self.inner.lock().push_work(work);
|
||||
let res = self.inner.lock().push_work(self, work);
|
||||
match res {
|
||||
Ok(()) => Ok(()),
|
||||
Err((err, work)) => {
|
||||
@@ -741,7 +759,7 @@ impl Process {
|
||||
} else {
|
||||
(0, 0, 0)
|
||||
};
|
||||
let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?;
|
||||
let node_ref = self.get_node(ptr, cookie, flags, true, thread)?;
|
||||
let node = node_ref.node.clone();
|
||||
self.ctx.set_manager_node(node_ref)?;
|
||||
self.inner.lock().is_manager = true;
|
||||
@@ -861,14 +879,17 @@ impl Process {
|
||||
let handle = unused_id.as_u32();
|
||||
|
||||
// Do a lookup again as node may have been inserted before the lock was reacquired.
|
||||
if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
|
||||
let handle = *handle_ref;
|
||||
let info = refs.by_handle.get_mut(&handle).unwrap();
|
||||
info.node_ref().absorb(node_ref);
|
||||
return Ok(handle);
|
||||
}
|
||||
let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) {
|
||||
rbtree::Entry::Vacant(by_node_slot) => by_node_slot,
|
||||
rbtree::Entry::Occupied(handle_ref) => {
|
||||
// The node was inserted by another thread while we didn't hold the lock.
|
||||
let handle = handle_ref.get();
|
||||
let info = refs.by_handle.get_mut(handle).unwrap();
|
||||
info.node_ref().absorb(node_ref);
|
||||
return Ok(*handle);
|
||||
}
|
||||
};
|
||||
|
||||
let gid = node_ref.node.global_id();
|
||||
let (info_proc, info_node) = {
|
||||
let info_init = NodeRefInfo::new(node_ref, handle, self.into());
|
||||
match info.pin_init_with(info_init) {
|
||||
@@ -884,6 +905,9 @@ impl Process {
|
||||
// first thing in `deferred_release`, process cleanup will not miss the items inserted into
|
||||
// `refs` below.
|
||||
if self.inner.lock().is_dead {
|
||||
// Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of
|
||||
// the lock.
|
||||
drop(refs_lock);
|
||||
return Err(ESRCH);
|
||||
}
|
||||
|
||||
@@ -891,7 +915,7 @@ impl Process {
|
||||
// `info_node` into the right node's `refs` list.
|
||||
unsafe { info_proc.node_ref2().node.insert_node_info(info_node) };
|
||||
|
||||
refs.by_node.insert(reserve1.into_node(gid, handle));
|
||||
by_node_slot.insert(handle, reserve1);
|
||||
by_handle_slot.insert(info_proc, reserve2);
|
||||
unused_id.acquire();
|
||||
Ok(handle)
|
||||
@@ -906,7 +930,13 @@ impl Process {
|
||||
}
|
||||
Ok(node_ref)
|
||||
} else {
|
||||
Ok(self.get_node_from_handle(handle, true)?)
|
||||
match self.get_node_from_handle(handle, true) {
|
||||
Ok(node_ref) => Ok(node_ref),
|
||||
Err(err) => {
|
||||
binder_debug!(UserError, "got transaction to invalid handle {handle}");
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -946,15 +976,19 @@ impl Process {
|
||||
|
||||
// To preserve original binder behaviour, we only fail requests where the manager tries to
|
||||
// increment references on itself.
|
||||
let _to_free_by_handle;
|
||||
let _to_free_by_node;
|
||||
let _to_free_freeze_listener;
|
||||
let _to_free_freeze_listener_cleanup;
|
||||
let mut refs = self.node_refs.lock();
|
||||
if let Some(info) = refs.by_handle.get_mut(&handle) {
|
||||
if info.node_ref().update(inc, strong) {
|
||||
// Clean up death if there is one attached to this node reference.
|
||||
if let Some(death) = info.death().take() {
|
||||
//
|
||||
// We remove the entire `info` below, so no need to remove `death` from `info`.
|
||||
if let Some(death) = info.death().as_ref() {
|
||||
death.set_cleared(true);
|
||||
self.remove_from_delivered_deaths(&death);
|
||||
self.remove_from_delivered_deaths(death);
|
||||
}
|
||||
|
||||
// Remove reference from process tables, and from the node's `refs` list.
|
||||
@@ -971,8 +1005,8 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
refs.by_handle.remove(&handle);
|
||||
refs.by_node.remove(&id);
|
||||
_to_free_by_handle = refs.by_handle.remove_node(&handle);
|
||||
_to_free_by_node = refs.by_node.remove_node(&id);
|
||||
refs.handle_is_present.release_id(handle as usize);
|
||||
|
||||
if let Some(shrink) = refs.handle_is_present.shrink_request() {
|
||||
@@ -987,7 +1021,7 @@ impl Process {
|
||||
} else {
|
||||
// All refs are cleared in process exit, so this warning is expected in that case.
|
||||
if !self.inner.lock().is_dead {
|
||||
pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns());
|
||||
binder_debug!(UserError, "no such ref {handle}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1008,7 +1042,7 @@ impl Process {
|
||||
if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
|
||||
if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) {
|
||||
// This only fails if the process is dead.
|
||||
let _ = inner.push_work(node);
|
||||
let _ = inner.push_work(self, node);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1237,16 +1271,26 @@ impl Process {
|
||||
// Queue BR_ERROR if we can't allocate memory for the death notification.
|
||||
let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| {
|
||||
thread.push_return_work(BR_ERROR);
|
||||
binder_debug!(
|
||||
DeathNotification,
|
||||
"BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure"
|
||||
);
|
||||
})?;
|
||||
let mut refs = self.node_refs.lock();
|
||||
let Some(info) = refs.by_handle.get_mut(&handle) else {
|
||||
pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}"
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Nothing to do if there is already a death notification request for this handle.
|
||||
if info.death().is_some() {
|
||||
pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_REQUEST_DEATH_NOTIFICATION death notification already set"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1274,6 +1318,11 @@ impl Process {
|
||||
info.node_ref().node.add_death(death, &mut owner_inner);
|
||||
}
|
||||
}
|
||||
binder_debug!(
|
||||
DeathNotification,
|
||||
"BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
|
||||
cookie
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1283,28 +1332,45 @@ impl Process {
|
||||
|
||||
let mut refs = self.node_refs.lock();
|
||||
let Some(info) = refs.by_handle.get_mut(&handle) else {
|
||||
pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}"
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(death) = info.death().take() else {
|
||||
pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_DEATH_NOTIFICATION death notification not active"
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
if death.cookie != cookie {
|
||||
*info.death() = Some(death);
|
||||
pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n");
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Update state and determine if we need to queue a work item. We only need to do it when
|
||||
// the node is not dead or if the user already completed the death notification.
|
||||
if death.set_cleared(false) {
|
||||
let should_schedule = death.set_cleared(false);
|
||||
drop(refs);
|
||||
|
||||
if should_schedule {
|
||||
if let Some(death) = ListArc::try_from_arc_or_drop(death) {
|
||||
let _ = thread.push_work_if_looper(death);
|
||||
}
|
||||
}
|
||||
|
||||
binder_debug!(
|
||||
DeathNotification,
|
||||
"BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
|
||||
cookie
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1328,6 +1394,7 @@ impl Process {
|
||||
}
|
||||
|
||||
fn deferred_flush(&self) {
|
||||
binder_debug!(pid = self.task.pid(), OpenClose, "flushing process");
|
||||
let inner = self.inner.lock();
|
||||
for thread in inner.threads.values() {
|
||||
thread.exit_looper();
|
||||
@@ -1335,6 +1402,8 @@ impl Process {
|
||||
}
|
||||
|
||||
fn deferred_release(self: Arc<Self>) {
|
||||
binder_debug!(pid = self.task.pid(), OpenClose, "releasing process");
|
||||
|
||||
let is_manager = {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.is_dead = true;
|
||||
@@ -1382,13 +1451,11 @@ impl Process {
|
||||
// SAFETY: We are removing the `NodeRefInfo` from the right node.
|
||||
unsafe { info.node_ref2().node.remove_node_info(info) };
|
||||
|
||||
// Remove all death notifications from the nodes (that belong to a different process).
|
||||
let death = if let Some(existing) = info.death().take() {
|
||||
existing
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
death.set_cleared(false);
|
||||
// Clear death notifications from the nodes (that belong to a different process).
|
||||
// No need to remove them from `info` as we clear info below.
|
||||
if let Some(death) = info.death().as_ref() {
|
||||
death.set_cleared(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up freeze listeners.
|
||||
@@ -1524,6 +1591,15 @@ impl Process {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn notify_poll(&self, sync: bool) {
|
||||
if let Some(poll) = self.poll.as_ref() {
|
||||
if sync {
|
||||
poll.notify_sync();
|
||||
}
|
||||
poll.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_frozen_status(data: UserSlice) -> Result {
|
||||
@@ -1536,13 +1612,13 @@ fn get_frozen_status(data: UserSlice) -> Result {
|
||||
|
||||
for ctx in crate::context::get_all_contexts()? {
|
||||
ctx.for_each_proc(|proc| {
|
||||
if proc.task.pid() == info.pid as _ {
|
||||
if proc.task.pid() == info.pid as Pid {
|
||||
found = true;
|
||||
let inner = proc.inner.lock();
|
||||
let txns_pending = inner.txns_pending_locked();
|
||||
info.async_recv |= inner.async_recv as u32;
|
||||
info.sync_recv |= inner.sync_recv as u32;
|
||||
info.sync_recv |= (txns_pending as u32) << 1;
|
||||
info.async_recv |= u32::from(inner.async_recv);
|
||||
info.sync_recv |= u32::from(inner.sync_recv);
|
||||
info.sync_recv |= u32::from(txns_pending) << 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1634,7 +1710,9 @@ impl Process {
|
||||
/// The file operations supported by `Process`.
|
||||
impl Process {
|
||||
pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result<Arc<Process>> {
|
||||
Self::new(ctx.into(), ARef::from(file.cred()))
|
||||
let proc = Self::new(ctx.into(), ARef::from(file.cred()))?;
|
||||
binder_debug!(OpenClose, "opened process");
|
||||
Ok(proc)
|
||||
}
|
||||
|
||||
pub(crate) fn release(this: Arc<Process>, _file: &File) {
|
||||
@@ -1642,8 +1720,8 @@ impl Process {
|
||||
let should_schedule;
|
||||
{
|
||||
let mut inner = this.inner.lock();
|
||||
should_schedule = inner.defer_work == 0;
|
||||
inner.defer_work |= PROC_DEFER_RELEASE;
|
||||
should_schedule = inner.defer_work == DeferWorks::empty();
|
||||
inner.defer_work |= DeferWork::Release;
|
||||
binderfs_file = inner.binderfs_file.take();
|
||||
}
|
||||
|
||||
@@ -1660,8 +1738,8 @@ impl Process {
|
||||
let should_schedule;
|
||||
{
|
||||
let mut inner = this.inner.lock();
|
||||
should_schedule = inner.defer_work == 0;
|
||||
inner.defer_work |= PROC_DEFER_FLUSH;
|
||||
should_schedule = inner.defer_work == DeferWorks::empty();
|
||||
inner.defer_work |= DeferWork::Flush;
|
||||
}
|
||||
|
||||
if should_schedule {
|
||||
@@ -1719,7 +1797,21 @@ impl Process {
|
||||
table: PollTable<'_>,
|
||||
) -> Result<u32> {
|
||||
let thread = this.get_current_thread()?;
|
||||
let (from_proc, mut mask) = thread.poll(file, table);
|
||||
{
|
||||
let poll = loop {
|
||||
if let Some(poll) = this.poll.as_ref() {
|
||||
break poll;
|
||||
}
|
||||
|
||||
let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?;
|
||||
// Reuse our existing lock to synchronize callers initializing.
|
||||
let _guard = this.node_refs.lock();
|
||||
this.poll.populate(poll);
|
||||
};
|
||||
|
||||
table.register_wait(file, poll);
|
||||
}
|
||||
let (from_proc, mut mask) = thread.poll()?;
|
||||
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
|
||||
mask |= bindings::POLLIN;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
|
||||
#![crate_name = "rust_binder"]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(
|
||||
clippy::as_underscore,
|
||||
clippy::ref_as_ptr,
|
||||
clippy::ptr_as_ptr,
|
||||
clippy::cast_lossless
|
||||
)]
|
||||
|
||||
use kernel::{
|
||||
bindings::{self, seq_file},
|
||||
@@ -38,7 +32,10 @@ mod allocation;
|
||||
mod context;
|
||||
mod deferred_close;
|
||||
mod defs;
|
||||
#[macro_use]
|
||||
mod debug;
|
||||
mod error;
|
||||
mod netlink;
|
||||
mod node;
|
||||
mod page_range;
|
||||
mod process;
|
||||
@@ -226,6 +223,7 @@ impl<T: ListArcSafe> DTRWrap<T> {
|
||||
struct DeliverCode {
|
||||
code: u32,
|
||||
skip: Atomic<bool>,
|
||||
pid: i32,
|
||||
}
|
||||
|
||||
kernel::list::impl_list_arc_safe! {
|
||||
@@ -233,10 +231,11 @@ kernel::list::impl_list_arc_safe! {
|
||||
}
|
||||
|
||||
impl DeliverCode {
|
||||
fn new(code: u32) -> Self {
|
||||
fn new(code: u32, pid: i32) -> Self {
|
||||
Self {
|
||||
code,
|
||||
skip: Atomic::new(false),
|
||||
pid,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +260,15 @@ impl DeliverToRead for DeliverCode {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cancel(self: DArc<Self>) {}
|
||||
fn cancel(self: DArc<Self>) {
|
||||
if !self.skip.load(Relaxed) {
|
||||
binder_debug!(
|
||||
pid = self.pid,
|
||||
DeadTransaction,
|
||||
"undelivered TRANSACTION_COMPLETE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
false
|
||||
@@ -289,19 +296,22 @@ fn ptr_align(value: usize) -> Option<usize> {
|
||||
// SAFETY: We call register in `init`.
|
||||
static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() };
|
||||
|
||||
struct BinderModule {}
|
||||
struct BinderModule {
|
||||
_netlink: kernel::net::netlink::Registration,
|
||||
}
|
||||
|
||||
impl kernel::Module for BinderModule {
|
||||
fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
|
||||
// SAFETY: The module initializer never runs twice, so we only call this once.
|
||||
unsafe { crate::context::CONTEXTS.init() };
|
||||
|
||||
let netlink = crate::netlink::BINDER_NL_FAMILY.register()?;
|
||||
BINDER_SHRINKER.register(c"android-binder")?;
|
||||
|
||||
// SAFETY: The module is being loaded, so we can initialize binderfs.
|
||||
unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? };
|
||||
|
||||
Ok(Self {})
|
||||
Ok(Self { _netlink: netlink })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,9 +325,6 @@ unsafe impl<T> Sync for AssertSync<T> {}
|
||||
#[no_mangle]
|
||||
#[used]
|
||||
pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
|
||||
// SAFETY: All zeroes is safe for the `file_operations` type.
|
||||
let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
|
||||
|
||||
let ops = kernel::bindings::file_operations {
|
||||
owner: this_module::<LocalModule>().as_ptr(),
|
||||
poll: Some(rust_binder_poll),
|
||||
@@ -327,7 +334,7 @@ pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
|
||||
open: Some(rust_binder_open),
|
||||
release: Some(rust_binder_release),
|
||||
flush: Some(rust_binder_flush),
|
||||
..zeroed_ops
|
||||
..pin_init::zeroed()
|
||||
};
|
||||
AssertSync(ops)
|
||||
};
|
||||
@@ -418,7 +425,7 @@ unsafe extern "C" fn rust_binder_ioctl(
|
||||
// SAFETY: We previously set `private_data` in `rust_binder_open`.
|
||||
let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
|
||||
// SAFETY: The caller ensures that the file is valid.
|
||||
match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) {
|
||||
match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) {
|
||||
Ok(()) => 0,
|
||||
Err(err) => err.to_errno() as isize,
|
||||
}
|
||||
@@ -512,7 +519,7 @@ unsafe extern "C" fn rust_binder_proc_show(
|
||||
_: *mut kernel::ffi::c_void,
|
||||
) -> kernel::ffi::c_int {
|
||||
// SAFETY: Accessing the private field of `seq_file` is okay.
|
||||
let pid = (unsafe { (*ptr).private }) as usize as Pid;
|
||||
let pid = unsafe { (*ptr).private }.addr() as Pid;
|
||||
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
|
||||
// this method is called.
|
||||
let m = unsafe { SeqFile::from_raw(ptr) };
|
||||
|
||||
@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);
|
||||
char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
|
||||
module_param_named(rust_devices, rust_binder_devices_param, charp, 0444);
|
||||
|
||||
extern u32 rust_binder_debug_mask;
|
||||
module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644);
|
||||
|
||||
static dev_t binderfs_dev;
|
||||
static DEFINE_MUTEX(binderfs_minors_mutex);
|
||||
static DEFINE_IDA(binderfs_minors);
|
||||
|
||||
+182
-114
@@ -9,15 +9,15 @@
|
||||
|
||||
use kernel::{
|
||||
bindings,
|
||||
fs::{File, LocalFile},
|
||||
bits::bit_u32,
|
||||
fs::LocalFile,
|
||||
list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
|
||||
prelude::*,
|
||||
security,
|
||||
seq_file::SeqFile,
|
||||
seq_print,
|
||||
sync::atomic::{ordering::Relaxed, Atomic},
|
||||
sync::poll::{PollCondVar, PollTable},
|
||||
sync::{aref::ARef, Arc, SpinLock},
|
||||
sync::{aref::ARef, Arc, CondVar, SpinLock},
|
||||
task::Task,
|
||||
uaccess::{UserPtr, UserSlice, UserSliceReader},
|
||||
uapi,
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
process::{GetWorkOrRegister, Process},
|
||||
ptr_align,
|
||||
stats::GLOBAL_STATS,
|
||||
transaction::{Transaction, TransactionInfo},
|
||||
transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo},
|
||||
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
|
||||
};
|
||||
|
||||
@@ -225,8 +225,10 @@ impl UnusedBufferSpace {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) enum PushWorkRes {
|
||||
Ok,
|
||||
OkNotifyPoll,
|
||||
FailedDead(DLArc<dyn DeliverToRead>),
|
||||
}
|
||||
|
||||
@@ -234,6 +236,7 @@ impl PushWorkRes {
|
||||
fn is_ok(&self) -> bool {
|
||||
match self {
|
||||
PushWorkRes::Ok => true,
|
||||
PushWorkRes::OkNotifyPoll => true,
|
||||
PushWorkRes::FailedDead(_) => false,
|
||||
}
|
||||
}
|
||||
@@ -243,7 +246,7 @@ impl PushWorkRes {
|
||||
struct InnerThread {
|
||||
/// Determines the looper state of the thread. It is a bit-wise combination of the constants
|
||||
/// prefixed with `LOOPER_`.
|
||||
looper_flags: u32,
|
||||
looper_flags: LooperFlags,
|
||||
|
||||
/// Determines whether the looper should return.
|
||||
looper_need_return: bool,
|
||||
@@ -270,28 +273,38 @@ struct InnerThread {
|
||||
extended_error: ExtendedError,
|
||||
}
|
||||
|
||||
const LOOPER_REGISTERED: u32 = 0x01;
|
||||
const LOOPER_ENTERED: u32 = 0x02;
|
||||
const LOOPER_EXITED: u32 = 0x04;
|
||||
const LOOPER_INVALID: u32 = 0x08;
|
||||
const LOOPER_WAITING: u32 = 0x10;
|
||||
const LOOPER_WAITING_PROC: u32 = 0x20;
|
||||
const LOOPER_POLL: u32 = 0x40;
|
||||
kernel::impl_flags!(
|
||||
/// Represents multiple looper flags.
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
|
||||
pub struct LooperFlags(u32);
|
||||
|
||||
/// Represents a single looper flag.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LooperFlag {
|
||||
Registered = bit_u32(0),
|
||||
Entered = bit_u32(1),
|
||||
Exited = bit_u32(2),
|
||||
Invalid = bit_u32(3),
|
||||
Waiting = bit_u32(4),
|
||||
WaitingProc = bit_u32(5),
|
||||
Poll = bit_u32(6),
|
||||
}
|
||||
);
|
||||
|
||||
impl InnerThread {
|
||||
fn new() -> Result<Self> {
|
||||
fn new(pid: i32) -> Result<Self> {
|
||||
fn next_err_id() -> u32 {
|
||||
static EE_ID: Atomic<u32> = Atomic::new(0);
|
||||
EE_ID.fetch_add(1, Relaxed)
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
looper_flags: 0,
|
||||
looper_flags: LooperFlags::default(),
|
||||
looper_need_return: false,
|
||||
is_dead: false,
|
||||
process_work_list: false,
|
||||
reply_work: ThreadError::try_new()?,
|
||||
return_work: ThreadError::try_new()?,
|
||||
reply_work: ThreadError::try_new(pid)?,
|
||||
return_work: ThreadError::try_new(pid)?,
|
||||
work_list: List::new(),
|
||||
current_transaction: None,
|
||||
extended_error: ExtendedError::new(next_err_id(), BR_OK, 0),
|
||||
@@ -310,27 +323,32 @@ impl InnerThread {
|
||||
|
||||
fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
|
||||
if self.is_dead {
|
||||
PushWorkRes::FailedDead(work)
|
||||
return PushWorkRes::FailedDead(work);
|
||||
}
|
||||
self.work_list.push_back(work);
|
||||
self.process_work_list = true;
|
||||
if self.looper_flags.contains(LooperFlag::Poll) {
|
||||
PushWorkRes::OkNotifyPoll
|
||||
} else {
|
||||
self.work_list.push_back(work);
|
||||
self.process_work_list = true;
|
||||
PushWorkRes::Ok
|
||||
}
|
||||
}
|
||||
|
||||
fn push_reply_work(&mut self, code: u32) {
|
||||
fn push_reply_work(&mut self, code: u32) -> PushWorkRes {
|
||||
if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {
|
||||
work.set_error_code(code);
|
||||
self.push_work(work);
|
||||
self.push_work(work)
|
||||
} else {
|
||||
pr_warn!("Thread reply work is already in use.");
|
||||
PushWorkRes::Ok
|
||||
}
|
||||
}
|
||||
|
||||
fn push_return_work(&mut self, reply: u32) {
|
||||
if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {
|
||||
work.set_error_code(reply);
|
||||
self.push_work(work);
|
||||
// Not notifying: Reply to current thread.
|
||||
let _ = self.push_work(work);
|
||||
} else {
|
||||
pr_warn!("Thread return work is already in use.");
|
||||
}
|
||||
@@ -373,26 +391,27 @@ impl InnerThread {
|
||||
}
|
||||
|
||||
fn looper_enter(&mut self) {
|
||||
self.looper_flags |= LOOPER_ENTERED;
|
||||
if self.looper_flags & LOOPER_REGISTERED != 0 {
|
||||
self.looper_flags |= LOOPER_INVALID;
|
||||
self.looper_flags |= LooperFlag::Entered;
|
||||
if self.looper_flags.contains(LooperFlag::Registered) {
|
||||
self.looper_flags |= LooperFlag::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
fn looper_register(&mut self, valid: bool) {
|
||||
self.looper_flags |= LOOPER_REGISTERED;
|
||||
if !valid || self.looper_flags & LOOPER_ENTERED != 0 {
|
||||
self.looper_flags |= LOOPER_INVALID;
|
||||
self.looper_flags |= LooperFlag::Registered;
|
||||
if !valid || self.looper_flags.contains(LooperFlag::Entered) {
|
||||
self.looper_flags |= LooperFlag::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
fn looper_exit(&mut self) {
|
||||
self.looper_flags |= LOOPER_EXITED;
|
||||
self.looper_flags |= LooperFlag::Exited;
|
||||
}
|
||||
|
||||
/// Determines whether the thread is part of a pool, i.e., if it is a looper.
|
||||
fn is_looper(&self) -> bool {
|
||||
self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0
|
||||
self.looper_flags
|
||||
.contains_any(LooperFlag::Entered | LooperFlag::Registered)
|
||||
}
|
||||
|
||||
/// Determines whether the thread should attempt to fetch work items from the process queue.
|
||||
@@ -404,7 +423,7 @@ impl InnerThread {
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> u32 {
|
||||
self.looper_flags |= LOOPER_POLL;
|
||||
self.looper_flags |= LooperFlag::Poll;
|
||||
if self.process_work_list || self.looper_need_return {
|
||||
bindings::POLLIN
|
||||
} else {
|
||||
@@ -422,7 +441,7 @@ pub(crate) struct Thread {
|
||||
#[pin]
|
||||
inner: SpinLock<InnerThread>,
|
||||
#[pin]
|
||||
work_condvar: PollCondVar,
|
||||
work_condvar: CondVar,
|
||||
/// Used to insert this thread into the process' `ready_threads` list.
|
||||
///
|
||||
/// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
|
||||
@@ -445,7 +464,7 @@ kernel::list::impl_list_item! {
|
||||
|
||||
impl Thread {
|
||||
pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
|
||||
let inner = InnerThread::new()?;
|
||||
let inner = InnerThread::new(process.task.pid())?;
|
||||
|
||||
Arc::pin_init(
|
||||
try_pin_init!(Thread {
|
||||
@@ -453,7 +472,7 @@ impl Thread {
|
||||
process,
|
||||
task: ARef::from(&**kernel::current!()),
|
||||
inner <- kernel::new_spinlock!(inner, "Thread::inner"),
|
||||
work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
|
||||
work_condvar <- kernel::new_condvar!("Thread::work_condvar"),
|
||||
links <- ListLinks::new(),
|
||||
links_track <- AtomicTracker::new(),
|
||||
}),
|
||||
@@ -470,7 +489,7 @@ impl Thread {
|
||||
m,
|
||||
" thread {}: l {:02x} need_return {}\n",
|
||||
self.id,
|
||||
inner.looper_flags,
|
||||
u32::from(inner.looper_flags),
|
||||
inner.looper_need_return,
|
||||
);
|
||||
}
|
||||
@@ -543,9 +562,9 @@ impl Thread {
|
||||
return Ok(Some(work));
|
||||
}
|
||||
|
||||
inner.looper_flags |= LOOPER_WAITING;
|
||||
inner.looper_flags |= LooperFlag::Waiting;
|
||||
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
|
||||
inner.looper_flags &= !LOOPER_WAITING;
|
||||
inner.looper_flags &= !LooperFlag::Waiting;
|
||||
|
||||
if signal_pending {
|
||||
return Err(EINTR);
|
||||
@@ -597,9 +616,9 @@ impl Thread {
|
||||
return Ok(Some(work));
|
||||
}
|
||||
|
||||
inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
|
||||
inner.looper_flags |= LooperFlag::Waiting | LooperFlag::WaitingProc;
|
||||
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
|
||||
inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
|
||||
inner.looper_flags &= !(LooperFlag::Waiting | LooperFlag::WaitingProc);
|
||||
|
||||
if signal_pending || inner.looper_need_return {
|
||||
// We need to return now. We need to pull the thread off the list of ready threads
|
||||
@@ -624,7 +643,14 @@ impl Thread {
|
||||
/// Returns whether the item was successfully pushed. This can only fail if the thread is dead.
|
||||
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
|
||||
let sync = work.should_sync_wakeup();
|
||||
self.push_work_inner(work, sync)
|
||||
}
|
||||
|
||||
pub(crate) fn push_work_inner(
|
||||
&self,
|
||||
work: DLArc<dyn DeliverToRead>,
|
||||
sync: bool,
|
||||
) -> PushWorkRes {
|
||||
let res = self.inner.lock().push_work(work);
|
||||
|
||||
if res.is_ok() {
|
||||
@@ -643,7 +669,8 @@ impl Thread {
|
||||
pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.is_looper() && !inner.is_dead {
|
||||
inner.push_work(work);
|
||||
// Not notifying: Reply to current thread.
|
||||
let _ = inner.push_work(work);
|
||||
Ok(())
|
||||
} else {
|
||||
drop(inner);
|
||||
@@ -673,9 +700,9 @@ impl Thread {
|
||||
let strong = obj.hdr.type_ == BINDER_TYPE_BINDER;
|
||||
// SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid
|
||||
// representation.
|
||||
let ptr = unsafe { obj.__bindgen_anon_1.binder } as _;
|
||||
let cookie = obj.cookie as _;
|
||||
let flags = obj.flags as _;
|
||||
let ptr = unsafe { obj.__bindgen_anon_1.binder };
|
||||
let cookie = obj.cookie;
|
||||
let flags = obj.flags;
|
||||
let node = self
|
||||
.process
|
||||
.as_arc_borrow()
|
||||
@@ -686,7 +713,7 @@ impl Thread {
|
||||
BinderObjectRef::Handle(obj) => {
|
||||
let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE;
|
||||
// SAFETY: `handle` is a `u32`; any bit pattern is a valid representation.
|
||||
let handle = unsafe { obj.__bindgen_anon_1.handle } as _;
|
||||
let handle = unsafe { obj.__bindgen_anon_1.handle };
|
||||
let node = self.process.get_node_from_handle(handle, strong)?;
|
||||
security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?;
|
||||
view.transfer_binder_object(offset, obj, strong, node)?;
|
||||
@@ -728,11 +755,12 @@ impl Thread {
|
||||
let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) {
|
||||
Ok(alloc_offset) => alloc_offset,
|
||||
Err(err) => {
|
||||
pr_warn!(
|
||||
"Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})",
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})",
|
||||
sg_state.unused_buffer_space.offset,
|
||||
sg_state.unused_buffer_space.limit,
|
||||
obj_length,
|
||||
obj_length
|
||||
);
|
||||
return Err(err.into());
|
||||
}
|
||||
@@ -743,7 +771,7 @@ impl Thread {
|
||||
ScatterGatherEntry {
|
||||
obj_index,
|
||||
offset: alloc_offset,
|
||||
sender_uaddr: obj.buffer as _,
|
||||
sender_uaddr: obj.buffer as usize,
|
||||
length: obj_length,
|
||||
pointer_fixups: KVec::new(),
|
||||
fixup_min_offset: 0,
|
||||
@@ -811,6 +839,7 @@ impl Thread {
|
||||
let fds_len = num_fds.checked_mul(size_of::<u32>()).ok_or(EINVAL)?;
|
||||
|
||||
if !is_aligned(parent_offset, size_of::<u32>()) {
|
||||
binder_debug!(UserError, "FDA parent offset not aligned correctly");
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
|
||||
@@ -829,6 +858,7 @@ impl Thread {
|
||||
};
|
||||
|
||||
if !is_aligned(parent_entry.sender_uaddr, size_of::<u32>()) {
|
||||
binder_debug!(UserError, "FDA parent buffer not aligned correctly");
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
|
||||
@@ -850,7 +880,7 @@ impl Thread {
|
||||
.ok_or(EINVAL)?;
|
||||
|
||||
let mut fda_bytes = KVec::new();
|
||||
UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len)
|
||||
UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len)
|
||||
.read_all(&mut fda_bytes, GFP_KERNEL)?;
|
||||
|
||||
if fds_len != fda_bytes.len() {
|
||||
@@ -912,12 +942,9 @@ impl Thread {
|
||||
|
||||
let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?;
|
||||
if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end {
|
||||
pr_warn!(
|
||||
"Fixups oob {} {} {} {}",
|
||||
fixup_offset,
|
||||
end_of_previous_fixup,
|
||||
offset_end,
|
||||
target_offset_end
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}"
|
||||
);
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
@@ -925,18 +952,21 @@ impl Thread {
|
||||
let copy_off = end_of_previous_fixup;
|
||||
let copy_len = fixup_offset - end_of_previous_fixup;
|
||||
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
|
||||
pr_warn!("Failed copying into alloc: {:?}", err);
|
||||
binder_debug!(UserError, "failed copying into alloc: {err:?}");
|
||||
return Err(err.into());
|
||||
}
|
||||
if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup {
|
||||
let res = alloc.write::<u64>(fixup_offset, pointer_value);
|
||||
if let Err(err) = res {
|
||||
pr_warn!("Failed copying ptr into alloc: {:?}", err);
|
||||
binder_debug!(UserError, "failed copying ptr into alloc: {err:?}");
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
if let Err(err) = reader.skip(fixup_len) {
|
||||
pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err);
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"failed skipping {fixup_len} from reader: {err:?}"
|
||||
);
|
||||
return Err(err.into());
|
||||
}
|
||||
end_of_previous_fixup = target_offset_end;
|
||||
@@ -944,7 +974,7 @@ impl Thread {
|
||||
let copy_off = end_of_previous_fixup;
|
||||
let copy_len = offset_end - end_of_previous_fixup;
|
||||
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
|
||||
pr_warn!("Failed copying remainder into alloc: {:?}", err);
|
||||
binder_debug!(UserError, "failed copying remainder into alloc: {err:?}");
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
@@ -1048,7 +1078,7 @@ impl Thread {
|
||||
let offset: usize = offset.try_into().map_err(|_| EINVAL)?;
|
||||
|
||||
if offset < end_of_previous_object || !is_aligned(offset, size_of::<u32>()) {
|
||||
pr_warn!("Got transaction with invalid offset.");
|
||||
binder_debug!(UserError, "got transaction with invalid offset");
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
|
||||
@@ -1073,7 +1103,7 @@ impl Thread {
|
||||
) {
|
||||
Ok(()) => end_of_previous_object = offset + object.size(),
|
||||
Err(err) => {
|
||||
pr_warn!("Error while translating object.");
|
||||
binder_debug!(UserError, "error while translating object: {err:?}");
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
@@ -1093,15 +1123,12 @@ impl Thread {
|
||||
)?;
|
||||
|
||||
if let Some(sg_state) = sg_state.as_mut() {
|
||||
if let Err(err) = self.apply_sg(&mut alloc, sg_state) {
|
||||
pr_warn!("Failure in apply_sg: {:?}", err);
|
||||
return Err(err);
|
||||
}
|
||||
self.apply_sg(&mut alloc, sg_state)?;
|
||||
}
|
||||
|
||||
if let Some((off_out, secctx)) = secctx.as_mut() {
|
||||
if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) {
|
||||
pr_warn!("Failed to write security context: {:?}", err);
|
||||
binder_debug!(UserError, "failed to write security context: {err:?}");
|
||||
return Err(err.into());
|
||||
}
|
||||
**off_out = secctx_off;
|
||||
@@ -1115,6 +1142,12 @@ impl Thread {
|
||||
let mut inner = thread.inner.lock();
|
||||
inner.pop_transaction_to_reply(thread.as_ref())
|
||||
} {
|
||||
binder_debug!(
|
||||
DeadTransaction,
|
||||
"release transaction {} in, still active",
|
||||
transaction.debug_id
|
||||
);
|
||||
|
||||
let reply = Err(BR_DEAD_REPLY);
|
||||
if !transaction
|
||||
.from
|
||||
@@ -1154,7 +1187,7 @@ impl Thread {
|
||||
transaction.set_outstanding(&mut self.process.inner.lock());
|
||||
}
|
||||
|
||||
{
|
||||
let ret = {
|
||||
let mut inner = self.inner.lock();
|
||||
if !inner.pop_transaction_replied(transaction) {
|
||||
return false;
|
||||
@@ -1171,15 +1204,16 @@ impl Thread {
|
||||
}
|
||||
|
||||
match reply {
|
||||
Ok(work) => {
|
||||
inner.push_work(work);
|
||||
}
|
||||
Ok(work) => inner.push_work(work),
|
||||
Err(code) => inner.push_reply_work(code),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Notify the thread now that we've released the inner lock.
|
||||
self.work_condvar.notify_sync();
|
||||
if matches!(ret, PushWorkRes::OkNotifyPoll) {
|
||||
self.process.notify_poll(true);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1232,7 +1266,7 @@ impl Thread {
|
||||
info.from_pid = self.process.task.pid();
|
||||
info.from_tid = self.id;
|
||||
info.code = td.transaction_data.code;
|
||||
info.flags = td.transaction_data.flags;
|
||||
info.flags = TransactionFlags::from_bits(td.transaction_data.flags);
|
||||
info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize);
|
||||
info.data_size = td.transaction_data.data_size as usize;
|
||||
info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize);
|
||||
@@ -1274,16 +1308,36 @@ impl Thread {
|
||||
ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
|
||||
}
|
||||
|
||||
pr_warn!(
|
||||
"{}:{} transaction to {} failed: {err:?}",
|
||||
info.from_pid,
|
||||
info.from_tid,
|
||||
info.to_pid
|
||||
binder_debug!(
|
||||
FailedTransaction,
|
||||
"transaction {} to {}:{} failed {:?}, code {} size {}-{}",
|
||||
if info.is_reply {
|
||||
"reply"
|
||||
} else if info.is_oneway() {
|
||||
"async"
|
||||
} else {
|
||||
"call"
|
||||
},
|
||||
info.to_pid,
|
||||
info.to_tid,
|
||||
err,
|
||||
info.code,
|
||||
info.data_size,
|
||||
info.offsets_size
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.oneway_spam_suspect {
|
||||
// If this is both a oneway spam suspect and a failure, we report it twice. This is
|
||||
// useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN.
|
||||
info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx);
|
||||
}
|
||||
if info.reply != 0 {
|
||||
info.report_netlink(info.reply, &self.process.ctx);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1294,7 +1348,10 @@ impl Thread {
|
||||
// TODO: We need to ensure that there isn't a pending transaction in the work queue. How
|
||||
// could this happen?
|
||||
let top = self.top_of_transaction_stack()?;
|
||||
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
|
||||
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(
|
||||
BR_TRANSACTION_COMPLETE,
|
||||
self.process.task.pid(),
|
||||
))?;
|
||||
let completion = list_completion.clone_arc();
|
||||
let transaction = Transaction::new(node_ref, top, self, info)?;
|
||||
|
||||
@@ -1303,7 +1360,7 @@ impl Thread {
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
if !transaction.is_stacked_on(&inner.current_transaction) {
|
||||
pr_warn!("Transaction stack changed during transaction!");
|
||||
binder_debug!(UserError, "got new transaction with bad transaction stack");
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
inner.current_transaction = Some(transaction.clone_arc());
|
||||
@@ -1326,8 +1383,18 @@ impl Thread {
|
||||
}
|
||||
|
||||
fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
|
||||
let orig = self.inner.lock().pop_transaction_to_reply(self)?;
|
||||
let orig = match self.inner.lock().pop_transaction_to_reply(self) {
|
||||
Ok(orig) => orig,
|
||||
Err(err) => {
|
||||
binder_debug!(UserError, "got reply transaction with no transaction stack");
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
if !orig.from.is_current_transaction(&orig) {
|
||||
binder_debug!(
|
||||
UserError,
|
||||
"got reply transaction with bad transaction stack"
|
||||
);
|
||||
return Err(EINVAL.into());
|
||||
}
|
||||
|
||||
@@ -1336,11 +1403,15 @@ impl Thread {
|
||||
|
||||
// We need to complete the transaction even if we cannot complete building the reply.
|
||||
let out = (|| -> BinderResult<_> {
|
||||
let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
|
||||
let completion = DTRWrap::arc_try_new(DeliverCode::new(
|
||||
BR_TRANSACTION_COMPLETE,
|
||||
self.process.task.pid(),
|
||||
))?;
|
||||
let process = orig.from.process.clone();
|
||||
let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
|
||||
let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds);
|
||||
let reply = Transaction::new_reply(self, process, info, allow_fds)?;
|
||||
self.inner.lock().push_work(completion);
|
||||
// Not notifying: Reply to current thread.
|
||||
let _ = self.inner.lock().push_work(completion);
|
||||
orig.from.deliver_reply(Ok(reply), &orig, None);
|
||||
Ok(())
|
||||
})()
|
||||
@@ -1354,11 +1425,11 @@ impl Thread {
|
||||
info.from_tid,
|
||||
info.to_pid
|
||||
);
|
||||
|
||||
let param = err.source.as_ref().map_or(0, |e| e.to_errno());
|
||||
let ee = ExtendedError::new(info.debug_id as u32, err.reply, param);
|
||||
orig.from
|
||||
.deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee));
|
||||
info.reply = BR_FAILED_REPLY;
|
||||
err.reply = BR_TRANSACTION_COMPLETE;
|
||||
err
|
||||
});
|
||||
@@ -1376,9 +1447,11 @@ impl Thread {
|
||||
} else {
|
||||
BR_TRANSACTION_COMPLETE
|
||||
};
|
||||
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
|
||||
let list_completion =
|
||||
DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?;
|
||||
let completion = list_completion.clone_arc();
|
||||
self.inner.lock().push_work(list_completion);
|
||||
// Not notifying: Reply to current thread.
|
||||
let _ = self.inner.lock().push_work(list_completion);
|
||||
match transaction.submit(info) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
@@ -1392,7 +1465,7 @@ impl Thread {
|
||||
let write_start = req.write_buffer.wrapping_add(req.write_consumed);
|
||||
let write_len = req.write_size.saturating_sub(req.write_consumed);
|
||||
let mut reader =
|
||||
UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader();
|
||||
UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader();
|
||||
|
||||
while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
|
||||
let before = reader.len();
|
||||
@@ -1463,7 +1536,7 @@ impl Thread {
|
||||
let read_start = req.read_buffer.wrapping_add(req.read_consumed);
|
||||
let read_len = req.read_size.saturating_sub(req.read_consumed);
|
||||
let mut writer = BinderReturnWriter::new(
|
||||
UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
|
||||
UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(),
|
||||
self,
|
||||
);
|
||||
let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
|
||||
@@ -1527,9 +1600,11 @@ impl Thread {
|
||||
|
||||
// Write BR_SPAWN_LOOPER if the process needs more threads for its pool.
|
||||
if has_noop_placeholder && in_pool && self.process.needs_thread() {
|
||||
let mut writer =
|
||||
UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _)
|
||||
.writer();
|
||||
let mut writer = UserSlice::new(
|
||||
UserPtr::from_addr(req.read_buffer as usize),
|
||||
req.read_size as usize,
|
||||
)
|
||||
.writer();
|
||||
writer.write(&BR_SPAWN_LOOPER)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1578,16 +1653,15 @@ impl Thread {
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) {
|
||||
table.register_wait(file, &self.work_condvar);
|
||||
pub(crate) fn poll(&self) -> Result<(bool, u32)> {
|
||||
let mut inner = self.inner.lock();
|
||||
(inner.should_use_process_work_queue(), inner.poll())
|
||||
Ok((inner.should_use_process_work_queue(), inner.poll()))
|
||||
}
|
||||
|
||||
/// Make the call to `get_work` or `get_work_local` return immediately, if any.
|
||||
pub(crate) fn exit_looper(&self) {
|
||||
let mut inner = self.inner.lock();
|
||||
let should_notify = inner.looper_flags & LOOPER_WAITING != 0;
|
||||
let should_notify = inner.looper_flags.contains(LooperFlag::Waiting);
|
||||
if should_notify {
|
||||
inner.looper_need_return = true;
|
||||
}
|
||||
@@ -1598,26 +1672,9 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
|
||||
// Determine if we need to notify. This requires the lock.
|
||||
let inner = self.inner.lock();
|
||||
let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
|
||||
drop(inner);
|
||||
|
||||
// Now that the lock is no longer held, notify the waiters if we have to.
|
||||
if notify {
|
||||
if sync {
|
||||
self.work_condvar.notify_sync();
|
||||
} else {
|
||||
self.work_condvar.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn release(self: &Arc<Self>) {
|
||||
self.inner.lock().is_dead = true;
|
||||
|
||||
//self.work_condvar.clear();
|
||||
self.unwind_transaction_stack();
|
||||
|
||||
// Cancel all pending work items.
|
||||
@@ -1630,14 +1687,16 @@ impl Thread {
|
||||
#[pin_data]
|
||||
struct ThreadError {
|
||||
error_code: Atomic<u32>,
|
||||
pid: i32,
|
||||
#[pin]
|
||||
links_track: AtomicTracker,
|
||||
}
|
||||
|
||||
impl ThreadError {
|
||||
fn try_new() -> Result<DArc<Self>> {
|
||||
fn try_new(pid: i32) -> Result<DArc<Self>> {
|
||||
DTRWrap::arc_pin_init(pin_init!(Self {
|
||||
error_code: Atomic::new(BR_OK),
|
||||
pid,
|
||||
links_track <- AtomicTracker::new(),
|
||||
}))
|
||||
.map(ListArc::into_arc)
|
||||
@@ -1664,7 +1723,16 @@ impl DeliverToRead for ThreadError {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cancel(self: DArc<Self>) {}
|
||||
fn cancel(self: DArc<Self>) {
|
||||
let code = self.error_code.load(Relaxed);
|
||||
if code != BR_OK {
|
||||
binder_debug!(
|
||||
pid = self.pid,
|
||||
DeadTransaction,
|
||||
"undelivered TRANSACTION_ERROR: {code}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
false
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use crate::transaction::Transaction;
|
||||
|
||||
use core::ptr;
|
||||
|
||||
use kernel::bindings::{rust_binder_transaction, task_struct};
|
||||
use kernel::error::Result;
|
||||
use kernel::ffi::{c_int, c_uint, c_ulong};
|
||||
@@ -26,7 +28,7 @@ declare_trace! {
|
||||
|
||||
#[inline]
|
||||
fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
|
||||
t as *const Transaction as rust_binder_transaction
|
||||
ptr::from_ref(t).cast_mut().cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Copyright (C) 2025 Google LLC.
|
||||
|
||||
use kernel::{
|
||||
net::netlink::GENLMSG_DEFAULT_SIZE,
|
||||
prelude::*,
|
||||
seq_file::SeqFile,
|
||||
seq_print,
|
||||
@@ -11,12 +12,14 @@ use kernel::{
|
||||
task::{Kuid, Pid},
|
||||
time::{Instant, Monotonic},
|
||||
types::ScopeGuard,
|
||||
uapi,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
allocation::{Allocation, TranslatedFds},
|
||||
defs::*,
|
||||
error::{BinderError, BinderResult},
|
||||
netlink::Report,
|
||||
node::{Node, NodeRef},
|
||||
process::{Process, ProcessInner},
|
||||
ptr_align,
|
||||
@@ -24,6 +27,33 @@ use crate::{
|
||||
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
|
||||
};
|
||||
|
||||
kernel::impl_flags!(
|
||||
/// Represents multiple transaction flags.
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)]
|
||||
pub struct TransactionFlags(u32);
|
||||
|
||||
/// Represents a single transaction flag.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TransactionFlag {
|
||||
OneWay = TF_ONE_WAY,
|
||||
AcceptFds = TF_ACCEPT_FDS,
|
||||
ClearBuf = TF_CLEAR_BUF,
|
||||
UpdateTxn = TF_UPDATE_TXN,
|
||||
}
|
||||
);
|
||||
|
||||
impl TransactionFlags {
|
||||
/// Creates a `TransactionFlags` from a raw `u32` value.
|
||||
pub(crate) fn from_bits(bits: u32) -> Self {
|
||||
Self(bits)
|
||||
}
|
||||
|
||||
/// Checks if the Oneway flag is set.
|
||||
pub(crate) fn is_oneway(self) -> bool {
|
||||
self.contains(TransactionFlag::OneWay)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Zeroable)]
|
||||
pub(crate) struct TransactionInfo {
|
||||
pub(crate) from_pid: Pid,
|
||||
@@ -31,7 +61,7 @@ pub(crate) struct TransactionInfo {
|
||||
pub(crate) to_pid: Pid,
|
||||
pub(crate) to_tid: Pid,
|
||||
pub(crate) code: u32,
|
||||
pub(crate) flags: u32,
|
||||
pub(crate) flags: TransactionFlags,
|
||||
pub(crate) data_ptr: UserPtr,
|
||||
pub(crate) data_size: usize,
|
||||
pub(crate) offsets_ptr: UserPtr,
|
||||
@@ -48,7 +78,45 @@ pub(crate) struct TransactionInfo {
|
||||
impl TransactionInfo {
|
||||
#[inline]
|
||||
pub(crate) fn is_oneway(&self) -> bool {
|
||||
self.flags & TF_ONE_WAY != 0
|
||||
self.flags.is_oneway()
|
||||
}
|
||||
|
||||
pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) {
|
||||
if let Err(err) = self.report_netlink_inner(reply, ctx) {
|
||||
pr_warn!(
|
||||
"{}:{} netlink report failed: {err:?}\n",
|
||||
self.from_pid,
|
||||
self.from_tid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result {
|
||||
if !Report::has_listeners() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?;
|
||||
|
||||
report.error(reply)?;
|
||||
report.context(&ctx.name)?;
|
||||
report.from_pid(self.from_pid as u32)?;
|
||||
report.from_tid(self.from_tid as u32)?;
|
||||
if self.to_pid != 0 {
|
||||
report.to_pid(self.to_pid as u32)?;
|
||||
}
|
||||
if self.to_tid != 0 {
|
||||
report.to_tid(self.to_tid as u32)?;
|
||||
}
|
||||
|
||||
if self.is_reply {
|
||||
report.is_reply()?;
|
||||
}
|
||||
report.flags(u32::from(self.flags))?;
|
||||
report.code(self.code)?;
|
||||
report.data_size(self.data_size as u32)?;
|
||||
|
||||
report.multicast(0, GFP_KERNEL)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +142,7 @@ pub(crate) struct Transaction {
|
||||
allocation: SpinLock<Option<Allocation>>,
|
||||
is_outstanding: Atomic<bool>,
|
||||
code: u32,
|
||||
pub(crate) flags: u32,
|
||||
pub(crate) flags: TransactionFlags,
|
||||
data_size: usize,
|
||||
offsets_size: usize,
|
||||
data_address: usize,
|
||||
@@ -120,7 +188,7 @@ impl Transaction {
|
||||
}
|
||||
alloc.set_info_oneway_node(node_ref.node.clone());
|
||||
}
|
||||
if info.flags & TF_CLEAR_BUF != 0 {
|
||||
if info.flags.contains(TransactionFlag::ClearBuf) {
|
||||
alloc.set_info_clear_on_drop();
|
||||
}
|
||||
let target_node = node_ref.node.clone();
|
||||
@@ -160,7 +228,7 @@ impl Transaction {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
if info.flags & TF_CLEAR_BUF != 0 {
|
||||
if info.flags.contains(TransactionFlag::ClearBuf) {
|
||||
alloc.set_info_clear_on_drop();
|
||||
}
|
||||
Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
|
||||
@@ -193,7 +261,7 @@ impl Transaction {
|
||||
self.from.id,
|
||||
self.to.task.pid(),
|
||||
self.code,
|
||||
self.flags,
|
||||
u32::from(self.flags),
|
||||
self.start_time.elapsed().as_millis(),
|
||||
);
|
||||
if let Some(target_node) = &self.target_node {
|
||||
@@ -272,7 +340,7 @@ impl Transaction {
|
||||
let _t_outdated;
|
||||
let _oneway_node;
|
||||
|
||||
let oneway = self.flags & TF_ONE_WAY != 0;
|
||||
let oneway = self.flags.is_oneway();
|
||||
let process = self.to.clone();
|
||||
let mut process_inner = process.inner.lock();
|
||||
|
||||
@@ -283,7 +351,7 @@ impl Transaction {
|
||||
crate::trace::trace_transaction(false, &self, None);
|
||||
if process_inner.is_frozen.is_frozen() {
|
||||
process_inner.async_recv = true;
|
||||
if self.flags & TF_UPDATE_TXN != 0 {
|
||||
if self.flags.contains(TransactionFlag::UpdateTxn) {
|
||||
if let Some(t_outdated) =
|
||||
target_node.take_outdated_transaction(&self, &mut process_inner)
|
||||
{
|
||||
@@ -330,11 +398,15 @@ impl Transaction {
|
||||
crate::trace::trace_transaction(false, &self, Some(&thread.task));
|
||||
match thread.push_work(self) {
|
||||
PushWorkRes::Ok => Ok(()),
|
||||
PushWorkRes::OkNotifyPoll => {
|
||||
process.notify_poll(true);
|
||||
Ok(())
|
||||
}
|
||||
PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
|
||||
}
|
||||
} else {
|
||||
crate::trace::trace_transaction(false, &self, None);
|
||||
process_inner.push_work(self)
|
||||
process_inner.push_work(&process, self)
|
||||
};
|
||||
drop(process_inner);
|
||||
|
||||
@@ -354,7 +426,8 @@ impl Transaction {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) {
|
||||
let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn;
|
||||
if !(self.flags.contains_all(required) && old.flags.contains_all(required)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -391,7 +464,7 @@ impl DeliverToRead for Transaction {
|
||||
writer: &mut BinderReturnWriter<'_>,
|
||||
) -> Result<bool> {
|
||||
let send_failed_reply = ScopeGuard::new(|| {
|
||||
if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
|
||||
if self.target_node.is_some() && !self.flags.is_oneway() {
|
||||
let reply = Err(BR_FAILED_REPLY);
|
||||
self.from.deliver_reply(reply, &self, None);
|
||||
}
|
||||
@@ -403,6 +476,14 @@ impl DeliverToRead for Transaction {
|
||||
} else {
|
||||
// On failure to process the list, we send a reply back to the sender and ignore the
|
||||
// transaction on the recipient.
|
||||
binder_debug!(
|
||||
FailedTransaction,
|
||||
"transaction {} to {} failed, fd fixups failed, size {}-{}",
|
||||
self.debug_id,
|
||||
self.to.task.pid(),
|
||||
self.data_size,
|
||||
self.offsets_size
|
||||
);
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
@@ -410,20 +491,21 @@ impl DeliverToRead for Transaction {
|
||||
let tr = tr_sec.tr_data();
|
||||
if let Some(target_node) = &self.target_node {
|
||||
let (ptr, cookie) = target_node.get_id();
|
||||
tr.target.ptr = ptr as _;
|
||||
tr.cookie = cookie as _;
|
||||
tr.target.ptr = ptr as uapi::binder_uintptr_t;
|
||||
tr.cookie = cookie as uapi::binder_uintptr_t;
|
||||
};
|
||||
tr.code = self.code;
|
||||
tr.flags = self.flags;
|
||||
tr.data_size = self.data_size as _;
|
||||
tr.data.ptr.buffer = self.data_address as _;
|
||||
tr.offsets_size = self.offsets_size as _;
|
||||
tr.flags = u32::from(self.flags);
|
||||
tr.data_size = self.data_size as uapi::binder_size_t;
|
||||
tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t;
|
||||
tr.offsets_size = self.offsets_size as uapi::binder_size_t;
|
||||
if tr.offsets_size > 0 {
|
||||
tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _;
|
||||
tr.data.ptr.offsets =
|
||||
(self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t;
|
||||
}
|
||||
tr.sender_euid = self.sender_euid.into_uid_in_current_ns();
|
||||
tr.sender_pid = 0;
|
||||
if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
|
||||
if self.target_node.is_some() && !self.flags.is_oneway() {
|
||||
// Not a reply and not one-way.
|
||||
tr.sender_pid = self.from.process.pid_in_current_ns();
|
||||
}
|
||||
@@ -475,16 +557,23 @@ impl DeliverToRead for Transaction {
|
||||
drop(allocation);
|
||||
|
||||
// If this is not a reply or oneway transaction, then send a dead reply.
|
||||
if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
|
||||
if self.target_node.is_some() && !self.flags.is_oneway() {
|
||||
let reply = Err(BR_DEAD_REPLY);
|
||||
self.from.deliver_reply(reply, &self, None);
|
||||
} else {
|
||||
binder_debug!(
|
||||
pid = self.to.task.pid(),
|
||||
DeadTransaction,
|
||||
"undelivered transaction {}, process died",
|
||||
self.debug_id
|
||||
);
|
||||
}
|
||||
|
||||
self.drop_outstanding_txn();
|
||||
}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
self.flags & TF_ONE_WAY == 0
|
||||
!self.flags.is_oneway()
|
||||
}
|
||||
|
||||
fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> {
|
||||
|
||||
@@ -401,9 +401,14 @@ static int cache_setup_properties(unsigned int cpu)
|
||||
else if (!acpi_disabled)
|
||||
ret = cache_setup_acpi(cpu);
|
||||
|
||||
// Assume there is no cache information available in DT/ACPI from now.
|
||||
if (ret && use_arch_cache_info())
|
||||
/*
|
||||
* No DT/ACPI cache nodes; fall back to arch-derived topology (e.g.
|
||||
* arm64 CLIDR_EL1) and clear the error to avoid a spurious warning.
|
||||
*/
|
||||
if (ret && use_arch_cache_info()) {
|
||||
use_arch_info = true;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+44
-12
@@ -1026,26 +1026,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
|
||||
struct mhi_ep_chan *mhi_chan;
|
||||
int i;
|
||||
|
||||
/* Stop all the channels */
|
||||
/* Disable all the channels to prevent new transfers */
|
||||
for (i = 0; i < mhi_cntrl->max_chan; i++) {
|
||||
mhi_chan = &mhi_cntrl->mhi_chan[i];
|
||||
if (!mhi_chan->ring.started)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
/* Drain ring workers and in-flight transfers before notifying disconnect */
|
||||
flush_workqueue(mhi_cntrl->wq);
|
||||
if (mhi_cntrl->flush_async)
|
||||
mhi_cntrl->flush_async(mhi_cntrl);
|
||||
|
||||
/* Send channel disconnect status to client drivers */
|
||||
for (i = 0; i < mhi_cntrl->max_chan; i++) {
|
||||
mhi_chan = &mhi_cntrl->mhi_chan[i];
|
||||
if (!mhi_chan->ring.started)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
/* Send channel disconnect status to client drivers */
|
||||
if (mhi_chan->xfer_cb) {
|
||||
result.transaction_status = -ENOTCONN;
|
||||
result.bytes_xferd = 0;
|
||||
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
|
||||
}
|
||||
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
flush_workqueue(mhi_cntrl->wq);
|
||||
|
||||
/* Destroy devices associated with all channels */
|
||||
device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
|
||||
|
||||
@@ -1340,14 +1351,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
|
||||
ret = dev_set_name(&mhi_dev->dev, "%s_%s",
|
||||
dev_name(&mhi_cntrl->mhi_dev->dev),
|
||||
mhi_dev->name);
|
||||
if (ret) {
|
||||
put_device(&mhi_dev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_put_channels;
|
||||
|
||||
ret = device_add(&mhi_dev->dev);
|
||||
if (ret)
|
||||
put_device(&mhi_dev->dev);
|
||||
goto err_put_channels;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_channels:
|
||||
put_device(&mhi_dev->dev); /* DL channel reference */
|
||||
put_device(&mhi_dev->dev); /* UL channel reference */
|
||||
put_device(&mhi_dev->dev); /* device_initialize() reference */
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1614,6 +1630,7 @@ static void mhi_ep_remove(struct device *dev)
|
||||
{
|
||||
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
|
||||
struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
|
||||
struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_result result = {};
|
||||
struct mhi_ep_chan *mhi_chan;
|
||||
int dir;
|
||||
@@ -1622,6 +1639,22 @@ static void mhi_ep_remove(struct device *dev)
|
||||
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
|
||||
return;
|
||||
|
||||
/* Disable the channels to prevent new transfers */
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
|
||||
|
||||
if (!mhi_chan)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
/* Flush in-flight transfers before notifying disconnect */
|
||||
if (mhi_cntrl->flush_async)
|
||||
mhi_cntrl->flush_async(mhi_cntrl);
|
||||
|
||||
/* Disconnect the channels associated with the driver */
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
|
||||
@@ -1637,7 +1670,6 @@ static void mhi_ep_remove(struct device *dev)
|
||||
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
|
||||
}
|
||||
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mhi_chan->xfer_cb = NULL;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
@@ -1029,7 +1029,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
|
||||
if (mhi_cntrl->edl_trigger) {
|
||||
ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
|
||||
if (ret)
|
||||
goto err_release_dev;
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
mhi_cntrl->mhi_dev = mhi_dev;
|
||||
@@ -1038,6 +1038,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
|
||||
|
||||
return 0;
|
||||
|
||||
err_del_dev:
|
||||
device_del(&mhi_dev->dev);
|
||||
err_release_dev:
|
||||
put_device(&mhi_dev->dev);
|
||||
error_setup_irq:
|
||||
|
||||
@@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state);
|
||||
|
||||
void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
int __maybe_unused ret;
|
||||
u32 tmp;
|
||||
|
||||
if (mhi_cntrl->reset) {
|
||||
mhi_cntrl->reset(mhi_cntrl);
|
||||
return;
|
||||
@@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
|
||||
/* Generic MHI SoC reset */
|
||||
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
|
||||
MHI_SOC_RESET_REQ);
|
||||
/* Flush the posted write to the device (ignore return value) */
|
||||
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
|
||||
&tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_soc_reset);
|
||||
|
||||
|
||||
@@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
|
||||
static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
|
||||
@@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
|
||||
static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
|
||||
@@ -1395,6 +1399,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width);
|
||||
mhi_cntrl->fw_image = info->fw;
|
||||
mhi_cntrl->edl_image = info->edl;
|
||||
mhi_cntrl->no_m3 = info->no_m3;
|
||||
|
||||
mhi_cntrl->read_reg = mhi_pci_read_reg;
|
||||
mhi_cntrl->write_reg = mhi_pci_write_reg;
|
||||
|
||||
+45
-26
@@ -651,21 +651,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
|
||||
|
||||
/* Trigger MHI RESET so that the device will not access host memory */
|
||||
if (reset_device) {
|
||||
u32 in_reset = -1;
|
||||
unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);
|
||||
|
||||
dev_dbg(dev, "Triggering MHI Reset in device\n");
|
||||
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
|
||||
|
||||
/* Wait for the reset bit to be cleared by the device */
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_read_reg_field(mhi_cntrl,
|
||||
mhi_cntrl->regs,
|
||||
MHICTRL,
|
||||
MHICTRL_RESET_MASK,
|
||||
&in_reset) ||
|
||||
!in_reset, timeout);
|
||||
if (!ret || in_reset) {
|
||||
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
|
||||
MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms);
|
||||
if (ret) {
|
||||
dev_err(dev, "Device failed to exit MHI Reset state\n");
|
||||
write_lock_irq(&mhi_cntrl->pm_lock);
|
||||
cur_state = mhi_tryset_pm_state(mhi_cntrl,
|
||||
@@ -922,22 +914,39 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set MHI to M3 and wait for completion */
|
||||
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
dev_dbg(dev, "Waiting for M3 completion\n");
|
||||
/*
|
||||
* For devices without M3 support, just set the host state to M3. This
|
||||
* host transition is needed to prevent the client drivers from
|
||||
* accessing the device during suspend.
|
||||
*/
|
||||
if (mhi_cntrl->no_m3) {
|
||||
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3);
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
if (new_state != MHI_PM_M3) {
|
||||
dev_err(dev,
|
||||
"Error setting to PM state: %s from: %s\n",
|
||||
to_mhi_pm_state_str(MHI_PM_M3),
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state));
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
/* Set MHI to M3 and wait for completion */
|
||||
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
dev_dbg(dev, "Waiting for M3 completion\n");
|
||||
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->dev_state == MHI_STATE_M3 ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->dev_state == MHI_STATE_M3 ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
dev_err(dev,
|
||||
"Did not enter M3 state, MHI state: %s, PM state: %s\n",
|
||||
mhi_state_str(mhi_cntrl->dev_state),
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state));
|
||||
return -EIO;
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
dev_err(dev,
|
||||
"Did not enter M3 state, MHI state: %s, PM state: %s\n",
|
||||
mhi_state_str(mhi_cntrl->dev_state),
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state));
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Notify clients about entering LPM */
|
||||
@@ -969,7 +978,8 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
return -EIO;
|
||||
|
||||
if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
|
||||
if (!mhi_cntrl->no_m3 &&
|
||||
mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
|
||||
dev_warn(dev, "Resuming from non M3 state (%s)\n",
|
||||
mhi_state_str(mhi_get_mhi_state(mhi_cntrl)));
|
||||
if (!force)
|
||||
@@ -995,6 +1005,15 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* For devices without M3 support, just move the host back to M0
|
||||
* directly.
|
||||
*/
|
||||
if (mhi_cntrl->no_m3) {
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
return mhi_pm_m0_transition(mhi_cntrl);
|
||||
}
|
||||
|
||||
/* Set MHI to M0 and wait for completion */
|
||||
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
|
||||
+5
-2
@@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
|
||||
|
||||
sysfs_bin_attr_init(res_attr);
|
||||
|
||||
cdx_dev->res_attr[num] = res_attr;
|
||||
sprintf(res_attr_name, "resource%d", num);
|
||||
|
||||
res_attr->mmap = cdx_mmap_resource;
|
||||
@@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
|
||||
res_attr->size = cdx_resource_len(cdx_dev, num);
|
||||
res_attr->private = (void *)(unsigned long)num;
|
||||
ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(res_attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cdx_dev->res_attr[num] = res_attr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,6 @@ static int __op_panel_update_display(void)
|
||||
static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
|
||||
size_t len, loff_t *f_pos)
|
||||
{
|
||||
loff_t f_pos_prev = *f_pos;
|
||||
ssize_t ret;
|
||||
int rc;
|
||||
|
||||
@@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
|
||||
if (rc != OPAL_SUCCESS) {
|
||||
pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n",
|
||||
rc);
|
||||
*f_pos = f_pos_prev;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode)
|
||||
return IEEE1284_PH_FWD_IDLE;
|
||||
}
|
||||
|
||||
static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
|
||||
static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
long to_jiffies;
|
||||
|
||||
if ((tv_sec < 0) || (tv_usec < 0))
|
||||
if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
to_jiffies = usecs_to_jiffies(tv_usec);
|
||||
to_jiffies += tv_sec * HZ;
|
||||
ts.tv_sec = tv_sec;
|
||||
ts.tv_nsec = tv_usec * NSEC_PER_USEC;
|
||||
to_jiffies = timespec64_to_jiffies(&ts);
|
||||
if (to_jiffies <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
+18
-18
@@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
static ssize_t store_received_ref_clk3a(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_received_ref_clk3b(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_enable_clk3b_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_select_amcb1_transmit_clock(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_select_redundant_clock(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_select_ref_frequency(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_filter_select(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
|
||||
static ssize_t store_hardware_switching_mode(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_hardware_switching(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
|
||||
static ssize_t store_refalign (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned long flags;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
@@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
|
||||
static ssize_t store_mode_select (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
|
||||
static ssize_t store_reset (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
@@ -506,11 +506,8 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
|
||||
ret = devm_request_irq(dev, irq, tpm_ioserirq_handler,
|
||||
IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
|
||||
chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
|
||||
irq);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
intmask |= TPM_INTF_CMD_READY_INT
|
||||
| TPM_INTF_STS_VALID_INT
|
||||
|
||||
@@ -574,8 +574,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client)
|
||||
dev_name(&chip->dev),
|
||||
chip);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s() Unable to request irq: %d for use\n",
|
||||
__func__, priv->irq);
|
||||
priv->irq = 0;
|
||||
} else {
|
||||
chip->flags |= TPM_CHIP_FLAG_IRQ;
|
||||
|
||||
@@ -751,10 +751,8 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
|
||||
rc = devm_request_irq(dev, client->irq, tpm_cr50_i2c_int_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
|
||||
dev->driver->name, chip);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "Failed to probe IRQ %d\n", client->irq);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
priv->irq = client->irq;
|
||||
} else {
|
||||
|
||||
+153
-43
@@ -157,6 +157,12 @@ struct ports_device {
|
||||
|
||||
/* Major number for this device. Ports will be created as minors. */
|
||||
int chr_major;
|
||||
|
||||
/*
|
||||
* Set to true during PM freeze to block TX paths that may race
|
||||
* with virtqueue teardown (e.g. hvc put_chars with no_console_suspend).
|
||||
*/
|
||||
bool pm_freezing;
|
||||
};
|
||||
|
||||
struct port_stats {
|
||||
@@ -304,6 +310,12 @@ out:
|
||||
return port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a port by the virtqueue and returns a pointer to struct port
|
||||
* with the reference count incremented.
|
||||
*
|
||||
* Callers MUST decrement it when finished.
|
||||
*/
|
||||
static struct port *find_port_by_vq(struct ports_device *portdev,
|
||||
struct virtqueue *vq)
|
||||
{
|
||||
@@ -312,8 +324,10 @@ static struct port *find_port_by_vq(struct ports_device *portdev,
|
||||
|
||||
spin_lock_irqsave(&portdev->ports_lock, flags);
|
||||
list_for_each_entry(port, &portdev->ports, list)
|
||||
if (port->in_vq == vq || port->out_vq == vq)
|
||||
if (port->in_vq == vq || port->out_vq == vq) {
|
||||
kref_get(&port->kref);
|
||||
goto out;
|
||||
}
|
||||
port = NULL;
|
||||
out:
|
||||
spin_unlock_irqrestore(&portdev->ports_lock, flags);
|
||||
@@ -402,7 +416,7 @@ static void reclaim_dma_bufs(void)
|
||||
}
|
||||
|
||||
static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size,
|
||||
int pages)
|
||||
int pages, gfp_t gfp)
|
||||
{
|
||||
struct port_buffer *buf;
|
||||
|
||||
@@ -436,11 +450,10 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size
|
||||
|
||||
/* Increase device refcnt to avoid freeing it */
|
||||
get_device(buf->dev);
|
||||
buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
|
||||
GFP_KERNEL);
|
||||
buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, gfp);
|
||||
} else {
|
||||
buf->dev = NULL;
|
||||
buf->buf = kmalloc(buf_size, GFP_KERNEL);
|
||||
buf->buf = kmalloc(buf_size, gfp);
|
||||
}
|
||||
|
||||
if (!buf->buf)
|
||||
@@ -595,27 +608,46 @@ static void reclaim_consumed_buffers(struct port *port)
|
||||
|
||||
static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
|
||||
int nents, size_t in_count,
|
||||
void *data, bool nonblock)
|
||||
struct port_buffer *buf, bool nonblock)
|
||||
{
|
||||
struct virtqueue *out_vq;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
unsigned int len;
|
||||
|
||||
out_vq = port->out_vq;
|
||||
struct ports_device *portdev;
|
||||
|
||||
spin_lock_irqsave(&port->outvq_lock, flags);
|
||||
|
||||
portdev = READ_ONCE(port->portdev);
|
||||
|
||||
if (!portdev) {
|
||||
in_count = 0;
|
||||
goto free_and_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check freeze flag under the lock so that the flag check and
|
||||
* virtqueue_add_outbuf() are atomic with respect to
|
||||
* remove_port_data() which also takes outvq_lock. This
|
||||
* guarantees that once remove_port_data() returns, no new
|
||||
* buffers can be added before remove_vqs() tears down the vq.
|
||||
* Pairs with smp_store_release() in virtcons_freeze/restore.
|
||||
*/
|
||||
if (smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */
|
||||
goto free_and_done;
|
||||
|
||||
out_vq = port->out_vq;
|
||||
|
||||
reclaim_consumed_buffers(port);
|
||||
|
||||
err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
|
||||
err = virtqueue_add_outbuf(out_vq, sg, nents, buf, GFP_ATOMIC);
|
||||
|
||||
/* Tell Host to go! */
|
||||
virtqueue_kick(out_vq);
|
||||
|
||||
if (err) {
|
||||
in_count = 0;
|
||||
goto done;
|
||||
goto free_and_done;
|
||||
}
|
||||
|
||||
if (out_vq->num_free == 0)
|
||||
@@ -632,10 +664,19 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
|
||||
* buffer and relax the spinning requirement. The downside is
|
||||
* we need to kmalloc a GFP_ATOMIC buffer each time the
|
||||
* console driver writes something out.
|
||||
*
|
||||
* Spin until host returns the buffer.
|
||||
* Capture the returned buf so we can free it.
|
||||
* If broken, buf == NULL and buf stays in the vq;
|
||||
* remove_vqs() will call virtqueue_detach_unused_buf() -> free_buf().
|
||||
*/
|
||||
while (!virtqueue_get_buf(out_vq, &len)
|
||||
while (!(buf = virtqueue_get_buf(out_vq, &len))
|
||||
&& !virtqueue_is_broken(out_vq))
|
||||
cpu_relax();
|
||||
|
||||
free_and_done:
|
||||
if (buf)
|
||||
free_buf(buf, false);
|
||||
done:
|
||||
spin_unlock_irqrestore(&port->outvq_lock, flags);
|
||||
|
||||
@@ -816,14 +857,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||
|
||||
count = min((size_t)(32 * 1024), count);
|
||||
|
||||
buf = alloc_buf(port->portdev->vdev, count, 0);
|
||||
buf = alloc_buf(port->portdev->vdev, count, 0, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_from_user(buf->buf, ubuf, count);
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto free_buf;
|
||||
free_buf(buf, true);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -835,15 +876,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||
*/
|
||||
nonblock = true;
|
||||
sg_init_one(sg, buf->buf, count);
|
||||
ret = __send_to_port(port, sg, 1, count, buf, nonblock);
|
||||
|
||||
if (nonblock && ret > 0)
|
||||
goto out;
|
||||
|
||||
free_buf:
|
||||
free_buf(buf, true);
|
||||
out:
|
||||
return ret;
|
||||
return __send_to_port(port, sg, 1, count, buf, nonblock);
|
||||
}
|
||||
|
||||
struct sg_list {
|
||||
@@ -932,7 +965,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
|
||||
goto error_out;
|
||||
|
||||
occupancy = pipe_buf_usage(pipe);
|
||||
buf = alloc_buf(port->portdev->vdev, 0, occupancy);
|
||||
buf = alloc_buf(port->portdev->vdev, 0, occupancy, GFP_KERNEL);
|
||||
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
@@ -946,11 +979,12 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
|
||||
sg_init_table(sgl.sg, sgl.size);
|
||||
ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
|
||||
pipe_unlock(pipe);
|
||||
|
||||
if (likely(ret > 0))
|
||||
ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
|
||||
|
||||
if (unlikely(ret <= 0))
|
||||
else
|
||||
free_buf(buf, true);
|
||||
|
||||
return ret;
|
||||
|
||||
error_out:
|
||||
@@ -1108,21 +1142,50 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count)
|
||||
{
|
||||
struct port *port;
|
||||
struct scatterlist sg[1];
|
||||
void *data;
|
||||
int ret;
|
||||
struct port_buffer *pbuf;
|
||||
struct ports_device *portdev;
|
||||
|
||||
port = find_port_by_vtermno(vtermno);
|
||||
if (!port)
|
||||
return -EPIPE;
|
||||
|
||||
data = kmemdup(buf, count, GFP_ATOMIC);
|
||||
if (!data)
|
||||
/*
|
||||
* Silently drop output in two cases, both by returning count so
|
||||
* that the hvc layer does not spin-retry:
|
||||
*
|
||||
* 1. Device hot-unplug (!portdev): portdev was NULLed by
|
||||
* unplug_port() after hvc_remove() was already called, so
|
||||
* the hvc layer will stop invoking put_chars() very soon.
|
||||
* Returning count avoids a pointless retry loop in the
|
||||
* interim.
|
||||
*
|
||||
* 2. PM freeze (pm_freezing): the hvc console stays active
|
||||
* under no_console_suspend but virtqueues are being torn
|
||||
* down. Drop the output silently so the hvc layer does not
|
||||
* stall suspend.
|
||||
*
|
||||
* This early check avoids a pointless GFP_ATOMIC allocation;
|
||||
* __send_to_port() rechecks under outvq_lock for correctness.
|
||||
* Pairs with smp_store_release() in virtcons_freeze/restore.
|
||||
*/
|
||||
portdev = READ_ONCE(port->portdev);
|
||||
if (!portdev ||
|
||||
smp_load_acquire(&portdev->pm_freezing)) /* pairs with freeze/restore */
|
||||
return count;
|
||||
|
||||
pbuf = alloc_buf(portdev->vdev, count, 0, GFP_ATOMIC);
|
||||
if (!pbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_one(sg, data, count);
|
||||
ret = __send_to_port(port, sg, 1, count, data, false);
|
||||
kfree(data);
|
||||
return ret;
|
||||
memcpy(pbuf->buf, buf, count);
|
||||
pbuf->len = count;
|
||||
sg_init_one(sg, pbuf->buf, count);
|
||||
|
||||
/*
|
||||
* Ownership of pbuf is transferred to __send_to_port().
|
||||
* Do not touch or free pbuf after this call.
|
||||
*/
|
||||
return __send_to_port(port, sg, 1, count, pbuf, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1295,7 +1358,7 @@ static int fill_queue(struct virtqueue *vq, spinlock_t *lock)
|
||||
|
||||
nr_added_bufs = 0;
|
||||
do {
|
||||
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
|
||||
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1498,11 +1561,16 @@ static void unplug_port(struct port *port)
|
||||
remove_port_data(port);
|
||||
|
||||
/*
|
||||
* We should just assume the device itself has gone off --
|
||||
* else a close on an open port later will try to send out a
|
||||
* control message.
|
||||
* Null out portdev under outvq_lock so that __send_to_port()
|
||||
* cannot race: it checks port->portdev inside the same lock
|
||||
* and bails out if NULL, preventing any buffer from being
|
||||
* enqueued to an already torn-down virtqueue. Also prevents
|
||||
* a close on an open port later from sending a stale control
|
||||
* message.
|
||||
*/
|
||||
spin_lock_irq(&port->outvq_lock);
|
||||
port->portdev = NULL;
|
||||
spin_unlock_irq(&port->outvq_lock);
|
||||
|
||||
sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
|
||||
device_destroy(&port_class, port->dev->devt);
|
||||
@@ -1536,7 +1604,8 @@ static void handle_control_message(struct virtio_device *vdev,
|
||||
cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) {
|
||||
/* No valid header at start of buffer. Drop it. */
|
||||
dev_dbg(&portdev->vdev->dev,
|
||||
"Invalid index %u in control packet\n", cpkt->id);
|
||||
"Invalid index %u in control packet\n",
|
||||
virtio32_to_cpu(vdev, cpkt->id));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1553,7 +1622,8 @@ static void handle_control_message(struct virtio_device *vdev,
|
||||
dev_warn(&portdev->vdev->dev,
|
||||
"Request for adding port with "
|
||||
"out-of-bound id %u, max. supported id: %u\n",
|
||||
cpkt->id, portdev->max_nr_ports - 1);
|
||||
virtio32_to_cpu(vdev, cpkt->id),
|
||||
portdev->max_nr_ports - 1);
|
||||
break;
|
||||
}
|
||||
add_port(portdev, virtio32_to_cpu(vdev, cpkt->id));
|
||||
@@ -1706,6 +1776,7 @@ static void out_intr(struct virtqueue *vq)
|
||||
}
|
||||
|
||||
wake_up_interruptible(&port->waitqueue);
|
||||
kref_put(&port->kref, remove_port);
|
||||
}
|
||||
|
||||
static void in_intr(struct virtqueue *vq)
|
||||
@@ -1751,6 +1822,8 @@ static void in_intr(struct virtqueue *vq)
|
||||
|
||||
if (is_console_port(port) && hvc_poll(port->cons.hvc))
|
||||
hvc_kick();
|
||||
|
||||
kref_put(&port->kref, remove_port);
|
||||
}
|
||||
|
||||
static void control_intr(struct virtqueue *vq)
|
||||
@@ -1980,6 +2053,7 @@ static int virtcons_probe(struct virtio_device *vdev)
|
||||
/* Attach this portdev to this virtio_device, and vice-versa. */
|
||||
portdev->vdev = vdev;
|
||||
vdev->priv = portdev;
|
||||
portdev->pm_freezing = false;
|
||||
|
||||
portdev->chr_major = register_chrdev(0, "virtio-portsdev",
|
||||
&portdev_fops);
|
||||
@@ -2099,9 +2173,30 @@ static int virtcons_freeze(struct virtio_device *vdev)
|
||||
{
|
||||
struct ports_device *portdev;
|
||||
struct port *port;
|
||||
unsigned long flags;
|
||||
|
||||
portdev = vdev->priv;
|
||||
|
||||
/*
|
||||
* Block TX paths (put_chars, __send_to_port) before resetting the
|
||||
* device and tearing down virtqueues. This prevents races with
|
||||
* hvc console writes that remain active under no_console_suspend.
|
||||
*/
|
||||
smp_store_release(&portdev->pm_freezing, true);
|
||||
|
||||
/*
|
||||
* Synchronize with any concurrent __send_to_port() that may have
|
||||
* passed the pm_freezing check. By acquiring and releasing the
|
||||
* outvq_lock for each port, we ensure all active TX paths have
|
||||
* completed before we reset the device.
|
||||
*/
|
||||
spin_lock_irqsave(&portdev->ports_lock, flags);
|
||||
list_for_each_entry(port, &portdev->ports, list) {
|
||||
spin_lock(&port->outvq_lock);
|
||||
spin_unlock(&port->outvq_lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&portdev->ports_lock, flags);
|
||||
|
||||
virtio_reset_device(vdev);
|
||||
|
||||
if (use_multiport(portdev))
|
||||
@@ -2144,9 +2239,6 @@ static int virtcons_restore(struct virtio_device *vdev)
|
||||
|
||||
virtio_device_ready(portdev->vdev);
|
||||
|
||||
if (use_multiport(portdev))
|
||||
fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
|
||||
|
||||
list_for_each_entry(port, &portdev->ports, list) {
|
||||
port->in_vq = portdev->in_vqs[port->id];
|
||||
port->out_vq = portdev->out_vqs[port->id];
|
||||
@@ -2163,6 +2255,24 @@ static int virtcons_restore(struct virtio_device *vdev)
|
||||
if (port->guest_connected)
|
||||
send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the control receive queue only after the list iteration
|
||||
* is complete. If we fill this queue before iterating, the host could
|
||||
* immediately deliver a VIRTIO_CONSOLE_PORT_REMOVE message.
|
||||
* This would trigger the control workqueue, which modifies the
|
||||
* portdev->ports list concurrently with the unprotected loop above,
|
||||
* leading to a Use-After-Free and list corruption.
|
||||
*/
|
||||
if (use_multiport(portdev))
|
||||
fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
|
||||
|
||||
/*
|
||||
* Allow TX paths only after all port->out_vq pointers have
|
||||
* been reassigned to the newly allocated virtqueues.
|
||||
*/
|
||||
smp_store_release(&portdev->pm_freezing, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -760,7 +760,7 @@ static int __init hwicap_module_init(void)
|
||||
HWICAP_DEVICES,
|
||||
DRIVER_NAME);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
goto failed_class;
|
||||
|
||||
retval = platform_driver_register(&hwicap_platform_driver);
|
||||
if (retval)
|
||||
@@ -771,6 +771,9 @@ static int __init hwicap_module_init(void)
|
||||
failed:
|
||||
unregister_chrdev_region(devt, HWICAP_DEVICES);
|
||||
|
||||
failed_class:
|
||||
class_unregister(&icap_class);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op)
|
||||
|
||||
rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint);
|
||||
|
||||
if (rc) {
|
||||
dev_err(endpoint->dev,
|
||||
"Failed to register IRQ handler. Aborting.\n");
|
||||
if (rc)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return xillybus_endpoint_discovery(endpoint);
|
||||
}
|
||||
|
||||
@@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev,
|
||||
}
|
||||
rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
|
||||
xillyname, endpoint);
|
||||
if (rc) {
|
||||
dev_err(endpoint->dev,
|
||||
"Failed to register MSI handler. Aborting.\n");
|
||||
if (rc)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
|
||||
|
||||
@@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d)
|
||||
unsigned int status;
|
||||
unsigned int val;
|
||||
|
||||
if (!dev->attached)
|
||||
return IRQ_NONE;
|
||||
|
||||
status = inb(dev->iobase + AIO_IIRO_16_STATUS);
|
||||
if (!(status & AIO_IIRO_16_STATUS_IRQE))
|
||||
return IRQ_NONE;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user