Char/Misc/IIO/etc driver update for 7.3-rc1

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, with the only reported
 issues being some major merge conflicts.  Miguel pointed out some of
 these with the Rust tree merge, which is the majority of them.  I'll
 follow up with a diffstat of the merge resolution I made against your
 most recent tree, which works for me.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCao143A8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymtUgCeKB62gT1JfA86cg3NDB1opp+10N0An3oBBegQ
 IJLqJgK45dTNtMGBwGFV
 =68af
 -----END PGP SIGNATURE-----

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:
Linus Torvalds 2026-08-25 09:38:50 -07:00
commit 93e4b3076b
758 changed files with 28139 additions and 21301 deletions

View File

@ -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>

View File

@ -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).

View File

@ -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

View File

@ -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".

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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>;
};
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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:

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};
...

View File

@ -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>;
};
};
...

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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 = <&reg_3v3>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
};
};

View File

@ -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;
};
};
...

View File

@ -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

View File

@ -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>;
};
};
...

View File

@ -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>;
};
};
...

View File

@ -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:

View File

@ -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

View File

@ -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>;
};

View File

@ -17,6 +17,8 @@ description:
properties:
compatible:
oneOf:
- enum:
- qcom,shikra-epss-l3
- items:
- enum:
- qcom,sc7180-osm-l3

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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>;
};
};
...

View File

@ -25,6 +25,7 @@ properties:
- const: microchip,lan9662-otpc
- enum:
- microchip,lan9662-otpc
- microchip,lan9691-otpc
reg:
maxItems: 1

View File

@ -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

View File

@ -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,.*":

View File

@ -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";
};

View File

@ -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";
};

View File

@ -105,6 +105,7 @@ Subsystem-specific APIs
interconnect
ipmb
ipmi
issei/index
libata
mailbox
md/index

View File

@ -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

View File

@ -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.

View File

@ -254,6 +254,7 @@ Hardware Monitoring Kernel Drivers
smsc47b397
smsc47m192
smsc47m1
socfpga-hwmon
sparx5-temp
spd5118
sq24860

View File

@ -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

View File

@ -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).

View File

@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers
ad7625
ad7944
ade9000
adf41513
adis16475
adis16480
adis16550

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -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);
}

View File

@ -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);

View File

@ -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",

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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 @@ static int __init speakup_init(void)
mutex_lock(&spk_mutex);
synth_release();
mutex_unlock(&spk_mutex);
spk_ttyio_unregister_ldisc();
speakup_kobj_exit();
error_kobjects:

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 };

View File

@ -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);
}

View File

@ -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.

View File

@ -384,8 +384,8 @@ pub(crate) fn transfer_binder_object(
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.

View File

@ -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)*)
);
}
};
}

View File

@ -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 @@ fn default() -> Self {
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>())
}
}
}

View File

@ -60,6 +60,7 @@ fn allow_duplicate(&self, node: &DArc<Node>) -> bool {
/// 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 @@ fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
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 @@ fn do_work(
}
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 @@ fn do_work(
}
}
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 @@ pub(crate) fn request_freeze_notif(
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 @@ pub(crate) fn request_freeze_notif(
}
*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 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)
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 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)
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 @@ pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader)
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 @@ pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocErro
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)?;
}

View File

@ -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)
}
}

View File

