Staging driver tree update for 3.10-rc1
Here's the big staging driver tree update for 3.10-rc1 This update contains loads of comedi driver cleanups and fixes in here, iio updates, android driver changes, and other various staging driver cleanups. Thanks to some drivers being removed, and the comedi driver cleanups, we have removed more code than we added: 627 files changed, 65145 insertions(+), 76321 deletions(-) which is always nice to see. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlF+nF8ACgkQMUfUDdst+ynlIwCfYm2pSkA0w1K56mftq1T0hpMH b9IAmwQlfEHSIKeAxqRO3RRrfLu5XD7L =Jnxr -----END PGP SIGNATURE----- Merge tag 'staging-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver tree update from Greg Kroah-Hartman: "Here's the big staging driver tree update for 3.10-rc1 This update contains loads of comedi driver cleanups and fixes in here, iio updates, android driver changes, and other various staging driver cleanups. Thanks to some drivers being removed, and the comedi driver cleanups, we have removed more code than we added: 627 files changed, 65145 insertions(+), 76321 deletions(-) which is always nice to see. All of these have been in linux-next for a while." * tag 'staging-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (940 commits) staging: comedi: ni_labpc: fix legacy driver build staging: comedi: das800: cleanup the cio-das802/16 fifo comments staging: comedi: das800: rename CamelCase vars in das800_ai_do_cmd() staging: comedi: das800: tidy up the private data staging: comedi: das800: tidy up das800_interrupt() staging: comedi: das800: tidy up das800_ai_insn_read() staging: comedi: das800: tidy up das800_di_insn_bits() staging: comedi: das800: tidy up das800_do_insn_bits() staging: comedi: das800: remove extra divisor calculation call staging: comedi: das800: rename {enable,disable}_das800 staging: comedi: das800: tidy up subdevice init staging: comedi: das800: allow attaching without interrupt support staging: comedi: das800: interrupts are required for async command support staging: comedi: das800: tidy up das800_ai_do_cmdtest() staging: comedi: das800: remove 'volatile' on private data variables staging: comedi: das800: cleanup the boardinfo staging: comedi: das800: cleanup range table declarations staging: comedi: das800: introduce das800_ind_{write, read}() staging: comedi: das800: remove forward declarations staging: comedi: das800: move das800_set_frequency() ...
This commit is contained in:
Коммит
fdc719b63a
|
@ -14,9 +14,19 @@ Required properties:
|
|||
- atmel,adc-status-register: Offset of the Interrupt Status Register
|
||||
- atmel,adc-trigger-register: Offset of the Trigger Register
|
||||
- atmel,adc-vref: Reference voltage in millivolts for the conversions
|
||||
- atmel,adc-res: List of resolution in bits supported by the ADC. List size
|
||||
must be two at least.
|
||||
- atmel,adc-res-names: Contains one identifier string for each resolution
|
||||
in atmel,adc-res property. "lowres" and "highres"
|
||||
identifiers are required.
|
||||
|
||||
Optional properties:
|
||||
- atmel,adc-use-external: Boolean to enable of external triggers
|
||||
- atmel,adc-use-res: String corresponding to an identifier from
|
||||
atmel,adc-res-names property. If not specified, the highest
|
||||
resolution will be used.
|
||||
- atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
|
||||
- atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
|
||||
|
||||
Optional trigger Nodes:
|
||||
- Required properties:
|
||||
|
@ -40,6 +50,9 @@ adc0: adc@fffb0000 {
|
|||
atmel,adc-trigger-register = <0x08>;
|
||||
atmel,adc-use-external;
|
||||
atmel,adc-vref = <3300>;
|
||||
atmel,adc-res = <8 10>;
|
||||
atmel,adc-res-names = "lowres", "highres";
|
||||
atmel,adc-use-res = "lowres";
|
||||
|
||||
trigger@0 {
|
||||
trigger-name = "external-rising";
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
Samsung Exynos Analog to Digital Converter bindings
|
||||
|
||||
The devicetree bindings are for the new ADC driver written for
|
||||
Exynos4 and upward SoCs from Samsung.
|
||||
|
||||
New driver handles the following
|
||||
1. Supports ADC IF found on EXYNOS4412/EXYNOS5250
|
||||
and future SoCs from Samsung
|
||||
2. Add ADC driver under iio/adc framework
|
||||
3. Also adds the Documentation for device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "samsung,exynos-adc-v1"
|
||||
for exynos4412/5250 controllers.
|
||||
Must be "samsung,exynos-adc-v2" for
|
||||
future controllers.
|
||||
- reg: Contains ADC register address range (base address and
|
||||
length) and the address of the phy enable register.
|
||||
- interrupts: Contains the interrupt information for the timer. The
|
||||
format is being dependent on which interrupt controller
|
||||
the Samsung device uses.
|
||||
- #io-channel-cells = <1>; As ADC has multiple outputs
|
||||
- clocks From common clock binding: handle to adc clock.
|
||||
- clock-names From common clock binding: Shall be "adc".
|
||||
- vdd-supply VDD input supply.
|
||||
|
||||
Note: child nodes can be added for auto probing from device tree.
|
||||
|
||||
Example: adding device info in dtsi file
|
||||
|
||||
adc: adc@12D10000 {
|
||||
compatible = "samsung,exynos-adc-v1";
|
||||
reg = <0x12D10000 0x100>, <0x10040718 0x4>;
|
||||
interrupts = <0 106 0>;
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
|
||||
clocks = <&clock 303>;
|
||||
clock-names = "adc";
|
||||
|
||||
vdd-supply = <&buck5_reg>;
|
||||
};
|
||||
|
||||
|
||||
Example: Adding child nodes in dts file
|
||||
|
||||
adc@12D10000 {
|
||||
|
||||
/* NTC thermistor is a hwmon device */
|
||||
ncp15wb473@0 {
|
||||
compatible = "ntc,ncp15wb473";
|
||||
pullup-uV = <1800000>;
|
||||
pullup-ohm = <47000>;
|
||||
pulldown-ohm = <0>;
|
||||
io-channels = <&adc 4>;
|
||||
};
|
||||
};
|
||||
|
||||
Note: Does not apply to ADC driver under arch/arm/plat-samsung/
|
||||
Note: The child node can be added under the adc node or separately.
|
|
@ -0,0 +1,97 @@
|
|||
This binding is derived from clock bindings, and based on suggestions
|
||||
from Lars-Peter Clausen [1].
|
||||
|
||||
Sources of IIO channels can be represented by any node in the device
|
||||
tree. Those nodes are designated as IIO providers. IIO consumer
|
||||
nodes use a phandle and IIO specifier pair to connect IIO provider
|
||||
outputs to IIO inputs. Similar to the gpio specifiers, an IIO
|
||||
specifier is an array of one or more cells identifying the IIO
|
||||
output on a device. The length of an IIO specifier is defined by the
|
||||
value of a #io-channel-cells property in the IIO provider node.
|
||||
|
||||
[1] http://marc.info/?l=linux-iio&m=135902119507483&w=2
|
||||
|
||||
==IIO providers==
|
||||
|
||||
Required properties:
|
||||
#io-channel-cells: Number of cells in an IIO specifier; Typically 0 for nodes
|
||||
with a single IIO output and 1 for nodes with multiple
|
||||
IIO outputs.
|
||||
|
||||
Example for a simple configuration with no trigger:
|
||||
|
||||
adc: voltage-sensor@35 {
|
||||
compatible = "maxim,max1139";
|
||||
reg = <0x35>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
Example for a configuration with trigger:
|
||||
|
||||
adc@35 {
|
||||
compatible = "some-vendor,some-adc";
|
||||
reg = <0x35>;
|
||||
|
||||
adc1: iio-device@0 {
|
||||
#io-channel-cells = <1>;
|
||||
/* other properties */
|
||||
};
|
||||
adc2: iio-device@1 {
|
||||
#io-channel-cells = <1>;
|
||||
/* other properties */
|
||||
};
|
||||
};
|
||||
|
||||
==IIO consumers==
|
||||
|
||||
Required properties:
|
||||
io-channels: List of phandle and IIO specifier pairs, one pair
|
||||
for each IIO input to the device. Note: if the
|
||||
IIO provider specifies '0' for #io-channel-cells,
|
||||
then only the phandle portion of the pair will appear.
|
||||
|
||||
Optional properties:
|
||||
io-channel-names:
|
||||
List of IIO input name strings sorted in the same
|
||||
order as the io-channels property. Consumers drivers
|
||||
will use io-channel-names to match IIO input names
|
||||
with IIO specifiers.
|
||||
io-channel-ranges:
|
||||
Empty property indicating that child nodes can inherit named
|
||||
IIO channels from this node. Useful for bus nodes to provide
|
||||
and IIO channel to their children.
|
||||
|
||||
For example:
|
||||
|
||||
device {
|
||||
io-channels = <&adc 1>, <&ref 0>;
|
||||
io-channel-names = "vcc", "vdd";
|
||||
};
|
||||
|
||||
This represents a device with two IIO inputs, named "vcc" and "vdd".
|
||||
The vcc channel is connected to output 1 of the &adc device, and the
|
||||
vdd channel is connected to output 0 of the &ref device.
|
||||
|
||||
==Example==
|
||||
|
||||
adc: max1139@35 {
|
||||
compatible = "maxim,max1139";
|
||||
reg = <0x35>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
iio_hwmon {
|
||||
compatible = "iio-hwmon";
|
||||
io-channels = <&adc 0>, <&adc 1>, <&adc 2>,
|
||||
<&adc 3>, <&adc 4>, <&adc 5>,
|
||||
<&adc 6>, <&adc 7>, <&adc 8>,
|
||||
<&adc 9>;
|
||||
};
|
||||
|
||||
some_consumer {
|
||||
compatible = "some-consumer";
|
||||
io-channels = <&adc 10>, <&adc 11>;
|
||||
io-channel-names = "adc1", "adc2";
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
Platform DesignWare HS OTG USB 2.0 controller
|
||||
-----------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : "snps,dwc2"
|
||||
- reg : Should contain 1 register range (address and length)
|
||||
- interrupts : Should contain 1 interrupt
|
||||
|
||||
Example:
|
||||
|
||||
usb@101c0000 {
|
||||
compatible = "ralink,rt3050-usb, snps,dwc2";
|
||||
reg = <0x101c0000 40000>;
|
||||
interrupts = <18>;
|
||||
};
|
|
@ -26,7 +26,7 @@ Required properties:
|
|||
- crtc: the crtc this display is connected to, see below
|
||||
Optional properties:
|
||||
- interface_pix_fmt: How this display is connected to the
|
||||
crtc. Currently supported types: "rgb24", "rgb565"
|
||||
crtc. Currently supported types: "rgb24", "rgb565", "bgr666"
|
||||
- edid: verbatim EDID data block describing attached display.
|
||||
- ddc: phandle describing the i2c bus handling the display data
|
||||
channel
|
||||
|
|
12
MAINTAINERS
12
MAINTAINERS
|
@ -2459,6 +2459,12 @@ M: Matthew Garrett <mjg59@srcf.ucam.org>
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi.c
|
||||
|
||||
DESIGNWARE USB2 DRD IP DRIVER
|
||||
M: Paul Zimmerman <paulz@synopsys.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/dwc2/
|
||||
|
||||
DESIGNWARE USB3 DRD IP DRIVER
|
||||
M: Felipe Balbi <balbi@ti.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
|
@ -7933,6 +7939,12 @@ T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/thinkpad_acpi.c
|
||||
|
||||
TI BANDGAP AND THERMAL DRIVER
|
||||
M: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/omap-thermal/
|
||||
|
||||
TI FLASH MEDIA INTERFACE DRIVER
|
||||
M: Alex Dubov <oakad@yahoo.com>
|
||||
S: Maintained
|
||||
|
|
|
@ -519,6 +519,15 @@ config SENSORS_IBMPEX
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called ibmpex.
|
||||
|
||||
config SENSORS_IIO_HWMON
|
||||
tristate "Hwmon driver that uses channels specified via iio maps"
|
||||
depends on IIO
|
||||
help
|
||||
This is a platform driver that in combination with a suitable
|
||||
map allows IIO devices to provide basic hwmon functionality
|
||||
for those channels specified in the map. This map can be provided
|
||||
either via platform data or the device tree bindings.
|
||||
|
||||
config SENSORS_IT87
|
||||
tristate "ITE IT87xx and compatibles"
|
||||
depends on !PPC
|
||||
|
|
|
@ -67,6 +67,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
|
|||
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
|
||||
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
|
||||
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
|
||||
obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
|
||||
obj-$(CONFIG_SENSORS_INA209) += ina209.o
|
||||
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
|
||||
obj-$(CONFIG_SENSORS_IT87) += it87.o
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
@ -58,7 +59,12 @@ static ssize_t iio_hwmon_read_val(struct device *dev,
|
|||
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "iio_hwmon\n");
|
||||
const char *name = "iio_hwmon";
|
||||
|
||||
if (dev->of_node && dev->of_node->name)
|
||||
name = dev->of_node->name;
|
||||
|
||||
return sprintf(buf, "%s\n", name);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
|
@ -60,28 +60,28 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -177,8 +177,8 @@ error_ret:
|
|||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = KXSD9_REG_##axis, \
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ static const struct iio_chan_spec kxsd9_channels[] = {
|
|||
KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.indexed = 1,
|
||||
.address = KXSD9_REG_AUX,
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,18 @@ config AD7298
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7923
|
||||
tristate "Analog Devices AD7923 and similar ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7904, AD7914, AD7923, AD7924 4 Channel ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7923.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
|
@ -91,6 +103,14 @@ config AT91_ADC
|
|||
help
|
||||
Say yes here to build support for Atmel AT91 ADC.
|
||||
|
||||
config EXYNOS_ADC
|
||||
bool "Exynos ADC driver support"
|
||||
depends on OF
|
||||
help
|
||||
Core support for the ADC block found in the Samsung EXYNOS series
|
||||
of SoCs for drivers such as the touchscreen and hwmon to use to share
|
||||
this resource.
|
||||
|
||||
config LP8788_ADC
|
||||
bool "LP8788 ADC driver"
|
||||
depends on MFD_LP8788
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
|
|
|
@ -201,9 +201,9 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
|
|||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \
|
||||
| IIO_CHAN_INFO_SCALE_SHARED_BIT \
|
||||
| IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = (_sign), \
|
||||
|
@ -249,9 +249,9 @@ static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's');
|
|||
.channel = (_chan) * 2, \
|
||||
.channel2 = (_chan) * 2 + 1, \
|
||||
.address = (_chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \
|
||||
| IIO_CHAN_INFO_SCALE_SHARED_BIT \
|
||||
| IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = _sign, \
|
||||
|
|
|
@ -63,8 +63,8 @@ struct ad7298_state {
|
|||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
|
@ -80,9 +80,9 @@ static const struct iio_chan_spec ad7298_channels[] = {
|
|||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = AD7298_CH_TEMP,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
|
|
|
@ -140,12 +140,12 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define _AD7476_CHAN(bits, _shift, _info_mask) \
|
||||
#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.info_mask = _info_mask | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = _info_mask_sep, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
|
@ -156,9 +156,9 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
|
|
|
@ -207,8 +207,8 @@ static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
|
|||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 1,
|
||||
.scan_index = 1,
|
||||
.scan_type = IIO_ST('u', 12, 16, 0),
|
||||
|
@ -217,8 +217,8 @@ static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
|
|||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 0,
|
||||
.scan_index = 0,
|
||||
.scan_type = IIO_ST('u', 12, 16, 0),
|
||||
|
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD7923_WRITE_CR (1 << 11) /* write control register */
|
||||
#define AD7923_RANGE (1 << 1) /* range to REFin */
|
||||
#define AD7923_CODING (1 << 0) /* coding is straight binary */
|
||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
||||
#define AD7923_CHANNEL_0 (0) /* analog input 0 */
|
||||
#define AD7923_CHANNEL_1 (1) /* analog input 1 */
|
||||
#define AD7923_CHANNEL_2 (2) /* analog input 2 */
|
||||
#define AD7923_CHANNEL_3 (3) /* analog input 3 */
|
||||
#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
|
||||
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
||||
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
||||
|
||||
#define AD7923_MAX_CHAN 4
|
||||
|
||||
#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
|
||||
#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
|
||||
+ ((sequence & 2) << 9))
|
||||
/* write sequence fonction */
|
||||
/* left shift for CR : bit 11 transmit in first */
|
||||
#define AD7923_SHIFT_REGISTER 4
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
|
||||
|
||||
struct ad7923_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[5];
|
||||
struct spi_transfer scan_single_xfer[2];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
struct regulator *reg;
|
||||
|
||||
unsigned int settings;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[4] ____cacheline_aligned;
|
||||
__be16 tx_buf[4];
|
||||
};
|
||||
|
||||
struct ad7923_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum ad7923_id {
|
||||
AD7904,
|
||||
AD7914,
|
||||
AD7924,
|
||||
};
|
||||
|
||||
#define AD7923_V_CHAN(index, bits) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7923_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
AD7923_V_CHAN(0, bits), \
|
||||
AD7923_V_CHAN(1, bits), \
|
||||
AD7923_V_CHAN(2, bits), \
|
||||
AD7923_V_CHAN(3, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7923_CHANNELS(ad7904, 8);
|
||||
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
||||
static DECLARE_AD7923_CHANNELS(ad7924, 12);
|
||||
|
||||
static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||
[AD7904] = {
|
||||
.channels = ad7904_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7904_channels),
|
||||
},
|
||||
[AD7914] = {
|
||||
.channels = ad7914_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7914_channels),
|
||||
},
|
||||
[AD7924] = {
|
||||
.channels = ad7924_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7924_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) {
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
}
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = len;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 1].len = 2;
|
||||
st->ring_xfer[i + 1].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7923_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7923_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns = 0;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
time_ns = iio_get_time_ns();
|
||||
memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ad7923_get_range(struct ad7923_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
if (!(st->settings & AD7923_RANGE))
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int ad7923_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7923_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address == EXTRACT(ret, 12, 4))
|
||||
*val = EXTRACT(ret, 0, 12);
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ad7923_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7923_info = {
|
||||
.read_raw = &ad7923_read_raw,
|
||||
.update_scan_mode = ad7923_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7923_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7923_state *st;
|
||||
struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st));
|
||||
const struct ad7923_chip_info *info;
|
||||
int ret;
|
||||
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = AD7923_CODING | AD7923_RANGE |
|
||||
AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
|
||||
|
||||
info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ad7923_info;
|
||||
|
||||
/* Setup default message */
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
|
||||
spi_message_init(&st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
|
||||
|
||||
st->reg = regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg)) {
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_free;
|
||||
}
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7923_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
regulator_put(st->reg);
|
||||
error_free:
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7923_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7923_id[] = {
|
||||
{"ad7904", AD7904},
|
||||
{"ad7914", AD7914},
|
||||
{"ad7923", AD7924},
|
||||
{"ad7924", AD7924},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7923_id);
|
||||
|
||||
static struct spi_driver ad7923_driver = {
|
||||
.driver = {
|
||||
.name = "ad7923",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7923_probe,
|
||||
.remove = ad7923_remove,
|
||||
.id_table = ad7923_id,
|
||||
};
|
||||
module_spi_driver(ad7923_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -470,7 +470,7 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
|
|||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
}
|
||||
sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
|
||||
sigma_delta->trig->private_data = sigma_delta;
|
||||
iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
|
||||
|
||||
ret = iio_trigger_register(sigma_delta->trig);
|
||||
if (ret)
|
||||
|
|
|
@ -52,11 +52,15 @@ struct at91_adc_state {
|
|||
void __iomem *reg_base;
|
||||
struct at91_adc_reg_desc *registers;
|
||||
u8 startup_time;
|
||||
u8 sample_hold_time;
|
||||
bool sleep_mode;
|
||||
struct iio_trigger **trig;
|
||||
struct at91_adc_trigger *trigger_list;
|
||||
u32 trigger_number;
|
||||
bool use_external;
|
||||
u32 vref_mv;
|
||||
u32 res; /* resolution used for convertions */
|
||||
bool low_res; /* the resolution corresponds to the lowest one */
|
||||
wait_queue_head_t wq_data_avail;
|
||||
};
|
||||
|
||||
|
@ -138,10 +142,10 @@ static int at91_adc_channel_init(struct iio_dev *idev)
|
|||
chan->channel = bit;
|
||||
chan->scan_index = idx;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 10;
|
||||
chan->scan_type.realbits = st->res;
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT;
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
idx++;
|
||||
}
|
||||
timestamp = chan_array + idx;
|
||||
|
@ -188,7 +192,7 @@ static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
|
|||
|
||||
static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *idev = trig->private_data;
|
||||
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
struct iio_buffer *buffer = idev->buffer;
|
||||
struct at91_adc_reg_desc *reg = st->registers;
|
||||
|
@ -254,7 +258,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
|
|||
return NULL;
|
||||
|
||||
trig->dev.parent = idev->dev.parent;
|
||||
trig->private_data = idev;
|
||||
iio_trigger_set_drvdata(trig, idev);
|
||||
trig->ops = &at91_adc_trigger_ops;
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
|
@ -372,6 +376,59 @@ static int at91_adc_read_raw(struct iio_dev *idev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int at91_adc_of_get_resolution(struct at91_adc_state *st,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *idev = iio_priv_to_dev(st);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int count, i, ret = 0;
|
||||
char *res_name, *s;
|
||||
u32 *resolutions;
|
||||
|
||||
count = of_property_count_strings(np, "atmel,adc-res-names");
|
||||
if (count < 2) {
|
||||
dev_err(&idev->dev, "You must specified at least two resolution names for "
|
||||
"adc-res-names property in the DT\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL);
|
||||
if (!resolutions)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u32_array(np, "atmel,adc-res", resolutions, count)) {
|
||||
dev_err(&idev->dev, "Missing adc-res property in the DT.\n");
|
||||
ret = -ENODEV;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
if (of_property_read_string(np, "atmel,adc-use-res", (const char **)&res_name))
|
||||
res_name = "highres";
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (of_property_read_string_index(np, "atmel,adc-res-names", i, (const char **)&s))
|
||||
continue;
|
||||
|
||||
if (strcmp(res_name, s))
|
||||
continue;
|
||||
|
||||
st->res = resolutions[i];
|
||||
if (!strcmp(res_name, "lowres"))
|
||||
st->low_res = true;
|
||||
else
|
||||
st->low_res = false;
|
||||
|
||||
dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);
|
||||
goto ret;
|
||||
}
|
||||
|
||||
dev_err(&idev->dev, "There is no resolution for %s\n", res_name);
|
||||
|
||||
ret:
|
||||
kfree(resolutions);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at91_adc_probe_dt(struct at91_adc_state *st,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
|
@ -400,6 +457,8 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
}
|
||||
st->num_channels = prop;
|
||||
|
||||
st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");
|
||||
|
||||
if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) {
|
||||
dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n");
|
||||
ret = -EINVAL;
|
||||
|
@ -407,6 +466,9 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
}
|
||||
st->startup_time = prop;
|
||||
|
||||
prop = 0;
|
||||
of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop);
|
||||
st->sample_hold_time = prop;
|
||||
|
||||
if (of_property_read_u32(node, "atmel,adc-vref", &prop)) {
|
||||
dev_err(&idev->dev, "Missing adc-vref property in the DT.\n");
|
||||
|
@ -415,6 +477,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
}
|
||||
st->vref_mv = prop;
|
||||
|
||||
ret = at91_adc_of_get_resolution(st, pdev);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
st->registers = devm_kzalloc(&idev->dev,
|
||||
sizeof(struct at91_adc_reg_desc),
|
||||
GFP_KERNEL);
|
||||
|
@ -516,11 +582,12 @@ static const struct iio_info at91_adc_info = {
|
|||
|
||||
static int at91_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int prsc, mstrclk, ticks, adc_clk;
|
||||
unsigned int prsc, mstrclk, ticks, adc_clk, shtim;
|
||||
int ret;
|
||||
struct iio_dev *idev;
|
||||
struct at91_adc_state *st;
|
||||
struct resource *res;
|
||||
u32 reg;
|
||||
|
||||
idev = iio_device_alloc(sizeof(struct at91_adc_state));
|
||||
if (idev == NULL) {
|
||||
|
@ -628,9 +695,22 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ticks = round_up((st->startup_time * adc_clk /
|
||||
1000000) - 1, 8) / 8;
|
||||
at91_adc_writel(st, AT91_ADC_MR,
|
||||
(AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
|
||||
(AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
|
||||
/*
|
||||
* a minimal Sample and Hold Time is necessary for the ADC to guarantee
|
||||
* the best converted final value between two channels selection
|
||||
* The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
|
||||
*/
|
||||
shtim = round_up((st->sample_hold_time * adc_clk /
|
||||
1000000) - 1, 1);
|
||||
|
||||
reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL;
|
||||
reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP;
|
||||
if (st->low_res)
|
||||
reg |= AT91_ADC_LOWRES;
|
||||
if (st->sleep_mode)
|
||||
reg |= AT91_ADC_SLEEP;
|
||||
reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM;
|
||||
at91_adc_writel(st, AT91_ADC_MR, reg);
|
||||
|
||||
/* Setup the ADC channels available on the board */
|
||||
ret = at91_adc_channel_init(idev);
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* exynos_adc.c - Support for ADC in EXYNOS SoCs
|
||||
*
|
||||
* 8 ~ 10 channel, 10/12-bit ADC
|
||||
*
|
||||
* Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
enum adc_version {
|
||||
ADC_V1,
|
||||
ADC_V2
|
||||
};
|
||||
|
||||
/* EXYNOS4412/5250 ADC_V1 registers definitions */
|
||||
#define ADC_V1_CON(x) ((x) + 0x00)
|
||||
#define ADC_V1_DLY(x) ((x) + 0x08)
|
||||
#define ADC_V1_DATX(x) ((x) + 0x0C)
|
||||
#define ADC_V1_INTCLR(x) ((x) + 0x18)
|
||||
#define ADC_V1_MUX(x) ((x) + 0x1c)
|
||||
|
||||
/* Future ADC_V2 registers definitions */
|
||||
#define ADC_V2_CON1(x) ((x) + 0x00)
|
||||
#define ADC_V2_CON2(x) ((x) + 0x04)
|
||||
#define ADC_V2_STAT(x) ((x) + 0x08)
|
||||
#define ADC_V2_INT_EN(x) ((x) + 0x10)
|
||||
#define ADC_V2_INT_ST(x) ((x) + 0x14)
|
||||
#define ADC_V2_VER(x) ((x) + 0x20)
|
||||
|
||||
/* Bit definitions for ADC_V1 */
|
||||
#define ADC_V1_CON_RES (1u << 16)
|
||||
#define ADC_V1_CON_PRSCEN (1u << 14)
|
||||
#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
|
||||
#define ADC_V1_CON_STANDBY (1u << 2)
|
||||
|
||||
/* Bit definitions for ADC_V2 */
|
||||
#define ADC_V2_CON1_SOFT_RESET (1u << 2)
|
||||
|
||||
#define ADC_V2_CON2_OSEL (1u << 10)
|
||||
#define ADC_V2_CON2_ESEL (1u << 9)
|
||||
#define ADC_V2_CON2_HIGHF (1u << 8)
|
||||
#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
|
||||
#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
|
||||
#define ADC_V2_CON2_ACH_MASK 0xF
|
||||
|
||||
#define MAX_ADC_V2_CHANNELS 10
|
||||
#define MAX_ADC_V1_CHANNELS 8
|
||||
|
||||
/* Bit definitions common for ADC_V1 and ADC_V2 */
|
||||
#define ADC_CON_EN_START (1u << 0)
|
||||
#define ADC_DATX_MASK 0xFFF
|
||||
|
||||
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(1000))
|
||||
|
||||
struct exynos_adc {
|
||||
void __iomem *regs;
|
||||
void __iomem *enable_reg;
|
||||
struct clk *clk;
|
||||
unsigned int irq;
|
||||
struct regulator *vdd;
|
||||
|
||||
struct completion completion;
|
||||
|
||||
u32 value;
|
||||
unsigned int version;
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_adc_match[] = {
|
||||
{ .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 },
|
||||
{ .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_adc_match);
|
||||
|
||||
static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(exynos_adc_match, pdev->dev.of_node);
|
||||
return (unsigned int)match->data;
|
||||
}
|
||||
|
||||
static int exynos_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
unsigned long timeout;
|
||||
u32 con1, con2;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
/* Select the channel to be used and Trigger conversion */
|
||||
if (info->version == ADC_V2) {
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_ACH_MASK;
|
||||
con2 |= ADC_V2_CON2_ACH_SEL(chan->address);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
con1 = readl(ADC_V2_CON1(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START,
|
||||
ADC_V2_CON1(info->regs));
|
||||
} else {
|
||||
writel(chan->address, ADC_V1_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START,
|
||||
ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, EXYNOS_ADC_TIMEOUT);
|
||||
*val = info->value;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (timeout == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
|
||||
/* Read value */
|
||||
info->value = readl(ADC_V1_DATX(info->regs)) &
|
||||
ADC_DATX_MASK;
|
||||
/* clear irq */
|
||||
if (info->version == ADC_V2)
|
||||
writel(1, ADC_V2_INT_ST(info->regs));
|
||||
else
|
||||
writel(1, ADC_V1_INTCLR(info->regs));
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int exynos_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if (readval == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info exynos_adc_iio_info = {
|
||||
.read_raw = &exynos_read_raw,
|
||||
.debugfs_reg_access = &exynos_adc_reg_access,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define ADC_CHANNEL(_index, _id) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.address = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.datasheet_name = _id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec exynos_adc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
ADC_CHANNEL(3, "adc3"),
|
||||
ADC_CHANNEL(4, "adc4"),
|
||||
ADC_CHANNEL(5, "adc5"),
|
||||
ADC_CHANNEL(6, "adc6"),
|
||||
ADC_CHANNEL(7, "adc7"),
|
||||
ADC_CHANNEL(8, "adc8"),
|
||||
ADC_CHANNEL(9, "adc9"),
|
||||
};
|
||||
|
||||
static int exynos_adc_remove_devices(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_adc_hw_init(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
if (info->version == ADC_V2) {
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
|
||||
ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
} else {
|
||||
/* set default prescaler values and Enable prescaler */
|
||||
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
|
||||
|
||||
/* Enable 12-bit ADC resolution */
|
||||
con1 |= ADC_V1_CON_RES;
|
||||
writel(con1, ADC_V1_CON(info->regs));
|
||||
}
|
||||
}
|
||||
|
||||
static int exynos_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_adc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
|
||||
if (!np)
|
||||
return ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(struct exynos_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_request_and_ioremap(&pdev->dev, mem);
|
||||
if (!info->regs) {
|
||||
ret = -ENOMEM;
|
||||
goto err_iio;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem);
|
||||
if (!info->enable_reg) {
|
||||
ret = -ENOMEM;
|
||||
goto err_iio;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
ret = irq;
|
||||
goto err_iio;
|
||||
}
|
||||
|
||||
info->irq = irq;
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
ret = request_irq(info->irq, exynos_adc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
|
||||
info->irq);
|
||||
goto err_iio;
|
||||
}
|
||||
|
||||
writel(1, info->enable_reg);
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
info->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
||||
if (IS_ERR(info->vdd)) {
|
||||
dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
|
||||
PTR_ERR(info->vdd));
|
||||
ret = PTR_ERR(info->vdd);
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
info->version = exynos_adc_get_version(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &exynos_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = exynos_adc_iio_channels;
|
||||
|
||||
if (info->version == ADC_V1)
|
||||
indio_dev->num_channels = MAX_ADC_V1_CHANNELS;
|
||||
else
|
||||
indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
goto err_iio_dev;
|
||||
|
||||
clk_prepare_enable(info->clk);
|
||||
|
||||
exynos_adc_hw_init(info);
|
||||
|
||||
ret = of_platform_populate(np, exynos_adc_match, NULL, &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed adding child nodes\n");
|
||||
goto err_of_populate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_of_populate:
|
||||
device_for_each_child(&pdev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
regulator_disable(info->vdd);
|
||||
clk_disable_unprepare(info->clk);
|
||||
err_iio_dev:
|
||||
iio_device_unregister(indio_dev);
|
||||
err_irq:
|
||||
free_irq(info->irq, info);
|
||||
err_iio:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
|
||||
device_for_each_child(&pdev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
regulator_disable(info->vdd);
|
||||
clk_disable_unprepare(info->clk);
|
||||
writel(0, info->enable_reg);
|
||||
iio_device_unregister(indio_dev);
|
||||
free_irq(info->irq, info);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct exynos_adc *info = platform_get_drvdata(pdev);
|
||||
u32 con;
|
||||
|
||||
if (info->version == ADC_V2) {
|
||||
con = readl(ADC_V2_CON1(info->regs));
|
||||
con &= ~ADC_CON_EN_START;
|
||||
writel(con, ADC_V2_CON1(info->regs));
|
||||
} else {
|
||||
con = readl(ADC_V1_CON(info->regs));
|
||||
con |= ADC_V1_CON_STANDBY;
|
||||
writel(con, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
writel(0, info->enable_reg);
|
||||
regulator_disable(info->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct exynos_adc *info = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(1, info->enable_reg);
|
||||
clk_prepare_enable(info->clk);
|
||||
|
||||
exynos_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
|
||||
exynos_adc_suspend,
|
||||
exynos_adc_resume);
|
||||
|
||||
static struct platform_driver exynos_adc_driver = {
|
||||
.probe = exynos_adc_probe,
|
||||
.remove = exynos_adc_remove,
|
||||
.driver = {
|
||||
.name = "exynos-adc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = exynos_adc_match,
|
||||
.pm = &exynos_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(exynos_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -132,8 +132,8 @@ static const struct iio_info lp8788_adc_info = {
|
|||
.type = _type, \
|
||||
.indexed = 1, \
|
||||
.channel = LPADC_##_id, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = #_id, \
|
||||
}
|
||||
|
||||
|
|
|
@ -427,15 +427,15 @@ static const enum max1363_modes max1363_mode_list[] = {
|
|||
#define MAX1363_EV_M \
|
||||
(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \
|
||||
| IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
|
||||
#define MAX1363_INFO_MASK (IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT)
|
||||
|
||||
#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = addr, \
|
||||
.info_mask = MAX1363_INFO_MASK, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "AIN"#num, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
|
@ -456,7 +456,8 @@ static const enum max1363_modes max1363_mode_list[] = {
|
|||
.channel = num, \
|
||||
.channel2 = num2, \
|
||||
.address = addr, \
|
||||
.info_mask = MAX1363_INFO_MASK, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "AIN"#num"-AIN"#num2, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
|
|
@ -55,8 +55,8 @@ static int adc081c_read_raw(struct iio_dev *iio,
|
|||
|
||||
static const struct iio_chan_spec adc081c_channel = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
};
|
||||
|
||||
static const struct iio_info adc081c_info = {
|
||||
|
|
|
@ -89,7 +89,7 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
|||
chan->type = IIO_VOLTAGE;
|
||||
chan->indexed = 1;
|
||||
chan->channel = i;
|
||||
chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT;
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
}
|
||||
|
||||
indio_dev->channels = chan_array;
|
||||
|
|
|
@ -41,7 +41,7 @@ struct vprbrd_adc {
|
|||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
|
|
|
@ -125,7 +125,7 @@ static const struct iio_info ad8366_info = {
|
|||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.info_mask = IIO_CHAN_INFO_HARDWAREGAIN_SEPARATE_BIT,\
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad8366_channels[] = {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct hid_sensor_common *st = trig->private_data;
|
||||
struct hid_sensor_common *st = iio_trigger_get_drvdata(trig);
|
||||
int state_val;
|
||||
|
||||
state_val = state ? 1 : 0;
|
||||
|
@ -76,7 +76,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
|||
}
|
||||
|
||||
trig->dev.parent = indio_dev->dev.parent;
|
||||
trig->private_data = attrb;
|
||||
iio_trigger_set_drvdata(trig, attrb);
|
||||
trig->ops = &hid_sensor_trigger_ops;
|
||||
ret = iio_trigger_register(trig);
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
|
|||
static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
|
@ -51,10 +50,7 @@ static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
|||
else
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
err = spi_sync(to_spi_device(dev), &msg);
|
||||
err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
|
||||
if (err)
|
||||
goto acc_spi_read_error;
|
||||
|
||||
|
@ -83,7 +79,6 @@ static int st_sensors_spi_read_multiple_byte(
|
|||
static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
|
@ -96,9 +91,7 @@ static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
|||
tb->tx_buf[0] = reg_addr;
|
||||
tb->tx_buf[1] = data;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers, &msg);
|
||||
err = spi_sync(to_spi_device(dev), &msg);
|
||||
err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -40,7 +40,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
if (err)
|
||||
goto request_irq_error;
|
||||
|
||||
sdata->trig->private_data = indio_dev;
|
||||
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
||||
sdata->trig->ops = trigger_ops;
|
||||
sdata->trig->dev.parent = sdata->dev;
|
||||
|
||||
|
|
|
@ -296,8 +296,8 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = addr, \
|
||||
.scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \
|
||||
.ext_info = ad5064_ext_info, \
|
||||
|
|
|
@ -102,11 +102,11 @@ enum ad5360_type {
|
|||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \
|
||||
}
|
||||
|
||||
|
|
|
@ -257,10 +257,10 @@ static struct iio_chan_spec_ext_info ad5380_ext_info[] = {
|
|||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \
|
||||
.ext_info = ad5380_ext_info, \
|
||||
}
|
||||
|
|
|
@ -86,11 +86,11 @@ static const struct iio_chan_spec ad5421_channels[] = {
|
|||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_type = IIO_ST('u', 16, 16, 0),
|
||||
.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) |
|
||||
IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
|
||||
|
|
|
@ -143,8 +143,8 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = IIO_ST('u', (bits), (storage), (shift)), \
|
||||
.ext_info = (ext), \
|
||||
}
|
||||
|
|
|
@ -206,8 +206,8 @@ static const struct iio_info ad5449_info = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (chan), \
|
||||
.scan_type = IIO_ST('u', (bits), 16, 12 - (bits)), \
|
||||
}
|
||||
|
|
|
@ -259,8 +259,8 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = AD5504_ADDR_DAC(_chan), \
|
||||
.scan_type = IIO_ST('u', 12, 16, 0), \
|
||||
.ext_info = ad5504_ext_info, \
|
||||
|
|
|
@ -174,8 +174,8 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (_chan), \
|
||||
.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \
|
||||
.ext_info = ad5624r_ext_info, \
|
||||
|
|
|
@ -276,9 +276,9 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.address = AD5686_ADDR_DAC(chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.address = AD5686_ADDR_DAC(chan), \
|
||||
.scan_type = IIO_ST('u', bits, 16, shift), \
|
||||
.ext_info = ad5686_ext_info, \
|
||||
}
|
||||
|
|
|
@ -393,11 +393,11 @@ static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
|
|||
#define AD5755_CHANNEL(_bits) { \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \
|
||||
.ext_info = ad5755_ext_info, \
|
||||
}
|
||||
|
|
|
@ -78,11 +78,11 @@ enum ad5764_type {
|
|||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \
|
||||
}
|
||||
|
||||
|
|
|
@ -302,9 +302,9 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
|
|||
.indexed = 1, \
|
||||
.address = AD5791_ADDR_DAC0, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = IIO_ST('u', bits, 24, shift), \
|
||||
.ext_info = ad5791_ext_info, \
|
||||
}
|
||||
|
|
|
@ -146,8 +146,8 @@ static const struct iio_info max517_info = {
|
|||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = IIO_ST('u', 8, 8, 0), \
|
||||
}
|
||||
|
||||
|
|
|
@ -69,8 +69,8 @@ static const struct iio_chan_spec mcp4725_channel = {
|
|||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_type = IIO_ST('u', 12, 16, 0),
|
||||
};
|
||||
|
||||
|
|
|
@ -920,10 +920,10 @@ static int ad9523_setup(struct iio_dev *indio_dev)
|
|||
st->ad9523_channels[i].channel = chan->channel_num;
|
||||
st->ad9523_channels[i].extend_name =
|
||||
chan->extended_name;
|
||||
st->ad9523_channels[i].info_mask =
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PHASE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT;
|
||||
st->ad9523_channels[i].info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_PHASE) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,32 +136,32 @@ static const struct iio_chan_spec adis16080_channels[] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ADIS16080_DIN_GYRO,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_AIN1,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_AIN2,
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_TEMP,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -357,10 +357,11 @@ static const struct iio_chan_spec adis16136_channels[] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
||||
.address = ADIS16136_REG_GYRO_OUT2,
|
||||
.scan_index = ADIS16136_SCAN_GYRO,
|
||||
.scan_type = {
|
||||
|
@ -373,8 +374,8 @@ static const struct iio_chan_spec adis16136_channels[] = {
|
|||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ADIS16136_REG_TEMP_OUT,
|
||||
.scan_index = ADIS16136_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
|
|
|
@ -383,16 +383,16 @@ static const struct iio_chan_spec adxrs450_channels[2][2] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
},
|
||||
[ID_ADXRS453] = {
|
||||
|
@ -400,15 +400,15 @@ static const struct iio_chan_spec adxrs450_channels[2][2] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW),
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -60,28 +60,28 @@ static const struct iio_chan_spec gyro_3d_channels[] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -81,7 +81,7 @@ void itg3200_buffer_unconfigure(struct iio_dev *indio_dev)
|
|||
static int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
int ret;
|
||||
u8 msc;
|
||||
|
||||
|
@ -129,7 +129,7 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev)
|
|||
|
||||
st->trig->dev.parent = &st->i2c->dev;
|
||||
st->trig->ops = &itg3200_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
|
|
@ -248,12 +248,6 @@ err_ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define ITG3200_TEMP_INFO_MASK (IIO_CHAN_INFO_OFFSET_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
#define ITG3200_GYRO_INFO_MASK (IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
|
||||
#define ITG3200_ST \
|
||||
{ .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE }
|
||||
|
||||
|
@ -261,7 +255,8 @@ err_ret:
|
|||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _mod, \
|
||||
.info_mask = ITG3200_GYRO_INFO_MASK, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \
|
||||
.scan_index = ITG3200_SCAN_GYRO_ ## _mod, \
|
||||
.scan_type = ITG3200_ST, \
|
||||
|
@ -271,7 +266,9 @@ static const struct iio_chan_spec itg3200_channels[] = {
|
|||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.info_mask = ITG3200_TEMP_INFO_MASK,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ITG3200_REG_TEMP_OUT_H,
|
||||
.scan_index = ITG3200_SCAN_TEMP,
|
||||
.scan_type = ITG3200_ST,
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
struct iio_chan_spec;
|
||||
struct iio_dev;
|
||||
|
||||
extern struct device_type iio_device_type;
|
||||
|
||||
int __iio_add_chan_devattr(const char *postfix,
|
||||
struct iio_chan_spec const *chan,
|
||||
|
|
|
@ -484,8 +484,8 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = name, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
|
@ -507,10 +507,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.address = addr, \
|
||||
.scan_index = ADIS16400_SCAN_GYRO_ ## mod, \
|
||||
.scan_type = { \
|
||||
|
@ -526,10 +526,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_ACC_ ## mod, \
|
||||
.scan_type = { \
|
||||
|
@ -545,9 +545,9 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_MAGN_ ## mod, \
|
||||
.scan_type = { \
|
||||
|
@ -568,10 +568,11 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = ADIS16400_MOD_TEMP_NAME_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_ ## mod, \
|
||||
.scan_type = { \
|
||||
|
@ -587,9 +588,9 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_X, \
|
||||
.scan_type = { \
|
||||
|
@ -605,8 +606,8 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_INCLI, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16300_SCAN_INCLI_ ## mod, \
|
||||
.scan_type = { \
|
||||
|
@ -646,8 +647,8 @@ static const struct iio_chan_spec adis16448_channels[] = {
|
|||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 16),
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ADIS16448_BARO_OUT,
|
||||
.scan_index = ADIS16400_SCAN_BARO,
|
||||
.scan_type = IIO_ST('s', 16, 16, 0),
|
||||
|
|
|
@ -591,15 +591,15 @@ static int adis16480_write_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \
|
||||
#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info_sep, _bits) \
|
||||
{ \
|
||||
.type = (_type), \
|
||||
.modified = 1, \
|
||||
.channel2 = (_mod), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
_info, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
_info_sep, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (_address), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
|
@ -613,21 +613,21 @@ static int adis16480_write_raw(struct iio_dev *indio_dev,
|
|||
#define ADIS16480_GYRO_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
32)
|
||||
|
||||
#define ADIS16480_ACCEL_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
32)
|
||||
|
||||
#define ADIS16480_MAGN_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
16)
|
||||
|
||||
#define ADIS16480_PRESSURE_CHANNEL() \
|
||||
|
@ -635,9 +635,9 @@ static int adis16480_write_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_PRESSURE, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = ADIS16480_REG_BAROM_OUT, \
|
||||
.scan_index = ADIS16480_SCAN_BARO, \
|
||||
.scan_type = { \
|
||||
|
@ -652,9 +652,9 @@ static int adis16480_write_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.address = ADIS16480_REG_TEMP_OUT, \
|
||||
.scan_index = ADIS16480_SCAN_TEMP, \
|
||||
.scan_type = { \
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct adis *adis = trig->private_data;
|
||||
struct adis *adis = iio_trigger_get_drvdata(trig);
|
||||
|
||||
return adis_enable_irq(adis, state);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
|
|||
|
||||
adis->trig->dev.parent = &adis->spi->dev;
|
||||
adis->trig->ops = &adis_trigger_ops;
|
||||
adis->trig->private_data = adis;
|
||||
iio_trigger_set_drvdata(adis->trig, adis);
|
||||
ret = iio_trigger_register(adis->trig);
|
||||
|
||||
indio_dev->trig = adis->trig;
|
||||
|
|
|
@ -544,8 +544,8 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
|
|||
.type = _type, \
|
||||
.modified = 1, \
|
||||
.channel2 = _channel2, \
|
||||
.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT \
|
||||
| IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
@ -564,9 +564,9 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
|
|||
*/
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT
|
||||
| IIO_CHAN_INFO_OFFSET_SEPARATE_BIT
|
||||
| IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
|
||||
| BIT(IIO_CHAN_INFO_OFFSET)
|
||||
| BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
|
|
|
@ -105,9 +105,8 @@ irqreturn_t inv_mpu6050_irq_handler(int irq, void *p)
|
|||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns();
|
||||
spin_lock(&st->time_stamp_lock);
|
||||
kfifo_in(&st->timestamps, ×tamp, 1);
|
||||
spin_unlock(&st->time_stamp_lock);
|
||||
kfifo_in_spinlocked(&st->timestamps, ×tamp, 1,
|
||||
&st->time_stamp_lock);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
|
|||
static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
return inv_mpu6050_set_enable(trig->private_data, state);
|
||||
return inv_mpu6050_set_enable(iio_trigger_get_drvdata(trig), state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops inv_mpu_trigger_ops = {
|
||||
|
@ -130,8 +130,8 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
|||
if (ret)
|
||||
goto error_free_trig;
|
||||
st->trig->dev.parent = &st->client->dev;
|
||||
st->trig->private_data = indio_dev;
|
||||
st->trig->ops = &inv_mpu_trigger_ops;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
|
|
@ -691,23 +691,36 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
|
||||
if (chan->channel < 0)
|
||||
return 0;
|
||||
for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
|
||||
for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
|
||||
chan,
|
||||
&iio_read_channel_info,
|
||||
&iio_write_channel_info,
|
||||
i/2,
|
||||
!(i%2),
|
||||
i,
|
||||
0,
|
||||
&indio_dev->dev,
|
||||
&indio_dev->channel_attr_list);
|
||||
if (ret == -EBUSY && (i%2 == 0)) {
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
attrcount++;
|
||||
}
|
||||
for_each_set_bit(i, &chan->info_mask_shared_by_type, sizeof(long)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
|
||||
chan,
|
||||
&iio_read_channel_info,
|
||||
&iio_write_channel_info,
|
||||
i,
|
||||
1,
|
||||
&indio_dev->dev,
|
||||
&indio_dev->channel_attr_list);
|
||||
if (ret == -EBUSY) {
|
||||
ret = 0;
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
goto error_ret;
|
||||
}
|
||||
attrcount++;
|
||||
}
|
||||
|
||||
if (chan->ext_info) {
|
||||
unsigned int i = 0;
|
||||
|
@ -847,7 +860,7 @@ static void iio_dev_release(struct device *device)
|
|||
kfree(indio_dev);
|
||||
}
|
||||
|
||||
static struct device_type iio_dev_type = {
|
||||
struct device_type iio_device_type = {
|
||||
.name = "iio_device",
|
||||
.release = iio_dev_release,
|
||||
};
|
||||
|
@ -869,7 +882,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
|
|||
|
||||
if (dev) {
|
||||
dev->dev.groups = dev->groups;
|
||||
dev->dev.type = &iio_dev_type;
|
||||
dev->dev.type = &iio_device_type;
|
||||
dev->dev.bus = &iio_bus_type;
|
||||
device_initialize(&dev->dev);
|
||||
dev_set_drvdata(&dev->dev, (void *)dev);
|
||||
|
@ -960,6 +973,10 @@ int iio_device_register(struct iio_dev *indio_dev)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* If the calling driver did not initialize of_node, do it here */
|
||||
if (!indio_dev->dev.of_node && indio_dev->dev.parent)
|
||||
indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
|
||||
|
||||
/* configure elements for the chrdev */
|
||||
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
|
||||
|
||||
|
|
|
@ -46,10 +46,11 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
|||
{
|
||||
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
||||
struct iio_event_data ev;
|
||||
unsigned long flags;
|
||||
int copied;
|
||||
|
||||
/* Does anyone care? */
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irqsave(&ev_int->wait.lock, flags);
|
||||
if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
||||
|
||||
ev.id = ev_code;
|
||||
|
@ -59,7 +60,7 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
|||
if (copied != 0)
|
||||
wake_up_locked_poll(&ev_int->wait, POLLIN);
|
||||
}
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irqrestore(&ev_int->wait.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -76,10 +77,10 @@ static unsigned int iio_event_poll(struct file *filep,
|
|||
|
||||
poll_wait(filep, &ev_int->wait, wait);
|
||||
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (!kfifo_is_empty(&ev_int->det_events))
|
||||
events = POLLIN | POLLRDNORM;
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
@ -96,14 +97,14 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
|
|||
if (count < sizeof(struct iio_event_data))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (kfifo_is_empty(&ev_int->det_events)) {
|
||||
if (filep->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
goto error_unlock;
|
||||
}
|
||||
/* Blocking on device; waiting for something to be there */
|
||||
ret = wait_event_interruptible_locked(ev_int->wait,
|
||||
ret = wait_event_interruptible_locked_irq(ev_int->wait,
|
||||
!kfifo_is_empty(&ev_int->det_events));
|
||||
if (ret)
|
||||
goto error_unlock;
|
||||
|
@ -113,7 +114,7 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
|
|||
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
|
||||
|
||||
error_unlock:
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
|
|||
{
|
||||
struct iio_event_interface *ev_int = filep->private_data;
|
||||
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
/*
|
||||
* In order to maintain a clean state for reopening,
|
||||
|
@ -130,7 +131,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
|
|||
* any new __iio_push_event calls running.
|
||||
*/
|
||||
kfifo_reset_out(&ev_int->det_events);
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -151,18 +152,18 @@ int iio_event_getfd(struct iio_dev *indio_dev)
|
|||
if (ev_int == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
fd = anon_inode_getfd("iio:event",
|
||||
&iio_event_chrdev_fileops, ev_int, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
spin_lock(&ev_int->wait.lock);
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
spin_unlock(&ev_int->wait.lock);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "iio_core.h"
|
||||
|
@ -92,6 +93,164 @@ static const struct iio_chan_spec
|
|||
return chan;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static int iio_dev_node_match(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data && dev->type == &iio_device_type;
|
||||
}
|
||||
|
||||
static int __of_iio_channel_get(struct iio_channel *channel,
|
||||
struct device_node *np, int index)
|
||||
{
|
||||
struct device *idev;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
struct of_phandle_args iiospec;
|
||||
|
||||
err = of_parse_phandle_with_args(np, "io-channels",
|
||||
"#io-channel-cells",
|
||||
index, &iiospec);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
idev = bus_find_device(&iio_bus_type, NULL, iiospec.np,
|
||||
iio_dev_node_match);
|
||||
of_node_put(iiospec.np);
|
||||
if (idev == NULL)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
indio_dev = dev_to_iio_dev(idev);
|
||||
channel->indio_dev = indio_dev;
|
||||
index = iiospec.args_count ? iiospec.args[0] : 0;
|
||||
if (index >= indio_dev->num_channels) {
|
||||
return -EINVAL;
|
||||
goto err_put;
|
||||
}
|
||||
channel->channel = &indio_dev->channels[index];
|
||||
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
iio_device_put(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
|
||||
{
|
||||
struct iio_channel *channel;
|
||||
int err;
|
||||
|
||||
if (index < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
if (channel == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = __of_iio_channel_get(channel, np, index);
|
||||
if (err)
|
||||
goto err_free_channel;
|
||||
|
||||
return channel;
|
||||
|
||||
err_free_channel:
|
||||
kfree(channel);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_channel *chan = NULL;
|
||||
|
||||
/* Walk up the tree of devices looking for a matching iio channel */
|
||||
while (np) {
|
||||
int index = 0;
|
||||
|
||||
/*
|
||||
* For named iio channels, first look up the name in the
|
||||
* "io-channel-names" property. If it cannot be found, the
|
||||
* index will be an error code, and of_iio_channel_get()
|
||||
* will fail.
|
||||
*/
|
||||
if (name)
|
||||
index = of_property_match_string(np, "io-channel-names",
|
||||
name);
|
||||
chan = of_iio_channel_get(np, index);
|
||||
if (!IS_ERR(chan))
|
||||
break;
|
||||
else if (name && index >= 0) {
|
||||
pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
|
||||
np->full_name, name ? name : "", index);
|
||||
return chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* No matching IIO channel found on this node.
|
||||
* If the parent node has a "io-channel-ranges" property,
|
||||
* then we can try one of its channels.
|
||||
*/
|
||||
np = np->parent;
|
||||
if (np && !of_get_property(np, "io-channel-ranges", NULL))
|
||||
break;
|
||||
}
|
||||
return chan;
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
struct iio_channel *chans;
|
||||
int i, mapind, nummaps = 0;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = of_parse_phandle_with_args(dev->of_node,
|
||||
"io-channels",
|
||||
"#io-channel-cells",
|
||||
nummaps, NULL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
} while (++nummaps);
|
||||
|
||||
if (nummaps == 0) /* no error, return NULL to search map table */
|
||||
return NULL;
|
||||
|
||||
/* NULL terminated array to save passing size */
|
||||
chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
||||
if (chans == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Search for OF matches */
|
||||
for (mapind = 0; mapind < nummaps; mapind++) {
|
||||
ret = __of_iio_channel_get(&chans[mapind], dev->of_node,
|
||||
mapind);
|
||||
if (ret)
|
||||
goto error_free_chans;
|
||||
}
|
||||
return chans;
|
||||
|
||||
error_free_chans:
|
||||
for (i = 0; i < mapind; i++)
|
||||
iio_device_put(chans[i].indio_dev);
|
||||
kfree(chans);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
|
||||
static inline struct iio_channel *
|
||||
of_iio_channel_get_by_name(struct device_node *np, const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static struct iio_channel *iio_channel_get_sys(const char *name,
|
||||
const char *channel_name)
|
||||
|
@ -150,7 +309,14 @@ struct iio_channel *iio_channel_get(struct device *dev,
|
|||
const char *channel_name)
|
||||
{
|
||||
const char *name = dev ? dev_name(dev) : NULL;
|
||||
struct iio_channel *channel;
|
||||
|
||||
if (dev) {
|
||||
channel = of_iio_channel_get_by_name(dev->of_node,
|
||||
channel_name);
|
||||
if (channel != NULL)
|
||||
return channel;
|
||||
}
|
||||
return iio_channel_get_sys(name, channel_name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_get);
|
||||
|
@ -173,6 +339,11 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
|
|||
|
||||
if (dev == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chans = of_iio_channel_get_all(dev);
|
||||
if (chans)
|
||||
return chans;
|
||||
|
||||
name = dev_name(dev);
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
|
|
|
@ -207,8 +207,8 @@ static const struct iio_chan_spec_ext_info adjd_s311_ext_info[] = {
|
|||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.address = (IDX_##_color), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_HARDWAREGAIN_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
|
||||
.channel2 = (IIO_MOD_LIGHT_##_color), \
|
||||
.scan_index = (_scan_idx), \
|
||||
.scan_type = IIO_ST('u', 10, 16, 0), \
|
||||
|
|
|
@ -49,10 +49,10 @@ static const struct iio_chan_spec als_channels[] = {
|
|||
.type = IIO_INTENSITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -231,7 +231,7 @@ static int lm3533_als_read_raw(struct iio_dev *indio_dev,
|
|||
.channel = _channel, \
|
||||
.indexed = true, \
|
||||
.output = true, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec lm3533_als_channels[] = {
|
||||
|
@ -239,8 +239,8 @@ static const struct iio_chan_spec lm3533_als_channels[] = {
|
|||
.type = IIO_LIGHT,
|
||||
.channel = 0,
|
||||
.indexed = true,
|
||||
.info_mask = (IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
|
||||
BIT(IIO_CHAN_INFO_RAW),
|
||||
},
|
||||
CHANNEL_CURRENT(0),
|
||||
CHANNEL_CURRENT(1),
|
||||
|
|
|
@ -530,14 +530,14 @@ static const struct iio_chan_spec tsl2563_channels[] = {
|
|||
{
|
||||
.type = IIO_LIGHT,
|
||||
.indexed = 1,
|
||||
.info_mask = IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.channel = 0,
|
||||
}, {
|
||||
.type = IIO_INTENSITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE),
|
||||
.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING) |
|
||||
IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
|
@ -546,8 +546,8 @@ static const struct iio_chan_spec tsl2563_channels[] = {
|
|||
.type = IIO_INTENSITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_IR,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -93,11 +93,11 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
|||
static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}, {
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
#
|
||||
menu "Magnetometer sensors"
|
||||
|
||||
config AK8975
|
||||
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say yes here to build support for Asahi Kasei AK8975 3-Axis
|
||||
Magnetometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ak8975.
|
||||
|
||||
config HID_SENSOR_MAGNETOMETER_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Makefile for industrial I/O Magnetometer sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
|
||||
|
|
|
@ -94,7 +94,6 @@ struct ak8975_data {
|
|||
long raw_to_gauss[3];
|
||||
u8 reg_cache[AK8975_MAX_REGS];
|
||||
int eoc_gpio;
|
||||
int eoc_irq;
|
||||
};
|
||||
|
||||
static const int ak8975_index_to_reg[] = {
|
||||
|
@ -123,36 +122,6 @@ static int ak8975_write_data(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to read a contiguous set of the I2C device's registers.
|
||||
*/
|
||||
static int ak8975_read_data(struct i2c_client *client,
|
||||
u8 reg, u8 length, u8 *buffer)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_NOSTART,
|
||||
.len = 1,
|
||||
.buf = ®,
|
||||
}, {
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = length,
|
||||
.buf = buffer,
|
||||
}
|
||||
};
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Read from device fails\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform some start-of-day setup, including reading the asa calibration
|
||||
* values and caching them.
|
||||
|
@ -165,11 +134,12 @@ static int ak8975_setup(struct i2c_client *client)
|
|||
int ret;
|
||||
|
||||
/* Confirm that the device we're talking to is really an AK8975. */
|
||||
ret = ak8975_read_data(client, AK8975_REG_WIA, 1, &device_id);
|
||||
ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading WIA\n");
|
||||
return ret;
|
||||
}
|
||||
device_id = ret;
|
||||
if (device_id != AK8975_DEVICE_ID) {
|
||||
dev_err(&client->dev, "Device ak8975 not found\n");
|
||||
return -ENODEV;
|
||||
|
@ -187,7 +157,8 @@ static int ak8975_setup(struct i2c_client *client)
|
|||
}
|
||||
|
||||
/* Get asa data and store in the device data. */
|
||||
ret = ak8975_read_data(client, AK8975_REG_ASAX, 3, data->asa);
|
||||
ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX,
|
||||
3, data->asa);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Not able to read asa data\n");
|
||||
return ret;
|
||||
|
@ -249,7 +220,6 @@ static int ak8975_setup(struct i2c_client *client)
|
|||
static int wait_conversion_complete_gpio(struct ak8975_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
u8 read_status;
|
||||
u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT;
|
||||
int ret;
|
||||
|
||||
|
@ -265,12 +235,11 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ak8975_read_data(client, AK8975_REG_ST1, 1, &read_status);
|
||||
if (ret < 0) {
|
||||
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Error in reading ST1\n");
|
||||
return ret;
|
||||
}
|
||||
return read_status;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wait_conversion_complete_polled(struct ak8975_data *data)
|
||||
|
@ -283,11 +252,12 @@ static int wait_conversion_complete_polled(struct ak8975_data *data)
|
|||
/* Wait for the conversion to complete. */
|
||||
while (timeout_ms) {
|
||||
msleep(AK8975_CONVERSION_DONE_POLL_TIME);
|
||||
ret = ak8975_read_data(client, AK8975_REG_ST1, 1, &read_status);
|
||||
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error in reading ST1\n");
|
||||
return ret;
|
||||
}
|
||||
read_status = ret;
|
||||
if (read_status)
|
||||
break;
|
||||
timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME;
|
||||
|
@ -308,7 +278,6 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
|||
struct i2c_client *client = data->client;
|
||||
u16 meas_reg;
|
||||
s16 raw;
|
||||
u8 read_status;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
@ -332,18 +301,15 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
|||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
read_status = ret;
|
||||
|
||||
if (read_status & AK8975_REG_ST1_DRDY_MASK) {
|
||||
ret = ak8975_read_data(client, AK8975_REG_ST2, 1, &read_status);
|
||||
if (ret & AK8975_REG_ST1_DRDY_MASK) {
|
||||
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error in reading ST2\n");
|
||||
goto exit;
|
||||
}
|
||||
if (read_status & (AK8975_REG_ST2_DERR_MASK |
|
||||
AK8975_REG_ST2_HOFL_MASK)) {
|
||||
dev_err(&client->dev, "ST2 status error 0x%x\n",
|
||||
read_status);
|
||||
if (ret & (AK8975_REG_ST2_DERR_MASK |
|
||||
AK8975_REG_ST2_HOFL_MASK)) {
|
||||
dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
@ -351,12 +317,12 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
|||
|
||||
/* Read the flux value from the appropriate register
|
||||
(the register is specified in the iio device attributes). */
|
||||
ret = ak8975_read_data(client, ak8975_index_to_reg[index],
|
||||
2, (u8 *)&meas_reg);
|
||||
ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Read axis data fails\n");
|
||||
goto exit;
|
||||
}
|
||||
meas_reg = ret;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
|
@ -395,8 +361,8 @@ static int ak8975_read_raw(struct iio_dev *indio_dev,
|
|||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
}
|
||||
|
||||
|
@ -452,7 +418,6 @@ static int ak8975_probe(struct i2c_client *client,
|
|||
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
data->eoc_irq = client->irq;
|
||||
data->eoc_gpio = eoc_gpio;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = ak8975_channels;
|
|
@ -60,28 +60,28 @@ static const struct iio_chan_spec magn_3d_channels[] = {
|
|||
.type = IIO_MAGN,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_MAGN,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_MAGN,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -110,19 +110,15 @@ source "drivers/staging/nvec/Kconfig"
|
|||
|
||||
source "drivers/staging/media/Kconfig"
|
||||
|
||||
source "drivers/staging/net/Kconfig"
|
||||
|
||||
source "drivers/staging/android/Kconfig"
|
||||
|
||||
source "drivers/staging/ozwpan/Kconfig"
|
||||
|
||||
source "drivers/staging/ccg/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm72xx/Kconfig"
|
||||
|
||||
source "drivers/staging/csr/Kconfig"
|
||||
|
||||
source "drivers/staging/omap-thermal/Kconfig"
|
||||
source "drivers/staging/ti-soc-thermal/Kconfig"
|
||||
|
||||
source "drivers/staging/silicom/Kconfig"
|
||||
|
||||
|
@ -140,4 +136,8 @@ source "drivers/staging/zcache/Kconfig"
|
|||
|
||||
source "drivers/staging/goldfish/Kconfig"
|
||||
|
||||
source "drivers/staging/netlogic/Kconfig"
|
||||
|
||||
source "drivers/staging/dwc2/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
obj-$(CONFIG_STAGING) += staging.o
|
||||
|
||||
obj-y += media/
|
||||
obj-y += net/
|
||||
obj-$(CONFIG_ET131X) += et131x/
|
||||
obj-$(CONFIG_SLICOSS) += slicoss/
|
||||
obj-$(CONFIG_USBIP_CORE) += usbip/
|
||||
|
@ -23,6 +22,7 @@ obj-$(CONFIG_RTS5139) += rts5139/
|
|||
obj-$(CONFIG_TRANZPORT) += frontier/
|
||||
obj-$(CONFIG_IDE_PHISON) += phison/
|
||||
obj-$(CONFIG_LINE6_USB) += line6/
|
||||
obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/
|
||||
obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/
|
||||
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
|
||||
obj-$(CONFIG_VT6655) += vt6655/
|
||||
|
@ -50,10 +50,9 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
|
|||
obj-$(CONFIG_MFD_NVEC) += nvec/
|
||||
obj-$(CONFIG_ANDROID) += android/
|
||||
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
|
||||
obj-$(CONFIG_USB_G_CCG) += ccg/
|
||||
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
||||
obj-$(CONFIG_CSR_WIFI) += csr/
|
||||
obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal/
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
|
||||
obj-$(CONFIG_CED1401) += ced1401/
|
||||
obj-$(CONFIG_DRM_IMX) += imx-drm/
|
||||
|
@ -62,3 +61,4 @@ obj-$(CONFIG_SB105X) += sb105x/
|
|||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_ZCACHE) += zcache/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2/
|
||||
|
|
|
@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC
|
|||
config ASHMEM
|
||||
bool "Enable the Anonymous Shared Memory Subsystem"
|
||||
default n
|
||||
depends on SHMEM || TINY_SHMEM
|
||||
depends on SHMEM
|
||||
---help---
|
||||
The ashmem subsystem is a new shared memory allocator, similar to
|
||||
POSIX SHM but with different behavior and sporting a simpler
|
||||
|
@ -72,6 +72,33 @@ config ANDROID_INTF_ALARM_DEV
|
|||
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
|
||||
Also exports the alarm interface to user-space.
|
||||
|
||||
config SYNC
|
||||
bool "Synchronization framework"
|
||||
default n
|
||||
select ANON_INODES
|
||||
help
|
||||
This option enables the framework for synchronization between multiple
|
||||
drivers. Sync implementations can take advantage of hardware
|
||||
synchronization built into devices like GPUs.
|
||||
|
||||
config SW_SYNC
|
||||
bool "Software synchronization objects"
|
||||
default n
|
||||
depends on SYNC
|
||||
help
|
||||
A sync object driver that uses a 32bit counter to coordinate
|
||||
syncrhronization. Useful when there is no hardware primitive backing
|
||||
the synchronization.
|
||||
|
||||
config SW_SYNC_USER
|
||||
bool "Userspace API for SW_SYNC"
|
||||
default n
|
||||
depends on SW_SYNC
|
||||
help
|
||||
Provides a user space API to the sw sync object.
|
||||
*WARNING* improper use of this can result in deadlocking kernel
|
||||
drivers from userspace.
|
||||
|
||||
endif # if ANDROID
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -7,3 +7,5 @@ obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
|||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
|
||||
obj-$(CONFIG_SYNC) += sync.o
|
||||
obj-$(CONFIG_SW_SYNC) += sw_sync.o
|
||||
|
|
|
@ -414,20 +414,29 @@ out:
|
|||
static int set_name(struct ashmem_area *asma, void __user *name)
|
||||
{
|
||||
int ret = 0;
|
||||
char local_name[ASHMEM_NAME_LEN];
|
||||
|
||||
/*
|
||||
* Holding the ashmem_mutex while doing a copy_from_user might cause
|
||||
* an data abort which would try to access mmap_sem. If another
|
||||
* thread has invoked ashmem_mmap then it will be holding the
|
||||
* semaphore and will be waiting for ashmem_mutex, there by leading to
|
||||
* deadlock. We'll release the mutex and take the name to a local
|
||||
* variable that does not need protection and later copy the local
|
||||
* variable to the structure member with lock held.
|
||||
*/
|
||||
if (copy_from_user(local_name, name, ASHMEM_NAME_LEN))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&ashmem_mutex);
|
||||
|
||||
/* cannot change an existing mapping's name */
|
||||
if (unlikely(asma->file)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(copy_from_user(asma->name + ASHMEM_NAME_PREFIX_LEN,
|
||||
name, ASHMEM_NAME_LEN)))
|
||||
ret = -EFAULT;
|
||||
memcpy(asma->name + ASHMEM_NAME_PREFIX_LEN,
|
||||
local_name, ASHMEM_NAME_LEN);
|
||||
asma->name[ASHMEM_FULL_NAME_LEN-1] = '\0';
|
||||
|
||||
out:
|
||||
mutex_unlock(&ashmem_mutex);
|
||||
|
||||
|
@ -437,26 +446,36 @@ out:
|
|||
static int get_name(struct ashmem_area *asma, void __user *name)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
/*
|
||||
* Have a local variable to which we'll copy the content
|
||||
* from asma with the lock held. Later we can copy this to the user
|
||||
* space safely without holding any locks. So even if we proceed to
|
||||
* wait for mmap_sem, it won't lead to deadlock.
|
||||
*/
|
||||
char local_name[ASHMEM_NAME_LEN];
|
||||
|
||||
mutex_lock(&ashmem_mutex);
|
||||
if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') {
|
||||
size_t len;
|
||||
|
||||
/*
|
||||
* Copying only `len', instead of ASHMEM_NAME_LEN, bytes
|
||||
* prevents us from revealing one user's stack to another.
|
||||
*/
|
||||
len = strlen(asma->name + ASHMEM_NAME_PREFIX_LEN) + 1;
|
||||
if (unlikely(copy_to_user(name,
|
||||
asma->name + ASHMEM_NAME_PREFIX_LEN, len)))
|
||||
ret = -EFAULT;
|
||||
memcpy(local_name, asma->name + ASHMEM_NAME_PREFIX_LEN, len);
|
||||
} else {
|
||||
if (unlikely(copy_to_user(name, ASHMEM_NAME_DEF,
|
||||
sizeof(ASHMEM_NAME_DEF))))
|
||||
ret = -EFAULT;
|
||||
len = sizeof(ASHMEM_NAME_DEF);
|
||||
memcpy(local_name, ASHMEM_NAME_DEF, len);
|
||||
}
|
||||
mutex_unlock(&ashmem_mutex);
|
||||
|
||||
/*
|
||||
* Now we are just copying from the stack variable to userland
|
||||
* No lock held
|
||||
*/
|
||||
if (unlikely(copy_to_user(name, local_name, len)))
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -683,6 +702,23 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* support of 32bit userspace on 64bit platforms */
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long compat_ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
||||
switch (cmd) {
|
||||
case COMPAT_ASHMEM_SET_SIZE:
|
||||
cmd = ASHMEM_SET_SIZE;
|
||||
break;
|
||||
case COMPAT_ASHMEM_SET_PROT_MASK:
|
||||
cmd = ASHMEM_SET_PROT_MASK;
|
||||
break;
|
||||
}
|
||||
return ashmem_ioctl(file, cmd, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations ashmem_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ashmem_open,
|
||||
|
@ -691,7 +727,9 @@ static const struct file_operations ashmem_fops = {
|
|||
.llseek = ashmem_llseek,
|
||||
.mmap = ashmem_mmap,
|
||||
.unlocked_ioctl = ashmem_ioctl,
|
||||
.compat_ioctl = ashmem_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = compat_ashmem_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct miscdevice ashmem_misc = {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <linux/limits.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#define ASHMEM_NAME_LEN 256
|
||||
|
||||
|
@ -45,4 +46,10 @@ struct ashmem_pin {
|
|||
#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9)
|
||||
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
|
||||
|
||||
/* support of 32bit userspace on 64bit platforms */
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define COMPAT_ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, compat_size_t)
|
||||
#define COMPAT_ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned int)
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_ASHMEM_H */
|
||||
|
|
|
@ -2878,82 +2878,109 @@ static int binder_release(struct inode *nodp, struct file *filp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int binder_node_release(struct binder_node *node, int refs)
|
||||
{
|
||||
struct binder_ref *ref;
|
||||
int death = 0;
|
||||
|
||||
list_del_init(&node->work.entry);
|
||||
binder_release_work(&node->async_todo);
|
||||
|
||||
if (hlist_empty(&node->refs)) {
|
||||
kfree(node);
|
||||
binder_stats_deleted(BINDER_STAT_NODE);
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
node->proc = NULL;
|
||||
node->local_strong_refs = 0;
|
||||
node->local_weak_refs = 0;
|
||||
hlist_add_head(&node->dead_node, &binder_dead_nodes);
|
||||
|
||||
hlist_for_each_entry(ref, &node->refs, node_entry) {
|
||||
refs++;
|
||||
|
||||
if (!ref->death)
|
||||
goto out;
|
||||
|
||||
death++;
|
||||
|
||||
if (list_empty(&ref->death->work.entry)) {
|
||||
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
|
||||
list_add_tail(&ref->death->work.entry,
|
||||
&ref->proc->todo);
|
||||
wake_up_interruptible(&ref->proc->wait);
|
||||
} else
|
||||
BUG();
|
||||
}
|
||||
|
||||
out:
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"node %d now dead, refs %d, death %d\n",
|
||||
node->debug_id, refs, death);
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
static void binder_deferred_release(struct binder_proc *proc)
|
||||
{
|
||||
struct binder_transaction *t;
|
||||
struct rb_node *n;
|
||||
int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count;
|
||||
int threads, nodes, incoming_refs, outgoing_refs, buffers,
|
||||
active_transactions, page_count;
|
||||
|
||||
BUG_ON(proc->vma);
|
||||
BUG_ON(proc->files);
|
||||
|
||||
hlist_del(&proc->proc_node);
|
||||
|
||||
if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"binder_release: %d context_mgr_node gone\n",
|
||||
proc->pid);
|
||||
"%s: %d context_mgr_node gone\n",
|
||||
__func__, proc->pid);
|
||||
binder_context_mgr_node = NULL;
|
||||
}
|
||||
|
||||
threads = 0;
|
||||
active_transactions = 0;
|
||||
while ((n = rb_first(&proc->threads))) {
|
||||
struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
|
||||
struct binder_thread *thread;
|
||||
|
||||
thread = rb_entry(n, struct binder_thread, rb_node);
|
||||
threads++;
|
||||
active_transactions += binder_free_thread(proc, thread);
|
||||
}
|
||||
|
||||
nodes = 0;
|
||||
incoming_refs = 0;
|
||||
while ((n = rb_first(&proc->nodes))) {
|
||||
struct binder_node *node = rb_entry(n, struct binder_node, rb_node);
|
||||
struct binder_node *node;
|
||||
|
||||
node = rb_entry(n, struct binder_node, rb_node);
|
||||
nodes++;
|
||||
rb_erase(&node->rb_node, &proc->nodes);
|
||||
list_del_init(&node->work.entry);
|
||||
binder_release_work(&node->async_todo);
|
||||
if (hlist_empty(&node->refs)) {
|
||||
kfree(node);
|
||||
binder_stats_deleted(BINDER_STAT_NODE);
|
||||
} else {
|
||||
struct binder_ref *ref;
|
||||
int death = 0;
|
||||
|
||||
node->proc = NULL;
|
||||
node->local_strong_refs = 0;
|
||||
node->local_weak_refs = 0;
|
||||
hlist_add_head(&node->dead_node, &binder_dead_nodes);
|
||||
|
||||
hlist_for_each_entry(ref, &node->refs, node_entry) {
|
||||
incoming_refs++;
|
||||
if (ref->death) {
|
||||
death++;
|
||||
if (list_empty(&ref->death->work.entry)) {
|
||||
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
|
||||
list_add_tail(&ref->death->work.entry, &ref->proc->todo);
|
||||
wake_up_interruptible(&ref->proc->wait);
|
||||
} else
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"node %d now dead, refs %d, death %d\n",
|
||||
node->debug_id, incoming_refs, death);
|
||||
}
|
||||
incoming_refs = binder_node_release(node, incoming_refs);
|
||||
}
|
||||
|
||||
outgoing_refs = 0;
|
||||
while ((n = rb_first(&proc->refs_by_desc))) {
|
||||
struct binder_ref *ref = rb_entry(n, struct binder_ref,
|
||||
rb_node_desc);
|
||||
struct binder_ref *ref;
|
||||
|
||||
ref = rb_entry(n, struct binder_ref, rb_node_desc);
|
||||
outgoing_refs++;
|
||||
binder_delete_ref(ref);
|
||||
}
|
||||
|
||||
binder_release_work(&proc->todo);
|
||||
binder_release_work(&proc->delivered_death);
|
||||
buffers = 0;
|
||||
|
||||
buffers = 0;
|
||||
while ((n = rb_first(&proc->allocated_buffers))) {
|
||||
struct binder_buffer *buffer = rb_entry(n, struct binder_buffer,
|
||||
rb_node);
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
buffer = rb_entry(n, struct binder_buffer, rb_node);
|
||||
|
||||
t = buffer->transaction;
|
||||
if (t) {
|
||||
t->buffer = NULL;
|
||||
|
@ -2962,6 +2989,7 @@ static void binder_deferred_release(struct binder_proc *proc)
|
|||
proc->pid, t->debug_id);
|
||||
/*BUG();*/
|
||||
}
|
||||
|
||||
binder_free_buf(proc, buffer);
|
||||
buffers++;
|
||||
}
|
||||
|
@ -2971,18 +2999,20 @@ static void binder_deferred_release(struct binder_proc *proc)
|
|||
page_count = 0;
|
||||
if (proc->pages) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) {
|
||||
if (proc->pages[i]) {
|
||||
void *page_addr = proc->buffer + i * PAGE_SIZE;
|
||||
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"binder_release: %d: page %d at %p not freed\n",
|
||||
proc->pid, i,
|
||||
page_addr);
|
||||
unmap_kernel_range((unsigned long)page_addr,
|
||||
PAGE_SIZE);
|
||||
__free_page(proc->pages[i]);
|
||||
page_count++;
|
||||
}
|
||||
void *page_addr;
|
||||
|
||||
if (!proc->pages[i])
|
||||
continue;
|
||||
|
||||
page_addr = proc->buffer + i * PAGE_SIZE;
|
||||
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%s: %d: page %d at %p not freed\n",
|
||||
__func__, proc->pid, i, page_addr);
|
||||
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
|
||||
__free_page(proc->pages[i]);
|
||||
page_count++;
|
||||
}
|
||||
kfree(proc->pages);
|
||||
vfree(proc->buffer);
|
||||
|
@ -2991,9 +3021,9 @@ static void binder_deferred_release(struct binder_proc *proc)
|
|||
put_task_struct(proc->tsk);
|
||||
|
||||
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
|
||||
"binder_release: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n",
|
||||
proc->pid, threads, nodes, incoming_refs, outgoing_refs,
|
||||
active_transactions, buffers, page_count);
|
||||
"%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n",
|
||||
__func__, proc->pid, threads, nodes, incoming_refs,
|
||||
outgoing_refs, active_transactions, buffers, page_count);
|
||||
|
||||
kfree(proc);
|
||||
}
|
||||
|
|
|
@ -85,11 +85,11 @@ struct binder_version {
|
|||
#define BINDER_CURRENT_PROTOCOL_VERSION 7
|
||||
|
||||
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
|
||||
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
|
||||
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
|
||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
|
||||
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
|
||||
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
|
||||
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
|
||||
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32)
|
||||
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
|
||||
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
|
||||
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
|
||||
|
||||
/*
|
||||
|
|
|
@ -68,6 +68,8 @@ static LIST_HEAD(log_list);
|
|||
* @log: The associated log
|
||||
* @list: The associated entry in @logger_log's list
|
||||
* @r_off: The current read head offset.
|
||||
* @r_all: Reader can read all entries
|
||||
* @r_ver: Reader ABI version
|
||||
*
|
||||
* This object lives from open to release, so we don't need additional
|
||||
* reference counting. The structure is protected by log->mutex.
|
||||
|
@ -76,6 +78,8 @@ struct logger_reader {
|
|||
struct logger_log *log;
|
||||
struct list_head list;
|
||||
size_t r_off;
|
||||
bool r_all;
|
||||
int r_ver;
|
||||
};
|
||||
|
||||
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
|
||||
|
@ -109,8 +113,29 @@ static inline struct logger_log *file_get_log(struct file *file)
|
|||
}
|
||||
|
||||
/*
|
||||
* get_entry_len - Grabs the length of the payload of the next entry starting
|
||||
* from 'off'.
|
||||
* get_entry_header - returns a pointer to the logger_entry header within
|
||||
* 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must
|
||||
* be provided. Typically the return value will be a pointer within
|
||||
* 'logger->buf'. However, a pointer to 'scratch' may be returned if
|
||||
* the log entry spans the end and beginning of the circular buffer.
|
||||
*/
|
||||
static struct logger_entry *get_entry_header(struct logger_log *log,
|
||||
size_t off, struct logger_entry *scratch)
|
||||
{
|
||||
size_t len = min(sizeof(struct logger_entry), log->size - off);
|
||||
if (len != sizeof(struct logger_entry)) {
|
||||
memcpy(((void *) scratch), log->buffer + off, len);
|
||||
memcpy(((void *) scratch) + len, log->buffer,
|
||||
sizeof(struct logger_entry) - len);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
return (struct logger_entry *) (log->buffer + off);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_entry_msg_len - Grabs the length of the message of the entry
|
||||
* starting from from 'off'.
|
||||
*
|
||||
* An entry length is 2 bytes (16 bits) in host endian order.
|
||||
* In the log, the length does not include the size of the log entry structure.
|
||||
|
@ -118,20 +143,45 @@ static inline struct logger_log *file_get_log(struct file *file)
|
|||
*
|
||||
* Caller needs to hold log->mutex.
|
||||
*/
|
||||
static __u32 get_entry_len(struct logger_log *log, size_t off)
|
||||
static __u32 get_entry_msg_len(struct logger_log *log, size_t off)
|
||||
{
|
||||
__u16 val;
|
||||
struct logger_entry scratch;
|
||||
struct logger_entry *entry;
|
||||
|
||||
/* copy 2 bytes from buffer, in memcpy order, */
|
||||
/* handling possible wrap at end of buffer */
|
||||
entry = get_entry_header(log, off, &scratch);
|
||||
return entry->len;
|
||||
}
|
||||
|
||||
((__u8 *)&val)[0] = log->buffer[off];
|
||||
if (likely(off+1 < log->size))
|
||||
((__u8 *)&val)[1] = log->buffer[off+1];
|
||||
static size_t get_user_hdr_len(int ver)
|
||||
{
|
||||
if (ver < 2)
|
||||
return sizeof(struct user_logger_entry_compat);
|
||||
else
|
||||
((__u8 *)&val)[1] = log->buffer[0];
|
||||
return sizeof(struct logger_entry);
|
||||
}
|
||||
|
||||
return sizeof(struct logger_entry) + val;
|
||||
static ssize_t copy_header_to_user(int ver, struct logger_entry *entry,
|
||||
char __user *buf)
|
||||
{
|
||||
void *hdr;
|
||||
size_t hdr_len;
|
||||
struct user_logger_entry_compat v1;
|
||||
|
||||
if (ver < 2) {
|
||||
v1.len = entry->len;
|
||||
v1.__pad = 0;
|
||||
v1.pid = entry->pid;
|
||||
v1.tid = entry->tid;
|
||||
v1.sec = entry->sec;
|
||||
v1.nsec = entry->nsec;
|
||||
hdr = &v1;
|
||||
hdr_len = sizeof(struct user_logger_entry_compat);
|
||||
} else {
|
||||
hdr = entry;
|
||||
hdr_len = sizeof(struct logger_entry);
|
||||
}
|
||||
|
||||
return copy_to_user(buf, hdr, hdr_len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -145,15 +195,31 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
|
|||
char __user *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct logger_entry scratch;
|
||||
struct logger_entry *entry;
|
||||
size_t len;
|
||||
size_t msg_start;
|
||||
|
||||
/*
|
||||
* We read from the log in two disjoint operations. First, we read from
|
||||
* the current read head offset up to 'count' bytes or to the end of
|
||||
* First, copy the header to userspace, using the version of
|
||||
* the header requested
|
||||
*/
|
||||
entry = get_entry_header(log, reader->r_off, &scratch);
|
||||
if (copy_header_to_user(reader->r_ver, entry, buf))
|
||||
return -EFAULT;
|
||||
|
||||
count -= get_user_hdr_len(reader->r_ver);
|
||||
buf += get_user_hdr_len(reader->r_ver);
|
||||
msg_start = logger_offset(log,
|
||||
reader->r_off + sizeof(struct logger_entry));
|
||||
|
||||
/*
|
||||
* We read from the msg in two disjoint operations. First, we read from
|
||||
* the current msg head offset up to 'count' bytes or to the end of
|
||||
* the log, whichever comes first.
|
||||
*/
|
||||
len = min(count, log->size - reader->r_off);
|
||||
if (copy_to_user(buf, log->buffer + reader->r_off, len))
|
||||
len = min(count, log->size - msg_start);
|
||||
if (copy_to_user(buf, log->buffer + msg_start, len))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
|
@ -164,9 +230,34 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
|
|||
if (copy_to_user(buf + len, log->buffer, count - len))
|
||||
return -EFAULT;
|
||||
|
||||
reader->r_off = logger_offset(log, reader->r_off + count);
|
||||
reader->r_off = logger_offset(log, reader->r_off +
|
||||
sizeof(struct logger_entry) + count);
|
||||
|
||||
return count;
|
||||
return count + get_user_hdr_len(reader->r_ver);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_next_entry_by_uid - Starting at 'off', returns an offset into
|
||||
* 'log->buffer' which contains the first entry readable by 'euid'
|
||||
*/
|
||||
static size_t get_next_entry_by_uid(struct logger_log *log,
|
||||
size_t off, uid_t euid)
|
||||
{
|
||||
while (off != log->w_off) {
|
||||
struct logger_entry *entry;
|
||||
struct logger_entry scratch;
|
||||
size_t next_len;
|
||||
|
||||
entry = get_entry_header(log, off, &scratch);
|
||||
|
||||
if (entry->euid == euid)
|
||||
return off;
|
||||
|
||||
next_len = sizeof(struct logger_entry) + entry->len;
|
||||
off = logger_offset(log, off + next_len);
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -178,7 +269,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
|
|||
* - If there are no log entries to read, blocks until log is written to
|
||||
* - Atomically reads exactly one log entry
|
||||
*
|
||||
* Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read
|
||||
* Will set errno to EINVAL if read
|
||||
* buffer is insufficient to hold next entry.
|
||||
*/
|
||||
static ssize_t logger_read(struct file *file, char __user *buf,
|
||||
|
@ -219,6 +310,10 @@ start:
|
|||
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
/* is there still something to read or did we race? */
|
||||
if (unlikely(log->w_off == reader->r_off)) {
|
||||
mutex_unlock(&log->mutex);
|
||||
|
@ -226,7 +321,8 @@ start:
|
|||
}
|
||||
|
||||
/* get the size of the next entry */
|
||||
ret = get_entry_len(log, reader->r_off);
|
||||
ret = get_user_hdr_len(reader->r_ver) +
|
||||
get_entry_msg_len(log, reader->r_off);
|
||||
if (count < ret) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
@ -252,7 +348,8 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
|
|||
size_t count = 0;
|
||||
|
||||
do {
|
||||
size_t nr = get_entry_len(log, off);
|
||||
size_t nr = sizeof(struct logger_entry) +
|
||||
get_entry_msg_len(log, off);
|
||||
off = logger_offset(log, off + nr);
|
||||
count += nr;
|
||||
} while (count < len);
|
||||
|
@ -382,7 +479,9 @@ static ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||
header.tid = current->pid;
|
||||
header.sec = now.tv_sec;
|
||||
header.nsec = now.tv_nsec;
|
||||
header.euid = current_euid();
|
||||
header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
|
||||
header.hdr_size = sizeof(struct logger_entry);
|
||||
|
||||
/* null writes succeed, return zero */
|
||||
if (unlikely(!header.len))
|
||||
|
@ -463,6 +562,10 @@ static int logger_open(struct inode *inode, struct file *file)
|
|||
return -ENOMEM;
|
||||
|
||||
reader->log = log;
|
||||
reader->r_ver = 1;
|
||||
reader->r_all = in_egroup_p(inode->i_gid) ||
|
||||
capable(CAP_SYSLOG);
|
||||
|
||||
INIT_LIST_HEAD(&reader->list);
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
|
@ -522,6 +625,10 @@ static unsigned int logger_poll(struct file *file, poll_table *wait)
|
|||
poll_wait(file, &log->wq, wait);
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
if (log->w_off != reader->r_off)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
mutex_unlock(&log->mutex);
|
||||
|
@ -529,11 +636,25 @@ static unsigned int logger_poll(struct file *file, poll_table *wait)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static long logger_set_version(struct logger_reader *reader, void __user *arg)
|
||||
{
|
||||
int version;
|
||||
if (copy_from_user(&version, arg, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
if ((version < 1) || (version > 2))
|
||||
return -EINVAL;
|
||||
|
||||
reader->r_ver = version;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct logger_log *log = file_get_log(file);
|
||||
struct logger_reader *reader;
|
||||
long ret = -ENOTTY;
|
||||
long ret = -EINVAL;
|
||||
void __user *argp = (void __user *) arg;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
|
@ -558,8 +679,14 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
|
||||
if (!reader->r_all)
|
||||
reader->r_off = get_next_entry_by_uid(log,
|
||||
reader->r_off, current_euid());
|
||||
|
||||
if (log->w_off != reader->r_off)
|
||||
ret = get_entry_len(log, reader->r_off);
|
||||
ret = get_user_hdr_len(reader->r_ver) +
|
||||
get_entry_msg_len(log, reader->r_off);
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
@ -568,11 +695,32 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) ||
|
||||
capable(CAP_SYSLOG))) {
|
||||
ret = -EPERM;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(reader, &log->readers, list)
|
||||
reader->r_off = log->w_off;
|
||||
log->head = log->w_off;
|
||||
ret = 0;
|
||||
break;
|
||||
case LOGGER_GET_VERSION:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
ret = reader->r_ver;
|
||||
break;
|
||||
case LOGGER_SET_VERSION:
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
break;
|
||||
}
|
||||
reader = file->private_data;
|
||||
ret = logger_set_version(reader, argp);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&log->mutex);
|
||||
|
@ -592,8 +740,8 @@ static const struct file_operations logger_fops = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Log size must be a power of two, greater than LOGGER_ENTRY_MAX_LEN,
|
||||
* and less than LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
|
||||
* Log size must must be a power of two, and greater than
|
||||
* (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)).
|
||||
*/
|
||||
static int __init create_log(char *log_name, int size)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
|
||||
/**
|
||||
* struct logger_entry - defines a single entry that is given to a logger
|
||||
* struct user_logger_entry_compat - defines a single entry that is given to a logger
|
||||
* @len: The length of the payload
|
||||
* @__pad: Two bytes of padding that appear to be required
|
||||
* @pid: The generating process' process ID
|
||||
|
@ -29,8 +29,12 @@
|
|||
* @sec: The number of seconds that have elapsed since the Epoch
|
||||
* @nsec: The number of nanoseconds that have elapsed since @sec
|
||||
* @msg: The message that is to be logged
|
||||
*
|
||||
* The userspace structure for version 1 of the logger_entry ABI.
|
||||
* This structure is returned to userspace unless the caller requests
|
||||
* an upgrade to a newer ABI version.
|
||||
*/
|
||||
struct logger_entry {
|
||||
struct user_logger_entry_compat {
|
||||
__u16 len;
|
||||
__u16 __pad;
|
||||
__s32 pid;
|
||||
|
@ -40,14 +44,38 @@ struct logger_entry {
|
|||
char msg[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct logger_entry - defines a single entry that is given to a logger
|
||||
* @len: The length of the payload
|
||||
* @hdr_size: sizeof(struct logger_entry_v2)
|
||||
* @pid: The generating process' process ID
|
||||
* @tid: The generating process' thread ID
|
||||
* @sec: The number of seconds that have elapsed since the Epoch
|
||||
* @nsec: The number of nanoseconds that have elapsed since @sec
|
||||
* @euid: Effective UID of logger
|
||||
* @msg: The message that is to be logged
|
||||
*
|
||||
* The structure for version 2 of the logger_entry ABI.
|
||||
* This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
|
||||
* is called with version >= 2
|
||||
*/
|
||||
struct logger_entry {
|
||||
__u16 len;
|
||||
__u16 hdr_size;
|
||||
__s32 pid;
|
||||
__s32 tid;
|
||||
__s32 sec;
|
||||
__s32 nsec;
|
||||
uid_t euid;
|
||||
char msg[0];
|
||||
};
|
||||
|
||||
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
|
||||
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
|
||||
#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
|
||||
#define LOGGER_LOG_MAIN "log_main" /* everything else */
|
||||
|
||||
#define LOGGER_ENTRY_MAX_LEN (4*1024)
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD \
|
||||
(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4076
|
||||
|
||||
#define __LOGGERIO 0xAE
|
||||
|
||||
|
@ -55,5 +83,7 @@ struct logger_entry {
|
|||
#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
|
||||
#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
|
||||
#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
|
||||
#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */
|
||||
#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
|
||||
|
||||
#endif /* _LINUX_LOGGER_H */
|
||||
|
|
|
@ -30,16 +30,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
static uint32_t lowmem_debug_level = 2;
|
||||
static uint32_t lowmem_debug_level = 1;
|
||||
static short lowmem_adj[6] = {
|
||||
0,
|
||||
1,
|
||||
|
@ -60,7 +63,7 @@ static unsigned long lowmem_deathpending_timeout;
|
|||
#define lowmem_print(level, x...) \
|
||||
do { \
|
||||
if (lowmem_debug_level >= (level)) \
|
||||
printk(x); \
|
||||
pr_info(x); \
|
||||
} while (0)
|
||||
|
||||
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||
|
@ -74,7 +77,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
|||
int selected_tasksize = 0;
|
||||
short selected_oom_score_adj;
|
||||
int array_size = ARRAY_SIZE(lowmem_adj);
|
||||
int other_free = global_page_state(NR_FREE_PAGES);
|
||||
int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
|
||||
int other_file = global_page_state(NR_FILE_PAGES) -
|
||||
global_page_state(NR_SHMEM);
|
||||
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* drivers/base/sw_sync.c
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "sw_sync.h"
|
||||
|
||||
static int sw_sync_cmp(u32 a, u32 b)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
|
||||
return ((s32)a - (s32)b) < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value)
|
||||
{
|
||||
struct sw_sync_pt *pt;
|
||||
|
||||
pt = (struct sw_sync_pt *)
|
||||
sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt));
|
||||
|
||||
pt->value = value;
|
||||
|
||||
return (struct sync_pt *)pt;
|
||||
}
|
||||
EXPORT_SYMBOL(sw_sync_pt_create);
|
||||
|
||||
static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt)
|
||||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt;
|
||||
struct sw_sync_timeline *obj =
|
||||
(struct sw_sync_timeline *)sync_pt->parent;
|
||||
|
||||
return (struct sync_pt *) sw_sync_pt_create(obj, pt->value);
|
||||
}
|
||||
|
||||
static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)
|
||||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
|
||||
struct sw_sync_timeline *obj =
|
||||
(struct sw_sync_timeline *)sync_pt->parent;
|
||||
|
||||
return sw_sync_cmp(obj->value, pt->value) >= 0;
|
||||
}
|
||||
|
||||
static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
|
||||
{
|
||||
struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a;
|
||||
struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b;
|
||||
|
||||
return sw_sync_cmp(pt_a->value, pt_b->value);
|
||||
}
|
||||
|
||||
static int sw_sync_fill_driver_data(struct sync_pt *sync_pt,
|
||||
void *data, int size)
|
||||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
|
||||
|
||||
if (size < sizeof(pt->value))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(data, &pt->value, sizeof(pt->value));
|
||||
|
||||
return sizeof(pt->value);
|
||||
}
|
||||
|
||||
static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline,
|
||||
char *str, int size)
|
||||
{
|
||||
struct sw_sync_timeline *timeline =
|
||||
(struct sw_sync_timeline *)sync_timeline;
|
||||
snprintf(str, size, "%d", timeline->value);
|
||||
}
|
||||
|
||||
static void sw_sync_pt_value_str(struct sync_pt *sync_pt,
|
||||
char *str, int size)
|
||||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
|
||||
snprintf(str, size, "%d", pt->value);
|
||||
}
|
||||
|
||||
static struct sync_timeline_ops sw_sync_timeline_ops = {
|
||||
.driver_name = "sw_sync",
|
||||
.dup = sw_sync_pt_dup,
|
||||
.has_signaled = sw_sync_pt_has_signaled,
|
||||
.compare = sw_sync_pt_compare,
|
||||
.fill_driver_data = sw_sync_fill_driver_data,
|
||||
.timeline_value_str = sw_sync_timeline_value_str,
|
||||
.pt_value_str = sw_sync_pt_value_str,
|
||||
};
|
||||
|
||||
|
||||
struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
|
||||
{
|
||||
struct sw_sync_timeline *obj = (struct sw_sync_timeline *)
|
||||
sync_timeline_create(&sw_sync_timeline_ops,
|
||||
sizeof(struct sw_sync_timeline),
|
||||
name);
|
||||
|
||||
return obj;
|
||||
}
|
||||
EXPORT_SYMBOL(sw_sync_timeline_create);
|
||||
|
||||
void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
|
||||
{
|
||||
obj->value += inc;
|
||||
|
||||
sync_timeline_signal(&obj->obj);
|
||||
}
|
||||
EXPORT_SYMBOL(sw_sync_timeline_inc);
|
||||
|
||||
#ifdef CONFIG_SW_SYNC_USER
|
||||
/* *WARNING*
|
||||
*
|
||||
* improper use of this can result in deadlocking kernel drivers from userspace.
|
||||
*/
|
||||
|
||||
/* opening sw_sync create a new sync obj */
|
||||
static int sw_sync_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sw_sync_timeline *obj;
|
||||
char task_comm[TASK_COMM_LEN];
|
||||
|
||||
get_task_comm(task_comm, current);
|
||||
|
||||
obj = sw_sync_timeline_create(task_comm);
|
||||
if (obj == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
file->private_data = obj;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sw_sync_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sw_sync_timeline *obj = file->private_data;
|
||||
sync_timeline_destroy(&obj->obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg)
|
||||
{
|
||||
int fd = get_unused_fd();
|
||||
int err;
|
||||
struct sync_pt *pt;
|
||||
struct sync_fence *fence;
|
||||
struct sw_sync_create_fence_data data;
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
|
||||
err = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pt = sw_sync_pt_create(obj, data.value);
|
||||
if (pt == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
data.name[sizeof(data.name) - 1] = '\0';
|
||||
fence = sync_fence_create(data.name, pt);
|
||||
if (fence == NULL) {
|
||||
sync_pt_free(pt);
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
data.fence = fd;
|
||||
if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
|
||||
sync_fence_put(fence);
|
||||
err = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
sync_fence_install(fence, fd);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
put_unused_fd(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
|
||||
return -EFAULT;
|
||||
|
||||
sw_sync_timeline_inc(obj, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct sw_sync_timeline *obj = file->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SW_SYNC_IOC_CREATE_FENCE:
|
||||
return sw_sync_ioctl_create_fence(obj, arg);
|
||||
|
||||
case SW_SYNC_IOC_INC:
|
||||
return sw_sync_ioctl_inc(obj, arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations sw_sync_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = sw_sync_open,
|
||||
.release = sw_sync_release,
|
||||
.unlocked_ioctl = sw_sync_ioctl,
|
||||
.compat_ioctl = sw_sync_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice sw_sync_dev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "sw_sync",
|
||||
.fops = &sw_sync_fops,
|
||||
};
|
||||
|
||||
static int __init sw_sync_device_init(void)
|
||||
{
|
||||
return misc_register(&sw_sync_dev);
|
||||
}
|
||||
|
||||
static void __exit sw_sync_device_remove(void)
|
||||
{
|
||||
misc_deregister(&sw_sync_dev);
|
||||
}
|
||||
|
||||
module_init(sw_sync_device_init);
|
||||
module_exit(sw_sync_device_remove);
|
||||
|
||||
#endif /* CONFIG_SW_SYNC_USER */
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* include/linux/sw_sync.h
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_SW_SYNC_H
|
||||
#define _LINUX_SW_SYNC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include "sync.h"
|
||||
|
||||
struct sw_sync_timeline {
|
||||
struct sync_timeline obj;
|
||||
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct sw_sync_pt {
|
||||
struct sync_pt pt;
|
||||
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct sw_sync_timeline *sw_sync_timeline_create(const char *name);
|
||||
void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc);
|
||||
|
||||
struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value);
|
||||
|
||||
#endif /* __KERNEL __ */
|
||||
|
||||
struct sw_sync_create_fence_data {
|
||||
__u32 value;
|
||||
char name[32];
|
||||
__s32 fence; /* fd of new fence */
|
||||
};
|
||||
|
||||
#define SW_SYNC_IOC_MAGIC 'W'
|
||||
|
||||
#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\
|
||||
struct sw_sync_create_fence_data)
|
||||
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
|
||||
|
||||
|
||||
#endif /* _LINUX_SW_SYNC_H */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* include/linux/sync.h
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_SYNC_H
|
||||
#define _LINUX_SYNC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
struct sync_timeline;
|
||||
struct sync_pt;
|
||||
struct sync_fence;
|
||||
|
||||
/**
|
||||
* struct sync_timeline_ops - sync object implementation ops
|
||||
* @driver_name: name of the implentation
|
||||
* @dup: duplicate a sync_pt
|
||||
* @has_signaled: returns:
|
||||
* 1 if pt has signaled
|
||||
* 0 if pt has not signaled
|
||||
* <0 on error
|
||||
* @compare: returns:
|
||||
* 1 if b will signal before a
|
||||
* 0 if a and b will signal at the same time
|
||||
* -1 if a will signabl before b
|
||||
* @free_pt: called before sync_pt is freed
|
||||
* @release_obj: called before sync_timeline is freed
|
||||
* @print_obj: deprecated
|
||||
* @print_pt: deprecated
|
||||
* @fill_driver_data: write implmentation specific driver data to data.
|
||||
* should return an error if there is not enough room
|
||||
* as specified by size. This information is returned
|
||||
* to userspace by SYNC_IOC_FENCE_INFO.
|
||||
* @timeline_value_str: fill str with the value of the sync_timeline's counter
|
||||
* @pt_value_str: fill str with the value of the sync_pt
|
||||
*/
|
||||
struct sync_timeline_ops {
|
||||
const char *driver_name;
|
||||
|
||||
/* required */
|
||||
struct sync_pt *(*dup)(struct sync_pt *pt);
|
||||
|
||||
/* required */
|
||||
int (*has_signaled)(struct sync_pt *pt);
|
||||
|
||||
/* required */
|
||||
int (*compare)(struct sync_pt *a, struct sync_pt *b);
|
||||
|
||||
/* optional */
|
||||
void (*free_pt)(struct sync_pt *sync_pt);
|
||||
|
||||
/* optional */
|
||||
void (*release_obj)(struct sync_timeline *sync_timeline);
|
||||
|
||||
/* deprecated */
|
||||
void (*print_obj)(struct seq_file *s,
|
||||
struct sync_timeline *sync_timeline);
|
||||
|
||||
/* deprecated */
|
||||
void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt);
|
||||
|
||||
/* optional */
|
||||
int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size);
|
||||
|
||||
/* optional */
|
||||
void (*timeline_value_str)(struct sync_timeline *timeline, char *str,
|
||||
int size);
|
||||
|
||||
/* optional */
|
||||
void (*pt_value_str)(struct sync_pt *pt, char *str, int size);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_timeline - sync object
|
||||
* @kref: reference count on fence.
|
||||
* @ops: ops that define the implementaiton of the sync_timeline
|
||||
* @name: name of the sync_timeline. Useful for debugging
|
||||
* @destoryed: set when sync_timeline is destroyed
|
||||
* @child_list_head: list of children sync_pts for this sync_timeline
|
||||
* @child_list_lock: lock protecting @child_list_head, destroyed, and
|
||||
* sync_pt.status
|
||||
* @active_list_head: list of active (unsignaled/errored) sync_pts
|
||||
* @sync_timeline_list: membership in global sync_timeline_list
|
||||
*/
|
||||
struct sync_timeline {
|
||||
struct kref kref;
|
||||
const struct sync_timeline_ops *ops;
|
||||
char name[32];
|
||||
|
||||
/* protected by child_list_lock */
|
||||
bool destroyed;
|
||||
|
||||
struct list_head child_list_head;
|
||||
spinlock_t child_list_lock;
|
||||
|
||||
struct list_head active_list_head;
|
||||
spinlock_t active_list_lock;
|
||||
|
||||
struct list_head sync_timeline_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_pt - sync point
|
||||
* @parent: sync_timeline to which this sync_pt belongs
|
||||
* @child_list: membership in sync_timeline.child_list_head
|
||||
* @active_list: membership in sync_timeline.active_list_head
|
||||
* @signaled_list: membership in temorary signaled_list on stack
|
||||
* @fence: sync_fence to which the sync_pt belongs
|
||||
* @pt_list: membership in sync_fence.pt_list_head
|
||||
* @status: 1: signaled, 0:active, <0: error
|
||||
* @timestamp: time which sync_pt status transitioned from active to
|
||||
* singaled or error.
|
||||
*/
|
||||
struct sync_pt {
|
||||
struct sync_timeline *parent;
|
||||
struct list_head child_list;
|
||||
|
||||
struct list_head active_list;
|
||||
struct list_head signaled_list;
|
||||
|
||||
struct sync_fence *fence;
|
||||
struct list_head pt_list;
|
||||
|
||||
/* protected by parent->active_list_lock */
|
||||
int status;
|
||||
|
||||
ktime_t timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_fence - sync fence
|
||||
* @file: file representing this fence
|
||||
* @kref: referenace count on fence.
|
||||
* @name: name of sync_fence. Useful for debugging
|
||||
* @pt_list_head: list of sync_pts in ths fence. immutable once fence
|
||||
* is created
|
||||
* @waiter_list_head: list of asynchronous waiters on this fence
|
||||
* @waiter_list_lock: lock protecting @waiter_list_head and @status
|
||||
* @status: 1: signaled, 0:active, <0: error
|
||||
*
|
||||
* @wq: wait queue for fence signaling
|
||||
* @sync_fence_list: membership in global fence list
|
||||
*/
|
||||
struct sync_fence {
|
||||
struct file *file;
|
||||
struct kref kref;
|
||||
char name[32];
|
||||
|
||||
/* this list is immutable once the fence is created */
|
||||
struct list_head pt_list_head;
|
||||
|
||||
struct list_head waiter_list_head;
|
||||
spinlock_t waiter_list_lock; /* also protects status */
|
||||
int status;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
|
||||
struct list_head sync_fence_list;
|
||||
};
|
||||
|
||||
struct sync_fence_waiter;
|
||||
typedef void (*sync_callback_t)(struct sync_fence *fence,
|
||||
struct sync_fence_waiter *waiter);
|
||||
|
||||
/**
|
||||
* struct sync_fence_waiter - metadata for asynchronous waiter on a fence
|
||||
* @waiter_list: membership in sync_fence.waiter_list_head
|
||||
* @callback: function pointer to call when fence signals
|
||||
* @callback_data: pointer to pass to @callback
|
||||
*/
|
||||
struct sync_fence_waiter {
|
||||
struct list_head waiter_list;
|
||||
|
||||
sync_callback_t callback;
|
||||
};
|
||||
|
||||
static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter,
|
||||
sync_callback_t callback)
|
||||
{
|
||||
waiter->callback = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
* API for sync_timeline implementers
|
||||
*/
|
||||
|
||||
/**
|
||||
* sync_timeline_create() - creates a sync object
|
||||
* @ops: specifies the implemention ops for the object
|
||||
* @size: size to allocate for this obj
|
||||
* @name: sync_timeline name
|
||||
*
|
||||
* Creates a new sync_timeline which will use the implemetation specified by
|
||||
* @ops. @size bytes will be allocated allowing for implemntation specific
|
||||
* data to be kept after the generic sync_timeline stuct.
|
||||
*/
|
||||
struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
|
||||
int size, const char *name);
|
||||
|
||||
/**
|
||||
* sync_timeline_destory() - destorys a sync object
|
||||
* @obj: sync_timeline to destroy
|
||||
*
|
||||
* A sync implemntation should call this when the @obj is going away
|
||||
* (i.e. module unload.) @obj won't actually be freed until all its childern
|
||||
* sync_pts are freed.
|
||||
*/
|
||||
void sync_timeline_destroy(struct sync_timeline *obj);
|
||||
|
||||
/**
|
||||
* sync_timeline_signal() - signal a status change on a sync_timeline
|
||||
* @obj: sync_timeline to signal
|
||||
*
|
||||
* A sync implemntation should call this any time one of it's sync_pts
|
||||
* has signaled or has an error condition.
|
||||
*/
|
||||
void sync_timeline_signal(struct sync_timeline *obj);
|
||||
|
||||
/**
|
||||
* sync_pt_create() - creates a sync pt
|
||||
* @parent: sync_pt's parent sync_timeline
|
||||
* @size: size to allocate for this pt
|
||||
*
|
||||
* Creates a new sync_pt as a chiled of @parent. @size bytes will be
|
||||
* allocated allowing for implemntation specific data to be kept after
|
||||
* the generic sync_timeline struct.
|
||||
*/
|
||||
struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size);
|
||||
|
||||
/**
|
||||
* sync_pt_free() - frees a sync pt
|
||||
* @pt: sync_pt to free
|
||||
*
|
||||
* This should only be called on sync_pts which have been created but
|
||||
* not added to a fence.
|
||||
*/
|
||||
void sync_pt_free(struct sync_pt *pt);
|
||||
|
||||
/**
|
||||
* sync_fence_create() - creates a sync fence
|
||||
* @name: name of fence to create
|
||||
* @pt: sync_pt to add to the fence
|
||||
*
|
||||
* Creates a fence containg @pt. Once this is called, the fence takes
|
||||
* ownership of @pt.
|
||||
*/
|
||||
struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt);
|
||||
|
||||
/*
|
||||
* API for sync_fence consumers
|
||||
*/
|
||||
|
||||
/**
|
||||
* sync_fence_merge() - merge two fences
|
||||
* @name: name of new fence
|
||||
* @a: fence a
|
||||
* @b: fence b
|
||||
*
|
||||
* Creates a new fence which contains copies of all the sync_pts in both
|
||||
* @a and @b. @a and @b remain valid, independent fences.
|
||||
*/
|
||||
struct sync_fence *sync_fence_merge(const char *name,
|
||||
struct sync_fence *a, struct sync_fence *b);
|
||||
|
||||
/**
|
||||
* sync_fence_fdget() - get a fence from an fd
|
||||
* @fd: fd referencing a fence
|
||||
*
|
||||
* Ensures @fd references a valid fence, increments the refcount of the backing
|
||||
* file, and returns the fence.
|
||||
*/
|
||||
struct sync_fence *sync_fence_fdget(int fd);
|
||||
|
||||
/**
|
||||
* sync_fence_put() - puts a refernnce of a sync fence
|
||||
* @fence: fence to put
|
||||
*
|
||||
* Puts a reference on @fence. If this is the last reference, the fence and
|
||||
* all it's sync_pts will be freed
|
||||
*/
|
||||
void sync_fence_put(struct sync_fence *fence);
|
||||
|
||||
/**
|
||||
* sync_fence_install() - installs a fence into a file descriptor
|
||||
* @fence: fence to instal
|
||||
* @fd: file descriptor in which to install the fence
|
||||
*
|
||||
* Installs @fence into @fd. @fd's should be acquired through get_unused_fd().
|
||||
*/
|
||||
void sync_fence_install(struct sync_fence *fence, int fd);
|
||||
|
||||
/**
|
||||
* sync_fence_wait_async() - registers and async wait on the fence
|
||||
* @fence: fence to wait on
|
||||
* @waiter: waiter callback struck
|
||||
*
|
||||
* Returns 1 if @fence has already signaled.
|
||||
*
|
||||
* Registers a callback to be called when @fence signals or has an error.
|
||||
* @waiter should be initialized with sync_fence_waiter_init().
|
||||
*/
|
||||
int sync_fence_wait_async(struct sync_fence *fence,
|
||||
struct sync_fence_waiter *waiter);
|
||||
|
||||
/**
|
||||
* sync_fence_cancel_async() - cancels an async wait
|
||||
* @fence: fence to wait on
|
||||
* @waiter: waiter callback struck
|
||||
*
|
||||
* returns 0 if waiter was removed from fence's async waiter list.
|
||||
* returns -ENOENT if waiter was not found on fence's async waiter list.
|
||||
*
|
||||
* Cancels a previously registered async wait. Will fail gracefully if
|
||||
* @waiter was never registered or if @fence has already signaled @waiter.
|
||||
*/
|
||||
int sync_fence_cancel_async(struct sync_fence *fence,
|
||||
struct sync_fence_waiter *waiter);
|
||||
|
||||
/**
|
||||
* sync_fence_wait() - wait on fence
|
||||
* @fence: fence to wait on
|
||||
* @tiemout: timeout in ms
|
||||
*
|
||||
* Wait for @fence to be signaled or have an error. Waits indefinitely
|
||||
* if @timeout < 0
|
||||
*/
|
||||
int sync_fence_wait(struct sync_fence *fence, long timeout);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/**
|
||||
* struct sync_merge_data - data passed to merge ioctl
|
||||
* @fd2: file descriptor of second fence
|
||||
* @name: name of new fence
|
||||
* @fence: returns the fd of the new fence to userspace
|
||||
*/
|
||||
struct sync_merge_data {
|
||||
__s32 fd2; /* fd of second fence */
|
||||
char name[32]; /* name of new fence */
|
||||
__s32 fence; /* fd on newly created fence */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_pt_info - detailed sync_pt information
|
||||
* @len: length of sync_pt_info including any driver_data
|
||||
* @obj_name: name of parent sync_timeline
|
||||
* @driver_name: name of driver implmenting the parent
|
||||
* @status: status of the sync_pt 0:active 1:signaled <0:error
|
||||
* @timestamp_ns: timestamp of status change in nanoseconds
|
||||
* @driver_data: any driver dependant data
|
||||
*/
|
||||
struct sync_pt_info {
|
||||
__u32 len;
|
||||
char obj_name[32];
|
||||
char driver_name[32];
|
||||
__s32 status;
|
||||
__u64 timestamp_ns;
|
||||
|
||||
__u8 driver_data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_fence_info_data - data returned from fence info ioctl
|
||||
* @len: ioctl caller writes the size of the buffer its passing in.
|
||||
* ioctl returns length of sync_fence_data reutnred to userspace
|
||||
* including pt_info.
|
||||
* @name: name of fence
|
||||
* @status: status of fence. 1: signaled 0:active <0:error
|
||||
* @pt_info: a sync_pt_info struct for every sync_pt in the fence
|
||||
*/
|
||||
struct sync_fence_info_data {
|
||||
__u32 len;
|
||||
char name[32];
|
||||
__s32 status;
|
||||
|
||||
__u8 pt_info[0];
|
||||
};
|
||||
|
||||
#define SYNC_IOC_MAGIC '>'
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_WAIT - wait for a fence to signal
|
||||
*
|
||||
* pass timeout in milliseconds. Waits indefinitely timeout < 0.
|
||||
*/
|
||||
#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_MERGE - merge two fences
|
||||
*
|
||||
* Takes a struct sync_merge_data. Creates a new fence containing copies of
|
||||
* the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
|
||||
* new fence's fd in sync_merge_data.fence
|
||||
*/
|
||||
#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence
|
||||
*
|
||||
* Takes a struct sync_fence_info_data with extra space allocated for pt_info.
|
||||
* Caller should write the size of the buffer into len. On return, len is
|
||||
* updated to reflect the total size of the sync_fence_info_data including
|
||||
* pt_info.
|
||||
*
|
||||
* pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
|
||||
* To itterate over the sync_pt_infos, use the sync_pt_info.len field.
|
||||
*/
|
||||
#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
|
||||
struct sync_fence_info_data)
|
||||
|
||||
#endif /* _LINUX_SYNC_H */
|
|
@ -0,0 +1,82 @@
|
|||
#undef TRACE_SYSTEM
|
||||
#define TRACE_INCLUDE_PATH ../../drivers/staging/android/trace
|
||||
#define TRACE_SYSTEM sync
|
||||
|
||||
#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_SYNC_H
|
||||
|
||||
#include "../sync.h"
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(sync_timeline,
|
||||
TP_PROTO(struct sync_timeline *timeline),
|
||||
|
||||
TP_ARGS(timeline),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, timeline->name)
|
||||
__array(char, value, 32)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, timeline->name);
|
||||
if (timeline->ops->timeline_value_str) {
|
||||
timeline->ops->timeline_value_str(timeline,
|
||||
__entry->value,
|
||||
sizeof(__entry->value));
|
||||
} else {
|
||||
__entry->value[0] = '\0';
|
||||
}
|
||||
),
|
||||
|
||||
TP_printk("name=%s value=%s", __get_str(name), __entry->value)
|
||||
);
|
||||
|
||||
TRACE_EVENT(sync_wait,
|
||||
TP_PROTO(struct sync_fence *fence, int begin),
|
||||
|
||||
TP_ARGS(fence, begin),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, fence->name)
|
||||
__field(s32, status)
|
||||
__field(u32, begin)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, fence->name);
|
||||
__entry->status = fence->status;
|
||||
__entry->begin = begin;
|
||||
),
|
||||
|
||||
TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end",
|
||||
__get_str(name), __entry->status)
|
||||
);
|
||||
|
||||
TRACE_EVENT(sync_pt,
|
||||
TP_PROTO(struct sync_pt *pt),
|
||||
|
||||
TP_ARGS(pt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(timeline, pt->parent->name)
|
||||
__array(char, value, 32)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(timeline, pt->parent->name);
|
||||
if (pt->parent->ops->pt_value_str) {
|
||||
pt->parent->ops->pt_value_str(pt, __entry->value,
|
||||
sizeof(__entry->value));
|
||||
} else {
|
||||
__entry->value[0] = '\0';
|
||||
}
|
||||
),
|
||||
|
||||
TP_printk("name=%s value=%s", __get_str(timeline), __entry->value)
|
||||
);
|
||||
|
||||
#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
|
@ -1148,8 +1148,8 @@ cntrlEnd:
|
|||
|
||||
if (((ULONG)pBulkBuffer->Register & 0x0F000000) != 0x0F000000 ||
|
||||
((ULONG)pBulkBuffer->Register & 0x3)) {
|
||||
kfree(pvBuffer);
|
||||
BCM_DEBUG_PRINT (Adapter, DBG_TYPE_PRINTK, 0, 0, "WRM Done On invalid Address : %x Access Denied.\n", (int)pBulkBuffer->Register);
|
||||
kfree(pvBuffer);
|
||||
Status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -205,30 +205,6 @@ static int bcm_download_config_file(struct bcm_mini_adapter *Adapter, struct bcm
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int bcm_compare_buff_contents(unsigned char *readbackbuff, unsigned char *buff, unsigned int len)
|
||||
{
|
||||
int retval = STATUS_SUCCESS;
|
||||
struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev);
|
||||
if ((len-sizeof(unsigned int)) < 4) {
|
||||
if (memcmp(readbackbuff , buff, len))
|
||||
retval = -EINVAL;
|
||||
} else {
|
||||
len -= 4;
|
||||
|
||||
while (len) {
|
||||
if (*(unsigned int *)&readbackbuff[len] != *(unsigned int *)&buff[len]) {
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Firmware Download is not proper");
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Val from Binary %x, Val From Read Back %x ", *(unsigned int *)&buff[len], *(unsigned int*)&readbackbuff[len]);
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "len =%x!!!", len);
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
len -= 4;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int bcm_ioctl_fw_download(struct bcm_mini_adapter *Adapter, struct bcm_firmware_info *psFwInfo)
|
||||
{
|
||||
int retval = STATUS_SUCCESS;
|
||||
|
@ -321,9 +297,11 @@ static INT buffRdbkVerify(struct bcm_mini_adapter *Adapter, PUCHAR mappedbuffer,
|
|||
break;
|
||||
}
|
||||
|
||||
retval = bcm_compare_buff_contents(readbackbuff, mappedbuffer, len);
|
||||
if (STATUS_SUCCESS != retval)
|
||||
break;
|
||||
if (memcmp(readbackbuff, mappedbuffer, len) != 0) {
|
||||
pr_err("%s() failed. The firmware doesn't match what was written",
|
||||
__func__);
|
||||
retval = -EIO;
|
||||
}
|
||||
|
||||
u32StartingAddress += len;
|
||||
u32FirmwareLength -= len;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2228,20 +2228,20 @@ int BcmAllocFlashCSStructure(struct bcm_mini_adapter *psAdapter)
|
|||
BCM_DEBUG_PRINT(psAdapter, DBG_TYPE_PRINTK, 0, 0, "Adapter structure point is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
psAdapter->psFlashCSInfo = (struct bcm_flash_cs_info *)kzalloc(sizeof(struct bcm_flash_cs_info), GFP_KERNEL);
|
||||
psAdapter->psFlashCSInfo = kzalloc(sizeof(struct bcm_flash_cs_info), GFP_KERNEL);
|
||||
if (psAdapter->psFlashCSInfo == NULL) {
|
||||
BCM_DEBUG_PRINT(psAdapter, DBG_TYPE_PRINTK, 0, 0, "Can't Allocate memory for Flash 1.x");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
psAdapter->psFlash2xCSInfo = (struct bcm_flash2x_cs_info *)kzalloc(sizeof(struct bcm_flash2x_cs_info), GFP_KERNEL);
|
||||
psAdapter->psFlash2xCSInfo = kzalloc(sizeof(struct bcm_flash2x_cs_info), GFP_KERNEL);
|
||||
if (!psAdapter->psFlash2xCSInfo) {
|
||||
BCM_DEBUG_PRINT(psAdapter, DBG_TYPE_PRINTK, 0, 0, "Can't Allocate memory for Flash 2.x");
|
||||
kfree(psAdapter->psFlashCSInfo);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
psAdapter->psFlash2xVendorInfo = (struct bcm_flash2x_vendor_info *)kzalloc(sizeof(struct bcm_flash2x_vendor_info), GFP_KERNEL);
|
||||
psAdapter->psFlash2xVendorInfo = kzalloc(sizeof(struct bcm_flash2x_vendor_info), GFP_KERNEL);
|
||||
if (!psAdapter->psFlash2xVendorInfo) {
|
||||
BCM_DEBUG_PRINT(psAdapter, DBG_TYPE_PRINTK, 0, 0, "Can't Allocate Vendor Info Memory for Flash 2.x");
|
||||
kfree(psAdapter->psFlashCSInfo);
|
||||
|
@ -4074,7 +4074,7 @@ int BcmCopySection(struct bcm_mini_adapter *Adapter,
|
|||
else
|
||||
BuffSize = numOfBytes;
|
||||
|
||||
pBuff = (PCHAR)kzalloc(BuffSize, GFP_KERNEL);
|
||||
pBuff = kzalloc(BuffSize, GFP_KERNEL);
|
||||
if (!pBuff) {
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Memory allocation failed.. ");
|
||||
return -ENOMEM;
|
||||
|
@ -4154,7 +4154,7 @@ int SaveHeaderIfPresent(struct bcm_mini_adapter *Adapter, PUCHAR pBuff, unsigned
|
|||
}
|
||||
/* If Header is present overwrite passed buffer with this */
|
||||
if (bHasHeader && (Adapter->bHeaderChangeAllowed == FALSE)) {
|
||||
pTempBuff = (PUCHAR)kzalloc(HeaderSizeToProtect, GFP_KERNEL);
|
||||
pTempBuff = kzalloc(HeaderSizeToProtect, GFP_KERNEL);
|
||||
if (!pTempBuff) {
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Memory allocation failed");
|
||||
return -ENOMEM;
|
||||
|
@ -4563,7 +4563,7 @@ static int CorruptDSDSig(struct bcm_mini_adapter *Adapter, enum bcm_flash2x_sect
|
|||
}
|
||||
}
|
||||
|
||||
pBuff = (PUCHAR)kzalloc(MAX_RW_SIZE, GFP_KERNEL);
|
||||
pBuff = kzalloc(MAX_RW_SIZE, GFP_KERNEL);
|
||||
if (!pBuff) {
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Can't allocate memorey");
|
||||
return -ENOMEM;
|
||||
|
@ -4622,7 +4622,7 @@ static int CorruptISOSig(struct bcm_mini_adapter *Adapter, enum bcm_flash2x_sect
|
|||
return SECTOR_IS_NOT_WRITABLE;
|
||||
}
|
||||
|
||||
pBuff = (PUCHAR)kzalloc(MAX_RW_SIZE, GFP_KERNEL);
|
||||
pBuff = kzalloc(MAX_RW_SIZE, GFP_KERNEL);
|
||||
if (!pBuff) {
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Can't allocate memorey");
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
if USB_GADGET
|
||||
|
||||
config USB_G_CCG
|
||||
tristate "Configurable Composite Gadget (STAGING)"
|
||||
depends on STAGING && BLOCK && NET && !USB_ZERO && !USB_ZERO_HNPTEST && !USB_AUDIO && !GADGET_UAC1 && !USB_ETH && !USB_ETH_RNDIS && !USB_ETH_EEM && !USB_G_NCM && !USB_GADGETFS && !USB_FUNCTIONFS && !USB_FUNCTIONFS_ETH && !USB_FUNCTIONFS_RNDIS && !USB_FUNCTIONFS_GENERIC && !USB_FILE_STORAGE && !USB_FILE_STORAGE_TEST && !USB_MASS_STORAGE && !USB_G_SERIAL && !USB_MIDI_GADGET && !USB_G_PRINTER && !USB_CDC_COMPOSITE && !USB_G_NOKIA && !USB_G_ACM_MS && !USB_G_MULTI && !USB_G_MULTI_RNDIS && !USB_G_MULTI_CDC && !USB_G_HID && !USB_G_DBGP && !USB_G_WEBCAM && TTY
|
||||
help
|
||||
The Configurable Composite Gadget supports multiple USB
|
||||
functions: acm, mass storage, rndis and FunctionFS.
|
||||
Each function can be configured and enabled/disabled
|
||||
dynamically from userspace through a sysfs interface.
|
||||
|
||||
In order to compile this (either as a module or built-in),
|
||||
"USB Gadget Drivers" and anything under it must not be
|
||||
selected compiled-in in
|
||||
Device Drivers->USB Support->USB Gadget Support.
|
||||
However, you can say "M" there, if you do, the
|
||||
Configurable Composite Gadget can be compiled "M" only
|
||||
or not at all.
|
||||
|
||||
BIG FAT NOTE: DON'T RELY ON THIS USERINTERFACE HERE! AS PART
|
||||
OF THE REWORK DONE HERE WILL BE A NEW USER INTERFACE WITHOUT ANY
|
||||
COMPATIBILITY TO THIS SYSFS INTERFACE HERE. BE AWARE OF THIS
|
||||
BEFORE SELECTING THIS.
|
||||
|
||||
endif # USB_GADGET
|
|
@ -1,2 +0,0 @@
|
|||
g_ccg-y := ccg.o
|
||||
obj-$(CONFIG_USB_G_CCG) += g_ccg.o
|
|
@ -1,6 +0,0 @@
|
|||
TODO:
|
||||
- change configuration interface from sysfs to configfs
|
||||
|
||||
Please send patches to Greg Kroah-Hartmann <gregkh@linuxfoundation.org>,
|
||||
Andrzej Pietrasiewicz <andrzej.p@samsung.com>, and
|
||||
Cc: Mike Lockwood <lockwood@android.com>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,395 +0,0 @@
|
|||
/*
|
||||
* composite.h -- framework for usb gadgets which are composite devices
|
||||
*
|
||||
* Copyright (C) 2006-2008 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_COMPOSITE_H
|
||||
#define __LINUX_USB_COMPOSITE_H
|
||||
|
||||
/*
|
||||
* This framework is an optional layer on top of the USB Gadget interface,
|
||||
* making it easier to build (a) Composite devices, supporting multiple
|
||||
* functions within any single configuration, and (b) Multi-configuration
|
||||
* devices, also supporting multiple functions but without necessarily
|
||||
* having more than one function per configuration.
|
||||
*
|
||||
* Example: a device with a single configuration supporting both network
|
||||
* link and mass storage functions is a composite device. Those functions
|
||||
* might alternatively be packaged in individual configurations, but in
|
||||
* the composite model the host can use both functions at the same time.
|
||||
*/
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/*
|
||||
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
|
||||
* wish to delay the data/status stages of the control transfer till they
|
||||
* are ready. The control transfer will then be kept from completing till
|
||||
* all the function drivers that requested for USB_GADGET_DELAYED_STAUS
|
||||
* invoke usb_composite_setup_continue().
|
||||
*/
|
||||
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
|
||||
|
||||
struct usb_configuration;
|
||||
|
||||
/**
|
||||
* struct usb_function - describes one function of a configuration
|
||||
* @name: For diagnostics, identifies the function.
|
||||
* @strings: tables of strings, keyed by identifiers assigned during bind()
|
||||
* and by language IDs provided in control requests
|
||||
* @descriptors: Table of full (or low) speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this pointer is null,
|
||||
* the function will not be available at full speed (or at low speed).
|
||||
* @hs_descriptors: Table of high speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this pointer is null,
|
||||
* the function will not be available at high speed.
|
||||
* @ss_descriptors: Table of super speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this
|
||||
* pointer is null after initiation, the function will not
|
||||
* be available at super speed.
|
||||
* @config: assigned when @usb_add_function() is called; this is the
|
||||
* configuration with which this function is associated.
|
||||
* @bind: Before the gadget can register, all of its functions bind() to the
|
||||
* available resources including string and interface identifiers used
|
||||
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
||||
* driver which added this function.
|
||||
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
|
||||
* initialize usb_ep.driver data at this time (when it is used).
|
||||
* Note that setting an interface to its current altsetting resets
|
||||
* interface state, and that all interfaces have a disabled state.
|
||||
* @get_alt: Returns the active altsetting. If this is not provided,
|
||||
* then only altsetting zero is supported.
|
||||
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
|
||||
* include host resetting or reconfiguring the gadget, and disconnection.
|
||||
* @setup: Used for interface-specific control requests.
|
||||
* @suspend: Notifies functions when the host stops sending USB traffic.
|
||||
* @resume: Notifies functions when the host restarts USB traffic.
|
||||
* @get_status: Returns function status as a reply to
|
||||
* GetStatus() request when the recepient is Interface.
|
||||
* @func_suspend: callback to be called when
|
||||
* SetFeature(FUNCTION_SUSPEND) is reseived
|
||||
*
|
||||
* A single USB function uses one or more interfaces, and should in most
|
||||
* cases support operation at both full and high speeds. Each function is
|
||||
* associated by @usb_add_function() with a one configuration; that function
|
||||
* causes @bind() to be called so resources can be allocated as part of
|
||||
* setting up a gadget driver. Those resources include endpoints, which
|
||||
* should be allocated using @usb_ep_autoconfig().
|
||||
*
|
||||
* To support dual speed operation, a function driver provides descriptors
|
||||
* for both high and full speed operation. Except in rare cases that don't
|
||||
* involve bulk endpoints, each speed needs different endpoint descriptors.
|
||||
*
|
||||
* Function drivers choose their own strategies for managing instance data.
|
||||
* The simplest strategy just declares it "static', which means the function
|
||||
* can only be activated once. If the function needs to be exposed in more
|
||||
* than one configuration at a given speed, it needs to support multiple
|
||||
* usb_function structures (one for each configuration).
|
||||
*
|
||||
* A more complex strategy might encapsulate a @usb_function structure inside
|
||||
* a driver-specific instance structure to allows multiple activations. An
|
||||
* example of multiple activations might be a CDC ACM function that supports
|
||||
* two or more distinct instances within the same configuration, providing
|
||||
* several independent logical data links to a USB host.
|
||||
*/
|
||||
struct usb_function {
|
||||
const char *name;
|
||||
struct usb_gadget_strings **strings;
|
||||
struct usb_descriptor_header **descriptors;
|
||||
struct usb_descriptor_header **hs_descriptors;
|
||||
struct usb_descriptor_header **ss_descriptors;
|
||||
|
||||
struct usb_configuration *config;
|
||||
|
||||
/* REVISIT: bind() functions can be marked __init, which
|
||||
* makes trouble for section mismatch analysis. See if
|
||||
* we can't restructure things to avoid mismatching.
|
||||
* Related: unbind() may kfree() but bind() won't...
|
||||
*/
|
||||
|
||||
/* configuration management: bind/unbind */
|
||||
int (*bind)(struct usb_configuration *,
|
||||
struct usb_function *);
|
||||
void (*unbind)(struct usb_configuration *,
|
||||
struct usb_function *);
|
||||
|
||||
/* runtime state management */
|
||||
int (*set_alt)(struct usb_function *,
|
||||
unsigned interface, unsigned alt);
|
||||
int (*get_alt)(struct usb_function *,
|
||||
unsigned interface);
|
||||
void (*disable)(struct usb_function *);
|
||||
int (*setup)(struct usb_function *,
|
||||
const struct usb_ctrlrequest *);
|
||||
void (*suspend)(struct usb_function *);
|
||||
void (*resume)(struct usb_function *);
|
||||
|
||||
/* USB 3.0 additions */
|
||||
int (*get_status)(struct usb_function *);
|
||||
int (*func_suspend)(struct usb_function *,
|
||||
u8 suspend_opt);
|
||||
/* private: */
|
||||
/* internals */
|
||||
struct list_head list;
|
||||
DECLARE_BITMAP(endpoints, 32);
|
||||
};
|
||||
|
||||
int usb_add_function(struct usb_configuration *, struct usb_function *);
|
||||
|
||||
int usb_function_deactivate(struct usb_function *);
|
||||
int usb_function_activate(struct usb_function *);
|
||||
|
||||
int usb_interface_id(struct usb_configuration *, struct usb_function *);
|
||||
|
||||
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
|
||||
struct usb_ep *_ep);
|
||||
|
||||
#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
|
||||
|
||||
/**
|
||||
* struct usb_configuration - represents one gadget configuration
|
||||
* @label: For diagnostics, describes the configuration.
|
||||
* @strings: Tables of strings, keyed by identifiers assigned during @bind()
|
||||
* and by language IDs provided in control requests.
|
||||
* @descriptors: Table of descriptors preceding all function descriptors.
|
||||
* Examples include OTG and vendor-specific descriptors.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
||||
* driver which added this configuration.
|
||||
* @setup: Used to delegate control requests that aren't handled by standard
|
||||
* device infrastructure or directed at a specific interface.
|
||||
* @bConfigurationValue: Copied into configuration descriptor.
|
||||
* @iConfiguration: Copied into configuration descriptor.
|
||||
* @bmAttributes: Copied into configuration descriptor.
|
||||
* @bMaxPower: Copied into configuration descriptor.
|
||||
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
|
||||
* the device associated with this configuration.
|
||||
*
|
||||
* Configurations are building blocks for gadget drivers structured around
|
||||
* function drivers. Simple USB gadgets require only one function and one
|
||||
* configuration, and handle dual-speed hardware by always providing the same
|
||||
* functionality. Slightly more complex gadgets may have more than one
|
||||
* single-function configuration at a given speed; or have configurations
|
||||
* that only work at one speed.
|
||||
*
|
||||
* Composite devices are, by definition, ones with configurations which
|
||||
* include more than one function.
|
||||
*
|
||||
* The lifecycle of a usb_configuration includes allocation, initialization
|
||||
* of the fields described above, and calling @usb_add_config() to set up
|
||||
* internal data and bind it to a specific device. The configuration's
|
||||
* @bind() method is then used to initialize all the functions and then
|
||||
* call @usb_add_function() for them.
|
||||
*
|
||||
* Those functions would normally be independent of each other, but that's
|
||||
* not mandatory. CDC WMC devices are an example where functions often
|
||||
* depend on other functions, with some functions subsidiary to others.
|
||||
* Such interdependency may be managed in any way, so long as all of the
|
||||
* descriptors complete by the time the composite driver returns from
|
||||
* its bind() routine.
|
||||
*/
|
||||
struct usb_configuration {
|
||||
const char *label;
|
||||
struct usb_gadget_strings **strings;
|
||||
const struct usb_descriptor_header **descriptors;
|
||||
|
||||
/* REVISIT: bind() functions can be marked __init, which
|
||||
* makes trouble for section mismatch analysis. See if
|
||||
* we can't restructure things to avoid mismatching...
|
||||
*/
|
||||
|
||||
/* configuration management: unbind/setup */
|
||||
void (*unbind)(struct usb_configuration *);
|
||||
int (*setup)(struct usb_configuration *,
|
||||
const struct usb_ctrlrequest *);
|
||||
|
||||
/* fields in the config descriptor */
|
||||
u8 bConfigurationValue;
|
||||
u8 iConfiguration;
|
||||
u8 bmAttributes;
|
||||
u8 bMaxPower;
|
||||
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
/* private: */
|
||||
/* internals */
|
||||
struct list_head list;
|
||||
struct list_head functions;
|
||||
u8 next_interface_id;
|
||||
unsigned superspeed:1;
|
||||
unsigned highspeed:1;
|
||||
unsigned fullspeed:1;
|
||||
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
||||
};
|
||||
|
||||
int usb_add_config(struct usb_composite_dev *,
|
||||
struct usb_configuration *,
|
||||
int (*)(struct usb_configuration *));
|
||||
|
||||
void usb_remove_config(struct usb_composite_dev *,
|
||||
struct usb_configuration *);
|
||||
|
||||
/**
|
||||
* struct usb_composite_driver - groups configurations into a gadget
|
||||
* @name: For diagnostics, identifies the driver.
|
||||
* @iProduct: Used as iProduct override if @dev->iProduct is not set.
|
||||
* If NULL value of @name is taken.
|
||||
* @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
|
||||
* not set. If NULL a default "<system> <release> with <udc>" value
|
||||
* will be used.
|
||||
* @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
|
||||
* not set.
|
||||
* @dev: Template descriptor for the device, including default device
|
||||
* identifiers.
|
||||
* @strings: tables of strings, keyed by identifiers assigned during @bind
|
||||
* and language IDs provided in control requests
|
||||
* @max_speed: Highest speed the driver supports.
|
||||
* @needs_serial: set to 1 if the gadget needs userspace to provide
|
||||
* a serial number. If one is not provided, warning will be printed.
|
||||
* @bind: (REQUIRED) Used to allocate resources that are shared across the
|
||||
* whole device, such as string IDs, and add its configurations using
|
||||
* @usb_add_config(). This may fail by returning a negative errno
|
||||
* value; it should return zero on successful initialization.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering
|
||||
* this driver.
|
||||
* @disconnect: optional driver disconnect method
|
||||
* @suspend: Notifies when the host stops sending USB traffic,
|
||||
* after function notifications
|
||||
* @resume: Notifies configuration when the host restarts USB traffic,
|
||||
* before function notifications
|
||||
*
|
||||
* Devices default to reporting self powered operation. Devices which rely
|
||||
* on bus powered operation should report this in their @bind method.
|
||||
*
|
||||
* Before returning from @bind, various fields in the template descriptor
|
||||
* may be overridden. These include the idVendor/idProduct/bcdDevice values
|
||||
* normally to bind the appropriate host side driver, and the three strings
|
||||
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
|
||||
* meaningful device identifiers. (The strings will not be defined unless
|
||||
* they are defined in @dev and @strings.) The correct ep0 maxpacket size
|
||||
* is also reported, as defined by the underlying controller driver.
|
||||
*/
|
||||
struct usb_composite_driver {
|
||||
const char *name;
|
||||
const char *iProduct;
|
||||
const char *iManufacturer;
|
||||
const char *iSerialNumber;
|
||||
const struct usb_device_descriptor *dev;
|
||||
struct usb_gadget_strings **strings;
|
||||
enum usb_device_speed max_speed;
|
||||
unsigned needs_serial:1;
|
||||
|
||||
int (*bind)(struct usb_composite_dev *cdev);
|
||||
int (*unbind)(struct usb_composite_dev *);
|
||||
|
||||
void (*disconnect)(struct usb_composite_dev *);
|
||||
|
||||
/* global suspend hooks */
|
||||
void (*suspend)(struct usb_composite_dev *);
|
||||
void (*resume)(struct usb_composite_dev *);
|
||||
};
|
||||
|
||||
extern int usb_composite_probe(struct usb_composite_driver *driver);
|
||||
extern void usb_composite_unregister(struct usb_composite_driver *driver);
|
||||
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
|
||||
|
||||
|
||||
/**
|
||||
* struct usb_composite_device - represents one composite usb gadget
|
||||
* @gadget: read-only, abstracts the gadget's usb peripheral controller
|
||||
* @req: used for control responses; buffer is pre-allocated
|
||||
* @bufsiz: size of buffer pre-allocated in @req
|
||||
* @config: the currently active configuration
|
||||
*
|
||||
* One of these devices is allocated and initialized before the
|
||||
* associated device driver's bind() is called.
|
||||
*
|
||||
* OPEN ISSUE: it appears that some WUSB devices will need to be
|
||||
* built by combining a normal (wired) gadget with a wireless one.
|
||||
* This revision of the gadget framework should probably try to make
|
||||
* sure doing that won't hurt too much.
|
||||
*
|
||||
* One notion for how to handle Wireless USB devices involves:
|
||||
* (a) a second gadget here, discovery mechanism TBD, but likely
|
||||
* needing separate "register/unregister WUSB gadget" calls;
|
||||
* (b) updates to usb_gadget to include flags "is it wireless",
|
||||
* "is it wired", plus (presumably in a wrapper structure)
|
||||
* bandgroup and PHY info;
|
||||
* (c) presumably a wireless_ep wrapping a usb_ep, and reporting
|
||||
* wireless-specific parameters like maxburst and maxsequence;
|
||||
* (d) configurations that are specific to wireless links;
|
||||
* (e) function drivers that understand wireless configs and will
|
||||
* support wireless for (additional) function instances;
|
||||
* (f) a function to support association setup (like CBAF), not
|
||||
* necessarily requiring a wireless adapter;
|
||||
* (g) composite device setup that can create one or more wireless
|
||||
* configs, including appropriate association setup support;
|
||||
* (h) more, TBD.
|
||||
*/
|
||||
struct usb_composite_dev {
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_request *req;
|
||||
unsigned bufsiz;
|
||||
|
||||
struct usb_configuration *config;
|
||||
|
||||
/* private: */
|
||||
/* internals */
|
||||
unsigned int suspended:1;
|
||||
struct usb_device_descriptor desc;
|
||||
struct list_head configs;
|
||||
struct usb_composite_driver *driver;
|
||||
u8 next_string_id;
|
||||
u8 manufacturer_override;
|
||||
u8 product_override;
|
||||
u8 serial_override;
|
||||
|
||||
/* the gadget driver won't enable the data pullup
|
||||
* while the deactivation count is nonzero.
|
||||
*/
|
||||
unsigned deactivations;
|
||||
|
||||
/* the composite driver won't complete the control transfer's
|
||||
* data/status stages till delayed_status is zero.
|
||||
*/
|
||||
int delayed_status;
|
||||
|
||||
/* protects deactivations and delayed_status counts*/
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
extern int usb_string_id(struct usb_composite_dev *c);
|
||||
extern int usb_string_ids_tab(struct usb_composite_dev *c,
|
||||
struct usb_string *str);
|
||||
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
|
||||
|
||||
|
||||
/* messaging utils */
|
||||
#define DBG(d, fmt, args...) \
|
||||
dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define VDBG(d, fmt, args...) \
|
||||
dev_vdbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define ERROR(d, fmt, args...) \
|
||||
dev_err(&(d)->gadget->dev , fmt , ## args)
|
||||
#define WARNING(d, fmt, args...) \
|
||||
dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) \
|
||||
dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
#endif /* __LINUX_USB_COMPOSITE_H */
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* usb/gadget/config.c -- simplify building config descriptors
|
||||
*
|
||||
* Copyright (C) 2003 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
|
||||
/**
|
||||
* usb_descriptor_fillbuf - fill buffer with descriptors
|
||||
* @buf: Buffer to be filled
|
||||
* @buflen: Size of buf
|
||||
* @src: Array of descriptor pointers, terminated by null pointer.
|
||||
*
|
||||
* Copies descriptors into the buffer, returning the length or a
|
||||
* negative error code if they can't all be copied. Useful when
|
||||
* assembling descriptors for an associated set of interfaces used
|
||||
* as part of configuring a composite device; or in other cases where
|
||||
* sets of descriptors need to be marshaled.
|
||||
*/
|
||||
int
|
||||
usb_descriptor_fillbuf(void *buf, unsigned buflen,
|
||||
const struct usb_descriptor_header **src)
|
||||
{
|
||||
u8 *dest = buf;
|
||||
|
||||
if (!src)
|
||||
return -EINVAL;
|
||||
|
||||
/* fill buffer from src[] until null descriptor ptr */
|
||||
for (; NULL != *src; src++) {
|
||||
unsigned len = (*src)->bLength;
|
||||
|
||||
if (len > buflen)
|
||||
return -EINVAL;
|
||||
memcpy(dest, *src, len);
|
||||
buflen -= len;
|
||||
dest += len;
|
||||
}
|
||||
return dest - (u8 *)buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_config_buf - builts a complete configuration descriptor
|
||||
* @config: Header for the descriptor, including characteristics such
|
||||
* as power requirements and number of interfaces.
|
||||
* @desc: Null-terminated vector of pointers to the descriptors (interface,
|
||||
* endpoint, etc) defining all functions in this device configuration.
|
||||
* @buf: Buffer for the resulting configuration descriptor.
|
||||
* @length: Length of buffer. If this is not big enough to hold the
|
||||
* entire configuration descriptor, an error code will be returned.
|
||||
*
|
||||
* This copies descriptors into the response buffer, building a descriptor
|
||||
* for that configuration. It returns the buffer length or a negative
|
||||
* status code. The config.wTotalLength field is set to match the length
|
||||
* of the result, but other descriptor fields (including power usage and
|
||||
* interface count) must be set by the caller.
|
||||
*
|
||||
* Gadget drivers could use this when constructing a config descriptor
|
||||
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
|
||||
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
|
||||
*/
|
||||
int usb_gadget_config_buf(
|
||||
const struct usb_config_descriptor *config,
|
||||
void *buf,
|
||||
unsigned length,
|
||||
const struct usb_descriptor_header **desc
|
||||
)
|
||||
{
|
||||
struct usb_config_descriptor *cp = buf;
|
||||
int len;
|
||||
|
||||
/* config descriptor first */
|
||||
if (length < USB_DT_CONFIG_SIZE || !desc)
|
||||
return -EINVAL;
|
||||
*cp = *config;
|
||||
|
||||
/* then interface/endpoint/class/vendor/... */
|
||||
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
|
||||
length - USB_DT_CONFIG_SIZE, desc);
|
||||
if (len < 0)
|
||||
return len;
|
||||
len += USB_DT_CONFIG_SIZE;
|
||||
if (len > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* patch up the config descriptor */
|
||||
cp->bLength = USB_DT_CONFIG_SIZE;
|
||||
cp->bDescriptorType = USB_DT_CONFIG;
|
||||
cp->wTotalLength = cpu_to_le16(len);
|
||||
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_copy_descriptors - copy a vector of USB descriptors
|
||||
* @src: null-terminated vector to copy
|
||||
* Context: initialization code, which may sleep
|
||||
*
|
||||
* This makes a copy of a vector of USB descriptors. Its primary use
|
||||
* is to support usb_function objects which can have multiple copies,
|
||||
* each needing different descriptors. Functions may have static
|
||||
* tables of descriptors, which are used as templates and customized
|
||||
* with identifiers (for interfaces, strings, endpoints, and more)
|
||||
* as needed by a given function instance.
|
||||
*/
|
||||
struct usb_descriptor_header **
|
||||
usb_copy_descriptors(struct usb_descriptor_header **src)
|
||||
{
|
||||
struct usb_descriptor_header **tmp;
|
||||
unsigned bytes;
|
||||
unsigned n_desc;
|
||||
void *mem;
|
||||
struct usb_descriptor_header **ret;
|
||||
|
||||
/* count descriptors and their sizes; then add vector size */
|
||||
for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
|
||||
bytes += (*tmp)->bLength;
|
||||
bytes += (n_desc + 1) * sizeof(*tmp);
|
||||
|
||||
mem = kmalloc(bytes, GFP_KERNEL);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
/* fill in pointers starting at "tmp",
|
||||
* to descriptors copied starting at "mem";
|
||||
* and return "ret"
|
||||
*/
|
||||
tmp = mem;
|
||||
ret = mem;
|
||||
mem += (n_desc + 1) * sizeof(*tmp);
|
||||
while (*src) {
|
||||
memcpy(mem, *src, (*src)->bLength);
|
||||
*tmp = mem;
|
||||
tmp++;
|
||||
mem += (*src)->bLength;
|
||||
src++;
|
||||
}
|
||||
*tmp = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,393 +0,0 @@
|
|||
/*
|
||||
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
|
||||
*
|
||||
* Copyright (C) 2004 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/* we must assign addresses for configurable endpoints (like net2280) */
|
||||
static unsigned epnum;
|
||||
|
||||
// #define MANY_ENDPOINTS
|
||||
#ifdef MANY_ENDPOINTS
|
||||
/* more than 15 configurable endpoints */
|
||||
static unsigned in_epnum;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* This should work with endpoints from controller drivers sharing the
|
||||
* same endpoint naming convention. By example:
|
||||
*
|
||||
* - ep1, ep2, ... address is fixed, not direction or type
|
||||
* - ep1in, ep2out, ... address and direction are fixed, not type
|
||||
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
|
||||
* - ep1in-bulk, ep2out-iso, ... all three are fixed
|
||||
* - ep-* ... no functionality restrictions
|
||||
*
|
||||
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
|
||||
* Less common restrictions are implied by gadget_is_*().
|
||||
*
|
||||
* NOTE: each endpoint is unidirectional, as specified by its USB
|
||||
* descriptor; and isn't specific to a configuration or altsetting.
|
||||
*/
|
||||
static int
|
||||
ep_matches (
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_ep *ep,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp
|
||||
)
|
||||
{
|
||||
u8 type;
|
||||
const char *tmp;
|
||||
u16 max;
|
||||
|
||||
int num_req_streams = 0;
|
||||
|
||||
/* endpoint already claimed? */
|
||||
if (NULL != ep->driver_data)
|
||||
return 0;
|
||||
|
||||
/* only support ep0 for portable CONTROL traffic */
|
||||
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
if (USB_ENDPOINT_XFER_CONTROL == type)
|
||||
return 0;
|
||||
|
||||
/* some other naming convention */
|
||||
if ('e' != ep->name[0])
|
||||
return 0;
|
||||
|
||||
/* type-restriction: "-iso", "-bulk", or "-int".
|
||||
* direction-restriction: "in", "out".
|
||||
*/
|
||||
if ('-' != ep->name[2]) {
|
||||
tmp = strrchr (ep->name, '-');
|
||||
if (tmp) {
|
||||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* bulk endpoints handle interrupt transfers,
|
||||
* except the toggle-quirky iso-synch kind
|
||||
*/
|
||||
if ('s' == tmp[2]) // == "-iso"
|
||||
return 0;
|
||||
/* for now, avoid PXA "interrupt-in";
|
||||
* it's documented as never using DATA1.
|
||||
*/
|
||||
if (gadget_is_pxa (gadget)
|
||||
&& 'i' == tmp [1])
|
||||
return 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if ('b' != tmp[1]) // != "-bulk"
|
||||
return 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if ('s' != tmp[2]) // != "-iso"
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
tmp = ep->name + strlen (ep->name);
|
||||
}
|
||||
|
||||
/* direction-restriction: "..in-..", "out-.." */
|
||||
tmp--;
|
||||
if (!isdigit (*tmp)) {
|
||||
if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if ('n' != *tmp)
|
||||
return 0;
|
||||
} else {
|
||||
if ('t' != *tmp)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of required streams from the EP companion
|
||||
* descriptor and see if the EP matches it
|
||||
*/
|
||||
if (usb_endpoint_xfer_bulk(desc)) {
|
||||
if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
|
||||
num_req_streams = ep_comp->bmAttributes & 0x1f;
|
||||
if (num_req_streams > ep->max_streams)
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If the protocol driver hasn't yet decided on wMaxPacketSize
|
||||
* and wants to know the maximum possible, provide the info.
|
||||
*/
|
||||
if (desc->wMaxPacketSize == 0)
|
||||
desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
|
||||
|
||||
/* endpoint maxpacket size is an input parameter, except for bulk
|
||||
* where it's an output parameter representing the full speed limit.
|
||||
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
|
||||
*/
|
||||
max = 0x7ff & usb_endpoint_maxp(desc);
|
||||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* INT: limit 64 bytes full speed, 1024 high/super speed */
|
||||
if (!gadget_is_dualspeed(gadget) && max > 64)
|
||||
return 0;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
|
||||
if (ep->maxpacket < max)
|
||||
return 0;
|
||||
if (!gadget_is_dualspeed(gadget) && max > 1023)
|
||||
return 0;
|
||||
|
||||
/* BOTH: "high bandwidth" works only at high speed */
|
||||
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
return 0;
|
||||
/* configure your hardware with enough buffering!! */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* MATCH!! */
|
||||
|
||||
/* report address */
|
||||
desc->bEndpointAddress &= USB_DIR_IN;
|
||||
if (isdigit (ep->name [2])) {
|
||||
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
|
||||
desc->bEndpointAddress |= num;
|
||||
#ifdef MANY_ENDPOINTS
|
||||
} else if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if (++in_epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
|
||||
#endif
|
||||
} else {
|
||||
if (++epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress |= epnum;
|
||||
}
|
||||
|
||||
/* report (variable) full speed bulk maxpacket */
|
||||
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
|
||||
int size = ep->maxpacket;
|
||||
|
||||
/* min() doesn't work on bitfields with gcc-3.5 */
|
||||
if (size > 64)
|
||||
size = 64;
|
||||
desc->wMaxPacketSize = cpu_to_le16(size);
|
||||
}
|
||||
ep->address = desc->bEndpointAddress;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct usb_ep *
|
||||
find_ep (struct usb_gadget *gadget, const char *name)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
if (0 == strcmp (ep->name, name))
|
||||
return ep;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_ss() - choose an endpoint matching the ep
|
||||
* descriptor and ep companion descriptor
|
||||
* @gadget: The device to which the endpoint must belong.
|
||||
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
|
||||
* initialized. For periodic transfers, the maximum packet
|
||||
* size must also be initialized. This is modified on
|
||||
* success.
|
||||
* @ep_comp: Endpoint companion descriptor, with the required
|
||||
* number of streams. Will be modified when the chosen EP
|
||||
* supports a different number of streams.
|
||||
*
|
||||
* This routine replaces the usb_ep_autoconfig when needed
|
||||
* superspeed enhancments. If such enhancemnets are required,
|
||||
* the FD should call usb_ep_autoconfig_ss directly and provide
|
||||
* the additional ep_comp parameter.
|
||||
*
|
||||
* By choosing an endpoint to use with the specified descriptor,
|
||||
* this routine simplifies writing gadget drivers that work with
|
||||
* multiple USB device controllers. The endpoint would be
|
||||
* passed later to usb_ep_enable(), along with some descriptor.
|
||||
*
|
||||
* That second descriptor won't always be the same as the first one.
|
||||
* For example, isochronous endpoints can be autoconfigured for high
|
||||
* bandwidth, and then used in several lower bandwidth altsettings.
|
||||
* Also, high and full speed descriptors will be different.
|
||||
*
|
||||
* Be sure to examine and test the results of autoconfiguration
|
||||
* on your hardware. This code may not make the best choices
|
||||
* about how to use the USB controller, and it can't know all
|
||||
* the restrictions that may apply. Some combinations of driver
|
||||
* and hardware won't be able to autoconfigure.
|
||||
*
|
||||
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
|
||||
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
|
||||
* is initialized as if the endpoint were used at full speed and
|
||||
* the bmAttribute field in the ep companion descriptor is
|
||||
* updated with the assigned number of streams if it is
|
||||
* different from the original value. To prevent the endpoint
|
||||
* from being returned by a later autoconfig call, claim it by
|
||||
* assigning ep->driver_data to some non-null value.
|
||||
*
|
||||
* On failure, this returns a null endpoint descriptor.
|
||||
*/
|
||||
struct usb_ep *usb_ep_autoconfig_ss(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp
|
||||
)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
u8 type;
|
||||
|
||||
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
|
||||
/* First, apply chip-specific "best usage" knowledge.
|
||||
* This might make a good usb_gadget_ops hook ...
|
||||
*/
|
||||
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
|
||||
/* ep-e, ep-f are PIO with only 64 byte fifos */
|
||||
ep = find_ep (gadget, "ep-e");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
ep = find_ep (gadget, "ep-f");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
if (USB_ENDPOINT_XFER_INT == type) {
|
||||
/* single buffering is enough */
|
||||
ep = find_ep(gadget, "ep3-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
} else if (USB_ENDPOINT_XFER_BULK == type
|
||||
&& (USB_DIR_IN & desc->bEndpointAddress)) {
|
||||
/* DMA may be available */
|
||||
ep = find_ep(gadget, "ep2-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc,
|
||||
ep_comp))
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLACKFIN
|
||||
} else if (gadget_is_musbhdrc(gadget)) {
|
||||
if ((USB_ENDPOINT_XFER_BULK == type) ||
|
||||
(USB_ENDPOINT_XFER_ISOC == type)) {
|
||||
if (USB_DIR_IN & desc->bEndpointAddress)
|
||||
ep = find_ep (gadget, "ep5in");
|
||||
else
|
||||
ep = find_ep (gadget, "ep6out");
|
||||
} else if (USB_ENDPOINT_XFER_INT == type) {
|
||||
if (USB_DIR_IN & desc->bEndpointAddress)
|
||||
ep = find_ep(gadget, "ep1in");
|
||||
else
|
||||
ep = find_ep(gadget, "ep2out");
|
||||
} else
|
||||
ep = NULL;
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Second, look at endpoints until an unclaimed one looks usable */
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
if (ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
/* Fail */
|
||||
return NULL;
|
||||
found_ep:
|
||||
ep->desc = NULL;
|
||||
ep->comp_desc = NULL;
|
||||
return ep;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig() - choose an endpoint matching the
|
||||
* descriptor
|
||||
* @gadget: The device to which the endpoint must belong.
|
||||
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
|
||||
* initialized. For periodic transfers, the maximum packet
|
||||
* size must also be initialized. This is modified on success.
|
||||
*
|
||||
* By choosing an endpoint to use with the specified descriptor, this
|
||||
* routine simplifies writing gadget drivers that work with multiple
|
||||
* USB device controllers. The endpoint would be passed later to
|
||||
* usb_ep_enable(), along with some descriptor.
|
||||
*
|
||||
* That second descriptor won't always be the same as the first one.
|
||||
* For example, isochronous endpoints can be autoconfigured for high
|
||||
* bandwidth, and then used in several lower bandwidth altsettings.
|
||||
* Also, high and full speed descriptors will be different.
|
||||
*
|
||||
* Be sure to examine and test the results of autoconfiguration on your
|
||||
* hardware. This code may not make the best choices about how to use the
|
||||
* USB controller, and it can't know all the restrictions that may apply.
|
||||
* Some combinations of driver and hardware won't be able to autoconfigure.
|
||||
*
|
||||
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
|
||||
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
|
||||
* is initialized as if the endpoint were used at full speed. To prevent
|
||||
* the endpoint from being returned by a later autoconfig call, claim it
|
||||
* by assigning ep->driver_data to some non-null value.
|
||||
*
|
||||
* On failure, this returns a null endpoint descriptor.
|
||||
*/
|
||||
struct usb_ep *usb_ep_autoconfig(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc
|
||||
)
|
||||
{
|
||||
return usb_ep_autoconfig_ss(gadget, desc, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
|
||||
* @gadget: device for which autoconfig state will be reset
|
||||
*
|
||||
* Use this for devices where one configuration may need to assign
|
||||
* endpoint resources very differently from the next one. It clears
|
||||
* state such as ep->driver_data and the record of assigned endpoints
|
||||
* used by usb_ep_autoconfig().
|
||||
*/
|
||||
void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
#ifdef MANY_ENDPOINTS
|
||||
in_epnum = 0;
|
||||
#endif
|
||||
epnum = 0;
|
||||
}
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче