mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 06:23:02 +02:00
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:
commit
93e4b3076b
2
.mailmap
2
.mailmap
|
|
@ -611,6 +611,7 @@ Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@s-opensource.com>
|
|||
Maxime Ripard <mripard@kernel.org> <maxime@cerno.tech>
|
||||
Maxime Ripard <mripard@kernel.org> <maxime.ripard@bootlin.com>
|
||||
Maxime Ripard <mripard@kernel.org> <maxime.ripard@free-electrons.com>
|
||||
Maxwell Doose <maxwell@maxwelld.cc> <m32285159@gmail.com>
|
||||
Maya Erez <quic_merez@quicinc.com> <merez@codeaurora.org>
|
||||
Mayuresh Janorkar <mayur@ti.com>
|
||||
Md Sadre Alam <quic_mdalam@quicinc.com> <mdalam@codeaurora.org>
|
||||
|
|
@ -899,6 +900,7 @@ Tobias Klauser <tklauser@distanz.ch> <klto@zhaw.ch>
|
|||
Tobias Klauser <tklauser@distanz.ch> <tklauser@nuerscht.ch>
|
||||
Tobias Klauser <tklauser@distanz.ch> <tklauser@xenon.tklauser.home>
|
||||
Todor Tomov <todor.too@gmail.com> <todor.tomov@linaro.org>
|
||||
Tomasz Duszynski <tduszyns@gmail.com> <tomasz.duszynski@octakon.com>
|
||||
Tomasz Jeznach <tomasz.jeznach@linux.dev> <tjeznach@rivosinc.com>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Trilok Soni <quic_tsoni@quicinc.com> <tsoni@codeaurora.org>
|
||||
|
|
|
|||
|
|
@ -162,10 +162,11 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Raw (unscaled) differential voltage measurement equivalent to
|
||||
channel Y - channel Z where these channel numbers apply to the
|
||||
physically equivalent inputs when non differential readings are
|
||||
separately available. In differential only parts, then all that
|
||||
is required is a consistent labeling. Units after application
|
||||
of scale and offset are millivolts.
|
||||
physically equivalent inputs when non-differential readings are
|
||||
separately available. Even though differential-only parts had
|
||||
been merely required to have consistent channel labeling, this
|
||||
ABI is preferred over the non-differential one for those chips.
|
||||
Units after application of scale and offset are millivolts.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw
|
||||
KernelVersion: 6.18
|
||||
|
|
@ -507,6 +508,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
|
@ -770,6 +773,7 @@ Description:
|
|||
3.85kohm_to_gnd: connected to ground via a 3.85kOhm resistor,
|
||||
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
|
||||
7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor,
|
||||
10kohm_to_gnd: connected to ground via a 10kOhm resistor,
|
||||
16kohm_to_gnd: connected to ground via a 16kOhm resistor,
|
||||
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
|
||||
32kohm_to_gnd: connected to ground via a 32kOhm resistor,
|
||||
|
|
@ -917,7 +921,7 @@ Description:
|
|||
|
||||
Note the driver will assume the last p events requested are
|
||||
to be enabled where p is how many it supports (which may vary
|
||||
depending on the exact set requested. So if you want to be
|
||||
depending on the exact set requested). So if you want to be
|
||||
sure you have set what you think you have, check the contents of
|
||||
these attributes after everything is configured. Drivers may
|
||||
have to buffer any parameters so that they are consistent when
|
||||
|
|
@ -973,7 +977,7 @@ Description:
|
|||
|
||||
Note the driver will assume the last p events requested are
|
||||
to be enabled where p is however many it supports (which may
|
||||
vary depending on the exact set requested. So if you want to be
|
||||
vary depending on the exact set requested). So if you want to be
|
||||
sure you have set what you think you have, check the contents of
|
||||
these attributes after everything is configured. Drivers may
|
||||
have to buffer any parameters so that they are consistent when
|
||||
|
|
@ -1528,9 +1532,9 @@ Description:
|
|||
the unused bits, so to get a clean value the bits value must be
|
||||
used to mask the buffer output value appropriately. The storagebits
|
||||
value also specifies the data alignment. So s48/64>>2 will be a
|
||||
signed 48 bit integer stored in a 64 bit location aligned to a 64
|
||||
bit boundary. To obtain the clean value, shift right 2 and apply a
|
||||
mask to zero the top 16 bits of the result.
|
||||
signed 48-bit integer stored in a 64-bit location aligned to a
|
||||
64-bit boundary. To obtain the clean value, shift right 2 and apply
|
||||
a mask to zero the top 16 bits of the result.
|
||||
For other storage combinations this attribute will be extended
|
||||
appropriately.
|
||||
|
||||
|
|
@ -2496,3 +2500,12 @@ Description:
|
|||
seconds, expressed as:
|
||||
|
||||
- a range specified as "[min step max]"
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflow_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_raw
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled) volumetric flow rate reading from the channel.
|
||||
To convert to standard units (cubic metres per second, m^3/s)
|
||||
apply the channel's _scale (and _offset, when present).
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Description:
|
|||
In bridge applications, such as strain gauges and load cells,
|
||||
the bridge itself consumes the majority of the current in the
|
||||
system. To minimize the current consumption of the system,
|
||||
the bridge can be disconnected (when it is not being used
|
||||
the bridge can be disconnected (when it is not being used)
|
||||
using the bridge_switch_en attribute.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw
|
||||
|
|
|
|||
21
Documentation/ABI/testing/sysfs-bus-iio-flow
Normal file
21
Documentation/ABI/testing/sysfs-bus-iio-flow
Normal 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".
|
||||
11
Documentation/ABI/testing/sysfs-bus-iio-frequency
Normal file
11
Documentation/ABI/testing/sysfs-bus-iio-frequency
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
73
Documentation/ABI/testing/sysfs-class-issei
Normal file
73
Documentation/ABI/testing/sysfs-class-issei
Normal 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
|
||||
|
|
@ -172,3 +172,61 @@ Contact: Richard Gong <richard.gong@linux.intel.com>
|
|||
Description:
|
||||
(RO) max retry parameter is stored in the firmware
|
||||
decision IO section, as a byte located at offset 0x18c.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size0
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Flash size in bytes for QSPI device 0 (32-bit hex, one
|
||||
line). EIO if unavailable.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size1
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 1.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size2
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 2.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/size3
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like size0 for device 3.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size0
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Erase size in bytes for QSPI device 0 (32-bit hex, one
|
||||
line). EIO if unavailable.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size1
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 1.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size2
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 2.
|
||||
|
||||
What: /sys/devices/platform/stratix10-rsu.0/erase_size3
|
||||
Date: May 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
Description:
|
||||
(RO) Like erase_size0 for device 3.
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ Description: Read-Write. Read this file to get errors detected on port and
|
|||
to clear errors. Write fails with -EINVAL if input parsing
|
||||
fails or input error code doesn't match. Write fails with
|
||||
-EBUSY or -ETIMEDOUT if error can't be cleared as hardware
|
||||
in low power state (-EBUSY) or not respoding (-ETIMEDOUT).
|
||||
in low power state (-EBUSY) or not responding (-ETIMEDOUT).
|
||||
|
||||
What: /sys/bus/platform/devices/dfl-port.0/errors/first_error
|
||||
Date: August 2019
|
||||
|
|
|
|||
|
|
@ -73,6 +73,47 @@ properties:
|
|||
device design and can interact with other characteristics such as
|
||||
settling time.
|
||||
|
||||
reference-sources:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
description:
|
||||
Indicates the voltage reference source or sources for this channel. Some
|
||||
ADCs usually allow choosing between internal reference sources or a pair
|
||||
of external pins.
|
||||
|
||||
input-chopping:
|
||||
type: boolean
|
||||
description:
|
||||
If set, the positive and negative input channels are swapped on every
|
||||
other conversion to reduce offset error. This generally comes at the
|
||||
expense of added settling time between conversions, so should be omitted
|
||||
on systems with high-frequency signals or high data rate requirements.
|
||||
|
||||
excitation-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description:
|
||||
Excitation current sources provide current to resistive temperature
|
||||
devices (RTDs), thermistors, diodes and other resistive sensors that
|
||||
require constant current biasing.
|
||||
|
||||
This array describes the mux configuration of the excitation current
|
||||
sources. The values correspond to pins similar to the diff-channels and
|
||||
single-channel properties.
|
||||
|
||||
excitation-current-nanoamp:
|
||||
description:
|
||||
Excitation current sources provide current to resistive temperature
|
||||
devices (RTDs), thermistors, diodes and other resistive sensors that
|
||||
require constant current biasing.
|
||||
|
||||
This array describes the current configuration of the excitation current
|
||||
sources. The index in the array corresponds to the same index in the
|
||||
excitation-channels array.
|
||||
|
||||
burn-out-current-nanoamp:
|
||||
description:
|
||||
Burn-out current sources provide current to the channel's input pins for
|
||||
open-circuit or short-circuit detection.
|
||||
|
||||
anyOf:
|
||||
- oneOf:
|
||||
- required:
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ properties:
|
|||
- adi,ad4087
|
||||
- adi,ad4088
|
||||
- adi,ad4880
|
||||
- adi,ad4883
|
||||
- adi,ad4884
|
||||
|
||||
reg:
|
||||
|
|
@ -102,6 +103,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- adi,ad4880
|
||||
- adi,ad4883
|
||||
- adi,ad4884
|
||||
then:
|
||||
properties:
|
||||
|
|
|
|||
170
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
Normal file
170
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -33,6 +33,26 @@ properties:
|
|||
description:
|
||||
Analog supply voltage (AVDD).
|
||||
|
||||
vsup-supply:
|
||||
description:
|
||||
Supply voltage for the on-chip regulator (VSUP).
|
||||
|
||||
dvdd-supply:
|
||||
description:
|
||||
Digital supply voltage (DVDD). On the HX711, DVDD is a
|
||||
board-supplied digital rail; it was missing from the original
|
||||
binding. On the HX710B, DVDD must be greater than or equal to
|
||||
AVDD; when DVDD is a battery rail and AVDD is a regulated supply,
|
||||
one channel monitors the DVDD-AVDD difference for battery level
|
||||
detection.
|
||||
|
||||
rate-gpios:
|
||||
description:
|
||||
GPIO connected to the RATE pin. When driven low the output data
|
||||
rate is 10 SPS; when driven high it is 80 SPS. If omitted the
|
||||
RATE pin state is determined by the board wiring.
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Controls the SCK bit-bang timing. The value is used to derive the
|
||||
|
|
@ -57,6 +77,7 @@ examples:
|
|||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
rate-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
|
||||
avdd-supply = <&avdd>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
$id: http://devicetree.org/schemas/iio/adc/mediatek,mt6359-auxadc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek MT6350 series PMIC AUXADC
|
||||
title: MediaTek MT6350 series and similar PMIC AUXADC
|
||||
|
||||
maintainers:
|
||||
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
|
||||
|
|
@ -20,6 +20,7 @@ properties:
|
|||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- mediatek,mt6323-auxadc
|
||||
- mediatek,mt6357-auxadc
|
||||
- mediatek,mt6358-auxadc
|
||||
- mediatek,mt6359-auxadc
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ properties:
|
|||
- const: rockchip,rk3562-saradc
|
||||
- const: rockchip,rk3588-saradc
|
||||
- items:
|
||||
- const: rockchip,rk3576-saradc
|
||||
- enum:
|
||||
- rockchip,rk3576-saradc
|
||||
- rockchip,rv1106-saradc
|
||||
- const: rockchip,rk3588-saradc
|
||||
- items:
|
||||
- enum:
|
||||
|
|
|
|||
219
Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml
Normal file
219
Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/xlnx,versal-sysmon.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: AMD/Xilinx Versal System Monitor
|
||||
|
||||
maintainers:
|
||||
- Salih Erim <salih.erim@amd.com>
|
||||
|
||||
description:
|
||||
The AMD/Xilinx Versal System Monitor (SysMon) is the successor to the
|
||||
Zynq UltraScale+ AMS block. It provides on-chip voltage and temperature
|
||||
monitoring with up to 160 voltage measurement points and up to
|
||||
64 temperature satellites distributed across the SoC. The hardware
|
||||
supports configurable threshold alarms and oversampling. The device
|
||||
can be accessed via memory-mapped I/O or via an I2C interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: xlnx,versal-sysmon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#io-channel-cells':
|
||||
const: 1
|
||||
|
||||
voltage-channels:
|
||||
type: object
|
||||
description:
|
||||
Container for voltage measurement channels.
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^channel@([0-9a-f]|[1-9][0-9a-f])$':
|
||||
$ref: adc.yaml
|
||||
|
||||
description:
|
||||
Measures a voltage rail. The register index and rail
|
||||
name are assigned by the hardware design tool (Vivado).
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 159
|
||||
description:
|
||||
Voltage measurement register index assigned by the hardware
|
||||
design tool.
|
||||
|
||||
required:
|
||||
- reg
|
||||
- label
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
temperature-channels:
|
||||
type: object
|
||||
description:
|
||||
Container for temperature satellite measurement channels.
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^channel@([1-9a-f]|[1-3][0-9a-f]|40)$':
|
||||
$ref: adc.yaml
|
||||
|
||||
description:
|
||||
Reads a temperature satellite sensor. Each satellite monitors
|
||||
a specific region of the SoC die.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 64
|
||||
description:
|
||||
Temperature satellite number (1-based hardware index).
|
||||
|
||||
required:
|
||||
- reg
|
||||
- label
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
sysmon@f1270000 {
|
||||
compatible = "xlnx,versal-sysmon";
|
||||
reg = <0xf1270000 0x4000>;
|
||||
interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
voltage-channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
label = "vccaux";
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
label = "vcc_ram";
|
||||
bipolar;
|
||||
};
|
||||
};
|
||||
|
||||
temperature-channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@a {
|
||||
reg = <10>;
|
||||
label = "aie-temp-ch1";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
|||
title: Sensirion SCD30 carbon dioxide sensor
|
||||
|
||||
maintainers:
|
||||
- Maxwell Doose <m32285159@gmail.com>
|
||||
- Maxwell Doose <maxwell@maxwelld.cc>
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Air quality sensor capable of measuring co2 concentration, temperature
|
||||
|
|
|
|||
|
|
@ -10,15 +10,17 @@ maintainers:
|
|||
- Kim Seer Paller <kimseer.paller@analog.com>
|
||||
|
||||
description: |
|
||||
The AD3530/AD3530R (8-channel) and AD3531/AD3531R (4-channel) are low-power,
|
||||
16-bit, buffered voltage output digital-to-analog converters (DACs) with
|
||||
software-programmable gain controls, providing full-scale output spans of 2.5V
|
||||
or 5V for reference voltages of 2.5V. These devices operate from a single 2.7V
|
||||
to 5.5V supply and are guaranteed monotonic by design. The "R" variants
|
||||
include a 2.5V, 5ppm/°C internal reference, which is disabled by default.
|
||||
The AD3530/AD3530R (8-channel), AD3531/AD3531R (4-channel), and AD3532/AD3532R
|
||||
(16-channel) are low-power, 16-bit, buffered voltage output digital-to-analog
|
||||
converters (DACs) with software-programmable gain controls, providing
|
||||
full-scale output spans of 2.5V or 5V for reference voltages of 2.5V. These
|
||||
devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic
|
||||
by design. The "R" variants include a 2.5V, 5ppm/°C internal reference, which
|
||||
is disabled by default.
|
||||
Datasheet can be found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3530_ad530r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3531-ad3531r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3532r.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -27,6 +29,8 @@ properties:
|
|||
- adi,ad3530r
|
||||
- adi,ad3531
|
||||
- adi,ad3531r
|
||||
- adi,ad3532
|
||||
- adi,ad3532r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
$id: http://devicetree.org/schemas/iio/dac/adi,ad5686.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD5360 and similar SPI DACs
|
||||
title: Analog Devices AD5686 and similar SPI DACs
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
|
|
@ -14,10 +14,14 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- adi,ad5310r
|
||||
- adi,ad5313r
|
||||
- adi,ad5317r
|
||||
- adi,ad5672r
|
||||
- adi,ad5674
|
||||
- adi,ad5674r
|
||||
- adi,ad5676
|
||||
- adi,ad5676r
|
||||
- adi,ad5679
|
||||
- adi,ad5679r
|
||||
- adi,ad5681r
|
||||
- adi,ad5682r
|
||||
|
|
@ -28,31 +32,107 @@ properties:
|
|||
- adi,ad5685r
|
||||
- adi,ad5686
|
||||
- adi,ad5686r
|
||||
- adi,ad5687
|
||||
- adi,ad5687r
|
||||
- adi,ad5689
|
||||
- adi,ad5689r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Input power supply.
|
||||
|
||||
vlogic-supply:
|
||||
description:
|
||||
Digital power supply. On some tiny package variants for single-channel
|
||||
devices, this supply is internally connected to vdd; in that case, specify
|
||||
this property with the same regulator as vdd.
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
Reference voltage supply. If not supplied the internal reference is used.
|
||||
|
||||
vcc-supply:
|
||||
description: If not supplied the internal reference is used.
|
||||
deprecated: true
|
||||
description: Use vref-supply instead.
|
||||
|
||||
reset-gpios:
|
||||
description: Active-low RESET pin to reset the device.
|
||||
maxItems: 1
|
||||
|
||||
ldac-gpios:
|
||||
description:
|
||||
Active-low LDAC pin used to asynchronously update the DAC channels.
|
||||
maxItems: 1
|
||||
|
||||
gain-gpios:
|
||||
description:
|
||||
GAIN pin that sets a multiplier for the DAC output voltage. When high,
|
||||
the DAC output voltage is multiplied by 2, otherwise it is unchanged.
|
||||
maxItems: 1
|
||||
|
||||
adi,range-double:
|
||||
description:
|
||||
Sets the initial voltage output range from 0 to 2xVREF. On devices that
|
||||
have a GAIN pin and no gain-gpios property is set, this indicates the pin
|
||||
is hardwired high.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vlogic-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5674
|
||||
- adi,ad5676
|
||||
- adi,ad5679
|
||||
- adi,ad5683
|
||||
- adi,ad5684
|
||||
- adi,ad5686
|
||||
- adi,ad5687
|
||||
- adi,ad5689
|
||||
then:
|
||||
required:
|
||||
- vref-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5310r
|
||||
- adi,ad5681r
|
||||
- adi,ad5682r
|
||||
- adi,ad5683
|
||||
- adi,ad5683r
|
||||
then:
|
||||
properties:
|
||||
gain-gpios: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dac@0 {
|
||||
reg = <0>;
|
||||
compatible = "adi,ad5310r";
|
||||
vcc-supply = <&dac_vref0>;
|
||||
vdd-supply = <&dac_vdd>;
|
||||
vlogic-supply = <&dac_vlogic>;
|
||||
vref-supply = <&dac_vref>;
|
||||
reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
|
||||
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
|||
|
|
@ -16,10 +16,14 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- adi,ad5311r
|
||||
- adi,ad5316r
|
||||
- adi,ad5337r
|
||||
- adi,ad5338r
|
||||
- adi,ad5671r
|
||||
- adi,ad5673r
|
||||
- adi,ad5675
|
||||
- adi,ad5675r
|
||||
- adi,ad5677r
|
||||
- adi,ad5691r
|
||||
- adi,ad5692r
|
||||
- adi,ad5693
|
||||
|
|
@ -29,22 +33,88 @@ properties:
|
|||
- adi,ad5695r
|
||||
- adi,ad5696
|
||||
- adi,ad5696r
|
||||
- adi,ad5697r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Input power supply.
|
||||
|
||||
vlogic-supply:
|
||||
description:
|
||||
Digital power supply. On some tiny package variants for single-channel
|
||||
devices, this supply is internally connected to vdd; in that case, specify
|
||||
this property with the same regulator as vdd.
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
Reference voltage supply. If not supplied the internal reference is used.
|
||||
|
||||
vcc-supply:
|
||||
description: |
|
||||
The regulator supply for DAC reference voltage.
|
||||
deprecated: true
|
||||
description: Use vref-supply instead.
|
||||
|
||||
reset-gpios:
|
||||
description: Active-low RESET pin to reset the device.
|
||||
maxItems: 1
|
||||
|
||||
ldac-gpios:
|
||||
description:
|
||||
Active-low LDAC pin used to asynchronously update the DAC channels.
|
||||
maxItems: 1
|
||||
|
||||
gain-gpios:
|
||||
description:
|
||||
GAIN pin that sets a multiplier for the DAC output voltage. When high,
|
||||
the DAC output voltage is multiplied by 2, otherwise it is unchanged.
|
||||
maxItems: 1
|
||||
|
||||
adi,range-double:
|
||||
description:
|
||||
Sets the initial voltage output range from 0 to 2xVREF. On devices that
|
||||
have a GAIN pin and no gain-gpios property is set, this indicates the pin
|
||||
is hardwired high.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vlogic-supply
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5675
|
||||
- adi,ad5693
|
||||
- adi,ad5694
|
||||
- adi,ad5696
|
||||
then:
|
||||
required:
|
||||
- vref-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad5311r
|
||||
- adi,ad5691r
|
||||
- adi,ad5692r
|
||||
- adi,ad5693
|
||||
- adi,ad5693r
|
||||
then:
|
||||
properties:
|
||||
gain-gpios: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
@ -52,7 +122,10 @@ examples:
|
|||
ad5696: dac@0 {
|
||||
compatible = "adi,ad5696";
|
||||
reg = <0>;
|
||||
vcc-supply = <&dac_vref>;
|
||||
vdd-supply = <&dac_vdd>;
|
||||
vlogic-supply = <&dac_vlogic>;
|
||||
vref-supply = <&dac_vref>;
|
||||
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ properties:
|
|||
|
||||
vrefp-supply:
|
||||
description:
|
||||
Positive referance input voltage range. From 5v to (vdd - 2.5)
|
||||
Positive reference input voltage range. From 5v to (vdd - 2.5)
|
||||
|
||||
vrefn-supply:
|
||||
description:
|
||||
Negative referance input voltage range. From (vss + 2.5) to 0.
|
||||
Negative reference input voltage range. From (vss + 2.5) to 0.
|
||||
|
||||
adi,rbuf-gain2-en:
|
||||
description: Specify to allow an external amplifier to be connected in a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47a1.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip MCP47A1 DAC
|
||||
|
||||
maintainers:
|
||||
- Joshua Crofts <joshua.crofts1@gmail.com>
|
||||
|
||||
description: |
|
||||
The Microchip MCP47A1 is a 6-bit single output volatile DAC.
|
||||
This device can have different IDs (0x2e and 0x3e).
|
||||
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,mcp47a1
|
||||
|
||||
reg:
|
||||
items:
|
||||
- enum: [0x2e, 0x3e]
|
||||
|
||||
vref-supply: true
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vref-supply
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@2e {
|
||||
compatible = "microchip,mcp47a1";
|
||||
reg = <0x2e>;
|
||||
vref-supply = <&vref_regulator>;
|
||||
vdd-supply = <&vdd_regulator>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/flow/sensirion,slf3s.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sensirion SLF3S liquid flow sensor
|
||||
|
||||
maintainers:
|
||||
- Wadim Mueller <wafgo01@gmail.com>
|
||||
|
||||
description:
|
||||
Family of digital liquid-flow sensors from Sensirion with I2C interface. All
|
||||
family members share the same register map; sub-types differ only in the flow
|
||||
scale factor and the calibrated measurement range. The sub-type can be
|
||||
identified from the product-information register.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensirion,slf3s-0600f
|
||||
- sensirion,slf3s-1300f
|
||||
- sensirion,slf3s-4000b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flow-sensor@8 {
|
||||
compatible = "sensirion,slf3s-0600f";
|
||||
reg = <0x08>;
|
||||
vdd-supply = <®_3v3>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADF41513 PLL Frequency Synthesizer
|
||||
|
||||
maintainers:
|
||||
- Rodrigo Alencar <rodrigo.alencar@analog.com>
|
||||
|
||||
description:
|
||||
The ADF41513 is an ultralow noise frequency synthesizer that can be used to
|
||||
implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
|
||||
downconversion sections of wireless receivers and transmitters. The ADF41510
|
||||
supports frequencies up to 10 GHz.
|
||||
|
||||
https://www.analog.com/en/products/adf41510.html
|
||||
https://www.analog.com/en/products/adf41513.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adf41510
|
||||
- adi,adf41513
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 25000000
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: Clock that provides the reference input frequency.
|
||||
|
||||
avdd1-supply:
|
||||
description: PFD and Up and Down Digital Driver Power Supply (3.3 V)
|
||||
|
||||
avdd2-supply:
|
||||
description: RF Buffer and Prescaler Power Supply (3.3 V)
|
||||
|
||||
avdd3-supply:
|
||||
description: N Divider Power Supply (3.3 V)
|
||||
|
||||
avdd4-supply:
|
||||
description: R Divider and Lock Detector Power Supply (3.3 V)
|
||||
|
||||
avdd5-supply:
|
||||
description: Sigma-Delta Modulator and SPI Power Supply (3.3 V)
|
||||
|
||||
vp-supply:
|
||||
description: Charge Pump Power Supply (3.3 V)
|
||||
|
||||
enable-gpios:
|
||||
description:
|
||||
GPIO that controls the chip enable pin. A logic low on this pin
|
||||
powers down the device and puts the charge pump output into
|
||||
three-state mode.
|
||||
maxItems: 1
|
||||
|
||||
lock-detect-gpios:
|
||||
description:
|
||||
GPIO for lock detect functionality. When configured for digital lock
|
||||
detect, this pin will output a logic high when the PLL is locked.
|
||||
maxItems: 1
|
||||
|
||||
adi,power-up-frequency-mhz:
|
||||
minimum: 1000
|
||||
maximum: 26500
|
||||
default: 10000
|
||||
description:
|
||||
The PLL tunes to this frequency during the initialization sequence.
|
||||
This property should be set to a frequency supported by the loop filter
|
||||
and VCO used in the design. Range is 1 GHz to 26.5 GHz for ADF41513,
|
||||
and 1 GHz to 10 GHz for ADF41510.
|
||||
|
||||
adi,reference-div-factor:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 32
|
||||
default: 1
|
||||
description:
|
||||
Value for the reference division factor (R Counter). The driver will
|
||||
increment R Counter as needed to achieve a PFD frequency within the
|
||||
allowed range. High R counter values will reduce the PFD frequency, which
|
||||
lowers the frequency resolution, and affects phase noise performance.
|
||||
As it affects the PFD frequency, this value depends on the loop filter
|
||||
design.
|
||||
|
||||
adi,reference-doubler-enable:
|
||||
description:
|
||||
Enables the reference doubler when deriving the PFD frequency.
|
||||
The maximum reference frequency when the doubler is enabled is 225 MHz.
|
||||
As it affects the PFD frequency, this value depends on the loop filter
|
||||
design.
|
||||
type: boolean
|
||||
|
||||
adi,reference-div2-enable:
|
||||
description:
|
||||
Enables the reference divide-by-2 function when deriving the PFD
|
||||
frequency. As it affects the PFD frequency, this value depends on the
|
||||
loop filter design.
|
||||
type: boolean
|
||||
|
||||
adi,charge-pump-resistor-ohms:
|
||||
minimum: 1800
|
||||
maximum: 10000
|
||||
default: 2700
|
||||
description:
|
||||
External charge pump resistor (R_SET) value in ohms. This sets the maximum
|
||||
charge pump current along with the charge pump current setting.
|
||||
|
||||
adi,charge-pump-current-microamp:
|
||||
minimum: 81
|
||||
maximum: 7200
|
||||
description:
|
||||
Charge pump current (I_CP) in microamps. The value will be rounded to the
|
||||
nearest supported value. Range of acceptable values depends on the
|
||||
charge pump resistor value, such that 810 mV <= I_CP * R_SET <= 12960 mV.
|
||||
This value depends on the loop filter and the VCO design.
|
||||
|
||||
adi,logic-level-1v8-enable:
|
||||
description:
|
||||
Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V.
|
||||
type: boolean
|
||||
|
||||
adi,phase-detector-polarity-positive-enable:
|
||||
description:
|
||||
Set phase detector polarity to positive. Default is negative.
|
||||
Use positive polarity with non-inverting loop filter and VCO with
|
||||
positive tuning slope, or with inverting loop filter and VCO with
|
||||
negative tuning slope.
|
||||
type: boolean
|
||||
|
||||
adi,lock-detector-count:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 64
|
||||
description:
|
||||
Sets the value for Lock Detector count of the PLL, which determines the
|
||||
number of consecutive phase detector cycles that must be within the lock
|
||||
detector window before lock is declared. Lower values increase the lock
|
||||
detection sensitivity, while higher values provides a more stable lock
|
||||
detection. Applications that consume the lock detect signal may require
|
||||
different settings based on system requirements.
|
||||
enum: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]
|
||||
|
||||
adi,phase-resync-period-ns:
|
||||
default: 0
|
||||
description:
|
||||
When this value is non-zero, enable phase resync functionality, which
|
||||
produces a consistent output phase offset with respect to the input
|
||||
reference. The value specifies the resync period in nanoseconds, used
|
||||
to configure clock dividers with respect to the PFD frequency. This value
|
||||
should be set to a value that is at least as long as the worst case lock
|
||||
time, i.e., it depends mostly on the loop filter design.
|
||||
|
||||
adi,le-sync-enable:
|
||||
description:
|
||||
Synchronizes Load Enable (LE) transitions with the reference signal to
|
||||
avoid asynchronous glitches in the output. This is recommended when using
|
||||
the PLL as a frequency synthesizer, where the reference signal will always
|
||||
be present while the device is being configured. When using the PLL as a
|
||||
frequency tracker, where the reference signal may be absent, LE sync
|
||||
should be left disabled.
|
||||
type: boolean
|
||||
|
||||
dependencies:
|
||||
adi,charge-pump-resistor-ohms: [ 'adi,charge-pump-current-microamp' ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- avdd1-supply
|
||||
- avdd2-supply
|
||||
- avdd3-supply
|
||||
- avdd4-supply
|
||||
- avdd5-supply
|
||||
- vp-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,adf41510
|
||||
then:
|
||||
properties:
|
||||
adi,power-up-frequency-mhz:
|
||||
maximum: 10000
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pll@0 {
|
||||
compatible = "adi,adf41513";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
clocks = <&ref_clk>;
|
||||
avdd1-supply = <&avdd1_3v3>;
|
||||
avdd2-supply = <&avdd2_3v3>;
|
||||
avdd3-supply = <&avdd3_3v3>;
|
||||
avdd4-supply = <&avdd4_3v3>;
|
||||
avdd5-supply = <&avdd5_3v3>;
|
||||
vp-supply = <&vp_3v3>;
|
||||
enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
|
||||
lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
adi,power-up-frequency-mhz = <15500>;
|
||||
adi,charge-pump-current-microamp = <3600>;
|
||||
adi,charge-pump-resistor-ohms = <2700>;
|
||||
adi,reference-doubler-enable;
|
||||
adi,lock-detector-count = <64>;
|
||||
adi,phase-resync-period-ns = <0>;
|
||||
adi,phase-detector-polarity-positive-enable;
|
||||
adi,le-sync-enable;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -30,6 +30,8 @@ properties:
|
|||
- invensense,icm42600
|
||||
- invensense,icm42602
|
||||
- invensense,icm42605
|
||||
- invensense,icm42607
|
||||
- invensense,icm42607p
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42686
|
||||
|
|
@ -53,6 +55,8 @@ properties:
|
|||
drive-open-drain:
|
||||
type: boolean
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
|
|
@ -65,10 +69,24 @@ properties:
|
|||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,icm42600
|
||||
- invensense,icm42602
|
||||
- invensense,icm42605
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42686
|
||||
- invensense,icm42688
|
||||
then:
|
||||
required:
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883l.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: QST QMC5883L 3-Axis Magnetic Sensor
|
||||
|
||||
maintainers:
|
||||
- Siratul Islam <siratul.islam@linux.dev>
|
||||
|
||||
description: |
|
||||
QST QMC5883L 3-Axis Magnetic Sensor on I2C bus.
|
||||
https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qstcorp,qmc5883l
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
vddio-supply: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@d {
|
||||
compatible = "qstcorp,qmc5883l";
|
||||
reg = <0x0d>;
|
||||
vdd-supply = <&vdd_3v3_reg>;
|
||||
vddio-supply = <&vdd_3v3_reg>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc6308.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: QST QMC6308 3-Axis Magnetic Sensor
|
||||
|
||||
maintainers:
|
||||
- Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||
|
||||
description:
|
||||
QST QMC6308 3-axis anisotropic magnetoresistive (AMR) magnetic sensor
|
||||
on I2C bus, with a single supply and no interrupt pin.
|
||||
https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qstcorp,qmc6308
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@2c {
|
||||
compatible = "qstcorp,qmc6308";
|
||||
reg = <0x2c>;
|
||||
vdd-supply = <&vdd_1v8_reg>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -8,7 +8,7 @@ title: ST VL53L0X/VL53L1X ToF ranging sensor
|
|||
|
||||
maintainers:
|
||||
- Song Qiang <songqiang1304521@gmail.com>
|
||||
- Siratul Islam <email@sirat.me>
|
||||
- Siratul Islam <siratul.islam@linux.dev>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -7,19 +7,25 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
|||
title: TI TMP117 - Digital temperature sensor with integrated NV memory
|
||||
|
||||
description: |
|
||||
TI TMP116/117 - Digital temperature sensor with integrated NV memory that
|
||||
supports I2C interface.
|
||||
TI TMP116/117/119 - Digital temperature sensor with integrated NV memory
|
||||
that supports I2C interface.
|
||||
https://www.ti.com/lit/gpn/tmp116
|
||||
https://www.ti.com/lit/gpn/tmp117
|
||||
https://www.ti.com/lit/gpn/tmp119
|
||||
|
||||
maintainers:
|
||||
- Puranjay Mohan <puranjay12@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,tmp116
|
||||
- ti,tmp117
|
||||
oneOf:
|
||||
- enum:
|
||||
- ti,tmp116
|
||||
- ti,tmp117
|
||||
- items:
|
||||
- enum:
|
||||
- ti,tmp119
|
||||
- const: ti,tmp117
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interconnect/qcom,maili-rpmh.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm RPMh Network-On-Chip Interconnect on Maili SoC
|
||||
|
||||
maintainers:
|
||||
- Raviteja Laggyshetty <raviteja.laggyshetty@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
RPMh interconnect providers support system bandwidth requirements through
|
||||
RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is
|
||||
able to communicate with the BCM through the Resource State Coordinator (RSC)
|
||||
associated with each execution environment. Provider nodes must point to at
|
||||
least one RPMh device child node pertaining to their RSC and each provider
|
||||
can map to multiple RPMh resources.
|
||||
|
||||
See also: include/dt-bindings/interconnect/qcom,maili-rpmh.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
- qcom,maili-clk-virt
|
||||
- qcom,maili-cnoc-main
|
||||
- qcom,maili-gem-noc
|
||||
- qcom,maili-llclpi-noc
|
||||
- qcom,maili-lpass-ag-noc
|
||||
- qcom,maili-lpass-lpiaon-noc
|
||||
- qcom,maili-lpass-lpicx-noc
|
||||
- qcom,maili-mc-virt
|
||||
- qcom,maili-mmss-noc
|
||||
- qcom,maili-nsp-noc
|
||||
- qcom,maili-pcie-anoc
|
||||
- qcom,maili-stdst-cfg
|
||||
- qcom,maili-stdst-main
|
||||
- qcom,maili-system-noc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,rpmh-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-clk-virt
|
||||
- qcom,maili-mc-virt
|
||||
then:
|
||||
properties:
|
||||
reg: false
|
||||
else:
|
||||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: RPMH CC IPA clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-pcie-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre-NOC PCIe AXI clock
|
||||
- description: cfg-NOC PCIe a-NOC AHB clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,maili-aggre-noc
|
||||
- qcom,maili-pcie-anoc
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
else:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
gem_noc: interconnect@31100000 {
|
||||
compatible = "qcom,maili-gem-noc";
|
||||
reg = <0x31100000 0x160200>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
aggre_noc: interconnect@f00000 {
|
||||
compatible = "qcom,maili-aggre-noc";
|
||||
reg = <0x00f00000 0x56200>;
|
||||
#interconnect-cells = <2>;
|
||||
clocks = <&gcc_phy_axi_clk>,
|
||||
<&gcc_prim_axi_clk>,
|
||||
<&rpmhcc_ipa_clk>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
|
@ -17,6 +17,8 @@ description:
|
|||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,shikra-epss-l3
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sc7180-osm-l3
|
||||
|
|
|
|||
|
|
@ -32,18 +32,115 @@ properties:
|
|||
- qcom,sc8280xp-nspb-noc
|
||||
- qcom,sc8280xp-system-noc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 7
|
||||
maxItems: 8
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,rpmh-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-clk-virt
|
||||
- qcom,sc8280xp-mc-virt
|
||||
then:
|
||||
properties:
|
||||
reg: false
|
||||
clocks: false
|
||||
else:
|
||||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre1-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: aggre USB3 SEC AXI clock
|
||||
- description: aggre USB3 MP AXI clock
|
||||
- description: aggre USB4 AXI clock
|
||||
- description: aggre USB4 1 AXI clock
|
||||
- description: aggre USB NOC SOUTH AXI clock
|
||||
- description: RPMH CC IPA clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre2-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre NOC PCIE0 tunnel AXI clock
|
||||
- description: aggre NOC PCIE1 tunnel AXI clock
|
||||
- description: aggre NOC PCIE 4 AXI clock
|
||||
- description: aggre NOC PCIE SOUTH SF AXI clock
|
||||
- description: aggre UFS CARD AXI clock
|
||||
- description: DDRSS GPU AXI clock
|
||||
- description: DDRSS PCIE SF TBU clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-aggre1-noc
|
||||
- qcom,sc8280xp-aggre2-noc
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
else:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
interconnect-0 {
|
||||
compatible = "qcom,sc8280xp-aggre1-noc";
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
|
||||
interconnect {
|
||||
compatible = "qcom,sc8280xp-clk-virt";
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
interconnect@9100000 {
|
||||
compatible = "qcom,sc8280xp-gem-noc";
|
||||
reg = <0x9100000 0xb8400>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
};
|
||||
|
||||
interconnect@16c0000 {
|
||||
compatible = "qcom,sc8280xp-aggre1-noc";
|
||||
reg = <0x16c0000 0x3af80>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_MP_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB4_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB4_1_AXI_CLK>,
|
||||
<&gcc GCC_AGGRE_USB_NOC_SOUTH_AXI_CLK>,
|
||||
<&rpmhcc RPMH_IPA_CLK>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 6
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
|
|
@ -65,6 +69,73 @@ allOf:
|
|||
required:
|
||||
- reg
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-aggre1-noc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre UFS PHY AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-usb-north-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre USB2 PRIM AXI clock
|
||||
- description: aggre USB3 MP AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-usb-south-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: aggre USB3 PRIM AXI clock
|
||||
- description: aggre USB3 SEC AXI clock
|
||||
- description: aggre USB3 TERT AXI clock
|
||||
- description: aggre USB4_0 AXI clock
|
||||
- description: aggre USB4_1 AXI clock
|
||||
- description: aggre USB4_2 AXI clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-aggre2-noc
|
||||
- qcom,x1e80100-clk-virt
|
||||
- qcom,x1e80100-cnoc-cfg
|
||||
- qcom,x1e80100-cnoc-main
|
||||
- qcom,x1e80100-gem-noc
|
||||
- qcom,x1e80100-lpass-ag-noc
|
||||
- qcom,x1e80100-lpass-lpiaon-noc
|
||||
- qcom,x1e80100-lpass-lpicx-noc
|
||||
- qcom,x1e80100-mc-virt
|
||||
- qcom,x1e80100-mmss-noc
|
||||
- qcom,x1e80100-nsp-noc
|
||||
- qcom,x1e80100-pcie-center-anoc
|
||||
- qcom,x1e80100-pcie-north-anoc
|
||||
- qcom,x1e80100-pcie-south-anoc
|
||||
- qcom,x1e80100-system-noc
|
||||
- qcom,x1e80100-usb-center-anoc
|
||||
then:
|
||||
properties:
|
||||
clocks: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
|
@ -80,4 +151,5 @@ examples:
|
|||
reg = <0x016e0000 0x14400>;
|
||||
#interconnect-cells = <2>;
|
||||
qcom,bcm-voters = <&apps_bcm_voter>;
|
||||
clocks = <&gcc_aggre_ufs_phy_axi_clk>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ properties:
|
|||
- enum:
|
||||
- qcom,glymur-fastrpc
|
||||
- qcom,hawi-fastrpc
|
||||
- qcom,maili-fastrpc
|
||||
- qcom,nord-fastrpc
|
||||
- const: qcom,kaanapali-fastrpc
|
||||
|
||||
label:
|
||||
|
|
@ -35,6 +37,8 @@ properties:
|
|||
- sdsp
|
||||
- cdsp
|
||||
- cdsp1
|
||||
- cdsp2
|
||||
- cdsp3
|
||||
- gdsp0
|
||||
- gdsp1
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/nvmem/airoha,smc-efuses.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Airoha SMC eFuses
|
||||
|
||||
description: |
|
||||
Airoha new SoC AN7581 expose banks of eFuse accessible
|
||||
via specific SMC commands.
|
||||
|
||||
2 different bank of eFuse or 64 cells of 32 bit are exposed
|
||||
read-only used to give information on HW Revision, PHY Calibration,
|
||||
Device Model, Private Key...
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- airoha,an7581-efuses
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^efuse-bank@[0-1]$':
|
||||
type: object
|
||||
|
||||
allOf:
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Identify the eFuse bank.
|
||||
enum: [0, 1]
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
efuse {
|
||||
compatible = "airoha,an7581-efuses";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
efuse-bank@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -25,6 +25,7 @@ properties:
|
|||
- const: microchip,lan9662-otpc
|
||||
- enum:
|
||||
- microchip,lan9662-otpc
|
||||
- microchip,lan9691-otpc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -19,14 +19,19 @@ properties:
|
|||
- enum:
|
||||
- qcom,apq8064-qfprom
|
||||
- qcom,apq8084-qfprom
|
||||
- qcom,glymur-qfprom
|
||||
- qcom,hawi-qfprom
|
||||
- qcom,ipq5018-qfprom
|
||||
- qcom,ipq5210-qfprom
|
||||
- qcom,ipq5332-qfprom
|
||||
- qcom,ipq5424-qfprom
|
||||
- qcom,ipq6018-qfprom
|
||||
- qcom,ipq8064-qfprom
|
||||
- qcom,ipq8074-qfprom
|
||||
- qcom,ipq9574-qfprom
|
||||
- qcom,ipq9650-qfprom
|
||||
- qcom,kaanapali-qfprom
|
||||
- qcom,milos-qfprom
|
||||
- qcom,msm8226-qfprom
|
||||
- qcom,msm8916-qfprom
|
||||
- qcom,msm8917-qfprom
|
||||
|
|
@ -48,6 +53,7 @@ properties:
|
|||
- qcom,sdm630-qfprom
|
||||
- qcom,sdm670-qfprom
|
||||
- qcom,sdm845-qfprom
|
||||
- qcom,shikra-qfprom
|
||||
- qcom,sm6115-qfprom
|
||||
- qcom,sm6350-qfprom
|
||||
- qcom,sm6375-qfprom
|
||||
|
|
|
|||
|
|
@ -1387,6 +1387,8 @@ patternProperties:
|
|||
description: Shenzhen QiShenglong Industrialist Co., Ltd.
|
||||
"^qnap,.*":
|
||||
description: QNAP Systems, Inc.
|
||||
"^qstcorp,.*":
|
||||
description: QST Corporation Limited
|
||||
"^quanta,.*":
|
||||
description: Quanta Computer Inc.
|
||||
"^radxa,.*":
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
* OMAP HDQ One wire bus master controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "ti,omap3-1w" or "ti,am4372-hdq"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts : interrupt line.
|
||||
- ti,hwmods : "hdq1w"
|
||||
|
||||
Optional properties:
|
||||
- ti,mode: should be "hdq": HDQ mode "1w": one-wire mode.
|
||||
If not specified HDQ mode is implied.
|
||||
|
||||
Example:
|
||||
|
||||
- From omap3.dtsi
|
||||
hdqw1w: 1w@480b2000 {
|
||||
compatible = "ti,omap3-1w";
|
||||
reg = <0x480b2000 0x1000>;
|
||||
interrupts = <58>;
|
||||
ti,hwmods = "hdq1w";
|
||||
ti,mode = "hdq";
|
||||
};
|
||||
70
Documentation/devicetree/bindings/w1/ti,hdq.yaml
Normal file
70
Documentation/devicetree/bindings/w1/ti,hdq.yaml
Normal 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";
|
||||
};
|
||||
|
|
@ -105,6 +105,7 @@ Subsystem-specific APIs
|
|||
interconnect
|
||||
ipmb
|
||||
ipmi
|
||||
issei/index
|
||||
libata
|
||||
mailbox
|
||||
md/index
|
||||
|
|
|
|||
16
Documentation/driver-api/issei/index.rst
Normal file
16
Documentation/driver-api/issei/index.rst
Normal 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
|
||||
135
Documentation/driver-api/issei/issei.rst
Normal file
135
Documentation/driver-api/issei/issei.rst
Normal 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.
|
||||
|
|
@ -254,6 +254,7 @@ Hardware Monitoring Kernel Drivers
|
|||
smsc47b397
|
||||
smsc47m192
|
||||
smsc47m1
|
||||
socfpga-hwmon
|
||||
sparx5-temp
|
||||
spd5118
|
||||
sq24860
|
||||
|
|
|
|||
34
Documentation/hwmon/socfpga-hwmon.rst
Normal file
34
Documentation/hwmon/socfpga-hwmon.rst
Normal 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
|
||||
199
Documentation/iio/adf41513.rst
Normal file
199
Documentation/iio/adf41513.rst
Normal 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).
|
||||
|
|
@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers
|
|||
ad7625
|
||||
ad7944
|
||||
ade9000
|
||||
adf41513
|
||||
adis16475
|
||||
adis16480
|
||||
adis16550
|
||||
|
|
|
|||
|
|
@ -48,6 +48,66 @@ Access restrictions:
|
|||
* APML Mailbox messages and Register xfer access are read-write,
|
||||
* CPUID and MCA_MSR access is read-only.
|
||||
|
||||
SBTSI device
|
||||
============
|
||||
|
||||
sbtsi driver under the drivers/misc/amd-sbi creates miscdevice
|
||||
/dev/sbtsi-* to let user space programs run APML TSI register transfer
|
||||
commands.
|
||||
|
||||
The driver supports both I2C and I3C transports for SB-TSI targets.
|
||||
The transport is selected by the bus where the device is enumerated.
|
||||
|
||||
Misc device:
|
||||
* In 1P socket 0: /dev/sbtsi-4c
|
||||
* In 2P socket 0: /dev/sbtsi-4c, socket 1: /dev/sbtsi-48
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ls -al /dev/sbtsi-4c
|
||||
crw------- 1 root root 10, 116 Apr 2 05:22 /dev/sbtsi-4c
|
||||
|
||||
|
||||
Access restrictions:
|
||||
* Only root user is allowed to open the file.
|
||||
* APML TSI Register transfer access is read-write.
|
||||
|
||||
SBTSI hwmon interface
|
||||
=====================
|
||||
|
||||
The sbtsi_temp auxiliary driver binds to the auxiliary device published
|
||||
by the core sbtsi driver on the auxiliary bus. The auxiliary device is
|
||||
named amd-sbtsi.temp-sensor.<id>, where <id> is the device's transfer
|
||||
address: the client address for I2C, or the assigned-address for I3C.
|
||||
|
||||
Note that the auxiliary bus formats <id> in decimal, whereas the
|
||||
/dev/sbtsi-* misc node formats its address in hex. The two therefore
|
||||
differ for the same device: an I2C/I3C sensor at address 0x4c appears as the
|
||||
misc node /dev/sbtsi-4c and the auxiliary device
|
||||
amd-sbtsi.temp-sensor.76.
|
||||
|
||||
It registers a hwmon device, providing a standard Linux hwmon interface
|
||||
for reading CPU temperature and managing temperature limits.
|
||||
|
||||
The hwmon device appears under ``/sys/class/hwmon/`` when both ``sbtsi.ko``
|
||||
and ``sbtsi_temp.ko`` are loaded.
|
||||
|
||||
Verify auxiliary bus device::
|
||||
|
||||
ls /sys/bus/auxiliary/devices/
|
||||
# e.g. amd-sbtsi.temp-sensor.76 for an I2C/I3C sensor at address 0x4c
|
||||
|
||||
Example usage::
|
||||
|
||||
# Read current temperature
|
||||
cat /sys/class/hwmon/hwmon<N>/temp1_input
|
||||
|
||||
# Set high temperature limit to 70 °C
|
||||
echo 70000 > /sys/class/hwmon/hwmon<N>/temp1_max
|
||||
|
||||
# Verify
|
||||
cat /sys/class/hwmon/hwmon<N>/temp1_max
|
||||
|
||||
Driver IOCTLs
|
||||
=============
|
||||
|
||||
|
|
@ -63,6 +123,9 @@ Driver IOCTLs
|
|||
.. c:macro:: SBRMI_IOCTL_REG_XFER_CMD
|
||||
.. kernel-doc:: include/uapi/misc/amd-apml.h
|
||||
:doc: SBRMI_IOCTL_REG_XFER_CMD
|
||||
.. c:macro:: SBTSI_IOCTL_REG_XFER_CMD
|
||||
.. kernel-doc:: include/uapi/misc/amd-apml.h
|
||||
:doc: SBTSI_IOCTL_REG_XFER_CMD
|
||||
|
||||
User-space usage
|
||||
================
|
||||
|
|
@ -85,6 +148,16 @@ Next thing, open the device file, as follows::
|
|||
exit(1);
|
||||
}
|
||||
|
||||
To open SB-TSI device::
|
||||
|
||||
int file;
|
||||
|
||||
file = open("/dev/sbtsi-4c", O_RDWR);
|
||||
if (file < 0) {
|
||||
/* ERROR HANDLING */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
The following IOCTLs are defined:
|
||||
|
||||
``#define SB_BASE_IOCTL_NR 0xF9``
|
||||
|
|
@ -92,6 +165,7 @@ The following IOCTLs are defined:
|
|||
``#define SBRMI_IOCTL_CPUID_CMD _IOWR(SB_BASE_IOCTL_NR, 1, struct apml_cpuid_msg)``
|
||||
``#define SBRMI_IOCTL_MCAMSR_CMD _IOWR(SB_BASE_IOCTL_NR, 2, struct apml_mcamsr_msg)``
|
||||
``#define SBRMI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg)``
|
||||
``#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)``
|
||||
|
||||
|
||||
User space C-APIs are made available by esmi_oob_library, hosted at
|
||||
|
|
|
|||
|
|
@ -83,18 +83,18 @@ structures used in ioctl calls::
|
|||
};
|
||||
|
||||
struct ntsync_event_args {
|
||||
__u32 signaled;
|
||||
__u32 manual;
|
||||
__u32 signaled;
|
||||
};
|
||||
|
||||
struct ntsync_wait_args {
|
||||
__u64 timeout;
|
||||
__u64 objs;
|
||||
__u32 count;
|
||||
__u32 owner;
|
||||
__u32 index;
|
||||
__u32 alert;
|
||||
__u32 flags;
|
||||
__u32 owner;
|
||||
__u32 alert;
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ The ioctls on the device file are as follows:
|
|||
|
||||
The ioctls on the individual objects are as follows:
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_SEM_POST
|
||||
.. c:macro:: NTSYNC_IOC_SEM_RELEASE
|
||||
|
||||
Post to a semaphore object. Takes a pointer to a 32-bit integer,
|
||||
which on input holds the count to be added to the semaphore, and on
|
||||
|
|
@ -186,7 +186,7 @@ The ioctls on the individual objects are as follows:
|
|||
unowned and signaled, and eligible threads waiting on it will be
|
||||
woken as appropriate.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_SET_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_SET
|
||||
|
||||
Signal an event object. Takes a pointer to a 32-bit integer, which on
|
||||
output contains the previous state of the event.
|
||||
|
|
@ -194,12 +194,12 @@ The ioctls on the individual objects are as follows:
|
|||
Eligible threads will be woken, and auto-reset events will be
|
||||
designaled appropriately.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_RESET_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_RESET
|
||||
|
||||
Designal an event object. Takes a pointer to a 32-bit integer, which
|
||||
on output contains the previous state of the event.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_PULSE_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_PULSE
|
||||
|
||||
Wake threads waiting on an event object while leaving it in an
|
||||
unsignaled state. Takes a pointer to a 32-bit integer, which on
|
||||
|
|
@ -213,7 +213,7 @@ The ioctls on the individual objects are as follows:
|
|||
afterwards, and a simultaneous read operation will always report the
|
||||
event as unsignaled.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_SEM
|
||||
.. c:macro:: NTSYNC_IOC_SEM_READ
|
||||
|
||||
Read the current state of a semaphore object. Takes a pointer to
|
||||
struct :c:type:`ntsync_sem_args`, which is used as follows:
|
||||
|
|
@ -225,7 +225,7 @@ The ioctls on the individual objects are as follows:
|
|||
* - ``max``
|
||||
- On output, contains the maximum count of the semaphore.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_MUTEX
|
||||
.. c:macro:: NTSYNC_IOC_MUTEX_READ
|
||||
|
||||
Read the current state of a mutex object. Takes a pointer to struct
|
||||
:c:type:`ntsync_mutex_args`, which is used as follows:
|
||||
|
|
@ -242,7 +242,7 @@ The ioctls on the individual objects are as follows:
|
|||
``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
|
||||
zero.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_READ_EVENT
|
||||
.. c:macro:: NTSYNC_IOC_EVENT_READ
|
||||
|
||||
Read the current state of an event object. Takes a pointer to struct
|
||||
:c:type:`ntsync_event_args`, which is used as follows:
|
||||
|
|
@ -255,7 +255,7 @@ The ioctls on the individual objects are as follows:
|
|||
- On output, contains 1 if the event is a manual-reset event,
|
||||
and 0 otherwise.
|
||||
|
||||
.. c:macro:: NTSYNC_IOC_KILL_OWNER
|
||||
.. c:macro:: NTSYNC_IOC_MUTEX_KILL
|
||||
|
||||
Mark a mutex as unowned and abandoned if it is owned by the given
|
||||
owner. Takes an input-only pointer to a 32-bit integer denoting the
|
||||
|
|
@ -276,11 +276,12 @@ The ioctls on the individual objects are as follows:
|
|||
* - ``timeout``
|
||||
- Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
|
||||
is set, the timeout is measured against the REALTIME clock;
|
||||
otherwise it is measured against the MONOTONIC clock. If the
|
||||
timeout is equal to or earlier than the current time, the
|
||||
function returns immediately without sleeping. If ``timeout``
|
||||
is U64_MAX, the function will sleep until an object is
|
||||
signaled, and will not fail with ``ETIMEDOUT``.
|
||||
otherwise it is measured against the MONOTONIC clock in the
|
||||
caller's time namespace. If the timeout is equal to or earlier
|
||||
than the current time, the function returns immediately
|
||||
without sleeping. If ``timeout`` is U64_MAX, the function will
|
||||
sleep until an object is signaled, and will not fail with
|
||||
``ETIMEDOUT``.
|
||||
* - ``objs``
|
||||
- Pointer to an array of ``count`` file descriptors
|
||||
(specified as an integer so that the structure has the same
|
||||
|
|
|
|||
|
|
@ -50,5 +50,5 @@ The driver also supports 1-wire mode. In this mode, there is no need to
|
|||
pass slave ID as parameter. The driver will auto-detect slaves connected
|
||||
to the bus using SEARCH_ROM procedure. 1-wire mode can be selected by
|
||||
setting "ti,mode" property to "1w" in DT (see
|
||||
Documentation/devicetree/bindings/w1/omap-hdq.txt for more details).
|
||||
Documentation/devicetree/bindings/w1/ti,hdq.yaml for more details).
|
||||
By default driver is in HDQ mode.
|
||||
|
|
|
|||
119
MAINTAINERS
119
MAINTAINERS
|
|
@ -964,6 +964,14 @@ L: linux-gpio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/gpio/gpio-altera.c
|
||||
|
||||
ALTERA SoC FPGA HWMON DRIVER
|
||||
M: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
|
||||
M: Tze Yee Ng <tze.yee.ng@altera.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/socfpga-hwmon.rst
|
||||
F: drivers/hwmon/socfpga-hwmon.c
|
||||
|
||||
ALTERA TRIPLE SPEED ETHERNET DRIVER
|
||||
M: Boon Khai Ng <boon.khai.ng@altera.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
@ -1718,6 +1726,15 @@ W: https://ez.analog.com/linux-software-drivers
|
|||
F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
|
||||
F: drivers/iio/adc/ade9000.c
|
||||
|
||||
ANALOG DEVICES INC ADF41513 DRIVER
|
||||
M: Rodrigo Alencar <rodrigo.alencar@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
|
||||
F: Documentation/iio/adf41513.rst
|
||||
F: drivers/iio/frequency/adf41513.c
|
||||
|
||||
ANALOG DEVICES INC ADF4377 DRIVER
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
|
@ -4184,11 +4201,11 @@ F: include/linux/async_tx.h
|
|||
|
||||
AT24 EEPROM DRIVER
|
||||
M: Bartosz Golaszewski <brgl@kernel.org>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
|
||||
F: Documentation/devicetree/bindings/eeprom/at24.yaml
|
||||
F: drivers/misc/eeprom/at24.c
|
||||
F: drivers/nvmem/at24.c
|
||||
|
||||
ATA OVER ETHERNET (AOE) DRIVER
|
||||
M: "Justin Sanders" <justin@coraid.com>
|
||||
|
|
@ -11713,12 +11730,6 @@ W: http://www.highpoint-tech.com
|
|||
F: Documentation/scsi/hptiop.rst
|
||||
F: drivers/scsi/hptiop.c
|
||||
|
||||
HIKEY960 ONBOARD USB GPIO HUB DRIVER
|
||||
M: John Stultz <jstultz@google.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/misc/hisi_hikey_usb.c
|
||||
|
||||
HIMAX HX83112B TOUCHSCREEN SUPPORT
|
||||
M: Job Noorman <job@noorman.info>
|
||||
L: linux-input@vger.kernel.org
|
||||
|
|
@ -11832,8 +11843,7 @@ F: drivers/perf/hisilicon
|
|||
F: tools/perf/pmu-events/arch/arm64/hisilicon/
|
||||
|
||||
HISILICON PTT DRIVER
|
||||
M: Yicong Yang <yangyicong@hisilicon.com>
|
||||
M: Jonathan Cameron <jonathan.cameron@huawei.com>
|
||||
M: Sizhe Liu <liusizhe5@huawei.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
|
||||
|
|
@ -13430,6 +13440,14 @@ F: drivers/platform/x86/intel/sdsi.c
|
|||
F: tools/arch/x86/intel_sdsi/
|
||||
F: tools/testing/selftests/drivers/sdsi/
|
||||
|
||||
INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI)
|
||||
M: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
S: Supported
|
||||
F: Documentation/ABI/testing/sysfs-class-issei
|
||||
F: Documentation/driver-api/issei/issei.rst
|
||||
F: drivers/misc/issei/
|
||||
F: include/uapi/linux/issei.h
|
||||
|
||||
INTEL SGX
|
||||
M: Jarkko Sakkinen <jarkko@kernel.org>
|
||||
R: Dave Hansen <dave.hansen@linux.intel.com>
|
||||
|
|
@ -15435,6 +15453,14 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
|
||||
F: drivers/iio/dac/ltc1660.c
|
||||
|
||||
LTC2378 IIO ADC DRIVER
|
||||
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
|
||||
F: drivers/iio/adc/ltc2378.c
|
||||
|
||||
LTC2664 IIO DAC DRIVER
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Kim Seer Paller <kimseer.paller@analog.com>
|
||||
|
|
@ -16657,6 +16683,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml
|
||||
F: drivers/mmc/host/mtk-sd.c
|
||||
|
||||
MEDIATEK MT6323 PMIC AUXADC DRIVER
|
||||
M: Roman Vivchar <rva333@protonmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/adc/mt6323-auxadc.c
|
||||
F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h
|
||||
|
||||
MEDIATEK MT6735 CLOCK & RESET DRIVERS
|
||||
M: Yassine Oudjana <y.oudjana@protonmail.com>
|
||||
L: linux-clk@vger.kernel.org
|
||||
|
|
@ -17567,6 +17600,7 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.*
|
|||
|
||||
MHI BUS
|
||||
M: Manivannan Sadhasivam <mani@kernel.org>
|
||||
R: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
|
||||
L: mhi@lists.linux.dev
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
@ -17788,6 +17822,12 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml
|
||||
F: drivers/iio/adc/mcp3911.c
|
||||
|
||||
MICROCHIP MCP47A1 DAC DRIVER
|
||||
M: Joshua Crofts <joshua.crofts1@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47a1.yaml
|
||||
F: drivers/iio/dac/mcp47a1.c
|
||||
|
||||
MICROCHIP MCP9982 TEMPERATURE DRIVER
|
||||
M: Victor Duicu <victor.duicu@microchip.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
|
@ -18982,6 +19022,8 @@ F: include/uapi/linux/sctp.h
|
|||
F: lib/net_utils.c
|
||||
F: lib/random32.c
|
||||
F: net/
|
||||
F: rust/helpers/net/
|
||||
F: rust/kernel/net/
|
||||
F: samples/pktgen/
|
||||
F: tools/net/
|
||||
F: tools/testing/selftests/net/
|
||||
|
|
@ -22188,6 +22230,20 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst
|
|||
F: drivers/bus/fsl-mc/
|
||||
F: include/uapi/linux/fsl_mc.h
|
||||
|
||||
QST QMC5883L 3-Axis Magnetic Sensor
|
||||
M: Siratul Islam <siratul.islam@linux.dev>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
|
||||
F: drivers/iio/magnetometer/qmc5883l.c
|
||||
|
||||
QST QMC6308 3-AXIS MAGNETIC SENSOR
|
||||
M: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc6308.yaml
|
||||
F: drivers/iio/magnetometer/qmc6308.c
|
||||
|
||||
QT1010 MEDIA DRIVER
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Orphan
|
||||
|
|
@ -22346,7 +22402,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
|
|||
|
||||
QUALCOMM FASTRPC DRIVER
|
||||
M: Srinivas Kandagatla <srini@kernel.org>
|
||||
M: Amol Maheshwari <amahesh@qti.qualcomm.com>
|
||||
R: Ekansh Gupta <ekansh.gupta@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
|
|
@ -24711,7 +24767,8 @@ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml
|
|||
F: drivers/iio/chemical/sunrise_co2.c
|
||||
|
||||
SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
|
||||
M: Maxwell Doose <m32285159@gmail.com>
|
||||
M: Maxwell Doose <maxwell@maxwelld.cc>
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
|
||||
F: drivers/iio/chemical/scd30.h
|
||||
|
|
@ -24737,6 +24794,14 @@ S: Maintained
|
|||
F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sgp40
|
||||
F: drivers/iio/chemical/sgp40.c
|
||||
|
||||
SENSIRION SLF3S LIQUID FLOW SENSOR DRIVER
|
||||
M: Wadim Mueller <wafgo01@gmail.com>
|
||||
R: Maxwell Doose <maxwell@maxwelld.cc>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/flow/sensirion,slf3s.yaml
|
||||
F: drivers/iio/flow/slf3s.c
|
||||
|
||||
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
|
|
@ -24800,17 +24865,6 @@ F: include/linux/phylink.h
|
|||
F: include/linux/sfp.h
|
||||
K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
|
||||
|
||||
SGI GRU DRIVER
|
||||
M: Dimitri Sivanich <dimitri.sivanich@hpe.com>
|
||||
S: Maintained
|
||||
F: drivers/misc/sgi-gru/
|
||||
|
||||
SGI XP/XPC/XPNET DRIVER
|
||||
M: Robin Holt <robinmholt@gmail.com>
|
||||
M: Steve Wahl <steve.wahl@hpe.com>
|
||||
S: Maintained
|
||||
F: drivers/misc/sgi-xp/
|
||||
|
||||
SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
|
||||
M: D. Wythe <alibuda@linux.alibaba.com>
|
||||
M: Dust Li <dust.li@linux.alibaba.com>
|
||||
|
|
@ -25891,7 +25945,7 @@ F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml
|
|||
F: drivers/iio/proximity/vl53l0x-i2c.c
|
||||
|
||||
ST VL53L1X ToF RANGER(I2C) IIO DRIVER
|
||||
M: Siratul Islam <email@sirat.me>
|
||||
M: Siratul Islam <siratul.islam@linux.dev>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml
|
||||
|
|
@ -27148,6 +27202,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml
|
||||
F: drivers/iio/adc/ti-ads1119.c
|
||||
|
||||
TI ADS112C14 ADC DRIVER
|
||||
M: David Lechner <dlechner@baylibre.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/ti,ads112c14.yaml
|
||||
F: drivers/iio/adc/ti-ads112c14.c
|
||||
|
||||
TI ADS1018 ADC DRIVER
|
||||
M: Kurt Borja <kuurtb@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
|
@ -29738,7 +29799,7 @@ F: include/uapi/linux/dqblk_xfs.h
|
|||
F: include/uapi/linux/fsmap.h
|
||||
|
||||
XILINX AMS DRIVER
|
||||
M: Salih Erim <salih.erim@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
M: Conall O'Griofa <conall.ogriofa@amd.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
@ -29837,6 +29898,14 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.y
|
|||
F: drivers/edac/versalnet_edac.c
|
||||
F: include/linux/cdx/edac_cdx_pcol.h
|
||||
|
||||
XILINX VERSAL SYSMON DRIVER
|
||||
M: Salih Erim <salih.erim@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
|
||||
F: drivers/iio/adc/versal-sysmon*
|
||||
|
||||
XILINX WATCHDOG DRIVER
|
||||
M: Srinivas Neeli <srinivas.neeli@amd.com>
|
||||
R: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ void speakup_fake_down_arrow(void)
|
|||
input_sync(virt_keyboard);
|
||||
__this_cpu_write(reporting_keystroke, false);
|
||||
|
||||
/* reenable preemption */
|
||||
/* re-enable preemption */
|
||||
preempt_enable();
|
||||
/* reenable keyboard interrupts */
|
||||
/* re-enable keyboard interrupts */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ main(int argc, char *argv[])
|
|||
|
||||
open_input(NULL, argv[1]);
|
||||
while (fgets(buffer, sizeof(buffer), infile)) {
|
||||
lc++;
|
||||
value = shift_state = 0;
|
||||
|
||||
cp = strtok(buffer, delims);
|
||||
|
|
@ -153,7 +152,7 @@ main(int argc, char *argv[])
|
|||
continue;
|
||||
printf("\n\t%d,", lc);
|
||||
for (i = 0; i < max_states; i++)
|
||||
printf(" %d,", (unsigned int)kp[i]);
|
||||
printf(" %u,", (unsigned int)kp[i]);
|
||||
}
|
||||
printf("\n\t0, %d\n", map_ver);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = {
|
|||
[MSG_CURSORING_OFF] = "cursoring off",
|
||||
[MSG_CURSORING_ON] = "cursoring on",
|
||||
[MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
|
||||
[MSG_READ_WINDOW] = "read windo",
|
||||
[MSG_READ_WINDOW] = "read window",
|
||||
[MSG_READ_ALL] = "read all",
|
||||
[MSG_EDIT_DONE] = "edit done",
|
||||
[MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
|
||||
|
|
|
|||
|
|
@ -8,14 +8,15 @@
|
|||
*/
|
||||
|
||||
#include <linux/keyboard.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "spk_priv.h"
|
||||
#include "speakup.h"
|
||||
|
||||
#define MAXFUNCS 130
|
||||
#define MAXKEYS 256
|
||||
static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1;
|
||||
static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS];
|
||||
static u_short masks[] = { 32, 16, 8, 4, 2, 1 };
|
||||
static u16 key_offsets[MAXFUNCS], key_data[MAXKEYS];
|
||||
static u16 masks[] = { 32, 16, 8, 4, 2, 1 };
|
||||
|
||||
static short letter_offsets[26] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
|
@ -49,7 +50,7 @@ static int cur_item, nstates;
|
|||
static void build_key_data(void)
|
||||
{
|
||||
u_char *kp, counters[MAXFUNCS], ch, ch1;
|
||||
u_short *p_key, key;
|
||||
u16 *p_key, key;
|
||||
int i, offset = 1;
|
||||
|
||||
nstates = (int)(state_tbl[-1]);
|
||||
|
|
@ -111,7 +112,7 @@ static void say_key(int key)
|
|||
spk_msg_get(MSG_KEYNAMES_START + (key - 1)));
|
||||
}
|
||||
|
||||
static int help_init(void)
|
||||
static void help_init(void)
|
||||
{
|
||||
char start = SPACE;
|
||||
int i;
|
||||
|
|
@ -120,21 +121,27 @@ static int help_init(void)
|
|||
state_tbl = spk_our_keys[0] + SHIFT_TBL_SIZE + 2;
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i);
|
||||
char first_letter;
|
||||
|
||||
if (start == *cur_funcname)
|
||||
first_letter = tolower(*cur_funcname);
|
||||
|
||||
/* Accept only 'a'..'z' to index letter_offsets[] safely */
|
||||
if (first_letter < 'a' || first_letter > 'z')
|
||||
continue;
|
||||
start = *cur_funcname;
|
||||
|
||||
if (start == first_letter)
|
||||
continue;
|
||||
start = first_letter;
|
||||
letter_offsets[(start & 31) - 1] = i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
int i, n;
|
||||
char *name;
|
||||
u_char func, *kp;
|
||||
u_short *p_keys, val;
|
||||
u16 *p_keys, val;
|
||||
|
||||
if (letter_offsets[0] == -1)
|
||||
help_init();
|
||||
|
|
@ -144,7 +151,7 @@ int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
|||
synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP));
|
||||
return 1;
|
||||
}
|
||||
ch |= 32; /* lower case */
|
||||
ch = tolower(ch);
|
||||
if (ch < 'a' || ch > 'z')
|
||||
return -1;
|
||||
if (letter_offsets[ch - 'a'] == -1) {
|
||||
|
|
|
|||
|
|
@ -92,13 +92,13 @@ static void report_char_chartab_status(int reset, int received, int used,
|
|||
if (reset) {
|
||||
pr_info("%s reset to defaults\n", object_type[do_characters]);
|
||||
} else if (received) {
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
" updated %d of %d %s\n",
|
||||
used, received, object_type[do_characters]);
|
||||
len = scnprintf(buf, sizeof(buf),
|
||||
" updated %d of %d %s\n",
|
||||
used, received, object_type[do_characters]);
|
||||
if (rejected)
|
||||
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
|
||||
" with %d reject%s\n",
|
||||
rejected, rejected > 1 ? "s" : "");
|
||||
rejected, str_plural(rejected));
|
||||
pr_info("%s", buf);
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ static ssize_t chars_chartab_store(struct kobject *kobj,
|
|||
ssize_t retval = count;
|
||||
unsigned long flags;
|
||||
unsigned long index = 0;
|
||||
int charclass = 0;
|
||||
u16 charclass = 0;
|
||||
int received = 0;
|
||||
int used = 0;
|
||||
int rejected = 0;
|
||||
|
|
@ -461,7 +461,7 @@ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
struct st_var_header *p_header;
|
||||
struct punc_var_t *var;
|
||||
struct st_bits_data *pb;
|
||||
short mask;
|
||||
u16 mask;
|
||||
unsigned long flags;
|
||||
|
||||
p_header = spk_var_header_by_name(attr->attr.name);
|
||||
|
|
@ -740,7 +740,7 @@ static void report_msg_status(int reset, int received, int used,
|
|||
if (rejected)
|
||||
snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
|
||||
" with %d reject%s\n",
|
||||
rejected, rejected > 1 ? "s" : "");
|
||||
rejected, str_plural(rejected));
|
||||
pr_info("%s", buf);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
|
|||
int spk_no_intr, spk_spell_delay;
|
||||
int spk_key_echo, spk_say_word_ctl;
|
||||
int spk_say_ctrl, spk_bell_pos;
|
||||
short spk_punc_mask;
|
||||
u16 spk_punc_mask;
|
||||
int spk_punc_level, spk_reading_punc;
|
||||
int spk_cur_phonetic;
|
||||
char spk_str_caps_start[MAXVARLEN + 1] = "\0";
|
||||
|
|
@ -183,13 +183,13 @@ char *spk_default_chars[256] = {
|
|||
/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
|
||||
};
|
||||
|
||||
/* array of 256 u_short (one for each character)
|
||||
/* array of 256 u16 (one for each character)
|
||||
* initialized to default_chartab and user selectable via
|
||||
* /sys/module/speakup/parameters/chartab
|
||||
*/
|
||||
u_short spk_chartab[256];
|
||||
u16 spk_chartab[256];
|
||||
|
||||
static u_short default_chartab[256] = {
|
||||
static u16 default_chartab[256] = {
|
||||
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
|
||||
B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
|
||||
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
|
||||
|
|
@ -267,10 +267,10 @@ static void speakup_date(struct vc_data *vc)
|
|||
spk_y = spk_cy = vc->state.y;
|
||||
spk_pos = spk_cp = vc->vc_pos;
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)spk_pos);
|
||||
spk_attr = get_attributes(vc, (u16 *)spk_pos);
|
||||
}
|
||||
|
||||
static void bleep(u_short val)
|
||||
static void bleep(u16 val)
|
||||
{
|
||||
static const short vals[] = {
|
||||
350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
|
||||
|
|
@ -346,14 +346,14 @@ static void speakup_cut(struct vc_data *vc)
|
|||
|
||||
if (!mark_cut_flag) {
|
||||
mark_cut_flag = 1;
|
||||
spk_xs = (u_short)spk_x;
|
||||
spk_ys = (u_short)spk_y;
|
||||
spk_xs = (u16)spk_x;
|
||||
spk_ys = (u16)spk_y;
|
||||
spk_sel_cons = vc;
|
||||
synth_printf("%s\n", spk_msg_get(MSG_MARK));
|
||||
return;
|
||||
}
|
||||
spk_xe = (u_short)spk_x;
|
||||
spk_ye = (u_short)spk_y;
|
||||
spk_xe = (u16)spk_x;
|
||||
spk_ye = (u16)spk_y;
|
||||
mark_cut_flag = 0;
|
||||
synth_printf("%s\n", spk_msg_get(MSG_CUT));
|
||||
|
||||
|
|
@ -482,7 +482,7 @@ static void say_char(struct vc_data *vc)
|
|||
u16 ch;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
|
||||
if (spk_attr != spk_old_attr) {
|
||||
if (spk_attrib_bleep & 1)
|
||||
bleep(spk_y);
|
||||
|
|
@ -497,7 +497,7 @@ static void say_phonetic_char(struct vc_data *vc)
|
|||
u16 ch;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &spk_attr);
|
||||
if (ch <= 0x7f && isalpha(ch)) {
|
||||
ch &= 0x1f;
|
||||
synth_printf("%s\n", phonetic[--ch]);
|
||||
|
|
@ -549,7 +549,7 @@ static u_long get_word(struct vc_data *vc)
|
|||
u_char temp;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
ch = get_char(vc, (u_short *)tmp_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos, &temp);
|
||||
|
||||
/* decided to take out the sayword if on a space (mis-information */
|
||||
if (spk_say_word_ctl && ch == SPACE) {
|
||||
|
|
@ -558,26 +558,26 @@ static u_long get_word(struct vc_data *vc)
|
|||
return 0;
|
||||
} else if (tmpx < vc->vc_cols - 2 &&
|
||||
(ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
|
||||
get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
|
||||
get_char(vc, (u16 *)tmp_pos + 1, &temp) > SPACE) {
|
||||
tmp_pos += 2;
|
||||
tmpx++;
|
||||
} else {
|
||||
while (tmpx > 0) {
|
||||
ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos - 1, &temp);
|
||||
if ((ch == SPACE || ch == 0 ||
|
||||
(ch < 0x100 && IS_WDLM(ch))) &&
|
||||
get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
|
||||
get_char(vc, (u16 *)tmp_pos, &temp) > SPACE)
|
||||
break;
|
||||
tmp_pos -= 2;
|
||||
tmpx--;
|
||||
}
|
||||
}
|
||||
attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
|
||||
attr_ch = get_char(vc, (u16 *)tmp_pos, &spk_attr);
|
||||
buf[cnt++] = attr_ch;
|
||||
while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) {
|
||||
tmp_pos += 2;
|
||||
tmpx++;
|
||||
ch = get_char(vc, (u_short *)tmp_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)tmp_pos, &temp);
|
||||
if (ch == SPACE || ch == 0 ||
|
||||
(buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
|
||||
ch > SPACE))
|
||||
|
|
@ -591,7 +591,7 @@ static u_long get_word(struct vc_data *vc)
|
|||
static void say_word(struct vc_data *vc)
|
||||
{
|
||||
u_long cnt = get_word(vc);
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
if (cnt == 0)
|
||||
return;
|
||||
|
|
@ -606,7 +606,7 @@ static void say_prev_word(struct vc_data *vc)
|
|||
u_char temp;
|
||||
u16 ch;
|
||||
enum edge edge_said = edge_none;
|
||||
u_short last_state = 0, state = 0;
|
||||
u16 last_state = 0, state = 0;
|
||||
|
||||
spk_parked |= 0x01;
|
||||
|
||||
|
|
@ -635,7 +635,7 @@ static void say_prev_word(struct vc_data *vc)
|
|||
spk_x--;
|
||||
}
|
||||
spk_pos -= 2;
|
||||
ch = get_char(vc, (u_short *)spk_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &temp);
|
||||
if (ch == SPACE || ch == 0)
|
||||
state = 0;
|
||||
else if (ch < 0x100 && IS_WDLM(ch))
|
||||
|
|
@ -661,7 +661,7 @@ static void say_next_word(struct vc_data *vc)
|
|||
u_char temp;
|
||||
u16 ch;
|
||||
enum edge edge_said = edge_none;
|
||||
u_short last_state = 2, state = 0;
|
||||
u16 last_state = 2, state = 0;
|
||||
|
||||
spk_parked |= 0x01;
|
||||
if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
|
||||
|
|
@ -669,7 +669,7 @@ static void say_next_word(struct vc_data *vc)
|
|||
return;
|
||||
}
|
||||
while (1) {
|
||||
ch = get_char(vc, (u_short *)spk_pos, &temp);
|
||||
ch = get_char(vc, (u16 *)spk_pos, &temp);
|
||||
if (ch == SPACE || ch == 0)
|
||||
state = 0;
|
||||
else if (ch < 0x100 && IS_WDLM(ch))
|
||||
|
|
@ -755,9 +755,9 @@ static int get_line(struct vc_data *vc)
|
|||
u_char tmp2;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)spk_pos);
|
||||
spk_attr = get_attributes(vc, (u16 *)spk_pos);
|
||||
for (i = 0; i < vc->vc_cols; i++) {
|
||||
buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
|
||||
buf[i] = get_char(vc, (u16 *)tmp, &tmp2);
|
||||
tmp += 2;
|
||||
}
|
||||
for (--i; i >= 0; i--)
|
||||
|
|
@ -770,7 +770,7 @@ static void say_line(struct vc_data *vc)
|
|||
{
|
||||
int i = get_line(vc);
|
||||
u16 *cp;
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
if (i == 0) {
|
||||
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
|
||||
|
|
@ -817,12 +817,12 @@ static int say_from_to(struct vc_data *vc, u_long from, u_long to,
|
|||
{
|
||||
int i = 0;
|
||||
u_char tmp;
|
||||
u_short saved_punc_mask = spk_punc_mask;
|
||||
u16 saved_punc_mask = spk_punc_mask;
|
||||
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)from);
|
||||
spk_attr = get_attributes(vc, (u16 *)from);
|
||||
while (from < to) {
|
||||
buf[i++] = get_char(vc, (u_short *)from, &tmp);
|
||||
buf[i++] = get_char(vc, (u16 *)from, &tmp);
|
||||
from += 2;
|
||||
if (i >= vc->vc_size_row)
|
||||
break;
|
||||
|
|
@ -895,10 +895,10 @@ static int get_sentence_buf(struct vc_data *vc, int read_punc)
|
|||
sentmarks[bn][0] = &sentbuf[bn][0];
|
||||
i = 0;
|
||||
spk_old_attr = spk_attr;
|
||||
spk_attr = get_attributes(vc, (u_short *)start);
|
||||
spk_attr = get_attributes(vc, (u16 *)start);
|
||||
|
||||
while (start < end) {
|
||||
sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
|
||||
sentbuf[bn][i] = get_char(vc, (u16 *)start, &tmp);
|
||||
if (i > 0) {
|
||||
if (sentbuf[bn][i] == SPACE &&
|
||||
sentbuf[bn][i - 1] == '.' &&
|
||||
|
|
@ -1047,7 +1047,7 @@ static void say_position(struct vc_data *vc)
|
|||
static void say_char_num(struct vc_data *vc)
|
||||
{
|
||||
u_char tmp;
|
||||
u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
|
||||
u16 ch = get_char(vc, (u16 *)spk_pos, &tmp);
|
||||
|
||||
synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
|
||||
}
|
||||
|
|
@ -1080,7 +1080,7 @@ static void spkup_write(const u16 *in_buf, int count)
|
|||
{
|
||||
static int rep_count;
|
||||
static u16 ch = '\0', old_ch = '\0';
|
||||
static u_short char_type, last_type;
|
||||
static u16 char_type, last_type;
|
||||
int in_count = count;
|
||||
|
||||
spk_keydown = 0;
|
||||
|
|
@ -1325,9 +1325,9 @@ void spk_reset_default_chartab(void)
|
|||
|
||||
static const struct st_bits_data *pb_edit;
|
||||
|
||||
static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
short mask = pb_edit->mask, ch_type = spk_chartab[ch];
|
||||
u16 mask = pb_edit->mask, ch_type = spk_chartab[ch];
|
||||
|
||||
if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
|
||||
return -1;
|
||||
|
|
@ -1947,7 +1947,7 @@ static void speakup_bits(struct vc_data *vc)
|
|||
spk_special_handler = edit_bits;
|
||||
}
|
||||
|
||||
static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
|
||||
static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u16 key)
|
||||
{
|
||||
static u_char goto_buf[8];
|
||||
static int num;
|
||||
|
|
@ -2105,7 +2105,7 @@ static void do_spkup(struct vc_data *vc, u_char value)
|
|||
static const char *pad_chars = "0123456789+-*/\015,.?()";
|
||||
|
||||
static int
|
||||
speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
|
||||
speakup_key(struct vc_data *vc, int shift_state, int keycode, u16 keysym,
|
||||
int up_flag)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
|
@ -2444,6 +2444,7 @@ 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:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include "speakup.h"
|
||||
|
||||
unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
struct vc_data *spk_sel_cons;
|
||||
|
||||
struct speakup_selection_work {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ int spk_set_num_var(int val, struct st_var_header *var, int how);
|
|||
int spk_set_string_var(const char *page, struct st_var_header *var, int len);
|
||||
int spk_set_mask_bits(const char *input, const int which, const int how);
|
||||
extern special_func spk_special_handler;
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key);
|
||||
int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u16 key);
|
||||
int synth_init(char *name);
|
||||
void synth_release(void);
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ void synth_writeu(const char *buf, size_t count);
|
|||
int synth_supports_indexing(void);
|
||||
|
||||
extern struct vc_data *spk_sel_cons;
|
||||
extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
extern u16 spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
|
||||
|
||||
extern wait_queue_head_t speakup_event;
|
||||
extern struct kobject *speakup_kobj;
|
||||
|
|
@ -95,20 +95,20 @@ extern struct st_spk_t *speakup_console[];
|
|||
extern struct spk_synth *synth;
|
||||
extern char spk_pitch_buff[];
|
||||
extern u_char *spk_our_keys[];
|
||||
extern short spk_punc_masks[];
|
||||
extern u16 spk_punc_masks[];
|
||||
extern char spk_str_caps_start[], spk_str_caps_stop[], spk_str_pause[];
|
||||
extern bool spk_paused;
|
||||
extern const struct st_bits_data spk_punc_info[];
|
||||
extern u_char spk_key_buf[600];
|
||||
extern char *spk_characters[];
|
||||
extern char *spk_default_chars[];
|
||||
extern u_short spk_chartab[];
|
||||
extern u16 spk_chartab[];
|
||||
extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level;
|
||||
extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps;
|
||||
extern int spk_bleep_time, spk_bell_pos;
|
||||
extern int spk_spell_delay, spk_key_echo;
|
||||
extern int spk_cur_phonetic;
|
||||
extern short spk_punc_mask;
|
||||
extern u16 spk_punc_mask;
|
||||
extern short spk_pitch_shift, synth_flags;
|
||||
extern bool spk_quiet_boot;
|
||||
extern char *synth_name;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* speakup_acntpc.h - header file for speakups Accent-PC driver. */
|
||||
#ifndef _SPEAKUP_ACNT_H
|
||||
#define _SPEAKUP_ACNT_H
|
||||
|
||||
#define SYNTH_IO_EXTENT 0x02
|
||||
|
||||
|
|
@ -17,3 +19,4 @@
|
|||
#define SYNTH_FULL 'F' /* synth is full. */
|
||||
#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */
|
||||
#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
#define STAT_new_index 0x0040 /* new last index ready */
|
||||
#define STAT_new_status 0x0080 /* new status posted */
|
||||
#define STAT_dma_state 0x0100 /* dma state toggle */
|
||||
#define STAT_index_valid 0x0200 /* indexs are valid */
|
||||
#define STAT_index_valid 0x0200 /* indexes are valid */
|
||||
#define STAT_flushing 0x0400 /* flush in progress */
|
||||
#define STAT_self_test 0x0800 /* module in self test */
|
||||
#define MODE_ready 0xc000 /* module ready for next phase */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */
|
||||
#ifndef _SPEAKUP_DTLK_H
|
||||
#define _SPEAKUP_DTLK_H
|
||||
|
||||
#define SYNTH_IO_EXTENT 0x02
|
||||
#define SYNTH_CLEAR 0x18 /* stops speech */
|
||||
|
|
@ -39,7 +41,7 @@
|
|||
|
||||
/* data returned by Interrogate command */
|
||||
struct synth_settings {
|
||||
u_short serial_number; /* 0-7Fh:0-7Fh */
|
||||
u16 serial_number; /* 0-7Fh:0-7Fh */
|
||||
u_char rom_version[24]; /* null terminated string */
|
||||
u_char mode; /* 0=Character; 1=Phoneme; 2=Text */
|
||||
u_char punc_level; /* nB; 0-7 */
|
||||
|
|
@ -61,3 +63,4 @@ struct synth_settings {
|
|||
*/
|
||||
u_char has_indexing; /* nonzero if indexing is implemented */
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -446,8 +446,9 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var)
|
|||
if (var->var_id != PUNC_LEVEL)
|
||||
return 0;
|
||||
|
||||
/* We want to set the the speech synthesis punctuation level
|
||||
* accordingly, so it properly tunes speaking A_PUNC characters */
|
||||
/* We want to set the speech synthesis punctuation level
|
||||
* accordingly, so it properly tunes speaking A_PUNC characters
|
||||
*/
|
||||
var_data = var->data;
|
||||
if (!var_data)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ enum var_id_t {
|
|||
};
|
||||
|
||||
typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch,
|
||||
u_short key);
|
||||
u16 key);
|
||||
|
||||
#define COLOR_BUFFER_SIZE 160
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ int spk_synth_is_alive_restart(struct spk_synth *synth)
|
|||
/* restart */
|
||||
synth->alive = 1;
|
||||
synth_printf("%s", synth->init);
|
||||
return 2; /* reenabled */
|
||||
return 2; /* re-enabled */
|
||||
}
|
||||
pr_warn("%s: can't restart synth\n", synth->long_name);
|
||||
return 0;
|
||||
|
|
@ -574,4 +574,4 @@ struct spk_synth *synth_current(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(synth_current);
|
||||
|
||||
short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
|
||||
u16 spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ static inline void open_input(const char *dir_name, const char *name)
|
|||
else
|
||||
snprintf(filename, sizeof(filename), "%s", name);
|
||||
infile = fopen(filename, "r");
|
||||
if (infile == 0) {
|
||||
if (!infile) {
|
||||
fprintf(stderr, "can't open %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC
|
|||
|
||||
config ANDROID_BINDER_IPC_RUST
|
||||
bool "Rust version of Android Binder IPC Driver"
|
||||
depends on RUST && MMU && !ANDROID_BINDER_IPC
|
||||
depends on RUST && MMU && NET && !ANDROID_BINDER_IPC
|
||||
help
|
||||
This enables the Rust implementation of the Binder driver.
|
||||
|
||||
|
|
|
|||
|
|
@ -384,8 +384,8 @@ 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.
|
||||
|
|
|
|||
76
drivers/android/binder/debug.rs
Normal file
76
drivers/android/binder/debug.rs
Normal 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)*)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
117
drivers/android/binder/netlink.rs
Normal file
117
drivers/android/binder/netlink.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) };
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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<()> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1026,26 +1026,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
|
|||
struct mhi_ep_chan *mhi_chan;
|
||||
int i;
|
||||
|
||||
/* Stop all the channels */
|
||||
/* Disable all the channels to prevent new transfers */
|
||||
for (i = 0; i < mhi_cntrl->max_chan; i++) {
|
||||
mhi_chan = &mhi_cntrl->mhi_chan[i];
|
||||
if (!mhi_chan->ring.started)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
/* Drain ring workers and in-flight transfers before notifying disconnect */
|
||||
flush_workqueue(mhi_cntrl->wq);
|
||||
if (mhi_cntrl->flush_async)
|
||||
mhi_cntrl->flush_async(mhi_cntrl);
|
||||
|
||||
/* Send channel disconnect status to client drivers */
|
||||
for (i = 0; i < mhi_cntrl->max_chan; i++) {
|
||||
mhi_chan = &mhi_cntrl->mhi_chan[i];
|
||||
if (!mhi_chan->ring.started)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
/* Send channel disconnect status to client drivers */
|
||||
if (mhi_chan->xfer_cb) {
|
||||
result.transaction_status = -ENOTCONN;
|
||||
result.bytes_xferd = 0;
|
||||
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
|
||||
}
|
||||
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
flush_workqueue(mhi_cntrl->wq);
|
||||
|
||||
/* Destroy devices associated with all channels */
|
||||
device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
|
||||
|
||||
|
|
@ -1340,14 +1351,19 @@ static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
|
|||
ret = dev_set_name(&mhi_dev->dev, "%s_%s",
|
||||
dev_name(&mhi_cntrl->mhi_dev->dev),
|
||||
mhi_dev->name);
|
||||
if (ret) {
|
||||
put_device(&mhi_dev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_put_channels;
|
||||
|
||||
ret = device_add(&mhi_dev->dev);
|
||||
if (ret)
|
||||
put_device(&mhi_dev->dev);
|
||||
goto err_put_channels;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_channels:
|
||||
put_device(&mhi_dev->dev); /* DL channel reference */
|
||||
put_device(&mhi_dev->dev); /* UL channel reference */
|
||||
put_device(&mhi_dev->dev); /* device_initialize() reference */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1614,6 +1630,7 @@ static void mhi_ep_remove(struct device *dev)
|
|||
{
|
||||
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
|
||||
struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
|
||||
struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_result result = {};
|
||||
struct mhi_ep_chan *mhi_chan;
|
||||
int dir;
|
||||
|
|
@ -1622,6 +1639,22 @@ static void mhi_ep_remove(struct device *dev)
|
|||
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
|
||||
return;
|
||||
|
||||
/* Disable the channels to prevent new transfers */
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
|
||||
|
||||
if (!mhi_chan)
|
||||
continue;
|
||||
|
||||
mutex_lock(&mhi_chan->lock);
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
||||
/* Flush in-flight transfers before notifying disconnect */
|
||||
if (mhi_cntrl->flush_async)
|
||||
mhi_cntrl->flush_async(mhi_cntrl);
|
||||
|
||||
/* Disconnect the channels associated with the driver */
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
|
||||
|
|
@ -1637,7 +1670,6 @@ static void mhi_ep_remove(struct device *dev)
|
|||
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
|
||||
}
|
||||
|
||||
mhi_chan->state = MHI_CH_STATE_DISABLED;
|
||||
mhi_chan->xfer_cb = NULL;
|
||||
mutex_unlock(&mhi_chan->lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1029,7 +1029,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
|
|||
if (mhi_cntrl->edl_trigger) {
|
||||
ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
|
||||
if (ret)
|
||||
goto err_release_dev;
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
mhi_cntrl->mhi_dev = mhi_dev;
|
||||
|
|
@ -1038,6 +1038,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
|
|||
|
||||
return 0;
|
||||
|
||||
err_del_dev:
|
||||
device_del(&mhi_dev->dev);
|
||||
err_release_dev:
|
||||
put_device(&mhi_dev->dev);
|
||||
error_setup_irq:
|
||||
|
|
|
|||
|
|
@ -170,6 +170,9 @@ EXPORT_SYMBOL_GPL(mhi_get_mhi_state);
|
|||
|
||||
void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
int __maybe_unused ret;
|
||||
u32 tmp;
|
||||
|
||||
if (mhi_cntrl->reset) {
|
||||
mhi_cntrl->reset(mhi_cntrl);
|
||||
return;
|
||||
|
|
@ -178,6 +181,9 @@ void mhi_soc_reset(struct mhi_controller *mhi_cntrl)
|
|||
/* Generic MHI SoC reset */
|
||||
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
|
||||
MHI_SOC_RESET_REQ);
|
||||
/* Flush the posted write to the device (ignore return value) */
|
||||
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHI_SOC_RESET_REQ_OFFSET,
|
||||
&tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_soc_reset);
|
||||
|
||||
|
|
|
|||
|
|
@ -491,6 +491,8 @@ static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
|
|||
static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
|
||||
|
|
@ -506,6 +508,8 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
|
|||
static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
|
||||
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
|
||||
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
|
||||
|
|
@ -1395,6 +1399,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width);
|
||||
mhi_cntrl->fw_image = info->fw;
|
||||
mhi_cntrl->edl_image = info->edl;
|
||||
mhi_cntrl->no_m3 = info->no_m3;
|
||||
|
||||
mhi_cntrl->read_reg = mhi_pci_read_reg;
|
||||
mhi_cntrl->write_reg = mhi_pci_write_reg;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
|
|||
|
||||
sysfs_bin_attr_init(res_attr);
|
||||
|
||||
cdx_dev->res_attr[num] = res_attr;
|
||||
sprintf(res_attr_name, "resource%d", num);
|
||||
|
||||
res_attr->mmap = cdx_mmap_resource;
|
||||
|
|
@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
|
|||
res_attr->size = cdx_resource_len(cdx_dev, num);
|
||||
res_attr->private = (void *)(unsigned long)num;
|
||||
ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(res_attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cdx_dev->res_attr[num] = res_attr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ static int __op_panel_update_display(void)
|
|||
static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
|
||||
size_t len, loff_t *f_pos)
|
||||
{
|
||||
loff_t f_pos_prev = *f_pos;
|
||||
ssize_t ret;
|
||||
int rc;
|
||||
|
||||
|
|
@ -105,7 +104,6 @@ static ssize_t oppanel_write(struct file *filp, const char __user *userbuf,
|
|||
if (rc != OPAL_SUCCESS) {
|
||||
pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n",
|
||||
rc);
|
||||
*f_pos = f_pos_prev;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -340,15 +340,17 @@ static enum ieee1284_phase init_phase(int mode)
|
|||
return IEEE1284_PH_FWD_IDLE;
|
||||
}
|
||||
|
||||
static int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
|
||||
static int pp_set_timeout(struct pardevice *pdev, s64 tv_sec, s64 tv_usec)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
long to_jiffies;
|
||||
|
||||
if ((tv_sec < 0) || (tv_usec < 0))
|
||||
if (tv_sec < 0 || tv_usec < 0 || tv_usec >= USEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
to_jiffies = usecs_to_jiffies(tv_usec);
|
||||
to_jiffies += tv_sec * HZ;
|
||||
ts.tv_sec = tv_sec;
|
||||
ts.tv_nsec = tv_usec * NSEC_PER_USEC;
|
||||
to_jiffies = timespec64_to_jiffies(&ts);
|
||||
if (to_jiffies <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
|||
static ssize_t store_received_ref_clk3a(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -350,7 +350,7 @@ static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_received_ref_clk3b(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_enable_clk3b_output(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -394,7 +394,7 @@ static ssize_t store_enable_clk3a_output(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -415,7 +415,7 @@ static ssize_t store_enable_clkb1_output(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -437,7 +437,7 @@ static ssize_t store_enable_clka1_output(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -458,7 +458,7 @@ static ssize_t store_enable_clkb0_output(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -479,7 +479,7 @@ static ssize_t store_enable_clka0_output(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -500,7 +500,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -541,7 +541,7 @@ static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_select_amcb1_transmit_clock(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -583,7 +583,7 @@ static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_select_redundant_clock(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -604,7 +604,7 @@ static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_select_ref_frequency(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -625,7 +625,7 @@ static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_filter_select(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -645,7 +645,7 @@ static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
|
|||
static ssize_t store_hardware_switching_mode(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -666,7 +666,7 @@ static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_hardware_switching(struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -687,7 +687,7 @@ static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
|
|||
static ssize_t store_refalign (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned long flags;
|
||||
|
||||
sscanf(buf, "%lX", &tmp);
|
||||
|
|
@ -706,7 +706,7 @@ static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
|
|||
static ssize_t store_mode_select (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -726,7 +726,7 @@ static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
|
|||
static ssize_t store_reset (struct device *d,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 0;
|
||||
unsigned char val;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
|
|||
|
|
@ -506,11 +506,8 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
|
|||
ret = devm_request_irq(dev, irq, tpm_ioserirq_handler,
|
||||
IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
|
||||
chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
|
||||
irq);
|
||||
if (ret < 0)
|
||||
goto _tpm_clean_answer;
|
||||
}
|
||||
|
||||
intmask |= TPM_INTF_CMD_READY_INT
|
||||
| TPM_INTF_STS_VALID_INT
|
||||
|
|
|
|||
|
|
@ -574,8 +574,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client)
|
|||
dev_name(&chip->dev),
|
||||
chip);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s() Unable to request irq: %d for use\n",
|
||||
__func__, priv->irq);
|
||||
priv->irq = 0;
|
||||
} else {
|
||||
chip->flags |= TPM_CHIP_FLAG_IRQ;
|
||||
|
|
|
|||
|
|
@ -751,10 +751,8 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
|
|||
rc = devm_request_irq(dev, client->irq, tpm_cr50_i2c_int_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
|
||||
dev->driver->name, chip);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "Failed to probe IRQ %d\n", client->irq);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
priv->irq = client->irq;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -760,7 +760,7 @@ static int __init hwicap_module_init(void)
|
|||
HWICAP_DEVICES,
|
||||
DRIVER_NAME);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
goto failed_class;
|
||||
|
||||
retval = platform_driver_register(&hwicap_platform_driver);
|
||||
if (retval)
|
||||
|
|
@ -771,6 +771,9 @@ static int __init hwicap_module_init(void)
|
|||
failed:
|
||||
unregister_chrdev_region(devt, HWICAP_DEVICES);
|
||||
|
||||
failed_class:
|
||||
class_unregister(&icap_class);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,11 +55,8 @@ static int xilly_drv_probe(struct platform_device *op)
|
|||
|
||||
rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint);
|
||||
|
||||
if (rc) {
|
||||
dev_err(endpoint->dev,
|
||||
"Failed to register IRQ handler. Aborting.\n");
|
||||
if (rc)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return xillybus_endpoint_discovery(endpoint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,11 +83,8 @@ static int xilly_probe(struct pci_dev *pdev,
|
|||
}
|
||||
rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
|
||||
xillyname, endpoint);
|
||||
if (rc) {
|
||||
dev_err(endpoint->dev,
|
||||
"Failed to register MSI handler. Aborting.\n");
|
||||
if (rc)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ static irqreturn_t aio_iiro_16_cos(int irq, void *d)
|
|||
unsigned int status;
|
||||
unsigned int val;
|
||||
|
||||
if (!dev->attached)
|
||||
return IRQ_NONE;
|
||||
|
||||
status = inb(dev->iobase + AIO_IIRO_16_STATUS);
|
||||
if (!(status & AIO_IIRO_16_STATUS_IRQE))
|
||||
return IRQ_NONE;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user