@ -9,6 +9,7 @@
seq_print,
sync::lock::{spinlock::SpinLockBackend, Guard},
sync::{Arc, LockedBy, SpinLock},
uapi,
};
use crate::{
@ -21,6 +22,7 @@
};
use core::mem;
use core::ptr;
mod wrapper;
pub(crate) use self::wrapper::CritIncrWrapper;
@ -321,7 +323,7 @@ pub(crate) unsafe fn remove_node_info(
/// 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 @@ pub(crate) fn add_death(
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 @@ pub(crate) fn inc_ref_done_locked(
) -> 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 @@ pub(crate) fn incr_refcount_allow_zero2one_with_wrapper(
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 @@ pub(crate) fn populate_counts(
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 @@ pub(crate) fn populate_debug_info(
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 @@ pub(crate) fn submit_oneway(
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 @@ pub(crate) fn pending_oneway_finished(&self) {
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 @@ fn do_work_locked(
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 @@ pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>>
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 @@ pub(crate) fn get_count(&self) -> (usize, usize) {
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 @@ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool {
*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 @@ fn do_work(
// 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 @@ fn do_work(
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

View File

@ -21,7 +21,7 @@ pub(crate) fn new() -> Result<Self> {
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 {},
}
}

View File

@ -312,7 +312,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result<usize> {
// 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 @@ pub(crate) unsafe fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
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 @@ pub(crate) unsafe fn write<T: ?Sized>(&self, offset: usize, obj: &T) -> Result {
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 @@ fn drop(self: Pin<&mut Self>) {
{
// 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.

View File

@ -16,6 +16,7 @@
use kernel::{
bindings,
bits::bit_u8,
cred::Credential,
error::Error,
fs::file::{self, File},
@ -30,9 +31,10 @@
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 @@ fn new(address: usize, size: usize) -> Self {
}
}
// 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 @@ fn new() -> Self {
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 @@ fn new() -> Self {
/// 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 @@ pub(crate) fn push_work(
// 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 @@ pub(crate) fn update_node_refcount(
// 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 @@ pub(crate) fn new_node_ref_with_thread(
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 @@ fn run(me: Arc<Self>) {
{
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 @@ fn new(ctx: Arc<Context>, cred: ARef<Credential>) -> Result<Arc<Self>> {
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 @@ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result<Arc<Thread>> {
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 @@ fn set_as_manager(
} 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 @@ pub(crate) fn insert_or_update_handle(
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 @@ pub(crate) fn insert_or_update_handle(
// 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 @@ pub(crate) fn insert_or_update_handle(
// `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 @@ pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult<NodeRef>
}
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 @@ pub(crate) fn update_ref(
// 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 @@ pub(crate) fn update_ref(
}
}
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 @@ pub(crate) fn update_ref(
} 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 @@ pub(crate) fn inc_ref_done(&self, reader: &mut UserSliceReader, strong: bool) ->
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 @@ pub(crate) fn request_death(
// 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 @@ pub(crate) fn request_death(
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 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread)
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 @@ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> {
}
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 @@ fn deferred_flush(&self) {
}
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 @@ fn deferred_release(self: Arc<Self>) {
// 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 @@ pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result {
}
}
}
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 @@ fn ioctl_write_read(
/// 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 @@ pub(crate) fn release(this: Arc<Process>, _file: &File) {
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 @@ pub(crate) fn flush(this: ArcBorrow<'_, Process>) -> Result {
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 @@ pub(crate) fn poll(
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;
}

View File

@ -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 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 @@ fn arc_pin_init(init: impl PinInit<T>) -> Result<DLArc<T>, kernel::error::Error>
struct DeliverCode {
code: u32,
skip: Atomic<bool>,
pid: i32,
}
kernel::list::impl_list_arc_safe! {
@ -233,10 +231,11 @@ struct DeliverCode {
}
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 @@ fn do_work(
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 @@ unsafe impl<T> Sync for AssertSync<T> {}
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 impl<T> Sync for AssertSync<T> {}
// 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 impl<T> Sync for AssertSync<T> {}
_: *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) };

View File

@ -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);

View File

@ -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 @@
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 @@ fn claim_next(&mut self, size: usize) -> Result<usize> {
}
}
#[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 @@ fn is_ok(&self) -> bool {
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 @@ fn pop_work(&mut self) -> Option<DLArc<dyn DeliverToRead>> {
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 @@ fn pop_transaction_replied(&mut self, transaction: &DArc<Transaction>) -> bool {
}
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 @@ fn should_use_process_work_queue(&self) -> bool {
}
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 @@ impl ListItem<0> for Thread {
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 @@ pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
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 @@ pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Res
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 @@ fn get_work_local(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn Deliv
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 @@ fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRe
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 @@ fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRe
/// 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 @@ pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
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 @@ fn translate_object(
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 @@ fn translate_object(
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 @@ fn translate_object(
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 @@ fn translate_object(
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 @@ fn translate_object(
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 @@ fn translate_object(
};
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 @@ fn translate_object(
.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 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
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 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
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 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
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 @@ pub(crate) fn copy_transaction_data(
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 @@ pub(crate) fn copy_transaction_data(
) {
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 @@ pub(crate) fn copy_transaction_data(
)?;
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 @@ fn unwind_transaction_stack(self: &Arc<Self>) {
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 @@ fn deliver_single_reply(
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 @@ fn deliver_single_reply(
}
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 @@ fn read_transaction_info(
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 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
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 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu
// 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 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu
{
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 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu
}
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 @@ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
// 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 @@ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
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 @@ fn oneway_transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> Bin
} 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 @@ fn write(self: &Arc<Self>, req: &mut BinderWriteRead) -> Result {
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 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
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 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
// 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 @@ pub(crate) fn write_read(self: &Arc<Self>, data: UserSlice, wait: bool) -> Resul
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 @@ pub(crate) fn exit_looper(&self) {
}
}
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 @@ pub(crate) fn release(self: &Arc<Self>) {
#[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 @@ fn do_work(
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

View File

@ -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 @@
#[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]

View File

@ -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 @@
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 @@
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 @@ pub(crate) fn new(
}
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 @@ pub(crate) fn new_reply(
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 @@ pub(crate) fn debug_print_inner(&self, m: &SeqFile, prefix: &str) {
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 @@ pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderRes
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 @@ pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderRes
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 @@ pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderRes
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 @@ pub(crate) fn can_replace(&self, old: &Transaction) -> bool {
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 @@ fn do_work(
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 @@ fn do_work(
} 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 @@ fn do_work(
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 @@ fn cancel(self: DArc<Self>) {
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<()> {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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 @@ static struct port *find_port_by_id(struct ports_device *portdev, u32 id)
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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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