mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
Merge e0fbd25bb3 ("Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input") into android-mainline
Steps on the way to 5.12-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I918585b39baad0dd40a126fff45077f6eae394ee
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
What: /sys/class/input/input(x)/device/function_row_physmap
|
||||
Date: January 2021
|
||||
Contact: Philip Chen <philipchen@chromium.org>
|
||||
Description: A space separated list of scancodes for the top row keys,
|
||||
ordered by the physical positions of the keys, from left
|
||||
to right.
|
||||
65
Documentation/devicetree/bindings/input/goodix,gt7375p.yaml
Normal file
65
Documentation/devicetree/bindings/input/goodix,gt7375p.yaml
Normal file
@@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/goodix,gt7375p.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Goodix GT7375P touchscreen
|
||||
|
||||
maintainers:
|
||||
- Douglas Anderson <dianders@chromium.org>
|
||||
|
||||
description:
|
||||
Supports the Goodix GT7375P touchscreen.
|
||||
This touchscreen uses the i2c-hid protocol but has some non-standard
|
||||
power sequencing required.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: goodix,gt7375p
|
||||
|
||||
reg:
|
||||
enum:
|
||||
- 0x5d
|
||||
- 0x14
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
true
|
||||
|
||||
vdd-supply:
|
||||
description: The 3.3V supply to the touchscreen.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- reset-gpios
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ap_ts: touchscreen@5d {
|
||||
compatible = "goodix,gt7375p";
|
||||
reg = <0x5d>;
|
||||
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&pp3300_ts>;
|
||||
};
|
||||
};
|
||||
@@ -31,6 +31,17 @@ properties:
|
||||
if the EC does not have its own logic or hardware for this.
|
||||
type: boolean
|
||||
|
||||
function-row-physmap:
|
||||
minItems: 1
|
||||
maxItems: 15
|
||||
description: |
|
||||
An ordered u32 array describing the rows/columns (in the scan matrix)
|
||||
of top row keys from physical left (KEY_F1) to right. Each entry
|
||||
encodes the row/column as:
|
||||
(((row) & 0xFF) << 24) | (((column) & 0xFF) << 16)
|
||||
where the lower 16 bits are reserved. This property is specified only
|
||||
when the keyboard has a custom design for the top row keys.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@@ -38,11 +49,24 @@ unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
cros-ec-keyb {
|
||||
compatible = "google,cros-ec-keyb";
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <13>;
|
||||
google,needs-ghost-filter;
|
||||
function-row-physmap = <
|
||||
MATRIX_KEY(0x00, 0x02, 0) /* T1 */
|
||||
MATRIX_KEY(0x03, 0x02, 0) /* T2 */
|
||||
MATRIX_KEY(0x02, 0x02, 0) /* T3 */
|
||||
MATRIX_KEY(0x01, 0x02, 0) /* T4 */
|
||||
MATRIX_KEY(0x03, 0x04, 0) /* T5 */
|
||||
MATRIX_KEY(0x02, 0x04, 0) /* T6 */
|
||||
MATRIX_KEY(0x01, 0x04, 0) /* T7 */
|
||||
MATRIX_KEY(0x02, 0x09, 0) /* T8 */
|
||||
MATRIX_KEY(0x01, 0x09, 0) /* T9 */
|
||||
MATRIX_KEY(0x00, 0x04, 0) /* T10 */
|
||||
>;
|
||||
/*
|
||||
* Keymap entries take the form of 0xRRCCKKKK where
|
||||
* RR=Row CC=Column KKKK=Key Code
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
AMD Sensor Fusion Hub
|
||||
=====================
|
||||
AMD Sensor Fusion Hub (SFH) is part of an SOC starting from Ryzen based platforms.
|
||||
AMD Sensor Fusion Hub (SFH) is part of an SOC starting from Ryzen-based platforms.
|
||||
The solution is working well on several OEM products. AMD SFH uses HID over PCIe bus.
|
||||
In terms of architecture it resembles ISH, however the major difference is all
|
||||
the HID reports are generated as part of the kernel driver.
|
||||
|
||||
1. Block Diagram
|
||||
================
|
||||
Block Diagram
|
||||
-------------
|
||||
|
||||
::
|
||||
|
||||
@@ -45,20 +45,20 @@ the HID reports are generated as part of the kernel driver.
|
||||
AMD HID Transport Layer
|
||||
-----------------------
|
||||
AMD SFH transport is also implemented as a bus. Each client application executing in the AMD MP2 is
|
||||
registered as a device on this bus. Here: MP2 which is an ARM core connected to x86 for processing
|
||||
registered as a device on this bus. Here, MP2 is an ARM core connected to x86 for processing
|
||||
sensor data. The layer, which binds each device (AMD SFH HID driver) identifies the device type and
|
||||
registers with the hid core. Transport layer attach a constant "struct hid_ll_driver" object with
|
||||
registers with the HID core. Transport layer attaches a constant "struct hid_ll_driver" object with
|
||||
each device. Once a device is registered with HID core, the callbacks provided via this struct are
|
||||
used by HID core to communicate with the device. AMD HID Transport layer implements the synchronous calls.
|
||||
|
||||
AMD HID Client Layer
|
||||
--------------------
|
||||
This layer is responsible to implement HID request and descriptors. As firmware is OS agnostic, HID
|
||||
This layer is responsible to implement HID requests and descriptors. As firmware is OS agnostic, HID
|
||||
client layer fills the HID request structure and descriptors. HID client layer is complex as it is
|
||||
interface between MP2 PCIe layer and HID. HID client layer initialized the MP2 PCIe layer and holds
|
||||
the instance of MP2 layer. It identifies the number of sensors connected using MP2-PCIe layer. Base
|
||||
on that allocates the DRAM address for each and every sensor and pass it to MP2-PCIe driver.On
|
||||
enumeration of each the sensor, client layer fills the HID Descriptor structure and HID input repor
|
||||
interface between MP2 PCIe layer and HID. HID client layer initializes the MP2 PCIe layer and holds
|
||||
the instance of MP2 layer. It identifies the number of sensors connected using MP2-PCIe layer. Based
|
||||
on that allocates the DRAM address for each and every sensor and passes it to MP2-PCIe driver. On
|
||||
enumeration of each sensor, client layer fills the HID Descriptor structure and HID input report
|
||||
structure. HID Feature report structure is optional. The report descriptor structure varies from
|
||||
sensor to sensor.
|
||||
|
||||
@@ -72,7 +72,7 @@ The communication between X86 and MP2 is split into three parts.
|
||||
2. Data transfer via DRAM.
|
||||
3. Supported sensor info via P2C registers.
|
||||
|
||||
Commands are sent to MP2 using C2P Mailbox registers. Writing into C2P Message registers generate
|
||||
Commands are sent to MP2 using C2P Mailbox registers. Writing into C2P Message registers generates
|
||||
interrupt to MP2. The client layer allocates the physical memory and the same is sent to MP2 via
|
||||
the PCI layer. MP2 firmware writes the command output to the access DRAM memory which the client
|
||||
layer has allocated. Firmware always writes minimum of 32 bytes into DRAM. So as a protocol driver
|
||||
|
||||
@@ -64,7 +64,7 @@ Case2 ReportID_3 TP Absolute
|
||||
|
||||
Command Read/Write
|
||||
------------------
|
||||
To read/write to RAM, need to send a commands to the device.
|
||||
To read/write to RAM, need to send a command to the device.
|
||||
|
||||
The command format is as below.
|
||||
|
||||
@@ -80,7 +80,7 @@ Byte6 Value Byte
|
||||
Byte7 Checksum
|
||||
===== ======================
|
||||
|
||||
Command Byte is read=0xD1/write=0xD2 .
|
||||
Command Byte is read=0xD1/write=0xD2.
|
||||
|
||||
Address is read/write RAM address.
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ for different sensors. For example an accelerometer can send X,Y and Z data, whe
|
||||
an ambient light sensor can send illumination data.
|
||||
So the implementation has two parts:
|
||||
|
||||
- Core hid driver
|
||||
- Core HID driver
|
||||
- Individual sensor processing part (sensor drivers)
|
||||
|
||||
Core driver
|
||||
-----------
|
||||
The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
|
||||
The core driver (hid-sensor-hub) registers as a HID driver. It parses
|
||||
report descriptors and identifies all the sensors present. It adds an MFD device
|
||||
with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
|
||||
|
||||
@@ -95,14 +95,14 @@ Registration functions::
|
||||
u32 usage_id,
|
||||
struct hid_sensor_hub_callbacks *usage_callback):
|
||||
|
||||
Registers callbacks for an usage id. The callback functions are not allowed
|
||||
Registers callbacks for a usage id. The callback functions are not allowed
|
||||
to sleep::
|
||||
|
||||
|
||||
int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id):
|
||||
|
||||
Removes callbacks for an usage id.
|
||||
Removes callbacks for a usage id.
|
||||
|
||||
|
||||
Parsing function::
|
||||
@@ -166,7 +166,7 @@ This allows some differentiating use cases, where vendor can provide application
|
||||
Some common use cases are debug other sensors or to provide some events like
|
||||
keyboard attached/detached or lid open/close.
|
||||
|
||||
To allow application to utilize these sensors, here they are exported uses sysfs
|
||||
To allow application to utilize these sensors, here they are exported using sysfs
|
||||
attribute groups, attributes and misc device interface.
|
||||
|
||||
An example of this representation on sysfs::
|
||||
@@ -207,9 +207,9 @@ An example of this representation on sysfs::
|
||||
│ │ │ ├── input-1-200202-units
|
||||
│ │ │ ├── input-1-200202-value
|
||||
|
||||
Here there is a custom sensors with four fields, two feature and two inputs.
|
||||
Here there is a custom sensor with four fields: two feature and two inputs.
|
||||
Each field is represented by a set of attributes. All fields except the "value"
|
||||
are read only. The value field is a RW field.
|
||||
are read only. The value field is a read-write field.
|
||||
|
||||
Example::
|
||||
|
||||
@@ -237,6 +237,6 @@ These reports are pushed using misc device interface in a FIFO order::
|
||||
│ │ │ ├── 10:53 -> ../HID-SENSOR-2000e1.6.auto
|
||||
│ ├── HID-SENSOR-2000e1.6.auto
|
||||
|
||||
Each reports can be of variable length preceded by a header. This header
|
||||
consist of a 32 bit usage id, 64 bit time stamp and 32 bit length field of raw
|
||||
Each report can be of variable length preceded by a header. This header
|
||||
consists of a 32-bit usage id, 64-bit time stamp and 32-bit length field of raw
|
||||
data.
|
||||
|
||||
@@ -12,8 +12,8 @@ Bluetooth, I2C and user-space I/O drivers.
|
||||
|
||||
The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
|
||||
devices and register them with the HID bus. HID core then loads generic device
|
||||
drivers on top of it. The transport drivers are responsible of raw data
|
||||
transport and device setup/management. HID core is responsible of
|
||||
drivers on top of it. The transport drivers are responsible for raw data
|
||||
transport and device setup/management. HID core is responsible for
|
||||
report-parsing, report interpretation and the user-space API. Device specifics
|
||||
and quirks are handled by all layers depending on the quirk.
|
||||
|
||||
@@ -67,7 +67,7 @@ Transport drivers attach a constant "struct hid_ll_driver" object with each
|
||||
device. Once a device is registered with HID core, the callbacks provided via
|
||||
this struct are used by HID core to communicate with the device.
|
||||
|
||||
Transport drivers are responsible of detecting device failures and unplugging.
|
||||
Transport drivers are responsible for detecting device failures and unplugging.
|
||||
HID core will operate a device as long as it is registered regardless of any
|
||||
device failures. Once transport drivers detect unplug or failure events, they
|
||||
must unregister the device from HID core and HID core will stop using the
|
||||
@@ -101,7 +101,7 @@ properties in common.
|
||||
channel. Any unrequested incoming or outgoing data report must be sent on
|
||||
this channel and is never acknowledged by the remote side. Devices usually
|
||||
send their input events on this channel. Outgoing events are normally
|
||||
not send via intr, except if high throughput is required.
|
||||
not sent via intr, except if high throughput is required.
|
||||
- Control Channel (ctrl): The ctrl channel is used for synchronous requests and
|
||||
device management. Unrequested data input events must not be sent on this
|
||||
channel and are normally ignored. Instead, devices only send management
|
||||
@@ -161,7 +161,7 @@ allowed on the intr channel and are the only means of data there.
|
||||
payload may be blocked by the underlying transport driver if the
|
||||
specification does not allow them.
|
||||
- SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
|
||||
sent from host to device and a device must update it's current report state
|
||||
sent from host to device and a device must update its current report state
|
||||
according to the given data. Any of the 3 report types can be used. However,
|
||||
INPUT reports as payload might be blocked by the underlying transport driver
|
||||
if the specification does not allow them.
|
||||
@@ -294,7 +294,7 @@ The available HID callbacks are:
|
||||
void (*request) (struct hid_device *hdev, struct hid_report *report,
|
||||
int reqtype)
|
||||
|
||||
Send an HID request on the ctrl channel. "report" contains the report that
|
||||
Send a HID request on the ctrl channel. "report" contains the report that
|
||||
should be sent and "reqtype" the request type. Request-type can be
|
||||
HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ the following::
|
||||
--> hiddev.c ----> POWER / MONITOR CONTROL
|
||||
|
||||
In addition, other subsystems (apart from USB) can potentially feed
|
||||
events into the input subsystem, but these have no effect on the hid
|
||||
events into the input subsystem, but these have no effect on the HID
|
||||
device interface.
|
||||
|
||||
Using the HID Device Interface
|
||||
@@ -73,7 +73,7 @@ The hiddev API uses a read() interface, and a set of ioctl() calls.
|
||||
HID devices exchange data with the host computer using data
|
||||
bundles called "reports". Each report is divided into "fields",
|
||||
each of which can have one or more "usages". In the hid-core,
|
||||
each one of these usages has a single signed 32 bit value.
|
||||
each one of these usages has a single signed 32-bit value.
|
||||
|
||||
read():
|
||||
-------
|
||||
@@ -113,7 +113,7 @@ HIDIOCAPPLICATION
|
||||
- (none)
|
||||
|
||||
This ioctl call returns the HID application usage associated with the
|
||||
hid device. The third argument to ioctl() specifies which application
|
||||
HID device. The third argument to ioctl() specifies which application
|
||||
index to get. This is useful when the device has more than one
|
||||
application collection. If the index is invalid (greater or equal to
|
||||
the number of application collections this device has) the ioctl
|
||||
@@ -181,7 +181,7 @@ looked up by type (input, output or feature) and id, so these fields
|
||||
must be filled in by the user. The ID can be absolute -- the actual
|
||||
report id as reported by the device -- or relative --
|
||||
HID_REPORT_ID_FIRST for the first report, and (HID_REPORT_ID_NEXT |
|
||||
report_id) for the next report after report_id. Without a-priori
|
||||
report_id) for the next report after report_id. Without a priori
|
||||
information about report ids, the right way to use this ioctl is to
|
||||
use the relative IDs above to enumerate the valid IDs. The ioctl
|
||||
returns non-zero when there is no more next ID. The real report ID is
|
||||
@@ -200,7 +200,7 @@ HIDIOCGUCODE
|
||||
- struct hiddev_usage_ref (read/write)
|
||||
|
||||
Returns the usage_code in a hiddev_usage_ref structure, given that
|
||||
given its report type, report id, field index, and index within the
|
||||
its report type, report id, field index, and index within the
|
||||
field have already been filled into the structure.
|
||||
|
||||
HIDIOCGUSAGE
|
||||
|
||||
@@ -21,7 +21,7 @@ Hidraw is the only alternative, short of writing a custom kernel driver, for
|
||||
these non-conformant devices.
|
||||
|
||||
A benefit of hidraw is that its use by userspace applications is independent
|
||||
of the underlying hardware type. Currently, Hidraw is implemented for USB
|
||||
of the underlying hardware type. Currently, hidraw is implemented for USB
|
||||
and Bluetooth. In the future, as new hardware bus types are developed which
|
||||
use the HID specification, hidraw will be expanded to add support for these
|
||||
new bus types.
|
||||
@@ -31,9 +31,10 @@ create hidraw device nodes. Udev will typically create the device nodes
|
||||
directly under /dev (eg: /dev/hidraw0). As this location is distribution-
|
||||
and udev rule-dependent, applications should use libudev to locate hidraw
|
||||
devices attached to the system. There is a tutorial on libudev with a
|
||||
working example at:
|
||||
working example at::
|
||||
|
||||
http://www.signal11.us/oss/udev/
|
||||
https://web.archive.org/web/2019*/www.signal11.us
|
||||
|
||||
The HIDRAW API
|
||||
---------------
|
||||
|
||||
@@ -4,19 +4,19 @@ Intel Integrated Sensor Hub (ISH)
|
||||
|
||||
A sensor hub enables the ability to offload sensor polling and algorithm
|
||||
processing to a dedicated low power co-processor. This allows the core
|
||||
processor to go into low power modes more often, resulting in the increased
|
||||
processor to go into low power modes more often, resulting in increased
|
||||
battery life.
|
||||
|
||||
There are many vendors providing external sensor hubs confirming to HID
|
||||
Sensor usage tables, and used in several tablets, 2 in 1 convertible laptops
|
||||
and embedded products. Linux had this support since Linux 3.9.
|
||||
There are many vendors providing external sensor hubs conforming to HID
|
||||
Sensor usage tables. These may be found in tablets, 2-in-1 convertible laptops
|
||||
and embedded products. Linux has had this support since Linux 3.9.
|
||||
|
||||
Intel® introduced integrated sensor hubs as a part of the SoC starting from
|
||||
Cherry Trail and now supported on multiple generations of CPU packages. There
|
||||
are many commercial devices already shipped with Integrated Sensor Hubs (ISH).
|
||||
These ISH also comply to HID sensor specification, but the difference is the
|
||||
These ISH also comply to HID sensor specification, but the difference is the
|
||||
transport protocol used for communication. The current external sensor hubs
|
||||
mainly use HID over i2C or USB. But ISH doesn't use either i2c or USB.
|
||||
mainly use HID over I2C or USB. But ISH doesn't use either I2C or USB.
|
||||
|
||||
1. Overview
|
||||
===========
|
||||
@@ -35,7 +35,7 @@ for a very high speed communication::
|
||||
----------------- ----------------------
|
||||
PCI PCI
|
||||
----------------- ----------------------
|
||||
|Host controller| --> | ISH processor |
|
||||
|Host controller| --> | ISH processor |
|
||||
----------------- ----------------------
|
||||
USB Link
|
||||
----------------- ----------------------
|
||||
@@ -50,13 +50,13 @@ applications implemented in the firmware.
|
||||
The ISH allows multiple sensor management applications executing in the
|
||||
firmware. Like USB endpoints the messaging can be to/from a client. As part of
|
||||
enumeration process, these clients are identified. These clients can be simple
|
||||
HID sensor applications, sensor calibration application or senor firmware
|
||||
update application.
|
||||
HID sensor applications, sensor calibration applications or sensor firmware
|
||||
update applications.
|
||||
|
||||
The implementation model is similar, like USB bus, ISH transport is also
|
||||
implemented as a bus. Each client application executing in the ISH processor
|
||||
is registered as a device on this bus. The driver, which binds each device
|
||||
(ISH HID driver) identifies the device type and registers with the hid core.
|
||||
(ISH HID driver) identifies the device type and registers with the HID core.
|
||||
|
||||
2. ISH Implementation: Block Diagram
|
||||
====================================
|
||||
@@ -104,7 +104,7 @@ is registered as a device on this bus. The driver, which binds each device
|
||||
|
||||
The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI
|
||||
product and vendor IDs are changed from different generations of processors. So
|
||||
the source code which enumerate drivers needs to update from generation to
|
||||
the source code which enumerates drivers needs to update from generation to
|
||||
generation.
|
||||
|
||||
3.2 Inter Processor Communication (IPC) driver
|
||||
@@ -112,41 +112,42 @@ generation.
|
||||
|
||||
Location: drivers/hid/intel-ish-hid/ipc
|
||||
|
||||
The IPC message used memory mapped I/O. The registers are defined in
|
||||
The IPC message uses memory mapped I/O. The registers are defined in
|
||||
hw-ish-regs.h.
|
||||
|
||||
3.2.1 IPC/FW message types
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are two types of messages, one for management of link and other messages
|
||||
are to and from transport layers.
|
||||
There are two types of messages, one for management of link and another for
|
||||
messages to and from transport layers.
|
||||
|
||||
TX and RX of Transport messages
|
||||
...............................
|
||||
|
||||
A set of memory mapped register offers support of multi byte messages TX and
|
||||
RX (E.g.IPC_REG_ISH2HOST_MSG, IPC_REG_HOST2ISH_MSG). The IPC layer maintains
|
||||
internal queues to sequence messages and send them in order to the FW.
|
||||
A set of memory mapped register offers support of multi-byte messages TX and
|
||||
RX (e.g. IPC_REG_ISH2HOST_MSG, IPC_REG_HOST2ISH_MSG). The IPC layer maintains
|
||||
internal queues to sequence messages and send them in order to the firmware.
|
||||
Optionally the caller can register handler to get notification of completion.
|
||||
A door bell mechanism is used in messaging to trigger processing in host and
|
||||
A doorbell mechanism is used in messaging to trigger processing in host and
|
||||
client firmware side. When ISH interrupt handler is called, the ISH2HOST
|
||||
doorbell register is used by host drivers to determine that the interrupt
|
||||
is for ISH.
|
||||
|
||||
Each side has 32 32-bit message registers and a 32-bit doorbell. Doorbell
|
||||
register has the following format:
|
||||
Bits 0..6: fragment length (7 bits are used)
|
||||
Bits 10..13: encapsulated protocol
|
||||
Bits 16..19: management command (for IPC management protocol)
|
||||
Bit 31: doorbell trigger (signal H/W interrupt to the other side)
|
||||
Other bits are reserved, should be 0.
|
||||
register has the following format::
|
||||
|
||||
Bits 0..6: fragment length (7 bits are used)
|
||||
Bits 10..13: encapsulated protocol
|
||||
Bits 16..19: management command (for IPC management protocol)
|
||||
Bit 31: doorbell trigger (signal H/W interrupt to the other side)
|
||||
Other bits are reserved, should be 0.
|
||||
|
||||
3.2.2 Transport layer interface
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To abstract HW level IPC communication, a set of callbacks are registered.
|
||||
To abstract HW level IPC communication, a set of callbacks is registered.
|
||||
The transport layer uses them to send and receive messages.
|
||||
Refer to struct ishtp_hw_ops for callbacks.
|
||||
Refer to struct ishtp_hw_ops for callbacks.
|
||||
|
||||
3.3 ISH Transport layer
|
||||
-----------------------
|
||||
@@ -158,7 +159,7 @@ Location: drivers/hid/intel-ish-hid/ishtp/
|
||||
|
||||
The transport layer is a bi-directional protocol, which defines:
|
||||
- Set of commands to start, stop, connect, disconnect and flow control
|
||||
(ishtp/hbm.h) for details
|
||||
(see ishtp/hbm.h for details)
|
||||
- A flow control mechanism to avoid buffer overflows
|
||||
|
||||
This protocol resembles bus messages described in the following document:
|
||||
@@ -168,14 +169,14 @@ specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer"
|
||||
3.3.2 Connection and Flow Control Mechanism
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Each FW client and a protocol is identified by an UUID. In order to communicate
|
||||
Each FW client and a protocol is identified by a UUID. In order to communicate
|
||||
to a FW client, a connection must be established using connect request and
|
||||
response bus messages. If successful, a pair (host_client_id and fw_client_id)
|
||||
will identify the connection.
|
||||
|
||||
Once connection is established, peers send each other flow control bus messages
|
||||
independently. Every peer may send a message only if it has received a
|
||||
flow-control credit before. Once it sent a message, it may not send another one
|
||||
flow-control credit before. Once it has sent a message, it may not send another one
|
||||
before receiving the next flow control credit.
|
||||
Either side can send disconnect request bus message to end communication. Also
|
||||
the link will be dropped if major FW reset occurs.
|
||||
@@ -209,7 +210,7 @@ and DMA_XFER_ACK act as ownership indicators.
|
||||
At initial state all outgoing memory belongs to the sender (TX to host, RX to
|
||||
FW), DMA_XFER transfers ownership on the region that contains ISHTP message to
|
||||
the receiving side, DMA_XFER_ACK returns ownership to the sender. A sender
|
||||
needs not wait for previous DMA_XFER to be ack'ed, and may send another message
|
||||
need not wait for previous DMA_XFER to be ack'ed, and may send another message
|
||||
as long as remaining continuous memory in its ownership is enough.
|
||||
In principle, multiple DMA_XFER and DMA_XFER_ACK messages may be sent at once
|
||||
(up to IPC MTU), thus allowing for interrupt throttling.
|
||||
@@ -219,8 +220,8 @@ fragments and via IPC otherwise.
|
||||
3.3.4 Ring Buffers
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When a client initiate a connection, a ring or RX and TX buffers are allocated.
|
||||
The size of ring can be specified by the client. HID client set 16 and 32 for
|
||||
When a client initiates a connection, a ring of RX and TX buffers is allocated.
|
||||
The size of ring can be specified by the client. HID client sets 16 and 32 for
|
||||
TX and RX buffers respectively. On send request from client, the data to be
|
||||
sent is copied to one of the send ring buffer and scheduled to be sent using
|
||||
bus message protocol. These buffers are required because the FW may have not
|
||||
@@ -230,10 +231,10 @@ to send. Same thing holds true on receive side and flow control is required.
|
||||
3.3.5 Host Enumeration
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The host enumeration bus command allow discovery of clients present in the FW.
|
||||
The host enumeration bus command allows discovery of clients present in the FW.
|
||||
There can be multiple sensor clients and clients for calibration function.
|
||||
|
||||
To ease in implantation and allow independent driver handle each client
|
||||
To ease implementation and allow independent drivers to handle each client,
|
||||
this transport layer takes advantage of Linux Bus driver model. Each
|
||||
client is registered as device on the transport bus (ishtp bus).
|
||||
|
||||
@@ -270,7 +271,7 @@ The ISHTP client driver is responsible for:
|
||||
The functionality in these drivers is the same as an external sensor hub.
|
||||
Refer to
|
||||
Documentation/hid/hid-sensor.rst for HID sensor
|
||||
Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space
|
||||
Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space.
|
||||
|
||||
3.6 End to End HID transport Sequence Diagram
|
||||
---------------------------------------------
|
||||
@@ -341,9 +342,10 @@ Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space
|
||||
3.7 ISH Debugging
|
||||
-----------------
|
||||
|
||||
To debug ISH, event tracing mechanism is used. To enable debug logs
|
||||
echo 1 > /sys/kernel/debug/tracing/events/intel_ish/enable
|
||||
cat sys/kernel/debug/tracing/trace
|
||||
To debug ISH, event tracing mechanism is used. To enable debug logs::
|
||||
|
||||
echo 1 > /sys/kernel/debug/tracing/events/intel_ish/enable
|
||||
cat sys/kernel/debug/tracing/trace
|
||||
|
||||
3.8 ISH IIO sysfs Example on Lenovo thinkpad Yoga 260
|
||||
-----------------------------------------------------
|
||||
|
||||
@@ -3,7 +3,7 @@ UHID - User-space I/O driver support for HID subsystem
|
||||
======================================================
|
||||
|
||||
UHID allows user-space to implement HID transport drivers. Please see
|
||||
hid-transport.txt for an introduction into HID transport drivers. This document
|
||||
hid-transport.rst for an introduction into HID transport drivers. This document
|
||||
relies heavily on the definitions declared there.
|
||||
|
||||
With UHID, a user-space transport driver can create kernel hid-devices for each
|
||||
@@ -15,7 +15,7 @@ There is an example user-space application in ./samples/uhid/uhid-example.c
|
||||
The UHID API
|
||||
------------
|
||||
|
||||
UHID is accessed through a character misc-device. The minor-number is allocated
|
||||
UHID is accessed through a character misc-device. The minor number is allocated
|
||||
dynamically so you need to rely on udev (or similar) to create the device node.
|
||||
This is /dev/uhid by default.
|
||||
|
||||
@@ -45,23 +45,23 @@ The "type" field defines the payload. For each type, there is a
|
||||
payload-structure available in the union "u" (except for empty payloads). This
|
||||
payload contains management and/or device data.
|
||||
|
||||
The first thing you should do is sending an UHID_CREATE2 event. This will
|
||||
register the device. UHID will respond with an UHID_START event. You can now
|
||||
The first thing you should do is send a UHID_CREATE2 event. This will
|
||||
register the device. UHID will respond with a UHID_START event. You can now
|
||||
start sending data to and reading data from UHID. However, unless UHID sends the
|
||||
UHID_OPEN event, the internally attached HID Device Driver has no user attached.
|
||||
That is, you might put your device asleep unless you receive the UHID_OPEN
|
||||
event. If you receive the UHID_OPEN event, you should start I/O. If the last
|
||||
user closes the HID device, you will receive an UHID_CLOSE event. This may be
|
||||
followed by an UHID_OPEN event again and so on. There is no need to perform
|
||||
user closes the HID device, you will receive a UHID_CLOSE event. This may be
|
||||
followed by a UHID_OPEN event again and so on. There is no need to perform
|
||||
reference-counting in user-space. That is, you will never receive multiple
|
||||
UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs
|
||||
UHID_OPEN events without a UHID_CLOSE event. The HID subsystem performs
|
||||
ref-counting for you.
|
||||
You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even
|
||||
though the device may have no users.
|
||||
|
||||
If you want to send data on the interrupt channel to the HID subsystem, you send
|
||||
an HID_INPUT2 event with your raw data payload. If the kernel wants to send data
|
||||
on the interrupt channel to the device, you will read an UHID_OUTPUT event.
|
||||
a HID_INPUT2 event with your raw data payload. If the kernel wants to send data
|
||||
on the interrupt channel to the device, you will read a UHID_OUTPUT event.
|
||||
Data requests on the control channel are currently limited to GET_REPORT and
|
||||
SET_REPORT (no other data reports on the control channel are defined so far).
|
||||
Those requests are always synchronous. That means, the kernel sends
|
||||
@@ -71,7 +71,7 @@ the response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.
|
||||
The kernel blocks internal driver-execution during such round-trips (times out
|
||||
after a hard-coded period).
|
||||
|
||||
If your device disconnects, you should send an UHID_DESTROY event. This will
|
||||
If your device disconnects, you should send a UHID_DESTROY event. This will
|
||||
unregister the device. You can now send UHID_CREATE2 again to register a new
|
||||
device.
|
||||
If you close() the fd, the device is automatically unregistered and destroyed
|
||||
@@ -125,7 +125,7 @@ UHID_START:
|
||||
This is sent when the HID device is started. Consider this as an answer to
|
||||
UHID_CREATE2. This is always the first event that is sent. Note that this
|
||||
event might not be available immediately after write(UHID_CREATE2) returns.
|
||||
Device drivers might required delayed setups.
|
||||
Device drivers might require delayed setups.
|
||||
This event contains a payload of type uhid_start_req. The "dev_flags" field
|
||||
describes special behaviors of a device. The following flags are defined:
|
||||
|
||||
@@ -149,7 +149,7 @@ UHID_STOP:
|
||||
reloaded/changed the device driver loaded on your HID device (or some other
|
||||
maintenance actions happened).
|
||||
|
||||
You can usually ignored any UHID_STOP events safely.
|
||||
You can usually ignore any UHID_STOP events safely.
|
||||
|
||||
UHID_OPEN:
|
||||
This is sent when the HID device is opened. That is, the data that the HID
|
||||
@@ -166,17 +166,17 @@ UHID_OUTPUT:
|
||||
This is sent if the HID device driver wants to send raw data to the I/O
|
||||
device on the interrupt channel. You should read the payload and forward it to
|
||||
the device. The payload is of type "struct uhid_output_req".
|
||||
This may be received even though you haven't received UHID_OPEN, yet.
|
||||
This may be received even though you haven't received UHID_OPEN yet.
|
||||
|
||||
UHID_GET_REPORT:
|
||||
This event is sent if the kernel driver wants to perform a GET_REPORT request
|
||||
on the control channeld as described in the HID specs. The report-type and
|
||||
on the control channel as described in the HID specs. The report-type and
|
||||
report-number are available in the payload.
|
||||
The kernel serializes GET_REPORT requests so there will never be two in
|
||||
parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the
|
||||
request might silently time out.
|
||||
Once you read a GET_REPORT request, you shall forward it to the hid device and
|
||||
remember the "id" field in the payload. Once your hid device responds to the
|
||||
Once you read a GET_REPORT request, you shall forward it to the HID device and
|
||||
remember the "id" field in the payload. Once your HID device responds to the
|
||||
GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the
|
||||
kernel with the exact same "id" as in the request. If the request already
|
||||
timed out, the kernel will ignore the response silently. The "id" field is
|
||||
@@ -184,7 +184,7 @@ UHID_GET_REPORT:
|
||||
|
||||
UHID_SET_REPORT:
|
||||
This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
|
||||
send a SET_REPORT request to your hid device. Once it replies, you must tell
|
||||
send a SET_REPORT request to your HID device. Once it replies, you must tell
|
||||
the kernel about it via UHID_SET_REPORT_REPLY.
|
||||
The same restrictions as for UHID_GET_REPORT apply.
|
||||
|
||||
|
||||
@@ -7908,6 +7908,12 @@ F: drivers/hid/
|
||||
F: include/linux/hid*
|
||||
F: include/uapi/linux/hid*
|
||||
|
||||
HID PLAYSTATION DRIVER
|
||||
M: Roderick Colenbrander <roderick.colenbrander@sony.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/hid/hid-playstation.c
|
||||
|
||||
HID SENSOR HUB DRIVERS
|
||||
M: Jiri Kosina <jikos@kernel.org>
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
|
||||
@@ -6,103 +6,18 @@
|
||||
*/
|
||||
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/input/cros-ec-keyboard.h>
|
||||
|
||||
&cros_ec {
|
||||
keyboard-controller {
|
||||
keyboard_controller: keyboard-controller {
|
||||
compatible = "google,cros-ec-keyb";
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <13>;
|
||||
google,needs-ghost-filter;
|
||||
|
||||
linux,keymap = <
|
||||
MATRIX_KEY(0x00, 0x01, KEY_LEFTMETA)
|
||||
MATRIX_KEY(0x00, 0x02, KEY_F1)
|
||||
MATRIX_KEY(0x00, 0x03, KEY_B)
|
||||
MATRIX_KEY(0x00, 0x04, KEY_F10)
|
||||
MATRIX_KEY(0x00, 0x05, KEY_RO)
|
||||
MATRIX_KEY(0x00, 0x06, KEY_N)
|
||||
MATRIX_KEY(0x00, 0x08, KEY_EQUAL)
|
||||
MATRIX_KEY(0x00, 0x0a, KEY_RIGHTALT)
|
||||
|
||||
MATRIX_KEY(0x01, 0x01, KEY_ESC)
|
||||
MATRIX_KEY(0x01, 0x02, KEY_F4)
|
||||
MATRIX_KEY(0x01, 0x03, KEY_G)
|
||||
MATRIX_KEY(0x01, 0x04, KEY_F7)
|
||||
MATRIX_KEY(0x01, 0x06, KEY_H)
|
||||
MATRIX_KEY(0x01, 0x08, KEY_APOSTROPHE)
|
||||
MATRIX_KEY(0x01, 0x09, KEY_F9)
|
||||
MATRIX_KEY(0x01, 0x0b, KEY_BACKSPACE)
|
||||
MATRIX_KEY(0x01, 0x0c, KEY_HENKAN)
|
||||
|
||||
MATRIX_KEY(0x02, 0x00, KEY_LEFTCTRL)
|
||||
MATRIX_KEY(0x02, 0x01, KEY_TAB)
|
||||
MATRIX_KEY(0x02, 0x02, KEY_F3)
|
||||
MATRIX_KEY(0x02, 0x03, KEY_T)
|
||||
MATRIX_KEY(0x02, 0x04, KEY_F6)
|
||||
MATRIX_KEY(0x02, 0x05, KEY_RIGHTBRACE)
|
||||
MATRIX_KEY(0x02, 0x06, KEY_Y)
|
||||
MATRIX_KEY(0x02, 0x07, KEY_102ND)
|
||||
MATRIX_KEY(0x02, 0x08, KEY_LEFTBRACE)
|
||||
MATRIX_KEY(0x02, 0x09, KEY_F8)
|
||||
MATRIX_KEY(0x02, 0x0a, KEY_YEN)
|
||||
|
||||
MATRIX_KEY(0x03, 0x00, KEY_LEFTMETA)
|
||||
MATRIX_KEY(0x03, 0x01, KEY_GRAVE)
|
||||
MATRIX_KEY(0x03, 0x02, KEY_F2)
|
||||
MATRIX_KEY(0x03, 0x03, KEY_5)
|
||||
MATRIX_KEY(0x03, 0x04, KEY_F5)
|
||||
MATRIX_KEY(0x03, 0x06, KEY_6)
|
||||
MATRIX_KEY(0x03, 0x08, KEY_MINUS)
|
||||
MATRIX_KEY(0x03, 0x09, KEY_F13)
|
||||
MATRIX_KEY(0x03, 0x0b, KEY_BACKSLASH)
|
||||
MATRIX_KEY(0x03, 0x0c, KEY_MUHENKAN)
|
||||
|
||||
MATRIX_KEY(0x04, 0x00, KEY_RIGHTCTRL)
|
||||
MATRIX_KEY(0x04, 0x01, KEY_A)
|
||||
MATRIX_KEY(0x04, 0x02, KEY_D)
|
||||
MATRIX_KEY(0x04, 0x03, KEY_F)
|
||||
MATRIX_KEY(0x04, 0x04, KEY_S)
|
||||
MATRIX_KEY(0x04, 0x05, KEY_K)
|
||||
MATRIX_KEY(0x04, 0x06, KEY_J)
|
||||
MATRIX_KEY(0x04, 0x08, KEY_SEMICOLON)
|
||||
MATRIX_KEY(0x04, 0x09, KEY_L)
|
||||
MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH)
|
||||
MATRIX_KEY(0x04, 0x0b, KEY_ENTER)
|
||||
|
||||
MATRIX_KEY(0x05, 0x01, KEY_Z)
|
||||
MATRIX_KEY(0x05, 0x02, KEY_C)
|
||||
MATRIX_KEY(0x05, 0x03, KEY_V)
|
||||
MATRIX_KEY(0x05, 0x04, KEY_X)
|
||||
MATRIX_KEY(0x05, 0x05, KEY_COMMA)
|
||||
MATRIX_KEY(0x05, 0x06, KEY_M)
|
||||
MATRIX_KEY(0x05, 0x07, KEY_LEFTSHIFT)
|
||||
MATRIX_KEY(0x05, 0x08, KEY_SLASH)
|
||||
MATRIX_KEY(0x05, 0x09, KEY_DOT)
|
||||
MATRIX_KEY(0x05, 0x0b, KEY_SPACE)
|
||||
|
||||
MATRIX_KEY(0x06, 0x01, KEY_1)
|
||||
MATRIX_KEY(0x06, 0x02, KEY_3)
|
||||
MATRIX_KEY(0x06, 0x03, KEY_4)
|
||||
MATRIX_KEY(0x06, 0x04, KEY_2)
|
||||
MATRIX_KEY(0x06, 0x05, KEY_8)
|
||||
MATRIX_KEY(0x06, 0x06, KEY_7)
|
||||
MATRIX_KEY(0x06, 0x08, KEY_0)
|
||||
MATRIX_KEY(0x06, 0x09, KEY_9)
|
||||
MATRIX_KEY(0x06, 0x0a, KEY_LEFTALT)
|
||||
MATRIX_KEY(0x06, 0x0b, KEY_DOWN)
|
||||
MATRIX_KEY(0x06, 0x0c, KEY_RIGHT)
|
||||
|
||||
MATRIX_KEY(0x07, 0x01, KEY_Q)
|
||||
MATRIX_KEY(0x07, 0x02, KEY_E)
|
||||
MATRIX_KEY(0x07, 0x03, KEY_R)
|
||||
MATRIX_KEY(0x07, 0x04, KEY_W)
|
||||
MATRIX_KEY(0x07, 0x05, KEY_I)
|
||||
MATRIX_KEY(0x07, 0x06, KEY_U)
|
||||
MATRIX_KEY(0x07, 0x07, KEY_RIGHTSHIFT)
|
||||
MATRIX_KEY(0x07, 0x08, KEY_P)
|
||||
MATRIX_KEY(0x07, 0x09, KEY_O)
|
||||
MATRIX_KEY(0x07, 0x0b, KEY_UP)
|
||||
MATRIX_KEY(0x07, 0x0c, KEY_LEFT)
|
||||
CROS_STD_TOP_ROW_KEYMAP
|
||||
CROS_STD_MAIN_KEYMAP
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -770,7 +770,8 @@ CONFIG_SND_SOC_LPASS_VA_MACRO=m
|
||||
CONFIG_SND_SIMPLE_CARD=m
|
||||
CONFIG_SND_AUDIO_GRAPH_CARD=m
|
||||
CONFIG_HID_MULTITOUCH=m
|
||||
CONFIG_I2C_HID=m
|
||||
CONFIG_I2C_HID_ACPI=m
|
||||
CONFIG_I2C_HID_OF=m
|
||||
CONFIG_USB_CONN_GPIO=m
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_OTG=y
|
||||
|
||||
@@ -864,6 +864,24 @@ config HID_PLANTRONICS
|
||||
|
||||
Say M here if you may ever plug in a Plantronics USB audio device.
|
||||
|
||||
config HID_PLAYSTATION
|
||||
tristate "PlayStation HID Driver"
|
||||
depends on HID
|
||||
select CRC32
|
||||
select POWER_SUPPLY
|
||||
help
|
||||
Provides support for Sony PS5 controllers including support for
|
||||
its special functionalities e.g. touchpad, lights and motion
|
||||
sensors.
|
||||
|
||||
config PLAYSTATION_FF
|
||||
bool "PlayStation force feedback support"
|
||||
depends on HID_PLAYSTATION
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
Say Y here if you would like to enable force feedback support for
|
||||
PlayStation game controllers.
|
||||
|
||||
config HID_PRIMAX
|
||||
tristate "Primax non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
@@ -920,6 +938,7 @@ config HID_SONY
|
||||
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
|
||||
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
|
||||
* Guitar Hero Live PS3 and Wii U guitar dongles
|
||||
* Guitar Hero PS3 and PC guitar dongles
|
||||
|
||||
config SONY_FF
|
||||
bool "Sony PS2/3/4 accessories force feedback support"
|
||||
|
||||
@@ -95,6 +95,7 @@ hid-picolcd-$(CONFIG_HID_PICOLCD_CIR) += hid-picolcd_cir.o
|
||||
hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o
|
||||
|
||||
obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
|
||||
obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o
|
||||
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
|
||||
obj-$(CONFIG_HID_REDRAGON) += hid-redragon.o
|
||||
obj-$(CONFIG_HID_RETRODE) += hid-retrode.o
|
||||
@@ -139,7 +140,7 @@ obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
obj-$(CONFIG_USB_KBD) += usbhid/
|
||||
|
||||
obj-$(CONFIG_I2C_HID) += i2c-hid/
|
||||
obj-$(CONFIG_I2C_HID_CORE) += i2c-hid/
|
||||
|
||||
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
|
||||
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
|
||||
|
||||
@@ -21,6 +21,39 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define CH_WIRELESS_CTL_REPORT_ID 0x11
|
||||
|
||||
static int ch_report_wireless(struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct hid_device *hdev = report->device;
|
||||
struct input_dev *input;
|
||||
|
||||
if (report->id != CH_WIRELESS_CTL_REPORT_ID || report->maxfield != 1)
|
||||
return 0;
|
||||
|
||||
input = report->field[0]->hidinput->input;
|
||||
if (!input) {
|
||||
hid_warn(hdev, "can't find wireless radio control's input");
|
||||
return 0;
|
||||
}
|
||||
|
||||
input_report_key(input, KEY_RFKILL, 1);
|
||||
input_sync(input);
|
||||
input_report_key(input, KEY_RFKILL, 0);
|
||||
input_sync(input);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ch_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
if (report->application == HID_GD_WIRELESS_RADIO_CTLS)
|
||||
return ch_report_wireless(report, data, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
EV_KEY, (c))
|
||||
static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
@@ -77,10 +110,30 @@ static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int ch_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Chicony hid parse failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Chicony hw start failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id ch_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) },
|
||||
{ }
|
||||
};
|
||||
@@ -91,6 +144,8 @@ static struct hid_driver ch_driver = {
|
||||
.id_table = ch_devices,
|
||||
.report_fixup = ch_switch12_report_fixup,
|
||||
.input_mapping = ch_input_mapping,
|
||||
.probe = ch_probe,
|
||||
.raw_event = ch_raw_event,
|
||||
};
|
||||
module_hid_driver(ch_driver);
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ EXPORT_SYMBOL_GPL(hid_register_report);
|
||||
* Register a new field for this report.
|
||||
*/
|
||||
|
||||
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
|
||||
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages)
|
||||
{
|
||||
struct hid_field *field;
|
||||
|
||||
@@ -101,7 +101,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
||||
|
||||
field = kzalloc((sizeof(struct hid_field) +
|
||||
usages * sizeof(struct hid_usage) +
|
||||
values * sizeof(unsigned)), GFP_KERNEL);
|
||||
usages * sizeof(unsigned)), GFP_KERNEL);
|
||||
if (!field)
|
||||
return NULL;
|
||||
|
||||
@@ -300,7 +300,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
|
||||
usages = max_t(unsigned, parser->local.usage_index,
|
||||
parser->global.report_count);
|
||||
|
||||
field = hid_register_field(report, usages, parser->global.report_count);
|
||||
field = hid_register_field(report, usages);
|
||||
if (!field)
|
||||
return 0;
|
||||
|
||||
@@ -1307,6 +1307,9 @@ EXPORT_SYMBOL_GPL(hid_open_report);
|
||||
|
||||
static s32 snto32(__u32 value, unsigned n)
|
||||
{
|
||||
if (!value || !n)
|
||||
return 0;
|
||||
|
||||
switch (n) {
|
||||
case 8: return ((__s8)value);
|
||||
case 16: return ((__s16)value);
|
||||
|
||||
@@ -392,30 +392,34 @@ static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hammer_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
static void hammer_folded_event(struct hid_device *hdev, bool folded)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cbas_ec_lock, flags);
|
||||
|
||||
/*
|
||||
* If we are getting events from Whiskers that means that it
|
||||
* is attached to the lid.
|
||||
*/
|
||||
cbas_ec.base_present = true;
|
||||
cbas_ec.base_folded = folded;
|
||||
hid_dbg(hdev, "%s: base: %d, folded: %d\n", __func__,
|
||||
cbas_ec.base_present, cbas_ec.base_folded);
|
||||
|
||||
if (cbas_ec.input) {
|
||||
input_report_switch(cbas_ec.input, SW_TABLET_MODE, folded);
|
||||
input_sync(cbas_ec.input);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cbas_ec_lock, flags);
|
||||
}
|
||||
|
||||
static int hammer_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
if (usage->hid == HID_USAGE_KBD_FOLDED) {
|
||||
spin_lock_irqsave(&cbas_ec_lock, flags);
|
||||
|
||||
/*
|
||||
* If we are getting events from Whiskers that means that it
|
||||
* is attached to the lid.
|
||||
*/
|
||||
cbas_ec.base_present = true;
|
||||
cbas_ec.base_folded = value;
|
||||
hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
|
||||
cbas_ec.base_present, cbas_ec.base_folded);
|
||||
|
||||
if (cbas_ec.input) {
|
||||
input_report_switch(cbas_ec.input,
|
||||
SW_TABLET_MODE, value);
|
||||
input_sync(cbas_ec.input);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cbas_ec_lock, flags);
|
||||
hammer_folded_event(hid, value);
|
||||
return 1; /* We handled this event */
|
||||
}
|
||||
|
||||
@@ -457,6 +461,47 @@ static bool hammer_has_backlight_control(struct hid_device *hdev)
|
||||
HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
|
||||
}
|
||||
|
||||
static void hammer_get_folded_state(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report *report;
|
||||
char *buf;
|
||||
int len, rlen;
|
||||
int a;
|
||||
|
||||
report = hdev->report_enum[HID_INPUT_REPORT].report_id_hash[0x0];
|
||||
|
||||
if (!report || report->maxfield < 1)
|
||||
return;
|
||||
|
||||
len = hid_report_len(report) + 1;
|
||||
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
rlen = hid_hw_raw_request(hdev, report->id, buf, len, report->type, HID_REQ_GET_REPORT);
|
||||
|
||||
if (rlen != len) {
|
||||
hid_warn(hdev, "Unable to read base folded state: %d (expected %d)\n", rlen, len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (a = 0; a < report->maxfield; a++) {
|
||||
struct hid_field *field = report->field[a];
|
||||
|
||||
if (field->usage->hid == HID_USAGE_KBD_FOLDED) {
|
||||
u32 value = hid_field_extract(hdev, buf+1,
|
||||
field->report_offset, field->report_size);
|
||||
|
||||
hammer_folded_event(hdev, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static int hammer_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
@@ -481,6 +526,8 @@ static int hammer_probe(struct hid_device *hdev,
|
||||
error = hid_hw_open(hdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
hammer_get_folded_state(hdev);
|
||||
}
|
||||
|
||||
if (hammer_has_backlight_control(hdev)) {
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
#define USB_VENDOR_ID_ACTIONSTAR 0x2101
|
||||
#define USB_DEVICE_ID_ACTIONSTAR_1011 0x1011
|
||||
|
||||
#define USB_VENDOR_ID_ACTIVISION 0x1430
|
||||
#define USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE 0x474c
|
||||
|
||||
#define USB_VENDOR_ID_ADS_TECH 0x06e1
|
||||
#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155
|
||||
|
||||
@@ -270,6 +273,7 @@
|
||||
#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
|
||||
#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2 0x0939
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS3 0x1236
|
||||
#define USB_DEVICE_ID_ASUS_AK1D 0x1125
|
||||
#define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A 0x1408
|
||||
#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421
|
||||
@@ -389,6 +393,7 @@
|
||||
#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
|
||||
#define USB_DEVICE_ID_HP_X2 0x074d
|
||||
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817
|
||||
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
@@ -641,6 +646,8 @@
|
||||
#define USB_DEVICE_ID_INNEX_GENESIS_ATARI 0x4745
|
||||
|
||||
#define USB_VENDOR_ID_ITE 0x048d
|
||||
#define I2C_VENDOR_ID_ITE 0x103c
|
||||
#define I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15 0x184f
|
||||
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
|
||||
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
|
||||
#define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a
|
||||
@@ -1076,13 +1083,15 @@
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0
|
||||
#define USB_DEVICE_ID_SONY_PS5_CONTROLLER 0x0ce6
|
||||
#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
|
||||
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
|
||||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
|
||||
|
||||
#define USB_VENDOR_ID_SONY_GHLIVE 0x12ba
|
||||
#define USB_VENDOR_ID_SONY_RHYTHM 0x12ba
|
||||
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
|
||||
#define USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE 0x0100
|
||||
|
||||
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
||||
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
|
||||
|
||||
@@ -324,6 +324,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1854,6 +1856,16 @@ static struct hid_input *hidinput_match_application(struct hid_report *report)
|
||||
list_for_each_entry(hidinput, &hid->inputs, list) {
|
||||
if (hidinput->application == report->application)
|
||||
return hidinput;
|
||||
|
||||
/*
|
||||
* Keep SystemControl and ConsumerControl applications together
|
||||
* with the main keyboard, if present.
|
||||
*/
|
||||
if ((report->application == HID_GD_SYSTEM_CONTROL ||
|
||||
report->application == HID_CP_CONSUMER_CONTROL) &&
|
||||
hidinput->application == HID_GD_KEYBOARD) {
|
||||
return hidinput;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -23,11 +23,16 @@ static __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int
|
||||
hid_info(hdev, "Fixing up Acer Sw5-012 ITE keyboard report descriptor\n");
|
||||
rdesc[163] = HID_MAIN_ITEM_RELATIVE;
|
||||
}
|
||||
/* For Acer One S1002 keyboard-dock */
|
||||
/* For Acer One S1002/S1003 keyboard-dock */
|
||||
if (*rsize == 188 && rdesc[185] == 0x81 && rdesc[186] == 0x02) {
|
||||
hid_info(hdev, "Fixing up Acer S1002 ITE keyboard report descriptor\n");
|
||||
hid_info(hdev, "Fixing up Acer S1002/S1003 ITE keyboard report descriptor\n");
|
||||
rdesc[186] = HID_MAIN_ITEM_RELATIVE;
|
||||
}
|
||||
/* For Acer Aspire Switch 10E (SW3-016) keyboard-dock */
|
||||
if (*rsize == 210 && rdesc[184] == 0x81 && rdesc[185] == 0x02) {
|
||||
hid_info(hdev, "Fixing up Acer Aspire Switch 10E (SW3-016) ITE keyboard report descriptor\n");
|
||||
rdesc[185] = HID_MAIN_ITEM_RELATIVE;
|
||||
}
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
@@ -114,7 +119,8 @@ static const struct hid_device_id ite_devices[] = {
|
||||
/* ITE8910 USB kbd ctlr, with Synaptics touchpad connected to it. */
|
||||
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||
USB_VENDOR_ID_SYNAPTICS,
|
||||
USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1003) },
|
||||
USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1003),
|
||||
.driver_data = QUIRK_TOUCHPAD_ON_OFF_REPORT },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ite_devices);
|
||||
|
||||
@@ -647,7 +647,7 @@ static void lg_g15_input_close(struct input_dev *dev)
|
||||
|
||||
static int lg_g15_register_led(struct lg_g15_data *g15, int i)
|
||||
{
|
||||
const char * const led_names[] = {
|
||||
static const char * const led_names[] = {
|
||||
"g15::kbd_backlight",
|
||||
"g15::lcd_backlight",
|
||||
"g15::macro_preset1",
|
||||
|
||||
@@ -980,6 +980,7 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
|
||||
case 0x07:
|
||||
device_type = "eQUAD step 4 Gaming";
|
||||
logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
|
||||
workitem.reports_supported |= STD_KEYBOARD;
|
||||
break;
|
||||
case 0x08:
|
||||
device_type = "eQUAD step 4 for gamepads";
|
||||
@@ -994,7 +995,12 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
|
||||
workitem.reports_supported |= STD_KEYBOARD;
|
||||
break;
|
||||
case 0x0d:
|
||||
device_type = "eQUAD Lightspeed 1_1";
|
||||
device_type = "eQUAD Lightspeed 1.1";
|
||||
logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
|
||||
workitem.reports_supported |= STD_KEYBOARD;
|
||||
break;
|
||||
case 0x0f:
|
||||
device_type = "eQUAD Lightspeed 1.2";
|
||||
logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
|
||||
workitem.reports_supported |= STD_KEYBOARD;
|
||||
break;
|
||||
|
||||
@@ -92,6 +92,8 @@ MODULE_PARM_DESC(disable_tap_to_click,
|
||||
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
|
||||
#define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3)
|
||||
#define HIDPP_CAPABILITY_BATTERY_VOLTAGE BIT(4)
|
||||
#define HIDPP_CAPABILITY_BATTERY_PERCENTAGE BIT(5)
|
||||
#define HIDPP_CAPABILITY_UNIFIED_BATTERY BIT(6)
|
||||
|
||||
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||
|
||||
@@ -152,6 +154,7 @@ struct hidpp_battery {
|
||||
int voltage;
|
||||
int charge_type;
|
||||
bool online;
|
||||
u8 supported_levels_1004;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1171,7 +1174,7 @@ static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp20_query_battery_info(struct hidpp_device *hidpp)
|
||||
static int hidpp20_query_battery_info_1000(struct hidpp_device *hidpp)
|
||||
{
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
@@ -1208,7 +1211,7 @@ static int hidpp20_query_battery_info(struct hidpp_device *hidpp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp20_battery_event(struct hidpp_device *hidpp,
|
||||
static int hidpp20_battery_event_1000(struct hidpp_device *hidpp,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct hidpp_report *report = (struct hidpp_report *)data;
|
||||
@@ -1380,6 +1383,224 @@ static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x1004: Unified battery */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_UNIFIED_BATTERY 0x1004
|
||||
|
||||
#define CMD_UNIFIED_BATTERY_GET_CAPABILITIES 0x00
|
||||
#define CMD_UNIFIED_BATTERY_GET_STATUS 0x10
|
||||
|
||||
#define EVENT_UNIFIED_BATTERY_STATUS_EVENT 0x00
|
||||
|
||||
#define FLAG_UNIFIED_BATTERY_LEVEL_CRITICAL BIT(0)
|
||||
#define FLAG_UNIFIED_BATTERY_LEVEL_LOW BIT(1)
|
||||
#define FLAG_UNIFIED_BATTERY_LEVEL_GOOD BIT(2)
|
||||
#define FLAG_UNIFIED_BATTERY_LEVEL_FULL BIT(3)
|
||||
|
||||
#define FLAG_UNIFIED_BATTERY_FLAGS_RECHARGEABLE BIT(0)
|
||||
#define FLAG_UNIFIED_BATTERY_FLAGS_STATE_OF_CHARGE BIT(1)
|
||||
|
||||
static int hidpp20_unifiedbattery_get_capabilities(struct hidpp_device *hidpp,
|
||||
u8 feature_index)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret;
|
||||
u8 *params = (u8 *)response.fap.params;
|
||||
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS ||
|
||||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE) {
|
||||
/* we have already set the device capabilities, so let's skip */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_UNIFIED_BATTERY_GET_CAPABILITIES,
|
||||
NULL, 0, &response);
|
||||
/* Ignore these intermittent errors */
|
||||
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
||||
return -EIO;
|
||||
if (ret > 0) {
|
||||
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
||||
__func__, ret);
|
||||
return -EPROTO;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the device supports state of charge (battery percentage) we won't
|
||||
* export the battery level information. there are 4 possible battery
|
||||
* levels and they all are optional, this means that the device might
|
||||
* not support any of them, we are just better off with the battery
|
||||
* percentage.
|
||||
*/
|
||||
if (params[1] & FLAG_UNIFIED_BATTERY_FLAGS_STATE_OF_CHARGE) {
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_PERCENTAGE;
|
||||
hidpp->battery.supported_levels_1004 = 0;
|
||||
} else {
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
|
||||
hidpp->battery.supported_levels_1004 = params[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp20_unifiedbattery_map_status(struct hidpp_device *hidpp,
|
||||
u8 charging_status,
|
||||
u8 external_power_status)
|
||||
{
|
||||
int status;
|
||||
|
||||
switch (charging_status) {
|
||||
case 0: /* discharging */
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case 1: /* charging */
|
||||
case 2: /* charging slow */
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case 3: /* complete */
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
case 4: /* error */
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
hid_info(hidpp->hid_dev, "%s: charging error",
|
||||
hidpp->name);
|
||||
break;
|
||||
default:
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hidpp20_unifiedbattery_map_level(struct hidpp_device *hidpp,
|
||||
u8 battery_level)
|
||||
{
|
||||
/* cler unsupported level bits */
|
||||
battery_level &= hidpp->battery.supported_levels_1004;
|
||||
|
||||
if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_FULL)
|
||||
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
||||
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_GOOD)
|
||||
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
||||
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_LOW)
|
||||
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
||||
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_CRITICAL)
|
||||
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
||||
|
||||
return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
||||
}
|
||||
|
||||
static int hidpp20_unifiedbattery_get_status(struct hidpp_device *hidpp,
|
||||
u8 feature_index,
|
||||
u8 *state_of_charge,
|
||||
int *status,
|
||||
int *level)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret;
|
||||
u8 *params = (u8 *)response.fap.params;
|
||||
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_UNIFIED_BATTERY_GET_STATUS,
|
||||
NULL, 0, &response);
|
||||
/* Ignore these intermittent errors */
|
||||
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
||||
return -EIO;
|
||||
if (ret > 0) {
|
||||
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
||||
__func__, ret);
|
||||
return -EPROTO;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*state_of_charge = params[0];
|
||||
*status = hidpp20_unifiedbattery_map_status(hidpp, params[2], params[3]);
|
||||
*level = hidpp20_unifiedbattery_map_level(hidpp, params[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp20_query_battery_info_1004(struct hidpp_device *hidpp)
|
||||
{
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
u8 state_of_charge;
|
||||
int status, level;
|
||||
|
||||
if (hidpp->battery.feature_index == 0xff) {
|
||||
ret = hidpp_root_get_feature(hidpp,
|
||||
HIDPP_PAGE_UNIFIED_BATTERY,
|
||||
&hidpp->battery.feature_index,
|
||||
&feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hidpp20_unifiedbattery_get_capabilities(hidpp,
|
||||
hidpp->battery.feature_index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hidpp20_unifiedbattery_get_status(hidpp,
|
||||
hidpp->battery.feature_index,
|
||||
&state_of_charge,
|
||||
&status,
|
||||
&level);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_UNIFIED_BATTERY;
|
||||
hidpp->battery.capacity = state_of_charge;
|
||||
hidpp->battery.status = status;
|
||||
hidpp->battery.level = level;
|
||||
hidpp->battery.online = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp20_battery_event_1004(struct hidpp_device *hidpp,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct hidpp_report *report = (struct hidpp_report *)data;
|
||||
u8 *params = (u8 *)report->fap.params;
|
||||
int state_of_charge, status, level;
|
||||
bool changed;
|
||||
|
||||
if (report->fap.feature_index != hidpp->battery.feature_index ||
|
||||
report->fap.funcindex_clientid != EVENT_UNIFIED_BATTERY_STATUS_EVENT)
|
||||
return 0;
|
||||
|
||||
state_of_charge = params[0];
|
||||
status = hidpp20_unifiedbattery_map_status(hidpp, params[2], params[3]);
|
||||
level = hidpp20_unifiedbattery_map_level(hidpp, params[1]);
|
||||
|
||||
changed = status != hidpp->battery.status ||
|
||||
(state_of_charge != hidpp->battery.capacity &&
|
||||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE) ||
|
||||
(level != hidpp->battery.level &&
|
||||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS);
|
||||
|
||||
if (changed) {
|
||||
hidpp->battery.capacity = state_of_charge;
|
||||
hidpp->battery.status = status;
|
||||
hidpp->battery.level = level;
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Battery feature helpers */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static enum power_supply_property hidpp_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
@@ -3307,7 +3528,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
||||
}
|
||||
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
|
||||
ret = hidpp20_battery_event(hidpp, data, size);
|
||||
ret = hidpp20_battery_event_1000(hidpp, data, size);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ret = hidpp20_battery_event_1004(hidpp, data, size);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ret = hidpp_solar_battery_event(hidpp, data, size);
|
||||
@@ -3443,9 +3667,14 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
|
||||
ret = hidpp_solar_request_battery_event(hidpp);
|
||||
else {
|
||||
ret = hidpp20_query_battery_voltage_info(hidpp);
|
||||
/* we only support one battery feature right now, so let's
|
||||
first check the ones that support battery level first
|
||||
and leave voltage for last */
|
||||
ret = hidpp20_query_battery_info_1000(hidpp);
|
||||
if (ret)
|
||||
ret = hidpp20_query_battery_info(hidpp);
|
||||
ret = hidpp20_query_battery_info_1004(hidpp);
|
||||
if (ret)
|
||||
ret = hidpp20_query_battery_voltage_info(hidpp);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@@ -3473,7 +3702,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
|
||||
num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
|
||||
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
|
||||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE)
|
||||
battery_props[num_battery_props++] =
|
||||
POWER_SUPPLY_PROP_CAPACITY;
|
||||
|
||||
@@ -3650,8 +3880,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
|
||||
hidpp20_query_battery_voltage_info(hidpp);
|
||||
else if (hidpp->capabilities & HIDPP_CAPABILITY_UNIFIED_BATTERY)
|
||||
hidpp20_query_battery_info_1004(hidpp);
|
||||
else
|
||||
hidpp20_query_battery_info(hidpp);
|
||||
hidpp20_query_battery_info_1000(hidpp);
|
||||
}
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
|
||||
@@ -1747,6 +1747,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mt_suspend(struct hid_device *hdev, pm_message_t state)
|
||||
{
|
||||
/* High latency is desirable for power savings during S3/S0ix */
|
||||
mt_set_modes(hdev, HID_LATENCY_HIGH, true, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt_reset_resume(struct hid_device *hdev)
|
||||
{
|
||||
mt_release_contacts(hdev);
|
||||
@@ -1762,6 +1769,8 @@ static int mt_resume(struct hid_device *hdev)
|
||||
|
||||
hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE);
|
||||
|
||||
mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -2155,6 +2164,7 @@ static struct hid_driver mt_driver = {
|
||||
.event = mt_event,
|
||||
.report = mt_report,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = mt_suspend,
|
||||
.reset_resume = mt_reset_resume,
|
||||
.resume = mt_resume,
|
||||
#endif
|
||||
|
||||
1351
drivers/hid/hid-playstation.c
Normal file
1351
drivers/hid/hid-playstation.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -180,7 +180,6 @@ static const struct hid_device_id hid_quirks[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8883), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TRUST, USB_DEVICE_ID_TRUST_PANORA_TABLET), HID_QUIRK_MULTI_INPUT | HID_QUIRK_HIDINPUT_FORCE },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60), HID_QUIRK_MULTI_INPUT },
|
||||
@@ -1029,7 +1028,7 @@ static DEFINE_MUTEX(dquirks_lock);
|
||||
/* Runtime ("dynamic") quirks manipulation functions */
|
||||
|
||||
/**
|
||||
* hid_exists_dquirk: find any dynamic quirks for a HID device
|
||||
* hid_exists_dquirk - find any dynamic quirks for a HID device
|
||||
* @hdev: the HID device to match
|
||||
*
|
||||
* Description:
|
||||
@@ -1037,7 +1036,7 @@ static DEFINE_MUTEX(dquirks_lock);
|
||||
* the pointer to the relevant struct hid_device_id if found.
|
||||
* Must be called with a read lock held on dquirks_lock.
|
||||
*
|
||||
* Returns: NULL if no quirk found, struct hid_device_id * if found.
|
||||
* Return: NULL if no quirk found, struct hid_device_id * if found.
|
||||
*/
|
||||
static struct hid_device_id *hid_exists_dquirk(const struct hid_device *hdev)
|
||||
{
|
||||
@@ -1061,7 +1060,7 @@ static struct hid_device_id *hid_exists_dquirk(const struct hid_device *hdev)
|
||||
|
||||
|
||||
/**
|
||||
* hid_modify_dquirk: add/replace a HID quirk
|
||||
* hid_modify_dquirk - add/replace a HID quirk
|
||||
* @id: the HID device to match
|
||||
* @quirks: the unsigned long quirks value to add/replace
|
||||
*
|
||||
@@ -1070,7 +1069,7 @@ static struct hid_device_id *hid_exists_dquirk(const struct hid_device *hdev)
|
||||
* quirks value with what was provided. Otherwise, add the quirk
|
||||
* to the dynamic quirks list.
|
||||
*
|
||||
* Returns: 0 OK, -error on failure.
|
||||
* Return: 0 OK, -error on failure.
|
||||
*/
|
||||
static int hid_modify_dquirk(const struct hid_device_id *id,
|
||||
const unsigned long quirks)
|
||||
@@ -1122,7 +1121,7 @@ static int hid_modify_dquirk(const struct hid_device_id *id,
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_remove_all_dquirks: remove all runtime HID quirks from memory
|
||||
* hid_remove_all_dquirks - remove all runtime HID quirks from memory
|
||||
* @bus: bus to match against. Use HID_BUS_ANY if all need to be removed.
|
||||
*
|
||||
* Description:
|
||||
@@ -1146,7 +1145,10 @@ static void hid_remove_all_dquirks(__u16 bus)
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_quirks_init: apply HID quirks specified at module load time
|
||||
* hid_quirks_init - apply HID quirks specified at module load time
|
||||
* @quirks_param: array of quirks strings (vendor:product:quirks)
|
||||
* @bus: bus type
|
||||
* @count: number of quirks to check
|
||||
*/
|
||||
int hid_quirks_init(char **quirks_param, __u16 bus, int count)
|
||||
{
|
||||
@@ -1177,7 +1179,7 @@ int hid_quirks_init(char **quirks_param, __u16 bus, int count)
|
||||
EXPORT_SYMBOL_GPL(hid_quirks_init);
|
||||
|
||||
/**
|
||||
* hid_quirks_exit: release memory associated with dynamic_quirks
|
||||
* hid_quirks_exit - release memory associated with dynamic_quirks
|
||||
* @bus: a bus to match against
|
||||
*
|
||||
* Description:
|
||||
@@ -1194,14 +1196,14 @@ void hid_quirks_exit(__u16 bus)
|
||||
EXPORT_SYMBOL_GPL(hid_quirks_exit);
|
||||
|
||||
/**
|
||||
* hid_gets_squirk: return any static quirks for a HID device
|
||||
* hid_gets_squirk - return any static quirks for a HID device
|
||||
* @hdev: the HID device to match
|
||||
*
|
||||
* Description:
|
||||
* Given a HID device, return a pointer to the quirked hid_device_id entry
|
||||
* associated with that device.
|
||||
*
|
||||
* Returns: the quirks.
|
||||
* Return: the quirks.
|
||||
*/
|
||||
static unsigned long hid_gets_squirk(const struct hid_device *hdev)
|
||||
{
|
||||
@@ -1225,13 +1227,13 @@ static unsigned long hid_gets_squirk(const struct hid_device *hdev)
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_lookup_quirk: return any quirks associated with a HID device
|
||||
* hid_lookup_quirk - return any quirks associated with a HID device
|
||||
* @hdev: the HID device to look for
|
||||
*
|
||||
* Description:
|
||||
* Given a HID device, return any quirks associated with that device.
|
||||
*
|
||||
* Returns: an unsigned long quirks value.
|
||||
* Return: an unsigned long quirks value.
|
||||
*/
|
||||
unsigned long hid_lookup_quirk(const struct hid_device *hdev)
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.state);
|
||||
return sysfs_emit(buf, "%d\n", temp_buf.state);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_mode_key(struct device *dev,
|
||||
@@ -92,7 +92,7 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev,
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.key_mask);
|
||||
return sysfs_emit(buf, "%d\n", temp_buf.key_mask);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_key_mask(struct device *dev,
|
||||
@@ -146,7 +146,7 @@ static ssize_t arvo_sysfs_show_actual_profile(struct device *dev,
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", arvo->actual_profile);
|
||||
return sysfs_emit(buf, "%d\n", arvo->actual_profile);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_actual_profile(struct device *dev,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
|
||||
* Copyright (c) 2018 Todd Kelner
|
||||
* Copyright (c) 2020 Pascal Giard <pascal.giard@etsmtl.ca>
|
||||
* Copyright (c) 2020 Sanjay Govind <sanjay.govind9@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -59,7 +60,8 @@
|
||||
#define NSG_MR5U_REMOTE_BT BIT(14)
|
||||
#define NSG_MR7U_REMOTE_BT BIT(15)
|
||||
#define SHANWAN_GAMEPAD BIT(16)
|
||||
#define GHL_GUITAR_PS3WIIU BIT(17)
|
||||
#define GH_GUITAR_CONTROLLER BIT(17)
|
||||
#define GHL_GUITAR_PS3WIIU BIT(18)
|
||||
|
||||
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
|
||||
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
|
||||
@@ -84,7 +86,7 @@
|
||||
#define NSG_MRXU_MAX_Y 1868
|
||||
|
||||
#define GHL_GUITAR_POKE_INTERVAL 10 /* In seconds */
|
||||
#define GHL_GUITAR_TILT_USAGE 44
|
||||
#define GUITAR_TILT_USAGE 44
|
||||
|
||||
/* Magic value and data taken from GHLtarUtility:
|
||||
* https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
|
||||
@@ -692,7 +694,7 @@ static int guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
|
||||
unsigned int abs = usage->hid & HID_USAGE;
|
||||
|
||||
if (abs == GHL_GUITAR_TILT_USAGE) {
|
||||
if (abs == GUITAR_TILT_USAGE) {
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
|
||||
return 1;
|
||||
}
|
||||
@@ -1481,7 +1483,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER)
|
||||
return ds4_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
if (sc->quirks & GHL_GUITAR_PS3WIIU)
|
||||
if (sc->quirks & GH_GUITAR_CONTROLLER)
|
||||
return guitar_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
/* Let hid-core decide for the others */
|
||||
@@ -3167,8 +3169,14 @@ static const struct hid_device_id sony_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
|
||||
.driver_data = NSG_MR7U_REMOTE_BT },
|
||||
/* Guitar Hero Live PS3 and Wii U guitar dongles */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_GHLIVE, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
|
||||
.driver_data = GHL_GUITAR_PS3WIIU},
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
|
||||
.driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER },
|
||||
/* Guitar Hero PC Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ACTIVISION, USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE),
|
||||
.driver_data = GH_GUITAR_CONTROLLER },
|
||||
/* Guitar Hero PS3 World Tour Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE),
|
||||
.driver_data = GH_GUITAR_CONTROLLER },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, sony_devices);
|
||||
|
||||
@@ -371,6 +371,8 @@ static const struct hid_device_id uclogic_devices[] = {
|
||||
USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,
|
||||
USB_DEVICE_ID_HUION_HS64) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TRUST,
|
||||
USB_DEVICE_ID_TRUST_PANORA_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
|
||||
@@ -1045,6 +1045,8 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
uclogic_params_init_with_pen_unused(&p);
|
||||
}
|
||||
break;
|
||||
case VID_PID(USB_VENDOR_ID_TRUST,
|
||||
USB_DEVICE_ID_TRUST_PANORA_TABLET):
|
||||
case VID_PID(USB_VENDOR_ID_UGEE,
|
||||
USB_DEVICE_ID_UGEE_TABLET_G5):
|
||||
/* Ignore non-pen interfaces */
|
||||
|
||||
@@ -2,18 +2,55 @@
|
||||
menu "I2C HID support"
|
||||
depends on I2C
|
||||
|
||||
config I2C_HID
|
||||
tristate "HID over I2C transport layer"
|
||||
config I2C_HID_ACPI
|
||||
tristate "HID over I2C transport layer ACPI driver"
|
||||
default n
|
||||
depends on I2C && INPUT
|
||||
select HID
|
||||
depends on I2C && INPUT && ACPI
|
||||
help
|
||||
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
|
||||
other HID based devices which is connected to your computer via I2C.
|
||||
This driver supports ACPI-based systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-hid.
|
||||
will be called i2c-hid-acpi. It will also build/depend on the
|
||||
module i2c-hid.
|
||||
|
||||
config I2C_HID_OF
|
||||
tristate "HID over I2C transport layer Open Firmware driver"
|
||||
default n
|
||||
depends on I2C && INPUT && OF
|
||||
help
|
||||
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
|
||||
other HID based devices which is connected to your computer via I2C.
|
||||
This driver supports Open Firmware (Device Tree)-based systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-hid-of. It will also build/depend on the
|
||||
module i2c-hid.
|
||||
|
||||
config I2C_HID_OF_GOODIX
|
||||
tristate "Driver for Goodix hid-i2c based devices on OF systems"
|
||||
default n
|
||||
depends on I2C && INPUT && OF
|
||||
help
|
||||
Say Y here if you want support for Goodix i2c devices that use
|
||||
the i2c-hid protocol on Open Firmware (Device Tree)-based
|
||||
systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-hid-of-goodix. It will also build/depend on
|
||||
the module i2c-hid.
|
||||
|
||||
endmenu
|
||||
|
||||
config I2C_HID_CORE
|
||||
tristate
|
||||
default y if I2C_HID_ACPI=y || I2C_HID_OF=y || I2C_HID_OF_GOODIX=y
|
||||
default m if I2C_HID_ACPI=m || I2C_HID_OF=m || I2C_HID_OF_GOODIX=m
|
||||
select HID
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
# Makefile for the I2C input drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_I2C_HID) += i2c-hid.o
|
||||
obj-$(CONFIG_I2C_HID_CORE) += i2c-hid.o
|
||||
|
||||
i2c-hid-objs = i2c-hid-core.o
|
||||
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
|
||||
|
||||
obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o
|
||||
obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o
|
||||
obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o
|
||||
|
||||
143
drivers/hid/i2c-hid/i2c-hid-acpi.c
Normal file
143
drivers/hid/i2c-hid/i2c-hid-acpi.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* HID over I2C ACPI Subclass
|
||||
*
|
||||
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
|
||||
* Copyright (c) 2012 Red Hat, Inc
|
||||
*
|
||||
* This code was forked out of the core code, which was partly based on
|
||||
* "USB HID support for Linux":
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2007-2008 Oliver Neukum
|
||||
* Copyright (c) 2006-2010 Jiri Kosina
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include "i2c-hid.h"
|
||||
|
||||
struct i2c_hid_acpi {
|
||||
struct i2chid_ops ops;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
|
||||
/*
|
||||
* The CHPN0001 ACPI device, which is used to describe the Chipone
|
||||
* ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
|
||||
*/
|
||||
{"CHPN0001", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int i2c_hid_acpi_get_descriptor(struct i2c_client *client)
|
||||
{
|
||||
static guid_t i2c_hid_guid =
|
||||
GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
|
||||
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
|
||||
union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
u16 hid_descriptor_address;
|
||||
|
||||
handle = ACPI_HANDLE(&client->dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev)) {
|
||||
dev_err(&client->dev, "Error could not get ACPI device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (!obj) {
|
||||
dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hid_descriptor_address = obj->integer.value;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return hid_descriptor_address;
|
||||
}
|
||||
|
||||
static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_acpi *ihid_acpi =
|
||||
container_of(ops, struct i2c_hid_acpi, ops);
|
||||
struct device *dev = &ihid_acpi->client->dev;
|
||||
acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD);
|
||||
}
|
||||
|
||||
static int i2c_hid_acpi_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct i2c_hid_acpi *ihid_acpi;
|
||||
struct acpi_device *adev;
|
||||
u16 hid_descriptor_address;
|
||||
int ret;
|
||||
|
||||
ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
|
||||
if (!ihid_acpi)
|
||||
return -ENOMEM;
|
||||
|
||||
ihid_acpi->client = client;
|
||||
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
|
||||
|
||||
ret = i2c_hid_acpi_get_descriptor(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
hid_descriptor_address = ret;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (adev)
|
||||
acpi_device_fix_up_power(adev);
|
||||
|
||||
if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
|
||||
device_set_wakeup_capable(dev, true);
|
||||
device_set_wakeup_enable(dev, false);
|
||||
}
|
||||
|
||||
return i2c_hid_core_probe(client, &ihid_acpi->ops,
|
||||
hid_descriptor_address);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_match[] = {
|
||||
{"ACPI0C50", 0 },
|
||||
{"PNP0C50", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
|
||||
|
||||
static struct i2c_driver i2c_hid_acpi_driver = {
|
||||
.driver = {
|
||||
.name = "i2c_hid_acpi",
|
||||
.pm = &i2c_hid_core_pm,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
|
||||
},
|
||||
|
||||
.probe = i2c_hid_acpi_probe,
|
||||
.remove = i2c_hid_core_remove,
|
||||
.shutdown = i2c_hid_core_shutdown,
|
||||
};
|
||||
|
||||
module_i2c_driver(i2c_hid_acpi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID over I2C ACPI driver");
|
||||
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -35,11 +35,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/platform_data/i2c-hid.h>
|
||||
|
||||
#include "../hid-ids.h"
|
||||
#include "i2c-hid.h"
|
||||
@@ -156,10 +151,10 @@ struct i2c_hid {
|
||||
|
||||
wait_queue_head_t wait; /* For waiting the interrupt */
|
||||
|
||||
struct i2c_hid_platform_data pdata;
|
||||
|
||||
bool irq_wake_enabled;
|
||||
struct mutex reset_lock;
|
||||
|
||||
struct i2chid_ops *ops;
|
||||
};
|
||||
|
||||
static const struct i2c_hid_quirks {
|
||||
@@ -171,6 +166,8 @@ static const struct i2c_hid_quirks {
|
||||
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
|
||||
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
|
||||
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
|
||||
{ I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15,
|
||||
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
|
||||
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118,
|
||||
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
|
||||
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
|
||||
@@ -884,144 +881,36 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
|
||||
/*
|
||||
* The CHPN0001 ACPI device, which is used to describe the Chipone
|
||||
* ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
|
||||
*/
|
||||
{"CHPN0001", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
static int i2c_hid_core_power_up(struct i2c_hid *ihid)
|
||||
{
|
||||
static guid_t i2c_hid_guid =
|
||||
GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
|
||||
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
|
||||
union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
if (!ihid->ops->power_up)
|
||||
return 0;
|
||||
|
||||
handle = ACPI_HANDLE(&client->dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev)) {
|
||||
dev_err(&client->dev, "Error could not get ACPI device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (!obj) {
|
||||
dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pdata->hid_descriptor_address = obj->integer.value;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return 0;
|
||||
return ihid->ops->power_up(ihid->ops);
|
||||
}
|
||||
|
||||
static void i2c_hid_acpi_fix_up_power(struct device *dev)
|
||||
static void i2c_hid_core_power_down(struct i2c_hid *ihid)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
if (!ihid->ops->power_down)
|
||||
return;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (adev)
|
||||
acpi_device_fix_up_power(adev);
|
||||
ihid->ops->power_down(ihid->ops);
|
||||
}
|
||||
|
||||
static void i2c_hid_acpi_enable_wakeup(struct device *dev)
|
||||
static void i2c_hid_core_shutdown_tail(struct i2c_hid *ihid)
|
||||
{
|
||||
if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
|
||||
device_set_wakeup_capable(dev, true);
|
||||
device_set_wakeup_enable(dev, false);
|
||||
}
|
||||
if (!ihid->ops->shutdown_tail)
|
||||
return;
|
||||
|
||||
ihid->ops->shutdown_tail(ihid->ops);
|
||||
}
|
||||
|
||||
static void i2c_hid_acpi_shutdown(struct device *dev)
|
||||
{
|
||||
acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_match[] = {
|
||||
{"ACPI0C50", 0 },
|
||||
{"PNP0C50", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
|
||||
#else
|
||||
static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {}
|
||||
|
||||
static inline void i2c_hid_acpi_enable_wakeup(struct device *dev) {}
|
||||
|
||||
static inline void i2c_hid_acpi_shutdown(struct device *dev) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int i2c_hid_of_probe(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "HID register address not provided\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (val >> 16) {
|
||||
dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdata->hid_descriptor_address = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_hid_of_match[] = {
|
||||
{ .compatible = "hid-over-i2c" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
|
||||
#else
|
||||
static inline int i2c_hid_of_probe(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void i2c_hid_fwnode_probe(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
|
||||
&val))
|
||||
pdata->post_power_delay_ms = val;
|
||||
}
|
||||
|
||||
static int i2c_hid_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
|
||||
u16 hid_descriptor_address)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_hid *ihid;
|
||||
struct hid_device *hid;
|
||||
__u16 hidRegister;
|
||||
struct i2c_hid_platform_data *platform_data = client->dev.platform_data;
|
||||
|
||||
dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
|
||||
|
||||
@@ -1042,44 +931,17 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
if (!ihid)
|
||||
return -ENOMEM;
|
||||
|
||||
if (client->dev.of_node) {
|
||||
ret = i2c_hid_of_probe(client, &ihid->pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (!platform_data) {
|
||||
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ihid->pdata = *platform_data;
|
||||
}
|
||||
ihid->ops = ops;
|
||||
|
||||
/* Parse platform agnostic common properties from ACPI / device tree */
|
||||
i2c_hid_fwnode_probe(client, &ihid->pdata);
|
||||
|
||||
ihid->pdata.supplies[0].supply = "vdd";
|
||||
ihid->pdata.supplies[1].supply = "vddl";
|
||||
|
||||
ret = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
ret = i2c_hid_core_power_up(ihid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ihid->pdata.post_power_delay_ms)
|
||||
msleep(ihid->pdata.post_power_delay_ms);
|
||||
|
||||
i2c_set_clientdata(client, ihid);
|
||||
|
||||
ihid->client = client;
|
||||
|
||||
hidRegister = ihid->pdata.hid_descriptor_address;
|
||||
ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
|
||||
ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address);
|
||||
|
||||
init_waitqueue_head(&ihid->wait);
|
||||
mutex_init(&ihid->reset_lock);
|
||||
@@ -1089,11 +951,7 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
* real computation later. */
|
||||
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
|
||||
if (ret < 0)
|
||||
goto err_regulator;
|
||||
|
||||
i2c_hid_acpi_fix_up_power(&client->dev);
|
||||
|
||||
i2c_hid_acpi_enable_wakeup(&client->dev);
|
||||
goto err_powered;
|
||||
|
||||
device_enable_async_suspend(&client->dev);
|
||||
|
||||
@@ -1102,19 +960,19 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
if (ret < 0) {
|
||||
dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
|
||||
ret = -ENXIO;
|
||||
goto err_regulator;
|
||||
goto err_powered;
|
||||
}
|
||||
|
||||
ret = i2c_hid_fetch_hid_descriptor(ihid);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to fetch the HID Descriptor\n");
|
||||
goto err_regulator;
|
||||
goto err_powered;
|
||||
}
|
||||
|
||||
ret = i2c_hid_init_irq(client);
|
||||
if (ret < 0)
|
||||
goto err_regulator;
|
||||
goto err_powered;
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid)) {
|
||||
@@ -1153,14 +1011,14 @@ err_mem_free:
|
||||
err_irq:
|
||||
free_irq(client->irq, ihid);
|
||||
|
||||
err_regulator:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
err_powered:
|
||||
i2c_hid_core_power_down(ihid);
|
||||
i2c_hid_free_buffers(ihid);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_hid_core_probe);
|
||||
|
||||
static int i2c_hid_remove(struct i2c_client *client)
|
||||
int i2c_hid_core_remove(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
struct hid_device *hid;
|
||||
@@ -1173,24 +1031,25 @@ static int i2c_hid_remove(struct i2c_client *client)
|
||||
if (ihid->bufsize)
|
||||
i2c_hid_free_buffers(ihid);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
i2c_hid_core_power_down(ihid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_hid_core_remove);
|
||||
|
||||
static void i2c_hid_shutdown(struct i2c_client *client)
|
||||
void i2c_hid_core_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
free_irq(client->irq, ihid);
|
||||
|
||||
i2c_hid_acpi_shutdown(&client->dev);
|
||||
i2c_hid_core_shutdown_tail(ihid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_hid_core_shutdown);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int i2c_hid_suspend(struct device *dev)
|
||||
static int i2c_hid_core_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
@@ -1217,14 +1076,13 @@ static int i2c_hid_suspend(struct device *dev)
|
||||
hid_warn(hid, "Failed to enable irq wake: %d\n",
|
||||
wake_status);
|
||||
} else {
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
i2c_hid_core_power_down(ihid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_hid_resume(struct device *dev)
|
||||
static int i2c_hid_core_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
@@ -1233,13 +1091,7 @@ static int i2c_hid_resume(struct device *dev)
|
||||
int wake_status;
|
||||
|
||||
if (!device_may_wakeup(&client->dev)) {
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
if (ret)
|
||||
hid_warn(hid, "Failed to enable supplies: %d\n", ret);
|
||||
|
||||
if (ihid->pdata.post_power_delay_ms)
|
||||
msleep(ihid->pdata.post_power_delay_ms);
|
||||
i2c_hid_core_power_up(ihid);
|
||||
} else if (ihid->irq_wake_enabled) {
|
||||
wake_status = disable_irq_wake(client->irq);
|
||||
if (!wake_status)
|
||||
@@ -1276,34 +1128,10 @@ static int i2c_hid_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops i2c_hid_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
|
||||
const struct dev_pm_ops i2c_hid_core_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_core_suspend, i2c_hid_core_resume)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id i2c_hid_id_table[] = {
|
||||
{ "hid", 0 },
|
||||
{ "hid-over-i2c", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
|
||||
|
||||
|
||||
static struct i2c_driver i2c_hid_driver = {
|
||||
.driver = {
|
||||
.name = "i2c_hid",
|
||||
.pm = &i2c_hid_pm,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
|
||||
.of_match_table = of_match_ptr(i2c_hid_of_match),
|
||||
},
|
||||
|
||||
.probe = i2c_hid_probe,
|
||||
.remove = i2c_hid_remove,
|
||||
.shutdown = i2c_hid_shutdown,
|
||||
.id_table = i2c_hid_id_table,
|
||||
};
|
||||
|
||||
module_i2c_driver(i2c_hid_driver);
|
||||
EXPORT_SYMBOL_GPL(i2c_hid_core_pm);
|
||||
|
||||
MODULE_DESCRIPTION("HID over I2C core driver");
|
||||
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
||||
|
||||
116
drivers/hid/i2c-hid/i2c-hid-of-goodix.c
Normal file
116
drivers/hid/i2c-hid/i2c-hid-of-goodix.c
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Goodix touchscreens that use the i2c-hid protocol.
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "i2c-hid.h"
|
||||
|
||||
struct goodix_i2c_hid_timing_data {
|
||||
unsigned int post_gpio_reset_delay_ms;
|
||||
unsigned int post_power_delay_ms;
|
||||
};
|
||||
|
||||
struct i2c_hid_of_goodix {
|
||||
struct i2chid_ops ops;
|
||||
|
||||
struct regulator *vdd;
|
||||
struct gpio_desc *reset_gpio;
|
||||
const struct goodix_i2c_hid_timing_data *timings;
|
||||
};
|
||||
|
||||
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix =
|
||||
container_of(ops, struct i2c_hid_of_goodix, ops);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(ihid_goodix->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ihid_goodix->timings->post_power_delay_ms)
|
||||
msleep(ihid_goodix->timings->post_power_delay_ms);
|
||||
|
||||
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
|
||||
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
|
||||
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix =
|
||||
container_of(ops, struct i2c_hid_of_goodix, ops);
|
||||
|
||||
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
|
||||
regulator_disable(ihid_goodix->vdd);
|
||||
}
|
||||
|
||||
static int i2c_hid_of_goodix_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_hid_of_goodix *ihid_goodix;
|
||||
|
||||
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
|
||||
GFP_KERNEL);
|
||||
if (!ihid_goodix)
|
||||
return -ENOMEM;
|
||||
|
||||
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
|
||||
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
|
||||
|
||||
/* Start out with reset asserted */
|
||||
ihid_goodix->reset_gpio =
|
||||
devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ihid_goodix->reset_gpio))
|
||||
return PTR_ERR(ihid_goodix->reset_gpio);
|
||||
|
||||
ihid_goodix->vdd = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(ihid_goodix->vdd))
|
||||
return PTR_ERR(ihid_goodix->vdd);
|
||||
|
||||
ihid_goodix->timings = device_get_match_data(&client->dev);
|
||||
|
||||
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
|
||||
}
|
||||
|
||||
static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = {
|
||||
.post_power_delay_ms = 10,
|
||||
.post_gpio_reset_delay_ms = 180,
|
||||
};
|
||||
|
||||
static const struct of_device_id goodix_i2c_hid_of_match[] = {
|
||||
{ .compatible = "goodix,gt7375p", .data = &goodix_gt7375p_timing_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, goodix_i2c_hid_of_match);
|
||||
|
||||
static struct i2c_driver goodix_i2c_hid_ts_driver = {
|
||||
.driver = {
|
||||
.name = "i2c_hid_of_goodix",
|
||||
.pm = &i2c_hid_core_pm,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
|
||||
},
|
||||
.probe = i2c_hid_of_goodix_probe,
|
||||
.remove = i2c_hid_core_remove,
|
||||
.shutdown = i2c_hid_core_shutdown,
|
||||
};
|
||||
module_i2c_driver(goodix_i2c_hid_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
|
||||
MODULE_DESCRIPTION("Goodix i2c-hid touchscreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
143
drivers/hid/i2c-hid/i2c-hid-of.c
Normal file
143
drivers/hid/i2c-hid/i2c-hid-of.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* HID over I2C Open Firmware Subclass
|
||||
*
|
||||
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
|
||||
* Copyright (c) 2012 Red Hat, Inc
|
||||
*
|
||||
* This code was forked out of the core code, which was partly based on
|
||||
* "USB HID support for Linux":
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2007-2008 Oliver Neukum
|
||||
* Copyright (c) 2006-2010 Jiri Kosina
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "i2c-hid.h"
|
||||
|
||||
struct i2c_hid_of {
|
||||
struct i2chid_ops ops;
|
||||
|
||||
struct i2c_client *client;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
int post_power_delay_ms;
|
||||
};
|
||||
|
||||
static int i2c_hid_of_power_up(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
|
||||
struct device *dev = &ihid_of->client->dev;
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
|
||||
ihid_of->supplies);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ihid_of->post_power_delay_ms)
|
||||
msleep(ihid_of->post_power_delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_hid_of_power_down(struct i2chid_ops *ops)
|
||||
{
|
||||
struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
|
||||
ihid_of->supplies);
|
||||
}
|
||||
|
||||
static int i2c_hid_of_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct i2c_hid_of *ihid_of;
|
||||
u16 hid_descriptor_address;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ihid_of = devm_kzalloc(&client->dev, sizeof(*ihid_of), GFP_KERNEL);
|
||||
if (!ihid_of)
|
||||
return -ENOMEM;
|
||||
|
||||
ihid_of->ops.power_up = i2c_hid_of_power_up;
|
||||
ihid_of->ops.power_down = i2c_hid_of_power_down;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "HID register address not provided\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (val >> 16) {
|
||||
dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
hid_descriptor_address = val;
|
||||
|
||||
if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
|
||||
&val))
|
||||
ihid_of->post_power_delay_ms = val;
|
||||
|
||||
ihid_of->supplies[0].supply = "vdd";
|
||||
ihid_of->supplies[1].supply = "vddl";
|
||||
ret = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(ihid_of->supplies),
|
||||
ihid_of->supplies);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return i2c_hid_core_probe(client, &ihid_of->ops,
|
||||
hid_descriptor_address);
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_hid_of_match[] = {
|
||||
{ .compatible = "hid-over-i2c" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
|
||||
|
||||
static const struct i2c_device_id i2c_hid_of_id_table[] = {
|
||||
{ "hid", 0 },
|
||||
{ "hid-over-i2c", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table);
|
||||
|
||||
static struct i2c_driver i2c_hid_of_driver = {
|
||||
.driver = {
|
||||
.name = "i2c_hid_of",
|
||||
.pm = &i2c_hid_core_pm,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = of_match_ptr(i2c_hid_of_match),
|
||||
},
|
||||
|
||||
.probe = i2c_hid_of_probe,
|
||||
.remove = i2c_hid_core_remove,
|
||||
.shutdown = i2c_hid_core_shutdown,
|
||||
.id_table = i2c_hid_of_id_table,
|
||||
};
|
||||
|
||||
module_i2c_driver(i2c_hid_of_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID over I2C OF driver");
|
||||
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef I2C_HID_H
|
||||
#define I2C_HID_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#ifdef CONFIG_DMI
|
||||
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
|
||||
@@ -17,4 +18,25 @@ static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||
{ return NULL; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct i2chid_ops - Ops provided to the core.
|
||||
*
|
||||
* @power_up: do sequencing to power up the device.
|
||||
* @power_down: do sequencing to power down the device.
|
||||
* @shutdown_tail: called at the end of shutdown.
|
||||
*/
|
||||
struct i2chid_ops {
|
||||
int (*power_up)(struct i2chid_ops *ops);
|
||||
void (*power_down)(struct i2chid_ops *ops);
|
||||
void (*shutdown_tail)(struct i2chid_ops *ops);
|
||||
};
|
||||
|
||||
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
|
||||
u16 hid_descriptor_address);
|
||||
int i2c_hid_core_remove(struct i2c_client *client);
|
||||
|
||||
void i2c_hid_core_shutdown(struct i2c_client *client);
|
||||
|
||||
extern const struct dev_pm_ops i2c_hid_core_pm;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define CMP_H_DEVICE_ID 0x06FC
|
||||
#define EHL_Ax_DEVICE_ID 0x4BB3
|
||||
#define TGL_LP_DEVICE_ID 0xA0FC
|
||||
#define TGL_H_DEVICE_ID 0x43FC
|
||||
|
||||
#define REVISION_ID_CHT_A0 0x6
|
||||
#define REVISION_ID_CHT_Ax_SI 0x0
|
||||
@@ -81,5 +82,6 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev);
|
||||
int ish_hw_start(struct ishtp_device *dev);
|
||||
void ish_device_disable(struct ishtp_device *dev);
|
||||
int ish_disable_dma(struct ishtp_device *dev);
|
||||
void ish_set_host_ready(struct ishtp_device *dev);
|
||||
|
||||
#endif /* _ISHTP_HW_ISH_H_ */
|
||||
|
||||
@@ -193,6 +193,33 @@ static void ish_clr_host_rdy(struct ishtp_device *dev)
|
||||
ish_reg_write(dev, IPC_REG_HOST_COMM, host_status);
|
||||
}
|
||||
|
||||
static bool ish_chk_host_rdy(struct ishtp_device *dev)
|
||||
{
|
||||
uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM);
|
||||
|
||||
return (host_status & IPC_HOSTCOMM_READY_BIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* ish_set_host_ready() - reconfig ipc host registers
|
||||
* @dev: ishtp device pointer
|
||||
*
|
||||
* Set host to ready state
|
||||
* This API is called in some case:
|
||||
* fw is still on, but ipc is powered down.
|
||||
* such as OOB case.
|
||||
*
|
||||
* Return: 0 for success else error fault code
|
||||
*/
|
||||
void ish_set_host_ready(struct ishtp_device *dev)
|
||||
{
|
||||
if (ish_chk_host_rdy(dev))
|
||||
return;
|
||||
|
||||
ish_set_host_rdy(dev);
|
||||
set_host_ready(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* _ishtp_read_hdr() - Read message header
|
||||
* @dev: ISHTP device pointer
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2014-2016, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -37,6 +38,7 @@ static const struct pci_device_id ish_pci_tbl[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)},
|
||||
{0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
|
||||
@@ -111,6 +113,42 @@ static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
|
||||
return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
||||
}
|
||||
|
||||
static int enable_gpe(struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_ACPI
|
||||
acpi_status acpi_sts;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_device_wakeup *wakeup;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev) {
|
||||
dev_err(dev, "get acpi handle failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
wakeup = &adev->wakeup;
|
||||
|
||||
acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
|
||||
if (ACPI_FAILURE(acpi_sts)) {
|
||||
dev_err(dev, "enable ose_gpe failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void enable_pme_wake(struct pci_dev *pdev)
|
||||
{
|
||||
if ((pci_pme_capable(pdev, PCI_D0) ||
|
||||
pci_pme_capable(pdev, PCI_D3hot) ||
|
||||
pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) {
|
||||
pci_pme_active(pdev, true);
|
||||
dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ish_probe() - PCI driver probe callback
|
||||
* @pdev: pci device
|
||||
@@ -179,6 +217,10 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
init_waitqueue_head(&ishtp->suspend_wait);
|
||||
init_waitqueue_head(&ishtp->resume_wait);
|
||||
|
||||
/* Enable PME for EHL */
|
||||
if (pdev->device == EHL_Ax_DEVICE_ID)
|
||||
enable_pme_wake(pdev);
|
||||
|
||||
ret = ish_init(ishtp);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -218,11 +260,15 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(ish_resume_device);
|
||||
struct ishtp_device *dev = pci_get_drvdata(pdev);
|
||||
uint32_t fwsts = dev->ops->get_fw_status(dev);
|
||||
int ret;
|
||||
|
||||
if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag) {
|
||||
if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
|
||||
&& IPC_IS_ISH_ILUP(fwsts)) {
|
||||
disable_irq_wake(pdev->irq);
|
||||
|
||||
ish_set_host_ready(dev);
|
||||
|
||||
ishtp_send_resume(dev);
|
||||
|
||||
/* Waiting to get resume response */
|
||||
@@ -317,6 +363,13 @@ static int __maybe_unused ish_resume(struct device *device)
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
struct ishtp_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
/* add this to finish power flow for EHL */
|
||||
if (dev->pdev->device == EHL_Ax_DEVICE_ID) {
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
enable_pme_wake(pdev);
|
||||
dev_dbg(dev->devc, "set power state to D0 for ehl\n");
|
||||
}
|
||||
|
||||
ish_resume_device = device;
|
||||
dev->resume_flag = 1;
|
||||
|
||||
|
||||
@@ -1825,7 +1825,7 @@ static ssize_t wacom_show_speed(struct device *dev,
|
||||
struct hid_device *hdev = to_hid_device(dev);
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed);
|
||||
return sysfs_emit(buf, "%i\n", wacom->wacom_wac.bt_high_speed);
|
||||
}
|
||||
|
||||
static ssize_t wacom_store_speed(struct device *dev,
|
||||
|
||||
@@ -2600,7 +2600,12 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
|
||||
wacom_wac->is_invalid_bt_frame = !value;
|
||||
return;
|
||||
case HID_DG_CONTACTMAX:
|
||||
features->touch_max = value;
|
||||
if (!features->touch_max) {
|
||||
features->touch_max = value;
|
||||
} else {
|
||||
hid_warn(hdev, "%s: ignoring attempt to overwrite non-zero touch_max "
|
||||
"%d -> %d\n", __func__, features->touch_max, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
|
||||
if (IS_ERR(abspam))
|
||||
return PTR_ERR(abspam);
|
||||
|
||||
for (i = 0; i < joydev->nabs; i++) {
|
||||
for (i = 0; i < len && i < joydev->nabs; i++) {
|
||||
if (abspam[i] > ABS_MAX) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
@@ -480,6 +480,9 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
|
||||
int i;
|
||||
int retval = 0;
|
||||
|
||||
if (len % sizeof(*keypam))
|
||||
return -EINVAL;
|
||||
|
||||
len = min(len, sizeof(joydev->keypam));
|
||||
|
||||
/* Validate the map. */
|
||||
@@ -487,7 +490,7 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
|
||||
if (IS_ERR(keypam))
|
||||
return PTR_ERR(keypam);
|
||||
|
||||
for (i = 0; i < joydev->nkey; i++) {
|
||||
for (i = 0; i < (len / 2) && i < joydev->nkey; i++) {
|
||||
if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
|
||||
@@ -382,4 +382,11 @@ config JOYSTICK_FSIA6B
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called fsia6b.
|
||||
|
||||
config JOYSTICK_N64
|
||||
bool "N64 controller"
|
||||
depends on MACH_NINTENDO64
|
||||
help
|
||||
Say Y here if you want enable support for the four
|
||||
built-in controller ports on the Nintendo 64 console.
|
||||
|
||||
endif
|
||||
|
||||
@@ -24,6 +24,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
|
||||
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
|
||||
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
|
||||
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
|
||||
obj-$(CONFIG_JOYSTICK_N64) += n64joy.o
|
||||
obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
|
||||
obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o
|
||||
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
|
||||
@@ -37,4 +38,3 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
|
||||
obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
|
||||
obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
|
||||
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
|
||||
|
||||
|
||||
345
drivers/input/joystick/n64joy.c
Normal file
345
drivers/input/joystick/n64joy.c
Normal file
@@ -0,0 +1,345 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Support for the four N64 controllers.
|
||||
*
|
||||
* Copyright (c) 2021 Lauri Kasanen
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
|
||||
MODULE_DESCRIPTION("Driver for N64 controllers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define PIF_RAM 0x1fc007c0
|
||||
|
||||
#define SI_DRAM_REG 0
|
||||
#define SI_READ_REG 1
|
||||
#define SI_WRITE_REG 4
|
||||
#define SI_STATUS_REG 6
|
||||
|
||||
#define SI_STATUS_DMA_BUSY BIT(0)
|
||||
#define SI_STATUS_IO_BUSY BIT(1)
|
||||
|
||||
#define N64_CONTROLLER_ID 0x0500
|
||||
|
||||
#define MAX_CONTROLLERS 4
|
||||
|
||||
static const char *n64joy_phys[MAX_CONTROLLERS] = {
|
||||
"n64joy/port0",
|
||||
"n64joy/port1",
|
||||
"n64joy/port2",
|
||||
"n64joy/port3",
|
||||
};
|
||||
|
||||
struct n64joy_priv {
|
||||
u64 si_buf[8] ____cacheline_aligned;
|
||||
struct timer_list timer;
|
||||
struct mutex n64joy_mutex;
|
||||
struct input_dev *n64joy_dev[MAX_CONTROLLERS];
|
||||
u32 __iomem *reg_base;
|
||||
u8 n64joy_opened;
|
||||
};
|
||||
|
||||
struct joydata {
|
||||
unsigned int: 16; /* unused */
|
||||
unsigned int err: 2;
|
||||
unsigned int: 14; /* unused */
|
||||
|
||||
union {
|
||||
u32 data;
|
||||
|
||||
struct {
|
||||
unsigned int a: 1;
|
||||
unsigned int b: 1;
|
||||
unsigned int z: 1;
|
||||
unsigned int start: 1;
|
||||
unsigned int up: 1;
|
||||
unsigned int down: 1;
|
||||
unsigned int left: 1;
|
||||
unsigned int right: 1;
|
||||
unsigned int: 2; /* unused */
|
||||
unsigned int l: 1;
|
||||
unsigned int r: 1;
|
||||
unsigned int c_up: 1;
|
||||
unsigned int c_down: 1;
|
||||
unsigned int c_left: 1;
|
||||
unsigned int c_right: 1;
|
||||
signed int x: 8;
|
||||
signed int y: 8;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value)
|
||||
{
|
||||
writel(value, reg_base + reg);
|
||||
}
|
||||
|
||||
static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg)
|
||||
{
|
||||
return readl(reg_base + reg);
|
||||
}
|
||||
|
||||
static void n64joy_wait_si_dma(u32 __iomem *reg_base)
|
||||
{
|
||||
while (n64joy_read_reg(reg_base, SI_STATUS_REG) &
|
||||
(SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8])
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dma_cache_wback_inv((unsigned long) in, 8 * 8);
|
||||
dma_cache_inv((unsigned long) priv->si_buf, 8 * 8);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in));
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM);
|
||||
barrier();
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf));
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM);
|
||||
barrier();
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static const u64 polldata[] ____cacheline_aligned = {
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xfe00000000000000,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
static void n64joy_poll(struct timer_list *t)
|
||||
{
|
||||
const struct joydata *data;
|
||||
struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer);
|
||||
struct input_dev *dev;
|
||||
u32 i;
|
||||
|
||||
n64joy_exec_pif(priv, polldata);
|
||||
|
||||
data = (struct joydata *) priv->si_buf;
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!priv->n64joy_dev[i])
|
||||
continue;
|
||||
|
||||
dev = priv->n64joy_dev[i];
|
||||
|
||||
/* d-pad */
|
||||
input_report_key(dev, BTN_DPAD_UP, data[i].up);
|
||||
input_report_key(dev, BTN_DPAD_DOWN, data[i].down);
|
||||
input_report_key(dev, BTN_DPAD_LEFT, data[i].left);
|
||||
input_report_key(dev, BTN_DPAD_RIGHT, data[i].right);
|
||||
|
||||
/* c buttons */
|
||||
input_report_key(dev, BTN_FORWARD, data[i].c_up);
|
||||
input_report_key(dev, BTN_BACK, data[i].c_down);
|
||||
input_report_key(dev, BTN_LEFT, data[i].c_left);
|
||||
input_report_key(dev, BTN_RIGHT, data[i].c_right);
|
||||
|
||||
/* matching buttons */
|
||||
input_report_key(dev, BTN_START, data[i].start);
|
||||
input_report_key(dev, BTN_Z, data[i].z);
|
||||
|
||||
/* remaining ones: a, b, l, r */
|
||||
input_report_key(dev, BTN_0, data[i].a);
|
||||
input_report_key(dev, BTN_1, data[i].b);
|
||||
input_report_key(dev, BTN_2, data[i].l);
|
||||
input_report_key(dev, BTN_3, data[i].r);
|
||||
|
||||
input_report_abs(dev, ABS_X, data[i].x);
|
||||
input_report_abs(dev, ABS_Y, data[i].y);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
|
||||
}
|
||||
|
||||
static int n64joy_open(struct input_dev *dev)
|
||||
{
|
||||
struct n64joy_priv *priv = input_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&priv->n64joy_mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!priv->n64joy_opened) {
|
||||
/*
|
||||
* We could use the vblank irq, but it's not important if
|
||||
* the poll point slightly changes.
|
||||
*/
|
||||
timer_setup(&priv->timer, n64joy_poll, 0);
|
||||
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
|
||||
}
|
||||
|
||||
priv->n64joy_opened++;
|
||||
|
||||
mutex_unlock(&priv->n64joy_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void n64joy_close(struct input_dev *dev)
|
||||
{
|
||||
struct n64joy_priv *priv = input_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&priv->n64joy_mutex);
|
||||
if (!--priv->n64joy_opened)
|
||||
del_timer_sync(&priv->timer);
|
||||
mutex_unlock(&priv->n64joy_mutex);
|
||||
}
|
||||
|
||||
static const u64 __initconst scandata[] ____cacheline_aligned = {
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xfe00000000000000,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
/*
|
||||
* The target device is embedded and RAM-constrained. We save RAM
|
||||
* by initializing in __init code that gets dropped late in boot.
|
||||
* For the same reason there is no module or unloading support.
|
||||
*/
|
||||
static int __init n64joy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct joydata *data;
|
||||
struct n64joy_priv *priv;
|
||||
struct input_dev *dev;
|
||||
int err = 0;
|
||||
u32 i, j, found = 0;
|
||||
|
||||
priv = kzalloc(sizeof(struct n64joy_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
mutex_init(&priv->n64joy_mutex);
|
||||
|
||||
priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (!priv->reg_base) {
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* The controllers are not hotpluggable, so we can scan in init */
|
||||
n64joy_exec_pif(priv, scandata);
|
||||
|
||||
data = (struct joydata *) priv->si_buf;
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) {
|
||||
found++;
|
||||
|
||||
dev = priv->n64joy_dev[i] = input_allocate_device();
|
||||
if (!priv->n64joy_dev[i]) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
input_set_drvdata(dev, priv);
|
||||
|
||||
dev->name = "N64 controller";
|
||||
dev->phys = n64joy_phys[i];
|
||||
dev->id.bustype = BUS_HOST;
|
||||
dev->id.vendor = 0;
|
||||
dev->id.product = data[i].data >> 16;
|
||||
dev->id.version = 0;
|
||||
dev->dev.parent = &pdev->dev;
|
||||
|
||||
dev->open = n64joy_open;
|
||||
dev->close = n64joy_close;
|
||||
|
||||
/* d-pad */
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_UP);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT);
|
||||
/* c buttons */
|
||||
input_set_capability(dev, EV_KEY, BTN_LEFT);
|
||||
input_set_capability(dev, EV_KEY, BTN_RIGHT);
|
||||
input_set_capability(dev, EV_KEY, BTN_FORWARD);
|
||||
input_set_capability(dev, EV_KEY, BTN_BACK);
|
||||
/* matching buttons */
|
||||
input_set_capability(dev, EV_KEY, BTN_START);
|
||||
input_set_capability(dev, EV_KEY, BTN_Z);
|
||||
/* remaining ones: a, b, l, r */
|
||||
input_set_capability(dev, EV_KEY, BTN_0);
|
||||
input_set_capability(dev, EV_KEY, BTN_1);
|
||||
input_set_capability(dev, EV_KEY, BTN_2);
|
||||
input_set_capability(dev, EV_KEY, BTN_3);
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
input_set_abs_params(dev, ABS_X + j,
|
||||
S8_MIN, S8_MAX, 0, 0);
|
||||
|
||||
err = input_register_device(dev);
|
||||
if (err) {
|
||||
input_free_device(dev);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("%u controller(s) connected\n", found);
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!priv->n64joy_dev[i])
|
||||
continue;
|
||||
input_unregister_device(priv->n64joy_dev[i]);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver n64joy_driver = {
|
||||
.driver = {
|
||||
.name = "n64joy",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init n64joy_init(void)
|
||||
{
|
||||
return platform_driver_probe(&n64joy_driver, n64joy_probe);
|
||||
}
|
||||
|
||||
module_init(n64joy_init);
|
||||
@@ -305,6 +305,7 @@ static const struct xpad_device {
|
||||
{ 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
|
||||
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
|
||||
{ 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
|
||||
@@ -446,7 +446,7 @@ config KEYBOARD_MPR121
|
||||
|
||||
config KEYBOARD_SNVS_PWRKEY
|
||||
tristate "IMX SNVS Power Key Driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on ARCH_MXC || (COMPILE_TEST && HAS_IOMEM)
|
||||
depends on OF
|
||||
help
|
||||
This is the snvs powerkey driver for the Freescale i.MX application
|
||||
@@ -685,7 +685,7 @@ config KEYBOARD_OMAP
|
||||
|
||||
config KEYBOARD_OMAP4
|
||||
tristate "TI OMAP4+ keypad support"
|
||||
depends on OF || ARCH_OMAP2PLUS
|
||||
depends on (OF && HAS_IOMEM) || ARCH_OMAP2PLUS
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the OMAP4+ keypad.
|
||||
@@ -773,7 +773,7 @@ config KEYBOARD_CAP11XX
|
||||
|
||||
config KEYBOARD_BCM
|
||||
tristate "Broadcom keypad driver"
|
||||
depends on OF && HAVE_CLK
|
||||
depends on OF && HAVE_CLK && HAS_IOMEM
|
||||
select INPUT_MATRIXKMAP
|
||||
default ARCH_BCM_CYGNUS
|
||||
help
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <linux/efi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -409,7 +410,7 @@ struct applespi_data {
|
||||
unsigned int cmd_msg_cntr;
|
||||
/* lock to protect the above parameters and flags below */
|
||||
spinlock_t cmd_msg_lock;
|
||||
bool cmd_msg_queued;
|
||||
ktime_t cmd_msg_queued;
|
||||
enum applespi_evt_type cmd_evt_type;
|
||||
|
||||
struct led_classdev backlight_info;
|
||||
@@ -729,7 +730,7 @@ static void applespi_msg_complete(struct applespi_data *applespi,
|
||||
wake_up_all(&applespi->drain_complete);
|
||||
|
||||
if (is_write_msg) {
|
||||
applespi->cmd_msg_queued = false;
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi_send_cmd_msg(applespi);
|
||||
}
|
||||
|
||||
@@ -748,6 +749,8 @@ static void applespi_async_write_complete(void *context)
|
||||
applespi->tx_status,
|
||||
APPLESPI_STATUS_SIZE);
|
||||
|
||||
udelay(SPI_RW_CHG_DELAY_US);
|
||||
|
||||
if (!applespi_check_write_status(applespi, applespi->wr_m.status)) {
|
||||
/*
|
||||
* If we got an error, we presumably won't get the expected
|
||||
@@ -771,8 +774,16 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi)
|
||||
return 0;
|
||||
|
||||
/* check whether send is in progress */
|
||||
if (applespi->cmd_msg_queued)
|
||||
return 0;
|
||||
if (applespi->cmd_msg_queued) {
|
||||
if (ktime_ms_delta(ktime_get(), applespi->cmd_msg_queued) < 1000)
|
||||
return 0;
|
||||
|
||||
dev_warn(&applespi->spi->dev, "Command %d timed out\n",
|
||||
applespi->cmd_evt_type);
|
||||
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi->write_active = false;
|
||||
}
|
||||
|
||||
/* set up packet */
|
||||
memset(packet, 0, APPLESPI_PACKET_SIZE);
|
||||
@@ -869,7 +880,7 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi)
|
||||
return sts;
|
||||
}
|
||||
|
||||
applespi->cmd_msg_queued = true;
|
||||
applespi->cmd_msg_queued = ktime_get_coarse();
|
||||
applespi->write_active = true;
|
||||
|
||||
return 0;
|
||||
@@ -1921,7 +1932,7 @@ static int __maybe_unused applespi_resume(struct device *dev)
|
||||
applespi->drain = false;
|
||||
applespi->have_cl_led_on = false;
|
||||
applespi->have_bl_level = 0;
|
||||
applespi->cmd_msg_queued = false;
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi->read_active = false;
|
||||
applespi->write_active = false;
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define MAX_NUM_TOP_ROW_KEYS 15
|
||||
|
||||
/**
|
||||
* struct cros_ec_keyb - Structure representing EC keyboard device
|
||||
*
|
||||
@@ -42,6 +44,9 @@
|
||||
* @idev: The input device for the matrix keys.
|
||||
* @bs_idev: The input device for non-matrix buttons and switches (or NULL).
|
||||
* @notifier: interrupt event notifier for transport devices
|
||||
* @function_row_physmap: An array of the encoded rows/columns for the top
|
||||
* row function keys, in an order from left to right
|
||||
* @num_function_row_keys: The number of top row keys in a custom keyboard
|
||||
*/
|
||||
struct cros_ec_keyb {
|
||||
unsigned int rows;
|
||||
@@ -58,6 +63,9 @@ struct cros_ec_keyb {
|
||||
struct input_dev *idev;
|
||||
struct input_dev *bs_idev;
|
||||
struct notifier_block notifier;
|
||||
|
||||
u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS];
|
||||
size_t num_function_row_keys;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -527,6 +535,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
struct input_dev *idev;
|
||||
const char *phys;
|
||||
int err;
|
||||
struct property *prop;
|
||||
const __be32 *p;
|
||||
u16 *physmap;
|
||||
u32 key_pos;
|
||||
int row, col;
|
||||
|
||||
err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols);
|
||||
if (err)
|
||||
@@ -578,6 +591,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
ckdev->idev = idev;
|
||||
cros_ec_keyb_compute_valid_keys(ckdev);
|
||||
|
||||
physmap = ckdev->function_row_physmap;
|
||||
of_property_for_each_u32(dev->of_node, "function-row-physmap",
|
||||
prop, p, key_pos) {
|
||||
if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) {
|
||||
dev_warn(dev, "Only support up to %d top row keys\n",
|
||||
MAX_NUM_TOP_ROW_KEYS);
|
||||
break;
|
||||
}
|
||||
row = KEY_ROW(key_pos);
|
||||
col = KEY_COL(key_pos);
|
||||
*physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
|
||||
physmap++;
|
||||
ckdev->num_function_row_keys++;
|
||||
}
|
||||
|
||||
err = input_register_device(ckdev->idev);
|
||||
if (err) {
|
||||
dev_err(dev, "cannot register input device\n");
|
||||
@@ -587,6 +615,51 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t function_row_physmap_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t size = 0;
|
||||
int i;
|
||||
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
|
||||
u16 *physmap = ckdev->function_row_physmap;
|
||||
|
||||
for (i = 0; i < ckdev->num_function_row_keys; i++)
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size,
|
||||
"%s%02X", size ? " " : "", physmap[i]);
|
||||
if (size)
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(function_row_physmap);
|
||||
|
||||
static struct attribute *cros_ec_keyb_attrs[] = {
|
||||
&dev_attr_function_row_physmap.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &dev_attr_function_row_physmap.attr &&
|
||||
!ckdev->num_function_row_keys)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group cros_ec_keyb_attr_group = {
|
||||
.is_visible = cros_ec_keyb_attr_is_visible,
|
||||
.attrs = cros_ec_keyb_attrs,
|
||||
};
|
||||
|
||||
|
||||
static int cros_ec_keyb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
@@ -617,6 +690,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_device_add_group(dev, &cros_ec_keyb_attr_group);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to create attributes. err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ckdev->notifier.notifier_call = cros_ec_keyb_work;
|
||||
err = blocking_notifier_chain_register(&ckdev->ec->event_notifier,
|
||||
&ckdev->notifier);
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1)
|
||||
#define OMAP4_VAL_DEBOUNCINGTIME_16MS \
|
||||
OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128)
|
||||
#define OMAP4_KEYPAD_AUTOIDLE_MS 50 /* Approximate measured time */
|
||||
#define OMAP4_KEYPAD_IDLE_CHECK_MS (OMAP4_KEYPAD_AUTOIDLE_MS / 2)
|
||||
|
||||
enum {
|
||||
KBD_REVISION_OMAP4 = 0,
|
||||
@@ -71,6 +73,7 @@ struct omap4_keypad {
|
||||
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
struct mutex lock; /* for key scan */
|
||||
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
@@ -78,7 +81,7 @@ struct omap4_keypad {
|
||||
u32 irqreg_offset;
|
||||
unsigned int row_shift;
|
||||
bool no_autorepeat;
|
||||
unsigned char key_state[8];
|
||||
u64 keys;
|
||||
unsigned short *keymap;
|
||||
};
|
||||
|
||||
@@ -107,6 +110,55 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
|
||||
keypad_data->base + keypad_data->irqreg_offset + offset);
|
||||
}
|
||||
|
||||
static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data,
|
||||
u64 keys, bool down)
|
||||
{
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned int col, row, code;
|
||||
DECLARE_BITMAP(mask, 64);
|
||||
unsigned long bit;
|
||||
int events = 0;
|
||||
|
||||
bitmap_from_u64(mask, keys);
|
||||
|
||||
for_each_set_bit(bit, mask, keypad_data->rows * BITS_PER_BYTE) {
|
||||
row = bit / BITS_PER_BYTE;
|
||||
col = bit % BITS_PER_BYTE;
|
||||
code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
|
||||
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev, keypad_data->keymap[code], down);
|
||||
|
||||
events++;
|
||||
}
|
||||
|
||||
if (events)
|
||||
input_sync(input_dev);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys)
|
||||
{
|
||||
u64 changed;
|
||||
|
||||
mutex_lock(&keypad_data->lock);
|
||||
|
||||
changed = keys ^ keypad_data->keys;
|
||||
|
||||
/*
|
||||
* Report key up events separately and first. This matters in case we
|
||||
* lost key-up interrupt and just now catching up.
|
||||
*/
|
||||
omap4_keypad_report_keys(keypad_data, changed & ~keys, false);
|
||||
|
||||
/* Report key down events */
|
||||
omap4_keypad_report_keys(keypad_data, changed & keys, true);
|
||||
|
||||
keypad_data->keys = keys;
|
||||
|
||||
mutex_unlock(&keypad_data->lock);
|
||||
}
|
||||
|
||||
/* Interrupt handlers */
|
||||
static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
||||
@@ -122,48 +174,44 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
||||
static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = dev_id;
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
|
||||
unsigned int col, row, code, changed;
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
struct device *dev = keypad_data->input->dev.parent;
|
||||
u32 low, high;
|
||||
int error;
|
||||
u64 keys;
|
||||
|
||||
*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
for (col = 0; col < keypad_data->cols; col++) {
|
||||
if (changed & (1 << col)) {
|
||||
code = MATRIX_SCAN_CODE(row, col,
|
||||
keypad_data->row_shift);
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev,
|
||||
keypad_data->keymap[code],
|
||||
key_state[row] & (1 << col));
|
||||
}
|
||||
}
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
keys = low | (u64)high << 32;
|
||||
|
||||
memcpy(keypad_data->key_state, key_state,
|
||||
sizeof(keypad_data->key_state));
|
||||
omap4_keypad_scan_keys(keypad_data, keys);
|
||||
|
||||
/* clear pending interrupts */
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int omap4_keypad_open(struct input_dev *input)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = input_get_drvdata(input);
|
||||
struct device *dev = input->dev.parent;
|
||||
int error;
|
||||
|
||||
pm_runtime_get_sync(input->dev.parent);
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
disable_irq(keypad_data->irq);
|
||||
|
||||
@@ -176,13 +224,15 @@ static int omap4_keypad_open(struct input_dev *input)
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN |
|
||||
OMAP4_DEF_IRQENABLE_LONGKEY);
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN);
|
||||
kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
|
||||
OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
|
||||
OMAP4_DEF_WUP_EVENT_ENA);
|
||||
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -200,14 +250,20 @@ static void omap4_keypad_stop(struct omap4_keypad *keypad_data)
|
||||
|
||||
static void omap4_keypad_close(struct input_dev *input)
|
||||
{
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct omap4_keypad *keypad_data = input_get_drvdata(input);
|
||||
struct device *dev = input->dev.parent;
|
||||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
keypad_data = input_get_drvdata(input);
|
||||
disable_irq(keypad_data->irq);
|
||||
omap4_keypad_stop(keypad_data);
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
pm_runtime_put_sync(input->dev.parent);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
static int omap4_keypad_parse_dt(struct device *dev,
|
||||
@@ -252,8 +308,41 @@ static int omap4_keypad_check_revision(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed".
|
||||
* Interrupt may not happen for key-up events. We must clear stuck
|
||||
* key-up events after the keyboard hardware has auto-idled.
|
||||
*/
|
||||
static int __maybe_unused omap4_keypad_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
u32 active;
|
||||
|
||||
active = kbd_readl(keypad_data, OMAP4_KBD_STATEMACHINE);
|
||||
if (active) {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
omap4_keypad_scan_keys(keypad_data, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops omap4_keypad_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(omap4_keypad_runtime_suspend, NULL, NULL)
|
||||
};
|
||||
|
||||
static void omap4_disable_pm(void *d)
|
||||
{
|
||||
pm_runtime_dont_use_autosuspend(d);
|
||||
pm_runtime_disable(d);
|
||||
}
|
||||
|
||||
static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
@@ -271,63 +360,62 @@ static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
|
||||
keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
|
||||
if (!keypad_data) {
|
||||
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
|
||||
dev_err(dev, "keypad_data memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad_data->irq = irq;
|
||||
mutex_init(&keypad_data->lock);
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
|
||||
error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
|
||||
error = omap4_keypad_parse_dt(dev, keypad_data);
|
||||
if (error)
|
||||
goto err_free_keypad;
|
||||
return error;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't request mem region\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_keypad;
|
||||
keypad_data->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(keypad_data->base))
|
||||
return PTR_ERR(keypad_data->base);
|
||||
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, OMAP4_KEYPAD_IDLE_CHECK_MS);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
error = devm_add_action_or_reset(dev, omap4_disable_pm, dev);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to register cleanup action\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
keypad_data->base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad_data->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap mem resource\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/*
|
||||
* Enable clocks for the keypad module so that we can read
|
||||
* revision register.
|
||||
*/
|
||||
error = pm_runtime_get_sync(&pdev->dev);
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
} else {
|
||||
error = omap4_keypad_check_revision(&pdev->dev,
|
||||
keypad_data);
|
||||
if (!error) {
|
||||
/* Ensure device does not raise interrupts */
|
||||
omap4_keypad_stop(keypad_data);
|
||||
}
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
||||
pm_runtime_put_noidle(dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = omap4_keypad_check_revision(dev, keypad_data);
|
||||
if (!error) {
|
||||
/* Ensure device does not raise interrupts */
|
||||
omap4_keypad_stop(keypad_data);
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
if (error)
|
||||
goto err_pm_disable;
|
||||
return error;
|
||||
|
||||
/* input device allocation */
|
||||
keypad_data->input = input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
keypad_data->input = input_dev = devm_input_allocate_device(dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
@@ -344,84 +432,51 @@ static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
keypad_data->row_shift = get_count_order(keypad_data->cols);
|
||||
max_keys = keypad_data->rows << keypad_data->row_shift;
|
||||
keypad_data->keymap = kcalloc(max_keys,
|
||||
sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
keypad_data->keymap = devm_kcalloc(dev,
|
||||
max_keys,
|
||||
sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
if (!keypad_data->keymap) {
|
||||
dev_err(&pdev->dev, "Not enough memory for keymap\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_input;
|
||||
dev_err(dev, "Not enough memory for keymap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
error = matrix_keypad_build_keymap(NULL, NULL,
|
||||
keypad_data->rows, keypad_data->cols,
|
||||
keypad_data->keymap, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_free_keymap;
|
||||
dev_err(dev, "failed to build keymap\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
|
||||
omap4_keypad_irq_thread_fn, IRQF_ONESHOT,
|
||||
"omap4-keypad", keypad_data);
|
||||
error = devm_request_threaded_irq(dev, keypad_data->irq,
|
||||
omap4_keypad_irq_handler,
|
||||
omap4_keypad_irq_thread_fn,
|
||||
IRQF_ONESHOT,
|
||||
"omap4-keypad", keypad_data);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register interrupt\n");
|
||||
goto err_free_keymap;
|
||||
dev_err(dev, "failed to register interrupt\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(keypad_data->input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
error = dev_pm_set_wake_irq(&pdev->dev, keypad_data->irq);
|
||||
device_init_wakeup(dev, true);
|
||||
error = dev_pm_set_wake_irq(dev, keypad_data->irq);
|
||||
if (error)
|
||||
dev_warn(&pdev->dev,
|
||||
"failed to set up wakeup irq: %d\n", error);
|
||||
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
dev_warn(dev, "failed to set up wakeup irq: %d\n", error);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
err_free_keymap:
|
||||
kfree(keypad_data->keymap);
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
iounmap(keypad_data->base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_keypad:
|
||||
kfree(keypad_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int omap4_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
input_unregister_device(keypad_data->input);
|
||||
|
||||
iounmap(keypad_data->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(keypad_data->keymap);
|
||||
kfree(keypad_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -437,6 +492,7 @@ static struct platform_driver omap4_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "omap4-keypad",
|
||||
.of_match_table = omap_keypad_dt_match,
|
||||
.pm = &omap4_keypad_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(omap4_keypad_driver);
|
||||
|
||||
@@ -863,6 +863,7 @@ static void da7280_parse_properties(struct device *dev,
|
||||
gpi_str3[7] = '0' + i;
|
||||
haptics->gpi_ctl[i].polarity = 0;
|
||||
error = device_property_read_string(dev, gpi_str3, &str);
|
||||
if (!error)
|
||||
haptics->gpi_ctl[i].polarity =
|
||||
da7280_haptic_of_gpi_pol_str(dev, str);
|
||||
}
|
||||
@@ -1299,11 +1300,13 @@ static int __maybe_unused da7280_resume(struct device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id da7280_of_match[] = {
|
||||
{ .compatible = "dlg,da7280", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, da7280_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id da7280_i2c_id[] = {
|
||||
{ "da7280", },
|
||||
|
||||
@@ -986,7 +986,7 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
|
||||
case V7_PACKET_ID_TWO:
|
||||
mt[1].x &= ~0x000F;
|
||||
mt[1].y |= 0x000F;
|
||||
/* Detect false-postive touches where x & y report max value */
|
||||
/* Detect false-positive touches where x & y report max value */
|
||||
if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
|
||||
mt[1].x = 0;
|
||||
/* y gets set to 0 at the end of this function */
|
||||
|
||||
@@ -1106,8 +1106,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
num_fingers = hw.w + 2;
|
||||
break;
|
||||
case 2:
|
||||
if (SYN_MODEL_PEN(info->model_id))
|
||||
; /* Nothing, treat a pen as a single finger */
|
||||
/*
|
||||
* SYN_MODEL_PEN(info->model_id): even if
|
||||
* the device supports pen, we treat it as
|
||||
* a single finger.
|
||||
*/
|
||||
break;
|
||||
case 4 ... 15:
|
||||
if (SYN_CAP_PALMDETECT(info->capabilities))
|
||||
|
||||
@@ -255,7 +255,7 @@ config SERIO_ARC_PS2
|
||||
|
||||
config SERIO_APBPS2
|
||||
tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller"
|
||||
depends on OF
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
Say Y here if you want support for GRLIB APBPS2 peripherals used
|
||||
to connect to PS/2 keyboard and/or mouse.
|
||||
|
||||
@@ -588,6 +588,10 @@ static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
|
||||
},
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -1036,9 +1036,9 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%dx%d\n",
|
||||
input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
|
||||
input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
|
||||
return sysfs_emit(buf, "%dx%d\n",
|
||||
input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
|
||||
input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
|
||||
}
|
||||
|
||||
/* These structs define the sysfs files, param #1 is the name of the
|
||||
@@ -1064,9 +1064,8 @@ static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(pointer_mode_map,
|
||||
aiptek->curSetting.pointerMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(pointer_mode_map,
|
||||
aiptek->curSetting.pointerMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1101,9 +1100,8 @@ static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attri
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(coordinate_mode_map,
|
||||
aiptek->curSetting.coordinateMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(coordinate_mode_map,
|
||||
aiptek->curSetting.coordinateMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1143,9 +1141,8 @@ static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(tool_mode_map,
|
||||
aiptek->curSetting.toolMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(tool_mode_map,
|
||||
aiptek->curSetting.toolMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1174,10 +1171,9 @@ static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.xTilt);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.xTilt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,10 +1212,9 @@ static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.yTilt);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.yTilt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,7 +1252,7 @@ static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.jitterDelay);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1286,8 +1281,7 @@ static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_at
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.programmableDelay);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.programmableDelay);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1316,7 +1310,7 @@ static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attri
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
|
||||
return sysfs_emit(buf, "%ld\n", aiptek->eventCount);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
|
||||
@@ -1355,7 +1349,7 @@ static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_at
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, retMsg);
|
||||
return sysfs_emit(buf, retMsg);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
|
||||
@@ -1375,9 +1369,8 @@ static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonUpper));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonUpper));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1406,9 +1399,8 @@ static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonLower));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonLower));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1444,9 +1436,8 @@ static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonLeft));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonLeft));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1474,9 +1465,8 @@ static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonMiddle));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonMiddle));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1504,9 +1494,8 @@ static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonRight));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonRight));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1535,10 +1524,9 @@ static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.wheel);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.wheel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1568,8 +1556,7 @@ static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *a
|
||||
/* There is nothing useful to display, so a one-line manual
|
||||
* is in order...
|
||||
*/
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"Write anything to this file to program your tablet.\n");
|
||||
return sysfs_emit(buf, "Write anything to this file to program your tablet.\n");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -1600,7 +1587,7 @@ static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *a
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
|
||||
return sysfs_emit(buf, "0x%04x\n", aiptek->features.odmCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
|
||||
@@ -1613,7 +1600,7 @@ static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
|
||||
return sysfs_emit(buf, "0x%04x\n", aiptek->features.modelCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
|
||||
@@ -1626,8 +1613,7 @@ static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *at
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%04x\n",
|
||||
aiptek->features.firmwareCode);
|
||||
return sysfs_emit(buf, "%04x\n", aiptek->features.firmwareCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
|
||||
|
||||
@@ -608,7 +608,7 @@ config TOUCHSCREEN_MTOUCH
|
||||
|
||||
config TOUCHSCREEN_IMX6UL_TSC
|
||||
tristate "Freescale i.MX6UL touchscreen controller"
|
||||
depends on (OF && GPIOLIB) || COMPILE_TEST
|
||||
depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM
|
||||
help
|
||||
Say Y here if you have a Freescale i.MX6UL, and want to
|
||||
use the internal touchscreen controller.
|
||||
|
||||
@@ -64,24 +64,13 @@
|
||||
|
||||
struct ads7846_buf {
|
||||
u8 cmd;
|
||||
/*
|
||||
* This union is a temporary hack. The driver does an in-place
|
||||
* endianness conversion. This will be cleaned up in the next
|
||||
* patch.
|
||||
*/
|
||||
union {
|
||||
__be16 data_be16;
|
||||
u16 data;
|
||||
};
|
||||
__be16 data;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct ts_event {
|
||||
bool ignore;
|
||||
struct ads7846_buf x;
|
||||
struct ads7846_buf y;
|
||||
struct ads7846_buf z1;
|
||||
struct ads7846_buf z2;
|
||||
struct ads7846_buf_layout {
|
||||
unsigned int offset;
|
||||
unsigned int count;
|
||||
unsigned int skip;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -90,12 +79,18 @@ struct ts_event {
|
||||
* systems where main memory is not DMA-coherent (most non-x86 boards).
|
||||
*/
|
||||
struct ads7846_packet {
|
||||
struct ts_event tc;
|
||||
struct ads7846_buf read_x_cmd;
|
||||
struct ads7846_buf read_y_cmd;
|
||||
struct ads7846_buf read_z1_cmd;
|
||||
struct ads7846_buf read_z2_cmd;
|
||||
unsigned int count;
|
||||
unsigned int count_skip;
|
||||
unsigned int cmds;
|
||||
unsigned int last_cmd_idx;
|
||||
struct ads7846_buf_layout l[5];
|
||||
struct ads7846_buf *rx;
|
||||
struct ads7846_buf *tx;
|
||||
|
||||
struct ads7846_buf pwrdown_cmd;
|
||||
|
||||
bool ignore;
|
||||
u16 x, y, z1, z2;
|
||||
};
|
||||
|
||||
struct ads7846 {
|
||||
@@ -194,7 +189,6 @@ struct ads7846 {
|
||||
#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))
|
||||
#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))
|
||||
#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))
|
||||
|
||||
#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))
|
||||
#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */
|
||||
|
||||
@@ -207,6 +201,21 @@ struct ads7846 {
|
||||
#define REF_ON (READ_12BIT_DFR(x, 1, 1))
|
||||
#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
|
||||
|
||||
/* Order commands in the most optimal way to reduce Vref switching and
|
||||
* settling time:
|
||||
* Measure: X; Vref: X+, X-; IN: Y+
|
||||
* Measure: Y; Vref: Y+, Y-; IN: X+
|
||||
* Measure: Z1; Vref: Y+, X-; IN: X+
|
||||
* Measure: Z2; Vref: Y+, X-; IN: Y-
|
||||
*/
|
||||
enum ads7846_cmds {
|
||||
ADS7846_X,
|
||||
ADS7846_Y,
|
||||
ADS7846_Z1,
|
||||
ADS7846_Z2,
|
||||
ADS7846_PWDOWN,
|
||||
};
|
||||
|
||||
static int get_pendown_state(struct ads7846 *ts)
|
||||
{
|
||||
if (ts->get_pendown_state)
|
||||
@@ -689,26 +698,109 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val)
|
||||
return ADS7846_FILTER_OK;
|
||||
}
|
||||
|
||||
static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
|
||||
static int ads7846_get_value(struct ads7846_buf *buf)
|
||||
{
|
||||
int value;
|
||||
struct spi_transfer *t =
|
||||
list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
||||
struct ads7846_buf *buf = t->rx_buf;
|
||||
|
||||
value = be16_to_cpup(&buf->data_be16);
|
||||
value = be16_to_cpup(&buf->data);
|
||||
|
||||
/* enforce ADC output is 12 bits width */
|
||||
return (value >> 3) & 0xfff;
|
||||
}
|
||||
|
||||
static void ads7846_update_value(struct spi_message *m, int val)
|
||||
static void ads7846_set_cmd_val(struct ads7846 *ts, enum ads7846_cmds cmd_idx,
|
||||
u16 val)
|
||||
{
|
||||
struct spi_transfer *t =
|
||||
list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
||||
struct ads7846_buf *buf = t->rx_buf;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
|
||||
buf->data = val;
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_Y:
|
||||
packet->y = val;
|
||||
break;
|
||||
case ADS7846_X:
|
||||
packet->x = val;
|
||||
break;
|
||||
case ADS7846_Z1:
|
||||
packet->z1 = val;
|
||||
break;
|
||||
case ADS7846_Z2:
|
||||
packet->z2 = val;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ads7846_get_cmd(enum ads7846_cmds cmd_idx, int vref)
|
||||
{
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_Y:
|
||||
return READ_Y(vref);
|
||||
case ADS7846_X:
|
||||
return READ_X(vref);
|
||||
|
||||
/* 7846 specific commands */
|
||||
case ADS7846_Z1:
|
||||
return READ_Z1(vref);
|
||||
case ADS7846_Z2:
|
||||
return READ_Z2(vref);
|
||||
case ADS7846_PWDOWN:
|
||||
return PWRDOWN;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ads7846_cmd_need_settle(enum ads7846_cmds cmd_idx)
|
||||
{
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_X:
|
||||
case ADS7846_Y:
|
||||
case ADS7846_Z1:
|
||||
case ADS7846_Z2:
|
||||
return true;
|
||||
case ADS7846_PWDOWN:
|
||||
return false;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ads7846_filter(struct ads7846 *ts)
|
||||
{
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
int action;
|
||||
int val;
|
||||
unsigned int cmd_idx, b;
|
||||
|
||||
packet->ignore = false;
|
||||
for (cmd_idx = packet->last_cmd_idx; cmd_idx < packet->cmds - 1; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
|
||||
packet->last_cmd_idx = cmd_idx;
|
||||
|
||||
for (b = l->skip; b < l->count; b++) {
|
||||
val = ads7846_get_value(&packet->rx[l->offset + b]);
|
||||
|
||||
action = ts->filter(ts->filter_data, cmd_idx, &val);
|
||||
if (action == ADS7846_FILTER_REPEAT) {
|
||||
if (b == l->count - 1)
|
||||
return -EAGAIN;
|
||||
} else if (action == ADS7846_FILTER_OK) {
|
||||
ads7846_set_cmd_val(ts, cmd_idx, val);
|
||||
break;
|
||||
} else {
|
||||
packet->ignore = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ads7846_read_state(struct ads7846 *ts)
|
||||
@@ -716,52 +808,26 @@ static void ads7846_read_state(struct ads7846 *ts)
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
struct spi_message *m;
|
||||
int msg_idx = 0;
|
||||
int val;
|
||||
int action;
|
||||
int error;
|
||||
|
||||
while (msg_idx < ts->msg_count) {
|
||||
packet->last_cmd_idx = 0;
|
||||
|
||||
while (true) {
|
||||
ts->wait_for_sync();
|
||||
|
||||
m = &ts->msg[msg_idx];
|
||||
error = spi_sync(ts->spi, m);
|
||||
if (error) {
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
|
||||
packet->tc.ignore = true;
|
||||
packet->ignore = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Last message is power down request, no need to convert
|
||||
* or filter the value.
|
||||
*/
|
||||
if (msg_idx < ts->msg_count - 1) {
|
||||
error = ads7846_filter(ts);
|
||||
if (error)
|
||||
continue;
|
||||
|
||||
val = ads7846_get_value(ts, m);
|
||||
|
||||
action = ts->filter(ts->filter_data, msg_idx, &val);
|
||||
switch (action) {
|
||||
case ADS7846_FILTER_REPEAT:
|
||||
continue;
|
||||
|
||||
case ADS7846_FILTER_IGNORE:
|
||||
packet->tc.ignore = true;
|
||||
msg_idx = ts->msg_count - 1;
|
||||
continue;
|
||||
|
||||
case ADS7846_FILTER_OK:
|
||||
ads7846_update_value(m, val);
|
||||
packet->tc.ignore = false;
|
||||
msg_idx++;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
} else {
|
||||
msg_idx++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,19 +837,14 @@ static void ads7846_report_state(struct ads7846 *ts)
|
||||
unsigned int Rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
/*
|
||||
* ads7846_get_value() does in-place conversion (including byte swap)
|
||||
* from on-the-wire format as part of debouncing to get stable
|
||||
* readings.
|
||||
*/
|
||||
x = packet->tc.x.data;
|
||||
y = packet->tc.y.data;
|
||||
x = packet->x;
|
||||
y = packet->y;
|
||||
if (ts->model == 7845) {
|
||||
z1 = 0;
|
||||
z2 = 0;
|
||||
} else {
|
||||
z1 = packet->tc.z1.data;
|
||||
z2 = packet->tc.z2.data;
|
||||
z1 = packet->z1;
|
||||
z2 = packet->z2;
|
||||
}
|
||||
|
||||
/* range filtering */
|
||||
@@ -816,9 +877,9 @@ static void ads7846_report_state(struct ads7846 *ts)
|
||||
* the maximum. Don't report it to user space, repeat at least
|
||||
* once more the measurement
|
||||
*/
|
||||
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
||||
if (packet->ignore || Rt > ts->pressure_max) {
|
||||
dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
|
||||
packet->tc.ignore, Rt);
|
||||
packet->ignore, Rt);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -979,13 +1040,59 @@ static int ads7846_setup_pendown(struct spi_device *spi,
|
||||
* Set up the transfers to read touchscreen state; this assumes we
|
||||
* use formula #2 for pressure, not #3.
|
||||
*/
|
||||
static void ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
static int ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
const struct ads7846_platform_data *pdata)
|
||||
{
|
||||
struct spi_message *m = &ts->msg[0];
|
||||
struct spi_transfer *x = ts->xfer;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
int vref = pdata->keep_vref_on;
|
||||
unsigned int count, offset = 0;
|
||||
unsigned int cmd_idx, b;
|
||||
unsigned long time;
|
||||
size_t size = 0;
|
||||
|
||||
/* time per bit */
|
||||
time = NSEC_PER_SEC / ts->spi->max_speed_hz;
|
||||
|
||||
count = pdata->settle_delay_usecs * NSEC_PER_USEC / time;
|
||||
packet->count_skip = DIV_ROUND_UP(count, 24);
|
||||
|
||||
if (ts->debounce_max && ts->debounce_rep)
|
||||
/* ads7846_debounce_filter() is making ts->debounce_rep + 2
|
||||
* reads. So we need to get all samples for normal case. */
|
||||
packet->count = ts->debounce_rep + 2;
|
||||
else
|
||||
packet->count = 1;
|
||||
|
||||
if (ts->model == 7846)
|
||||
packet->cmds = 5; /* x, y, z1, z2, pwdown */
|
||||
else
|
||||
packet->cmds = 3; /* x, y, pwdown */
|
||||
|
||||
for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
unsigned int max_count;
|
||||
|
||||
if (ads7846_cmd_need_settle(cmd_idx))
|
||||
max_count = packet->count + packet->count_skip;
|
||||
else
|
||||
max_count = packet->count;
|
||||
|
||||
l->offset = offset;
|
||||
offset += max_count;
|
||||
l->count = max_count;
|
||||
l->skip = packet->count_skip;
|
||||
size += sizeof(*packet->tx) * max_count;
|
||||
}
|
||||
|
||||
packet->tx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL);
|
||||
if (!packet->tx)
|
||||
return -ENOMEM;
|
||||
|
||||
packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL);
|
||||
if (!packet->rx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ts->model == 7873) {
|
||||
/*
|
||||
@@ -1001,117 +1108,20 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
packet->read_y_cmd.cmd = READ_Y(vref);
|
||||
x->tx_buf = &packet->read_y_cmd;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
u8 cmd = ads7846_get_cmd(cmd_idx, vref);
|
||||
|
||||
/*
|
||||
* The first sample after switching drivers can be low quality;
|
||||
* optionally discard it, using a second one after the signals
|
||||
* have had enough time to stabilize.
|
||||
*/
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
x++;
|
||||
|
||||
x->tx_buf = &packet->read_y_cmd;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
for (b = 0; b < l->count; b++)
|
||||
packet->tx[l->offset + b].cmd = cmd;
|
||||
}
|
||||
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
/* turn y- off, x+ on, then leave in lowpower */
|
||||
x++;
|
||||
packet->read_x_cmd.cmd = READ_X(vref);
|
||||
x->tx_buf = &packet->read_x_cmd;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 3;
|
||||
x->tx_buf = packet->tx;
|
||||
x->rx_buf = packet->rx;
|
||||
x->len = size;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_x_cmd;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
||||
/* turn y+ off, x- on; we'll use formula #2 */
|
||||
if (ts->model == 7846) {
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->read_z1_cmd.cmd = READ_Z1(vref);
|
||||
x->tx_buf = &packet->read_z1_cmd;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_z1_cmd;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->read_z2_cmd.cmd = READ_Z2(vref);
|
||||
x->tx_buf = &packet->read_z2_cmd;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_z2_cmd;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
}
|
||||
|
||||
/* power down */
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->pwrdown_cmd.cmd = PWRDOWN;
|
||||
x->tx_buf = &packet->pwrdown_cmd;
|
||||
x->len = 3;
|
||||
|
||||
CS_CHANGE(*x);
|
||||
spi_message_add_tail(x, m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#define QUEUE_HEADER_SINGLE 0x62
|
||||
#define QUEUE_HEADER_NORMAL 0X63
|
||||
#define QUEUE_HEADER_WAIT 0x64
|
||||
#define QUEUE_HEADER_NORMAL2 0x66
|
||||
|
||||
/* Command header definition */
|
||||
#define CMD_HEADER_WRITE 0x54
|
||||
@@ -69,6 +70,7 @@
|
||||
#define CMD_HEADER_REK 0x66
|
||||
|
||||
/* FW position data */
|
||||
#define PACKET_SIZE_OLD 40
|
||||
#define PACKET_SIZE 55
|
||||
#define MAX_CONTACT_NUM 10
|
||||
#define FW_POS_HEADER 0
|
||||
@@ -90,6 +92,8 @@
|
||||
/* FW read command, 0x53 0x?? 0x0, 0x01 */
|
||||
#define E_ELAN_INFO_FW_VER 0x00
|
||||
#define E_ELAN_INFO_BC_VER 0x10
|
||||
#define E_ELAN_INFO_X_RES 0x60
|
||||
#define E_ELAN_INFO_Y_RES 0x63
|
||||
#define E_ELAN_INFO_REK 0xD0
|
||||
#define E_ELAN_INFO_TEST_VER 0xE0
|
||||
#define E_ELAN_INFO_FW_ID 0xF0
|
||||
@@ -112,6 +116,11 @@
|
||||
#define ELAN_POWERON_DELAY_USEC 500
|
||||
#define ELAN_RESET_DELAY_MSEC 20
|
||||
|
||||
enum elants_chip_id {
|
||||
EKTH3500,
|
||||
EKTF3624,
|
||||
};
|
||||
|
||||
enum elants_state {
|
||||
ELAN_STATE_NORMAL,
|
||||
ELAN_WAIT_QUEUE_HEADER,
|
||||
@@ -143,9 +152,12 @@ struct elants_data {
|
||||
unsigned int y_res;
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
unsigned int phy_x;
|
||||
unsigned int phy_y;
|
||||
struct touchscreen_properties prop;
|
||||
|
||||
enum elants_state state;
|
||||
enum elants_chip_id chip_id;
|
||||
enum elants_iap_mode iap_mode;
|
||||
|
||||
/* Guards against concurrent access to the device via sysfs */
|
||||
@@ -433,7 +445,51 @@ static int elants_i2c_query_bc_version(struct elants_data *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elants_i2c_query_ts_info(struct elants_data *ts)
|
||||
static int elants_i2c_query_ts_info_ektf(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error;
|
||||
u8 resp[4];
|
||||
u16 phy_x, phy_y;
|
||||
const u8 get_xres_cmd[] = {
|
||||
CMD_HEADER_READ, E_ELAN_INFO_X_RES, 0x00, 0x00
|
||||
};
|
||||
const u8 get_yres_cmd[] = {
|
||||
CMD_HEADER_READ, E_ELAN_INFO_Y_RES, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Get X/Y size in mm */
|
||||
error = elants_i2c_execute_command(client, get_xres_cmd,
|
||||
sizeof(get_xres_cmd),
|
||||
resp, sizeof(resp), 1,
|
||||
"get X size");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_x = resp[2] | ((resp[3] & 0xF0) << 4);
|
||||
|
||||
error = elants_i2c_execute_command(client, get_yres_cmd,
|
||||
sizeof(get_yres_cmd),
|
||||
resp, sizeof(resp), 1,
|
||||
"get Y size");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_y = resp[2] | ((resp[3] & 0xF0) << 4);
|
||||
|
||||
dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y);
|
||||
|
||||
ts->phy_x = phy_x;
|
||||
ts->phy_y = phy_y;
|
||||
|
||||
/* eKTF doesn't report max size, set it to default values */
|
||||
ts->x_max = 2240 - 1;
|
||||
ts->y_max = 1408 - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elants_i2c_query_ts_info_ekth(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error;
|
||||
@@ -508,6 +564,8 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
|
||||
ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x);
|
||||
ts->y_max = ELAN_TS_RESOLUTION(cols, osr);
|
||||
ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y);
|
||||
ts->phy_x = phy_x;
|
||||
ts->phy_y = phy_y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -587,8 +645,19 @@ static int elants_i2c_initialize(struct elants_data *ts)
|
||||
error = elants_i2c_query_fw_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_test_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info(ts);
|
||||
|
||||
switch (ts->chip_id) {
|
||||
case EKTH3500:
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info_ekth(ts);
|
||||
break;
|
||||
case EKTF3624:
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info_ektf(ts);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (error)
|
||||
ts->iap_mode = ELAN_IAP_RECOVERY;
|
||||
@@ -853,7 +922,8 @@ out:
|
||||
* Event reporting.
|
||||
*/
|
||||
|
||||
static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
|
||||
static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf,
|
||||
size_t packet_size)
|
||||
{
|
||||
struct input_dev *input = ts->input;
|
||||
unsigned int n_fingers;
|
||||
@@ -880,8 +950,24 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
|
||||
pos = &buf[FW_POS_XY + i * 3];
|
||||
x = (((u16)pos[0] & 0xf0) << 4) | pos[1];
|
||||
y = (((u16)pos[0] & 0x0f) << 8) | pos[2];
|
||||
p = buf[FW_POS_PRESSURE + i];
|
||||
w = buf[FW_POS_WIDTH + i];
|
||||
|
||||
/*
|
||||
* eKTF3624 may have use "old" touch-report format,
|
||||
* depending on a device and TS firmware version.
|
||||
* For example, ASUS Transformer devices use the "old"
|
||||
* format, while ASUS Nexus 7 uses the "new" formant.
|
||||
*/
|
||||
if (packet_size == PACKET_SIZE_OLD &&
|
||||
ts->chip_id == EKTF3624) {
|
||||
w = buf[FW_POS_WIDTH + i / 2];
|
||||
w >>= 4 * (~i & 1);
|
||||
w |= w << 4;
|
||||
w |= !w;
|
||||
p = w;
|
||||
} else {
|
||||
p = buf[FW_POS_PRESSURE + i];
|
||||
w = buf[FW_POS_WIDTH + i];
|
||||
}
|
||||
|
||||
dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n",
|
||||
i, x, y, p, w);
|
||||
@@ -913,7 +999,8 @@ static u8 elants_i2c_calculate_checksum(u8 *buf)
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static void elants_i2c_event(struct elants_data *ts, u8 *buf)
|
||||
static void elants_i2c_event(struct elants_data *ts, u8 *buf,
|
||||
size_t packet_size)
|
||||
{
|
||||
u8 checksum = elants_i2c_calculate_checksum(buf);
|
||||
|
||||
@@ -927,7 +1014,7 @@ static void elants_i2c_event(struct elants_data *ts, u8 *buf)
|
||||
"%s: unknown packet type: %02x\n",
|
||||
__func__, buf[FW_POS_HEADER]);
|
||||
else
|
||||
elants_i2c_mt_event(ts, buf);
|
||||
elants_i2c_mt_event(ts, buf, packet_size);
|
||||
}
|
||||
|
||||
static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
@@ -970,7 +1057,6 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
switch (ts->buf[FW_HDR_TYPE]) {
|
||||
case CMD_HEADER_HELLO:
|
||||
case CMD_HEADER_RESP:
|
||||
case CMD_HEADER_REK:
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_WAIT:
|
||||
@@ -985,9 +1071,24 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_SINGLE:
|
||||
elants_i2c_event(ts, &ts->buf[HEADER_SIZE]);
|
||||
elants_i2c_event(ts, &ts->buf[HEADER_SIZE],
|
||||
ts->buf[FW_HDR_LENGTH]);
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_NORMAL2: /* CMD_HEADER_REK */
|
||||
/*
|
||||
* Depending on firmware version, eKTF3624 touchscreens
|
||||
* may utilize one of these opcodes for the touch events:
|
||||
* 0x63 (NORMAL) and 0x66 (NORMAL2). The 0x63 is used by
|
||||
* older firmware version and differs from 0x66 such that
|
||||
* touch pressure value needs to be adjusted. The 0x66
|
||||
* opcode of newer firmware is equal to 0x63 of eKTH3500.
|
||||
*/
|
||||
if (ts->chip_id != EKTF3624)
|
||||
break;
|
||||
|
||||
fallthrough;
|
||||
|
||||
case QUEUE_HEADER_NORMAL:
|
||||
report_count = ts->buf[FW_HDR_COUNT];
|
||||
if (report_count == 0 || report_count > 3) {
|
||||
@@ -998,7 +1099,12 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
}
|
||||
|
||||
report_len = ts->buf[FW_HDR_LENGTH] / report_count;
|
||||
if (report_len != PACKET_SIZE) {
|
||||
|
||||
if (report_len == PACKET_SIZE_OLD &&
|
||||
ts->chip_id == EKTF3624) {
|
||||
dev_dbg_once(&client->dev,
|
||||
"using old report format\n");
|
||||
} else if (report_len != PACKET_SIZE) {
|
||||
dev_err(&client->dev,
|
||||
"mismatching report length: %*ph\n",
|
||||
HEADER_SIZE, ts->buf);
|
||||
@@ -1007,8 +1113,8 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
|
||||
for (i = 0; i < report_count; i++) {
|
||||
u8 *buf = ts->buf + HEADER_SIZE +
|
||||
i * PACKET_SIZE;
|
||||
elants_i2c_event(ts, buf);
|
||||
i * report_len;
|
||||
elants_i2c_event(ts, buf, report_len);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1250,6 +1356,7 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
init_completion(&ts->cmd_done);
|
||||
|
||||
ts->client = client;
|
||||
ts->chip_id = (enum elants_chip_id)id->driver_data;
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
|
||||
@@ -1331,13 +1438,20 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_PALM, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(ts->input, true, &ts->prop);
|
||||
|
||||
if (ts->chip_id == EKTF3624) {
|
||||
/* calculate resolution from size */
|
||||
ts->x_res = DIV_ROUND_CLOSEST(ts->prop.max_x, ts->phy_x);
|
||||
ts->y_res = DIV_ROUND_CLOSEST(ts->prop.max_y, ts->phy_y);
|
||||
}
|
||||
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
|
||||
if (ts->major_res > 0)
|
||||
input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res);
|
||||
|
||||
touchscreen_parse_properties(ts->input, true, &ts->prop);
|
||||
|
||||
error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error) {
|
||||
@@ -1466,14 +1580,16 @@ static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops,
|
||||
elants_i2c_suspend, elants_i2c_resume);
|
||||
|
||||
static const struct i2c_device_id elants_i2c_id[] = {
|
||||
{ DEVICE_NAME, 0 },
|
||||
{ DEVICE_NAME, EKTH3500 },
|
||||
{ "ekth3500", EKTH3500 },
|
||||
{ "ektf3624", EKTF3624 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, elants_i2c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id elants_acpi_id[] = {
|
||||
{ "ELAN0001", 0 },
|
||||
{ "ELAN0001", EKTH3500 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
|
||||
@@ -1482,6 +1598,7 @@ MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id elants_of_match[] = {
|
||||
{ .compatible = "elan,ekth3500" },
|
||||
{ .compatible = "elan,ektf3624" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, elants_of_match);
|
||||
|
||||
@@ -341,8 +341,10 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
|
||||
switch (elo->id) {
|
||||
|
||||
case 0: /* 10-byte protocol */
|
||||
if (elo_setup_10(elo))
|
||||
if (elo_setup_10(elo)) {
|
||||
err = -EIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
/*
|
||||
* Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
|
||||
*
|
||||
* Copyright (C) 2018
|
||||
* Author: Jeff LaBundy <jeff@labundy.com>
|
||||
* Copyright (C) 2018 Jeff LaBundy <jeff@labundy.com>
|
||||
*
|
||||
* These devices require firmware exported from a PC-based configuration tool
|
||||
* made available by the vendor. Firmware files may be pushed to the device's
|
||||
@@ -12,6 +11,7 @@
|
||||
* Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
#define IQS5XX_FW_FILE_LEN 64
|
||||
#define IQS5XX_NUM_RETRIES 10
|
||||
#define IQS5XX_NUM_POINTS 256
|
||||
#define IQS5XX_NUM_CONTACTS 5
|
||||
#define IQS5XX_WR_BYTES_MAX 2
|
||||
#define IQS5XX_XY_RES_MAX 0xFFFE
|
||||
|
||||
#define IQS5XX_PROD_NUM_IQS550 40
|
||||
#define IQS5XX_PROD_NUM_IQS572 58
|
||||
@@ -41,28 +41,27 @@
|
||||
#define IQS5XX_PROJ_NUM_B000 15
|
||||
#define IQS5XX_MAJOR_VER_MIN 2
|
||||
|
||||
#define IQS5XX_RESUME 0x00
|
||||
#define IQS5XX_SUSPEND 0x01
|
||||
#define IQS5XX_SHOW_RESET BIT(7)
|
||||
#define IQS5XX_ACK_RESET BIT(7)
|
||||
|
||||
#define IQS5XX_SW_INPUT_EVENT 0x10
|
||||
#define IQS5XX_SETUP_COMPLETE 0x40
|
||||
#define IQS5XX_EVENT_MODE 0x01
|
||||
#define IQS5XX_TP_EVENT 0x04
|
||||
#define IQS5XX_SUSPEND BIT(0)
|
||||
#define IQS5XX_RESUME 0
|
||||
|
||||
#define IQS5XX_FLIP_X 0x01
|
||||
#define IQS5XX_FLIP_Y 0x02
|
||||
#define IQS5XX_SWITCH_XY_AXIS 0x04
|
||||
#define IQS5XX_SETUP_COMPLETE BIT(6)
|
||||
#define IQS5XX_WDT BIT(5)
|
||||
#define IQS5XX_ALP_REATI BIT(3)
|
||||
#define IQS5XX_REATI BIT(2)
|
||||
|
||||
#define IQS5XX_TP_EVENT BIT(2)
|
||||
#define IQS5XX_EVENT_MODE BIT(0)
|
||||
|
||||
#define IQS5XX_PROD_NUM 0x0000
|
||||
#define IQS5XX_ABS_X 0x0016
|
||||
#define IQS5XX_ABS_Y 0x0018
|
||||
#define IQS5XX_SYS_INFO0 0x000F
|
||||
#define IQS5XX_SYS_INFO1 0x0010
|
||||
#define IQS5XX_SYS_CTRL0 0x0431
|
||||
#define IQS5XX_SYS_CTRL1 0x0432
|
||||
#define IQS5XX_SYS_CFG0 0x058E
|
||||
#define IQS5XX_SYS_CFG1 0x058F
|
||||
#define IQS5XX_TOTAL_RX 0x063D
|
||||
#define IQS5XX_TOTAL_TX 0x063E
|
||||
#define IQS5XX_XY_CFG0 0x0669
|
||||
#define IQS5XX_X_RES 0x066E
|
||||
#define IQS5XX_Y_RES 0x0670
|
||||
#define IQS5XX_CHKSM 0x83C0
|
||||
@@ -99,6 +98,7 @@ struct iqs5xx_private {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct touchscreen_properties prop;
|
||||
struct mutex lock;
|
||||
u8 bl_status;
|
||||
};
|
||||
@@ -126,6 +126,14 @@ struct iqs5xx_touch_data {
|
||||
u8 area;
|
||||
} __packed;
|
||||
|
||||
struct iqs5xx_status {
|
||||
u8 sys_info[2];
|
||||
u8 num_active;
|
||||
__be16 rel_x;
|
||||
__be16 rel_y;
|
||||
struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
|
||||
} __packed;
|
||||
|
||||
static int iqs5xx_read_burst(struct i2c_client *client,
|
||||
u16 reg, void *val, u16 len)
|
||||
{
|
||||
@@ -182,11 +190,6 @@ static int iqs5xx_read_word(struct i2c_client *client, u16 reg, u16 *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iqs5xx_read_byte(struct i2c_client *client, u16 reg, u8 *val)
|
||||
{
|
||||
return iqs5xx_read_burst(client, reg, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static int iqs5xx_write_burst(struct i2c_client *client,
|
||||
u16 reg, const void *val, u16 len)
|
||||
{
|
||||
@@ -337,11 +340,16 @@ static int iqs5xx_bl_open(struct i2c_client *client)
|
||||
*/
|
||||
for (i = 0; i < IQS5XX_BL_ATTEMPTS; i++) {
|
||||
iqs5xx_reset(client);
|
||||
usleep_range(350, 400);
|
||||
|
||||
for (j = 0; j < IQS5XX_NUM_RETRIES; j++) {
|
||||
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
|
||||
if (!error || error == -EINVAL)
|
||||
return error;
|
||||
if (!error)
|
||||
usleep_range(10000, 10100);
|
||||
else if (error != -EINVAL)
|
||||
continue;
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,12 +489,10 @@ static void iqs5xx_close(struct input_dev *input)
|
||||
static int iqs5xx_axis_init(struct i2c_client *client)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
struct touchscreen_properties prop;
|
||||
struct touchscreen_properties *prop = &iqs5xx->prop;
|
||||
struct input_dev *input;
|
||||
u16 max_x, max_y;
|
||||
int error;
|
||||
u16 max_x, max_x_hw;
|
||||
u16 max_y, max_y_hw;
|
||||
u8 val;
|
||||
|
||||
if (!iqs5xx->input) {
|
||||
input = devm_input_allocate_device(&client->dev);
|
||||
@@ -506,89 +512,39 @@ static int iqs5xx_axis_init(struct i2c_client *client)
|
||||
iqs5xx->input = input;
|
||||
}
|
||||
|
||||
touchscreen_parse_properties(iqs5xx->input, true, &prop);
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_TOTAL_RX, &val);
|
||||
if (error)
|
||||
return error;
|
||||
max_x_hw = (val - 1) * IQS5XX_NUM_POINTS;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_TOTAL_TX, &val);
|
||||
if (error)
|
||||
return error;
|
||||
max_y_hw = (val - 1) * IQS5XX_NUM_POINTS;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_XY_CFG0, &val);
|
||||
error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val & IQS5XX_SWITCH_XY_AXIS)
|
||||
swap(max_x_hw, max_y_hw);
|
||||
|
||||
if (prop.swap_x_y)
|
||||
val ^= IQS5XX_SWITCH_XY_AXIS;
|
||||
|
||||
if (prop.invert_x)
|
||||
val ^= prop.swap_x_y ? IQS5XX_FLIP_Y : IQS5XX_FLIP_X;
|
||||
|
||||
if (prop.invert_y)
|
||||
val ^= prop.swap_x_y ? IQS5XX_FLIP_X : IQS5XX_FLIP_Y;
|
||||
|
||||
error = iqs5xx_write_byte(client, IQS5XX_XY_CFG0, val);
|
||||
error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (prop.max_x > max_x_hw) {
|
||||
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x);
|
||||
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y);
|
||||
|
||||
touchscreen_parse_properties(iqs5xx->input, true, prop);
|
||||
|
||||
if (prop->max_x > IQS5XX_XY_RES_MAX) {
|
||||
dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
|
||||
prop.max_x, max_x_hw);
|
||||
prop->max_x, IQS5XX_XY_RES_MAX);
|
||||
return -EINVAL;
|
||||
} else if (prop.max_x == 0) {
|
||||
error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
|
||||
} else if (prop->max_x != max_x) {
|
||||
error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_abs_set_max(iqs5xx->input,
|
||||
prop.swap_x_y ? ABS_MT_POSITION_Y :
|
||||
ABS_MT_POSITION_X,
|
||||
max_x);
|
||||
} else {
|
||||
max_x = (u16)prop.max_x;
|
||||
}
|
||||
|
||||
if (prop.max_y > max_y_hw) {
|
||||
if (prop->max_y > IQS5XX_XY_RES_MAX) {
|
||||
dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
|
||||
prop.max_y, max_y_hw);
|
||||
prop->max_y, IQS5XX_XY_RES_MAX);
|
||||
return -EINVAL;
|
||||
} else if (prop.max_y == 0) {
|
||||
error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
|
||||
} else if (prop->max_y != max_y) {
|
||||
error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_abs_set_max(iqs5xx->input,
|
||||
prop.swap_x_y ? ABS_MT_POSITION_X :
|
||||
ABS_MT_POSITION_Y,
|
||||
max_y);
|
||||
} else {
|
||||
max_y = (u16)prop.max_y;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write horizontal and vertical resolution to the device in case its
|
||||
* original defaults were overridden or swapped as per the properties
|
||||
* specified in the device tree.
|
||||
*/
|
||||
error = iqs5xx_write_word(client,
|
||||
prop.swap_x_y ? IQS5XX_Y_RES : IQS5XX_X_RES,
|
||||
max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = iqs5xx_write_word(client,
|
||||
prop.swap_x_y ? IQS5XX_X_RES : IQS5XX_Y_RES,
|
||||
max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS,
|
||||
INPUT_MT_DIRECT);
|
||||
if (error)
|
||||
@@ -603,7 +559,6 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
struct iqs5xx_dev_id_info *dev_id_info;
|
||||
int error;
|
||||
u8 val;
|
||||
u8 buf[sizeof(*dev_id_info) + 1];
|
||||
|
||||
error = iqs5xx_read_burst(client, IQS5XX_PROD_NUM,
|
||||
@@ -666,18 +621,18 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL0, IQS5XX_ACK_RESET);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
val |= IQS5XX_SETUP_COMPLETE;
|
||||
val &= ~IQS5XX_SW_INPUT_EVENT;
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0,
|
||||
IQS5XX_SETUP_COMPLETE | IQS5XX_WDT |
|
||||
IQS5XX_ALP_REATI | IQS5XX_REATI);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
val = IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE;
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1,
|
||||
IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -688,13 +643,12 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
iqs5xx->bl_status = dev_id_info->bl_status;
|
||||
|
||||
/*
|
||||
* Closure of the first communication window that appears following the
|
||||
* release of reset appears to kick off an initialization period during
|
||||
* which further communication is met with clock stretching. The return
|
||||
* from this function is delayed so that further communication attempts
|
||||
* avoid this period.
|
||||
* The following delay allows ATI to complete before the open and close
|
||||
* callbacks are free to elicit I2C communication. Any attempts to read
|
||||
* from or write to the device during this time may face extended clock
|
||||
* stretching and prompt the I2C controller to report an error.
|
||||
*/
|
||||
msleep(100);
|
||||
msleep(250);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -702,7 +656,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
static irqreturn_t iqs5xx_irq(int irq, void *data)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = data;
|
||||
struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
|
||||
struct iqs5xx_status status;
|
||||
struct i2c_client *client = iqs5xx->client;
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error, i;
|
||||
@@ -715,21 +669,35 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
|
||||
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
|
||||
return IRQ_NONE;
|
||||
|
||||
error = iqs5xx_read_burst(client, IQS5XX_ABS_X,
|
||||
touch_data, sizeof(touch_data));
|
||||
error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
|
||||
&status, sizeof(status));
|
||||
if (error)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(touch_data); i++) {
|
||||
u16 pressure = be16_to_cpu(touch_data[i].strength);
|
||||
if (status.sys_info[0] & IQS5XX_SHOW_RESET) {
|
||||
dev_err(&client->dev, "Unexpected device reset\n");
|
||||
|
||||
error = iqs5xx_dev_init(client);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to re-initialize device: %d\n", error);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status.touch_data); i++) {
|
||||
struct iqs5xx_touch_data *touch_data = &status.touch_data[i];
|
||||
u16 pressure = be16_to_cpu(touch_data->strength);
|
||||
|
||||
input_mt_slot(input, i);
|
||||
if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
|
||||
pressure != 0)) {
|
||||
input_report_abs(input, ABS_MT_POSITION_X,
|
||||
be16_to_cpu(touch_data[i].abs_x));
|
||||
input_report_abs(input, ABS_MT_POSITION_Y,
|
||||
be16_to_cpu(touch_data[i].abs_y));
|
||||
touchscreen_report_pos(iqs5xx->input, &iqs5xx->prop,
|
||||
be16_to_cpu(touch_data->abs_x),
|
||||
be16_to_cpu(touch_data->abs_y),
|
||||
true);
|
||||
input_report_abs(input, ABS_MT_PRESSURE, pressure);
|
||||
}
|
||||
}
|
||||
@@ -884,7 +852,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
|
||||
static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
int error;
|
||||
int error, error_bl = 0;
|
||||
u8 *pmap;
|
||||
|
||||
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
|
||||
@@ -938,6 +906,7 @@ err_reset:
|
||||
usleep_range(10000, 10100);
|
||||
}
|
||||
|
||||
error_bl = error;
|
||||
error = iqs5xx_dev_init(client);
|
||||
if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
|
||||
error = -EINVAL;
|
||||
@@ -949,11 +918,15 @@ err_reset:
|
||||
err_kfree:
|
||||
kfree(pmap);
|
||||
|
||||
if (error_bl)
|
||||
return error_bl;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t fw_file_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = iqs5xx->client;
|
||||
@@ -1012,7 +985,7 @@ static int __maybe_unused iqs5xx_suspend(struct device *dev)
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error = 0;
|
||||
|
||||
if (!input)
|
||||
if (!input || device_may_wakeup(dev))
|
||||
return error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
@@ -1031,7 +1004,7 @@ static int __maybe_unused iqs5xx_resume(struct device *dev)
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error = 0;
|
||||
|
||||
if (!input)
|
||||
if (!input || device_may_wakeup(dev))
|
||||
return error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
@@ -465,13 +465,13 @@ static void mip4_report_keys(struct mip4_ts *ts, u8 *packet)
|
||||
static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
|
||||
{
|
||||
int id;
|
||||
bool hover;
|
||||
bool palm;
|
||||
bool __always_unused hover;
|
||||
bool __always_unused palm;
|
||||
bool state;
|
||||
u16 x, y;
|
||||
u8 pressure_stage = 0;
|
||||
u8 __always_unused pressure_stage = 0;
|
||||
u8 pressure;
|
||||
u8 size;
|
||||
u8 __always_unused size;
|
||||
u8 touch_major;
|
||||
u8 touch_minor;
|
||||
|
||||
|
||||
@@ -445,6 +445,7 @@ static int raydium_i2c_write_object(struct i2c_client *client,
|
||||
enum raydium_bl_ack state)
|
||||
{
|
||||
int error;
|
||||
static const u8 cmd[] = { 0xFF, 0x39 };
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
|
||||
if (error) {
|
||||
@@ -453,7 +454,7 @@ static int raydium_i2c_write_object(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, cmd, sizeof(cmd));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Ack obj command failed: %d\n", error);
|
||||
return error;
|
||||
|
||||
@@ -94,8 +94,13 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts)
|
||||
|
||||
for (retries = 10; retries; retries--) {
|
||||
error = st1232_ts_read_data(ts, REG_STATUS, 1);
|
||||
if (!error && ts->read_buf[0] == (STATUS_NORMAL | ERROR_NONE))
|
||||
return 0;
|
||||
if (!error) {
|
||||
switch (ts->read_buf[0]) {
|
||||
case STATUS_NORMAL | ERROR_NONE:
|
||||
case STATUS_IDLE | ERROR_NONE:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
* @idev: registered input device
|
||||
* @work: a work item used to scan the device
|
||||
* @dev: a pointer back to the MFD cell struct device*
|
||||
* @prop: Touchscreen properties
|
||||
* @ave_ctrl: Sample average control
|
||||
* (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
|
||||
* @touch_det_delay: Touch detect interrupt delay
|
||||
|
||||
@@ -787,6 +787,7 @@ static int sur40_probe(struct usb_interface *interface,
|
||||
dev_err(&interface->dev,
|
||||
"Unable to register video controls.");
|
||||
v4l2_ctrl_handler_free(&sur40->hdl);
|
||||
error = sur40->hdl.error;
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,9 +94,7 @@ static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
|
||||
|
||||
static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
|
||||
{
|
||||
u16 timestamp;
|
||||
unsigned int i;
|
||||
timestamp = get_unaligned_le16(&data[15]);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
struct surface3_ts_data_finger *finger;
|
||||
|
||||
@@ -1044,6 +1044,7 @@ static void nexio_exit(struct usbtouch_usb *usbtouch)
|
||||
|
||||
static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
|
||||
{
|
||||
struct device *dev = &usbtouch->interface->dev;
|
||||
struct nexio_touch_packet *packet = (void *) pkt;
|
||||
struct nexio_priv *priv = usbtouch->priv;
|
||||
unsigned int data_len = be16_to_cpu(packet->data_len);
|
||||
@@ -1062,6 +1063,8 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
|
||||
|
||||
/* send ACK */
|
||||
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to submit ACK URB: %d\n", ret);
|
||||
|
||||
if (!usbtouch->type->max_xc) {
|
||||
usbtouch->type->max_xc = 2 * x_len;
|
||||
|
||||
@@ -161,7 +161,7 @@ static int zinitix_read_data(struct i2c_client *client,
|
||||
|
||||
ret = i2c_master_recv(client, (u8 *)values, length);
|
||||
if (ret != length)
|
||||
return ret < 0 ? ret : -EIO; ;
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ static int zinitix_write_cmd(struct i2c_client *client, u16 reg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int i;
|
||||
|
||||
@@ -1230,6 +1230,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length,
|
||||
|
||||
gfs2_inplace_release(ip);
|
||||
|
||||
if (ip->i_qadata && ip->i_qadata->qa_qd_num)
|
||||
gfs2_quota_unlock(ip);
|
||||
|
||||
if (length != written && (iomap->flags & IOMAP_F_NEW)) {
|
||||
/* Deallocate blocks that were just allocated. */
|
||||
loff_t blockmask = i_blocksize(inode) - 1;
|
||||
@@ -1242,9 +1245,6 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length,
|
||||
}
|
||||
}
|
||||
|
||||
if (ip->i_qadata && ip->i_qadata->qa_qd_num)
|
||||
gfs2_quota_unlock(ip);
|
||||
|
||||
if (unlikely(!written))
|
||||
goto out_unlock;
|
||||
|
||||
@@ -1538,13 +1538,13 @@ more_rgrps:
|
||||
goto out;
|
||||
}
|
||||
ret = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
0, rd_gh);
|
||||
LM_FLAG_NODE_SCOPE, rd_gh);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Must be done with the rgrp glock held: */
|
||||
if (gfs2_rs_active(&ip->i_res) &&
|
||||
rgd == ip->i_res.rs_rbm.rgd)
|
||||
rgd == ip->i_res.rs_rgd)
|
||||
gfs2_rs_deltree(&ip->i_res);
|
||||
}
|
||||
|
||||
|
||||
@@ -716,10 +716,10 @@ static int gfs2_release(struct inode *inode, struct file *file)
|
||||
kfree(file->private_data);
|
||||
file->private_data = NULL;
|
||||
|
||||
if (file->f_mode & FMODE_WRITE) {
|
||||
if (gfs2_rs_active(&ip->i_res))
|
||||
gfs2_rs_delete(ip, &inode->i_writecount);
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
gfs2_qa_put(ip);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1112,8 +1112,8 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
|
||||
goto out_qunlock;
|
||||
|
||||
/* check if the selected rgrp limits our max_blks further */
|
||||
if (ap.allowed && ap.allowed < max_blks)
|
||||
max_blks = ap.allowed;
|
||||
if (ip->i_res.rs_reserved < max_blks)
|
||||
max_blks = ip->i_res.rs_reserved;
|
||||
|
||||
/* Almost done. Calculate bytes that can be written using
|
||||
* max_blks. We also recompute max_bytes, data_blocks and
|
||||
|
||||
@@ -313,9 +313,23 @@ void gfs2_glock_put(struct gfs2_glock *gl)
|
||||
static inline int may_grant(const struct gfs2_glock *gl, const struct gfs2_holder *gh)
|
||||
{
|
||||
const struct gfs2_holder *gh_head = list_first_entry(&gl->gl_holders, const struct gfs2_holder, gh_list);
|
||||
if ((gh->gh_state == LM_ST_EXCLUSIVE ||
|
||||
gh_head->gh_state == LM_ST_EXCLUSIVE) && gh != gh_head)
|
||||
return 0;
|
||||
|
||||
if (gh != gh_head) {
|
||||
/**
|
||||
* Here we make a special exception to grant holders who agree
|
||||
* to share the EX lock with other holders who also have the
|
||||
* bit set. If the original holder has the LM_FLAG_NODE_SCOPE bit
|
||||
* is set, we grant more holders with the bit set.
|
||||
*/
|
||||
if (gh_head->gh_state == LM_ST_EXCLUSIVE &&
|
||||
(gh_head->gh_flags & LM_FLAG_NODE_SCOPE) &&
|
||||
gh->gh_state == LM_ST_EXCLUSIVE &&
|
||||
(gh->gh_flags & LM_FLAG_NODE_SCOPE))
|
||||
return 1;
|
||||
if ((gh->gh_state == LM_ST_EXCLUSIVE ||
|
||||
gh_head->gh_state == LM_ST_EXCLUSIVE))
|
||||
return 0;
|
||||
}
|
||||
if (gl->gl_state == gh->gh_state)
|
||||
return 1;
|
||||
if (gh->gh_flags & GL_EXACT)
|
||||
@@ -2030,6 +2044,8 @@ static const char *hflags2str(char *buf, u16 flags, unsigned long iflags)
|
||||
*p++ = 'A';
|
||||
if (flags & LM_FLAG_PRIORITY)
|
||||
*p++ = 'p';
|
||||
if (flags & LM_FLAG_NODE_SCOPE)
|
||||
*p++ = 'n';
|
||||
if (flags & GL_ASYNC)
|
||||
*p++ = 'a';
|
||||
if (flags & GL_EXACT)
|
||||
|
||||
@@ -75,6 +75,11 @@ enum {
|
||||
* request and directly join the other shared lock. A shared lock request
|
||||
* without the priority flag might be forced to wait until the deferred
|
||||
* requested had acquired and released the lock.
|
||||
*
|
||||
* LM_FLAG_NODE_SCOPE
|
||||
* This holder agrees to share the lock within this node. In other words,
|
||||
* the glock is held in EX mode according to DLM, but local holders on the
|
||||
* same node can share it.
|
||||
*/
|
||||
|
||||
#define LM_FLAG_TRY 0x0001
|
||||
@@ -82,6 +87,7 @@ enum {
|
||||
#define LM_FLAG_NOEXP 0x0004
|
||||
#define LM_FLAG_ANY 0x0008
|
||||
#define LM_FLAG_PRIORITY 0x0010
|
||||
#define LM_FLAG_NODE_SCOPE 0x0020
|
||||
#define GL_ASYNC 0x0040
|
||||
#define GL_EXACT 0x0080
|
||||
#define GL_SKIP 0x0100
|
||||
|
||||
@@ -86,16 +86,12 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl)
|
||||
{
|
||||
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
|
||||
struct gfs2_trans tr;
|
||||
unsigned int revokes;
|
||||
int ret;
|
||||
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
INIT_LIST_HEAD(&tr.tr_buf);
|
||||
INIT_LIST_HEAD(&tr.tr_databuf);
|
||||
INIT_LIST_HEAD(&tr.tr_ail1_list);
|
||||
INIT_LIST_HEAD(&tr.tr_ail2_list);
|
||||
tr.tr_revokes = atomic_read(&gl->gl_ail_count);
|
||||
revokes = atomic_read(&gl->gl_ail_count);
|
||||
|
||||
if (!tr.tr_revokes) {
|
||||
if (!revokes) {
|
||||
bool have_revokes;
|
||||
bool log_in_flight;
|
||||
|
||||
@@ -122,20 +118,14 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A shortened, inline version of gfs2_trans_begin()
|
||||
* tr->alloced is not set since the transaction structure is
|
||||
* on the stack */
|
||||
tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes);
|
||||
tr.tr_ip = _RET_IP_;
|
||||
ret = gfs2_log_reserve(sdp, tr.tr_reserved);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
WARN_ON_ONCE(current->journal_info);
|
||||
current->journal_info = &tr;
|
||||
|
||||
__gfs2_ail_flush(gl, 0, tr.tr_revokes);
|
||||
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
set_bit(TR_ONSTACK, &tr.tr_flags);
|
||||
ret = __gfs2_trans_begin(&tr, sdp, 0, revokes, _RET_IP_);
|
||||
if (ret)
|
||||
goto flush;
|
||||
__gfs2_ail_flush(gl, 0, revokes);
|
||||
gfs2_trans_end(sdp);
|
||||
|
||||
flush:
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_AIL_EMPTY_GL);
|
||||
@@ -146,19 +136,15 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
|
||||
{
|
||||
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
|
||||
unsigned int revokes = atomic_read(&gl->gl_ail_count);
|
||||
unsigned int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64);
|
||||
int ret;
|
||||
|
||||
if (!revokes)
|
||||
return;
|
||||
|
||||
while (revokes > max_revokes)
|
||||
max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64);
|
||||
|
||||
ret = gfs2_trans_begin(sdp, 0, max_revokes);
|
||||
ret = gfs2_trans_begin(sdp, 0, revokes);
|
||||
if (ret)
|
||||
return;
|
||||
__gfs2_ail_flush(gl, fsync, max_revokes);
|
||||
__gfs2_ail_flush(gl, fsync, revokes);
|
||||
gfs2_trans_end(sdp);
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_AIL_FLUSH);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/lockref.h>
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define DIO_WAIT 0x00000010
|
||||
#define DIO_METADATA 0x00000020
|
||||
@@ -106,7 +107,8 @@ struct gfs2_rgrpd {
|
||||
u32 rd_data; /* num of data blocks in rgrp */
|
||||
u32 rd_bitbytes; /* number of bytes in data bitmaps */
|
||||
u32 rd_free;
|
||||
u32 rd_reserved; /* number of blocks reserved */
|
||||
u32 rd_requested; /* number of blocks in rd_rstree */
|
||||
u32 rd_reserved; /* number of reserved blocks */
|
||||
u32 rd_free_clone;
|
||||
u32 rd_dinodes;
|
||||
u64 rd_igeneration;
|
||||
@@ -122,34 +124,10 @@ struct gfs2_rgrpd {
|
||||
#define GFS2_RDF_PREFERRED 0x80000000 /* This rgrp is preferred */
|
||||
#define GFS2_RDF_MASK 0xf0000000 /* mask for internal flags */
|
||||
spinlock_t rd_rsspin; /* protects reservation related vars */
|
||||
struct mutex rd_mutex;
|
||||
struct rb_root rd_rstree; /* multi-block reservation tree */
|
||||
};
|
||||
|
||||
struct gfs2_rbm {
|
||||
struct gfs2_rgrpd *rgd;
|
||||
u32 offset; /* The offset is bitmap relative */
|
||||
int bii; /* Bitmap index */
|
||||
};
|
||||
|
||||
static inline struct gfs2_bitmap *rbm_bi(const struct gfs2_rbm *rbm)
|
||||
{
|
||||
return rbm->rgd->rd_bits + rbm->bii;
|
||||
}
|
||||
|
||||
static inline u64 gfs2_rbm_to_block(const struct gfs2_rbm *rbm)
|
||||
{
|
||||
BUG_ON(rbm->offset >= rbm->rgd->rd_data);
|
||||
return rbm->rgd->rd_data0 + (rbm_bi(rbm)->bi_start * GFS2_NBBY) +
|
||||
rbm->offset;
|
||||
}
|
||||
|
||||
static inline bool gfs2_rbm_eq(const struct gfs2_rbm *rbm1,
|
||||
const struct gfs2_rbm *rbm2)
|
||||
{
|
||||
return (rbm1->rgd == rbm2->rgd) && (rbm1->bii == rbm2->bii) &&
|
||||
(rbm1->offset == rbm2->offset);
|
||||
}
|
||||
|
||||
enum gfs2_state_bits {
|
||||
BH_Pinned = BH_PrivateStart,
|
||||
BH_Escaped = BH_PrivateStart + 1,
|
||||
@@ -313,9 +291,11 @@ struct gfs2_qadata { /* quota allocation data */
|
||||
*/
|
||||
|
||||
struct gfs2_blkreserv {
|
||||
struct rb_node rs_node; /* link to other block reservations */
|
||||
struct gfs2_rbm rs_rbm; /* Start of reservation */
|
||||
u32 rs_free; /* how many blocks are still free */
|
||||
struct rb_node rs_node; /* node within rd_rstree */
|
||||
struct gfs2_rgrpd *rs_rgd;
|
||||
u64 rs_start;
|
||||
u32 rs_requested;
|
||||
u32 rs_reserved; /* number of reserved blocks */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -490,7 +470,7 @@ struct gfs2_quota_data {
|
||||
enum {
|
||||
TR_TOUCHED = 1,
|
||||
TR_ATTACHED = 2,
|
||||
TR_ALLOCED = 3,
|
||||
TR_ONSTACK = 3,
|
||||
};
|
||||
|
||||
struct gfs2_trans {
|
||||
@@ -506,7 +486,6 @@ struct gfs2_trans {
|
||||
unsigned int tr_num_buf_rm;
|
||||
unsigned int tr_num_databuf_rm;
|
||||
unsigned int tr_num_revoke;
|
||||
unsigned int tr_num_revoke_rm;
|
||||
|
||||
struct list_head tr_list;
|
||||
struct list_head tr_databuf;
|
||||
@@ -531,6 +510,7 @@ struct gfs2_jdesc {
|
||||
unsigned int nr_extents;
|
||||
struct work_struct jd_work;
|
||||
struct inode *jd_inode;
|
||||
struct bio *jd_log_bio;
|
||||
unsigned long jd_flags;
|
||||
#define JDF_RECOVERY 1
|
||||
unsigned int jd_jid;
|
||||
@@ -585,6 +565,7 @@ struct gfs2_args {
|
||||
unsigned int ar_errors:2; /* errors=withdraw | panic */
|
||||
unsigned int ar_nobarrier:1; /* do not send barriers */
|
||||
unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */
|
||||
unsigned int ar_got_rgrplvb:1; /* Was the rgrplvb opt given? */
|
||||
unsigned int ar_loccookie:1; /* use location based readdir
|
||||
cookies */
|
||||
s32 ar_commit; /* Commit interval */
|
||||
@@ -821,7 +802,6 @@ struct gfs2_sbd {
|
||||
|
||||
struct gfs2_trans *sd_log_tr;
|
||||
unsigned int sd_log_blks_reserved;
|
||||
int sd_log_committed_revoke;
|
||||
|
||||
atomic_t sd_log_pinned;
|
||||
unsigned int sd_log_num_revoke;
|
||||
@@ -834,24 +814,22 @@ struct gfs2_sbd {
|
||||
atomic_t sd_log_thresh2;
|
||||
atomic_t sd_log_blks_free;
|
||||
atomic_t sd_log_blks_needed;
|
||||
atomic_t sd_log_revokes_available;
|
||||
wait_queue_head_t sd_log_waitq;
|
||||
wait_queue_head_t sd_logd_waitq;
|
||||
|
||||
u64 sd_log_sequence;
|
||||
unsigned int sd_log_head;
|
||||
unsigned int sd_log_tail;
|
||||
int sd_log_idle;
|
||||
|
||||
struct rw_semaphore sd_log_flush_lock;
|
||||
atomic_t sd_log_in_flight;
|
||||
struct bio *sd_log_bio;
|
||||
wait_queue_head_t sd_log_flush_wait;
|
||||
int sd_log_error; /* First log error */
|
||||
wait_queue_head_t sd_withdraw_wait;
|
||||
|
||||
atomic_t sd_reserving_log;
|
||||
wait_queue_head_t sd_reserving_log_wait;
|
||||
|
||||
unsigned int sd_log_tail;
|
||||
unsigned int sd_log_flush_tail;
|
||||
unsigned int sd_log_head;
|
||||
unsigned int sd_log_flush_head;
|
||||
|
||||
spinlock_t sd_ail_lock;
|
||||
|
||||
@@ -1147,7 +1147,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
|
||||
if (!rgd)
|
||||
goto out_inodes;
|
||||
|
||||
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
|
||||
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, LM_FLAG_NODE_SCOPE, ghs + 2);
|
||||
|
||||
|
||||
error = gfs2_glock_nq(ghs); /* parent */
|
||||
@@ -1453,8 +1453,8 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
||||
error = -ENOENT;
|
||||
goto out_gunlock;
|
||||
}
|
||||
error = gfs2_glock_nq_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0,
|
||||
&rd_gh);
|
||||
error = gfs2_glock_nq_init(nrgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &rd_gh);
|
||||
if (error)
|
||||
goto out_gunlock;
|
||||
}
|
||||
|
||||
@@ -284,7 +284,6 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
|
||||
{
|
||||
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
|
||||
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
||||
int lvb_needs_unlock = 0;
|
||||
int error;
|
||||
|
||||
if (gl->gl_lksb.sb_lkid == 0) {
|
||||
@@ -297,13 +296,10 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
|
||||
gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT);
|
||||
gfs2_update_request_times(gl);
|
||||
|
||||
/* don't want to skip dlm_unlock writing the lvb when lock is ex */
|
||||
|
||||
if (gl->gl_lksb.sb_lvbptr && (gl->gl_state == LM_ST_EXCLUSIVE))
|
||||
lvb_needs_unlock = 1;
|
||||
/* don't want to skip dlm_unlock writing the lvb when lock has one */
|
||||
|
||||
if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) &&
|
||||
!lvb_needs_unlock) {
|
||||
!gl->gl_lksb.sb_lvbptr) {
|
||||
gfs2_glock_free(gl);
|
||||
return;
|
||||
}
|
||||
|
||||
531
fs/gfs2/log.c
531
fs/gfs2/log.c
@@ -50,10 +50,12 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct)
|
||||
unsigned int blks;
|
||||
unsigned int first, second;
|
||||
|
||||
/* The initial struct gfs2_log_descriptor block */
|
||||
blks = 1;
|
||||
first = sdp->sd_ldptrs;
|
||||
|
||||
if (nstruct > first) {
|
||||
/* Subsequent struct gfs2_meta_header blocks */
|
||||
second = sdp->sd_inptrs;
|
||||
blks += DIV_ROUND_UP(nstruct - first, second);
|
||||
}
|
||||
@@ -89,7 +91,7 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
|
||||
|
||||
static int gfs2_ail1_start_one(struct gfs2_sbd *sdp,
|
||||
struct writeback_control *wbc,
|
||||
struct gfs2_trans *tr)
|
||||
struct gfs2_trans *tr, struct blk_plug *plug)
|
||||
__releases(&sdp->sd_ail_lock)
|
||||
__acquires(&sdp->sd_ail_lock)
|
||||
{
|
||||
@@ -131,6 +133,11 @@ __acquires(&sdp->sd_ail_lock)
|
||||
continue;
|
||||
spin_unlock(&sdp->sd_ail_lock);
|
||||
ret = generic_writepages(mapping, wbc);
|
||||
if (need_resched()) {
|
||||
blk_finish_plug(plug);
|
||||
cond_resched();
|
||||
blk_start_plug(plug);
|
||||
}
|
||||
spin_lock(&sdp->sd_ail_lock);
|
||||
if (ret == -ENODATA) /* if a jdata write into a new hole */
|
||||
ret = 0; /* ignore it */
|
||||
@@ -205,7 +212,7 @@ restart:
|
||||
list_for_each_entry_reverse(tr, head, tr_list) {
|
||||
if (wbc->nr_to_write <= 0)
|
||||
break;
|
||||
ret = gfs2_ail1_start_one(sdp, wbc, tr);
|
||||
ret = gfs2_ail1_start_one(sdp, wbc, tr, &plug);
|
||||
if (ret) {
|
||||
if (ret == -EBUSY)
|
||||
goto restart;
|
||||
@@ -240,6 +247,45 @@ static void gfs2_ail1_start(struct gfs2_sbd *sdp)
|
||||
return gfs2_ail1_flush(sdp, &wbc);
|
||||
}
|
||||
|
||||
static void gfs2_log_update_flush_tail(struct gfs2_sbd *sdp)
|
||||
{
|
||||
unsigned int new_flush_tail = sdp->sd_log_head;
|
||||
struct gfs2_trans *tr;
|
||||
|
||||
if (!list_empty(&sdp->sd_ail1_list)) {
|
||||
tr = list_last_entry(&sdp->sd_ail1_list,
|
||||
struct gfs2_trans, tr_list);
|
||||
new_flush_tail = tr->tr_first;
|
||||
}
|
||||
sdp->sd_log_flush_tail = new_flush_tail;
|
||||
}
|
||||
|
||||
static void gfs2_log_update_head(struct gfs2_sbd *sdp)
|
||||
{
|
||||
unsigned int new_head = sdp->sd_log_flush_head;
|
||||
|
||||
if (sdp->sd_log_flush_tail == sdp->sd_log_head)
|
||||
sdp->sd_log_flush_tail = new_head;
|
||||
sdp->sd_log_head = new_head;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_ail_empty_tr - empty one of the ail lists of a transaction
|
||||
*/
|
||||
|
||||
static void gfs2_ail_empty_tr(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct gfs2_bufdata *bd;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
bd = list_first_entry(head, struct gfs2_bufdata,
|
||||
bd_ail_st_list);
|
||||
gfs2_assert(sdp, bd->bd_tr == tr);
|
||||
gfs2_remove_from_ail(bd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced
|
||||
* @sdp: the filesystem
|
||||
@@ -315,6 +361,7 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int max_revokes)
|
||||
else
|
||||
oldest_tr = 0;
|
||||
}
|
||||
gfs2_log_update_flush_tail(sdp);
|
||||
ret = list_empty(&sdp->sd_ail1_list);
|
||||
spin_unlock(&sdp->sd_ail_lock);
|
||||
|
||||
@@ -348,49 +395,71 @@ static void gfs2_ail1_wait(struct gfs2_sbd *sdp)
|
||||
spin_unlock(&sdp->sd_ail_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_ail_empty_tr - empty one of the ail lists for a transaction
|
||||
*/
|
||||
|
||||
static void gfs2_ail_empty_tr(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
struct list_head *head)
|
||||
static void __ail2_empty(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
|
||||
{
|
||||
struct gfs2_bufdata *bd;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
bd = list_first_entry(head, struct gfs2_bufdata,
|
||||
bd_ail_st_list);
|
||||
gfs2_assert(sdp, bd->bd_tr == tr);
|
||||
gfs2_remove_from_ail(bd);
|
||||
}
|
||||
gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
|
||||
list_del(&tr->tr_list);
|
||||
gfs2_assert_warn(sdp, list_empty(&tr->tr_ail1_list));
|
||||
gfs2_assert_warn(sdp, list_empty(&tr->tr_ail2_list));
|
||||
gfs2_trans_free(sdp, tr);
|
||||
}
|
||||
|
||||
static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
|
||||
{
|
||||
struct gfs2_trans *tr, *safe;
|
||||
struct list_head *ail2_list = &sdp->sd_ail2_list;
|
||||
unsigned int old_tail = sdp->sd_log_tail;
|
||||
int wrap = (new_tail < old_tail);
|
||||
int a, b, rm;
|
||||
struct gfs2_trans *tr, *safe;
|
||||
|
||||
spin_lock(&sdp->sd_ail_lock);
|
||||
|
||||
list_for_each_entry_safe(tr, safe, &sdp->sd_ail2_list, tr_list) {
|
||||
a = (old_tail <= tr->tr_first);
|
||||
b = (tr->tr_first < new_tail);
|
||||
rm = (wrap) ? (a || b) : (a && b);
|
||||
if (!rm)
|
||||
continue;
|
||||
|
||||
gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
|
||||
list_del(&tr->tr_list);
|
||||
gfs2_assert_warn(sdp, list_empty(&tr->tr_ail1_list));
|
||||
gfs2_assert_warn(sdp, list_empty(&tr->tr_ail2_list));
|
||||
gfs2_trans_free(sdp, tr);
|
||||
if (old_tail <= new_tail) {
|
||||
list_for_each_entry_safe(tr, safe, ail2_list, tr_list) {
|
||||
if (old_tail <= tr->tr_first && tr->tr_first < new_tail)
|
||||
__ail2_empty(sdp, tr);
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry_safe(tr, safe, ail2_list, tr_list) {
|
||||
if (old_tail <= tr->tr_first || tr->tr_first < new_tail)
|
||||
__ail2_empty(sdp, tr);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&sdp->sd_ail_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_log_is_empty - Check if the log is empty
|
||||
* @sdp: The GFS2 superblock
|
||||
*/
|
||||
|
||||
bool gfs2_log_is_empty(struct gfs2_sbd *sdp) {
|
||||
return atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks;
|
||||
}
|
||||
|
||||
static bool __gfs2_log_try_reserve_revokes(struct gfs2_sbd *sdp, unsigned int revokes)
|
||||
{
|
||||
unsigned int available;
|
||||
|
||||
available = atomic_read(&sdp->sd_log_revokes_available);
|
||||
while (available >= revokes) {
|
||||
if (atomic_try_cmpxchg(&sdp->sd_log_revokes_available,
|
||||
&available, available - revokes))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_log_release_revokes - Release a given number of revokes
|
||||
* @sdp: The GFS2 superblock
|
||||
* @revokes: The number of revokes to release
|
||||
*
|
||||
* sdp->sd_log_flush_lock must be held.
|
||||
*/
|
||||
void gfs2_log_release_revokes(struct gfs2_sbd *sdp, unsigned int revokes)
|
||||
{
|
||||
if (revokes)
|
||||
atomic_add(revokes, &sdp->sd_log_revokes_available);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_log_release - Release a given number of log blocks
|
||||
* @sdp: The GFS2 superblock
|
||||
@@ -400,86 +469,141 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
|
||||
|
||||
void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
|
||||
{
|
||||
|
||||
atomic_add(blks, &sdp->sd_log_blks_free);
|
||||
trace_gfs2_log_blocks(sdp, blks);
|
||||
gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
|
||||
sdp->sd_jdesc->jd_blocks);
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
if (atomic_read(&sdp->sd_log_blks_needed))
|
||||
wake_up(&sdp->sd_log_waitq);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_log_reserve - Make a log reservation
|
||||
* __gfs2_log_try_reserve - Try to make a log reservation
|
||||
* @sdp: The GFS2 superblock
|
||||
* @blks: The number of blocks to reserve
|
||||
* @taboo_blks: The number of blocks to leave free
|
||||
*
|
||||
* Note that we never give out the last few blocks of the journal. Thats
|
||||
* due to the fact that there is a small number of header blocks
|
||||
* associated with each log flush. The exact number can't be known until
|
||||
* flush time, so we ensure that we have just enough free blocks at all
|
||||
* times to avoid running out during a log flush.
|
||||
* Try to do the same as __gfs2_log_reserve(), but fail if no more log
|
||||
* space is immediately available.
|
||||
*/
|
||||
static bool __gfs2_log_try_reserve(struct gfs2_sbd *sdp, unsigned int blks,
|
||||
unsigned int taboo_blks)
|
||||
{
|
||||
unsigned wanted = blks + taboo_blks;
|
||||
unsigned int free_blocks;
|
||||
|
||||
free_blocks = atomic_read(&sdp->sd_log_blks_free);
|
||||
while (free_blocks >= wanted) {
|
||||
if (atomic_try_cmpxchg(&sdp->sd_log_blks_free, &free_blocks,
|
||||
free_blocks - blks)) {
|
||||
trace_gfs2_log_blocks(sdp, -blks);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* __gfs2_log_reserve - Make a log reservation
|
||||
* @sdp: The GFS2 superblock
|
||||
* @blks: The number of blocks to reserve
|
||||
* @taboo_blks: The number of blocks to leave free
|
||||
*
|
||||
* @taboo_blks is set to 0 for logd, and to GFS2_LOG_FLUSH_MIN_BLOCKS
|
||||
* for all other processes. This ensures that when the log is almost full,
|
||||
* logd will still be able to call gfs2_log_flush one more time without
|
||||
* blocking, which will advance the tail and make some more log space
|
||||
* available.
|
||||
*
|
||||
* We no longer flush the log here, instead we wake up logd to do that
|
||||
* for us. To avoid the thundering herd and to ensure that we deal fairly
|
||||
* with queued waiters, we use an exclusive wait. This means that when we
|
||||
* get woken with enough journal space to get our reservation, we need to
|
||||
* wake the next waiter on the list.
|
||||
*
|
||||
* Returns: errno
|
||||
*/
|
||||
|
||||
int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
|
||||
static void __gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks,
|
||||
unsigned int taboo_blks)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned reserved_blks = 7 * (4096 / sdp->sd_vfs->s_blocksize);
|
||||
unsigned wanted = blks + reserved_blks;
|
||||
DEFINE_WAIT(wait);
|
||||
int did_wait = 0;
|
||||
unsigned wanted = blks + taboo_blks;
|
||||
unsigned int free_blocks;
|
||||
|
||||
if (gfs2_assert_warn(sdp, blks) ||
|
||||
gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
|
||||
return -EINVAL;
|
||||
atomic_add(blks, &sdp->sd_log_blks_needed);
|
||||
retry:
|
||||
free_blocks = atomic_read(&sdp->sd_log_blks_free);
|
||||
if (unlikely(free_blocks <= wanted)) {
|
||||
do {
|
||||
prepare_to_wait_exclusive(&sdp->sd_log_waitq, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
for (;;) {
|
||||
if (current != sdp->sd_logd_process)
|
||||
wake_up(&sdp->sd_logd_waitq);
|
||||
did_wait = 1;
|
||||
if (atomic_read(&sdp->sd_log_blks_free) <= wanted)
|
||||
io_schedule();
|
||||
free_blocks = atomic_read(&sdp->sd_log_blks_free);
|
||||
} while(free_blocks <= wanted);
|
||||
finish_wait(&sdp->sd_log_waitq, &wait);
|
||||
io_wait_event(sdp->sd_log_waitq,
|
||||
(free_blocks = atomic_read(&sdp->sd_log_blks_free),
|
||||
free_blocks >= wanted));
|
||||
do {
|
||||
if (atomic_try_cmpxchg(&sdp->sd_log_blks_free,
|
||||
&free_blocks,
|
||||
free_blocks - blks))
|
||||
goto reserved;
|
||||
} while (free_blocks >= wanted);
|
||||
}
|
||||
atomic_inc(&sdp->sd_reserving_log);
|
||||
if (atomic_cmpxchg(&sdp->sd_log_blks_free, free_blocks,
|
||||
free_blocks - blks) != free_blocks) {
|
||||
if (atomic_dec_and_test(&sdp->sd_reserving_log))
|
||||
wake_up(&sdp->sd_reserving_log_wait);
|
||||
goto retry;
|
||||
}
|
||||
atomic_sub(blks, &sdp->sd_log_blks_needed);
|
||||
|
||||
reserved:
|
||||
trace_gfs2_log_blocks(sdp, -blks);
|
||||
|
||||
/*
|
||||
* If we waited, then so might others, wake them up _after_ we get
|
||||
* our share of the log.
|
||||
*/
|
||||
if (unlikely(did_wait))
|
||||
if (atomic_sub_return(blks, &sdp->sd_log_blks_needed))
|
||||
wake_up(&sdp->sd_log_waitq);
|
||||
}
|
||||
|
||||
down_read(&sdp->sd_log_flush_lock);
|
||||
if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) {
|
||||
gfs2_log_release(sdp, blks);
|
||||
ret = -EROFS;
|
||||
/**
|
||||
* gfs2_log_try_reserve - Try to make a log reservation
|
||||
* @sdp: The GFS2 superblock
|
||||
* @tr: The transaction
|
||||
* @extra_revokes: The number of additional revokes reserved (output)
|
||||
*
|
||||
* This is similar to gfs2_log_reserve, but sdp->sd_log_flush_lock must be
|
||||
* held for correct revoke accounting.
|
||||
*/
|
||||
|
||||
bool gfs2_log_try_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
unsigned int *extra_revokes)
|
||||
{
|
||||
unsigned int blks = tr->tr_reserved;
|
||||
unsigned int revokes = tr->tr_revokes;
|
||||
unsigned int revoke_blks = 0;
|
||||
|
||||
*extra_revokes = 0;
|
||||
if (revokes && !__gfs2_log_try_reserve_revokes(sdp, revokes)) {
|
||||
revoke_blks = DIV_ROUND_UP(revokes, sdp->sd_inptrs);
|
||||
*extra_revokes = revoke_blks * sdp->sd_inptrs - revokes;
|
||||
blks += revoke_blks;
|
||||
}
|
||||
if (atomic_dec_and_test(&sdp->sd_reserving_log))
|
||||
wake_up(&sdp->sd_reserving_log_wait);
|
||||
return ret;
|
||||
if (!blks)
|
||||
return true;
|
||||
if (__gfs2_log_try_reserve(sdp, blks, GFS2_LOG_FLUSH_MIN_BLOCKS))
|
||||
return true;
|
||||
if (!revoke_blks)
|
||||
gfs2_log_release_revokes(sdp, revokes);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_log_reserve - Make a log reservation
|
||||
* @sdp: The GFS2 superblock
|
||||
* @tr: The transaction
|
||||
* @extra_revokes: The number of additional revokes reserved (output)
|
||||
*
|
||||
* sdp->sd_log_flush_lock must not be held.
|
||||
*/
|
||||
|
||||
void gfs2_log_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
unsigned int *extra_revokes)
|
||||
{
|
||||
unsigned int blks = tr->tr_reserved;
|
||||
unsigned int revokes = tr->tr_revokes;
|
||||
unsigned int revoke_blks = 0;
|
||||
|
||||
*extra_revokes = 0;
|
||||
if (revokes) {
|
||||
revoke_blks = DIV_ROUND_UP(revokes, sdp->sd_inptrs);
|
||||
*extra_revokes = revoke_blks * sdp->sd_inptrs - revokes;
|
||||
blks += revoke_blks;
|
||||
}
|
||||
__gfs2_log_reserve(sdp, blks, GFS2_LOG_FLUSH_MIN_BLOCKS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,24 +631,20 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer
|
||||
}
|
||||
|
||||
/**
|
||||
* calc_reserved - Calculate the number of blocks to reserve when
|
||||
* refunding a transaction's unused buffers.
|
||||
* calc_reserved - Calculate the number of blocks to keep reserved
|
||||
* @sdp: The GFS2 superblock
|
||||
*
|
||||
* This is complex. We need to reserve room for all our currently used
|
||||
* metadata buffers (e.g. normal file I/O rewriting file time stamps) and
|
||||
* all our journaled data buffers for journaled files (e.g. files in the
|
||||
* metadata blocks (e.g. normal file I/O rewriting file time stamps) and
|
||||
* all our journaled data blocks for journaled files (e.g. files in the
|
||||
* meta_fs like rindex, or files for which chattr +j was done.)
|
||||
* If we don't reserve enough space, gfs2_log_refund and gfs2_log_flush
|
||||
* will count it as free space (sd_log_blks_free) and corruption will follow.
|
||||
* If we don't reserve enough space, corruption will follow.
|
||||
*
|
||||
* We can have metadata bufs and jdata bufs in the same journal. So each
|
||||
* type gets its own log header, for which we need to reserve a block.
|
||||
* In fact, each type has the potential for needing more than one header
|
||||
* in cases where we have more buffers than will fit on a journal page.
|
||||
* We can have metadata blocks and jdata blocks in the same journal. Each
|
||||
* type gets its own log descriptor, for which we need to reserve a block.
|
||||
* In fact, each type has the potential for needing more than one log descriptor
|
||||
* in cases where we have more blocks than will fit in a log descriptor.
|
||||
* Metadata journal entries take up half the space of journaled buffer entries.
|
||||
* Thus, metadata entries have buf_limit (502) and journaled buffers have
|
||||
* databuf_limit (251) before they cause a wrap around.
|
||||
*
|
||||
* Also, we need to reserve blocks for revoke journal entries and one for an
|
||||
* overall header for the lot.
|
||||
@@ -533,59 +653,29 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer
|
||||
*/
|
||||
static unsigned int calc_reserved(struct gfs2_sbd *sdp)
|
||||
{
|
||||
unsigned int reserved = 0;
|
||||
unsigned int mbuf;
|
||||
unsigned int dbuf;
|
||||
unsigned int reserved = GFS2_LOG_FLUSH_MIN_BLOCKS;
|
||||
unsigned int blocks;
|
||||
struct gfs2_trans *tr = sdp->sd_log_tr;
|
||||
|
||||
if (tr) {
|
||||
mbuf = tr->tr_num_buf_new - tr->tr_num_buf_rm;
|
||||
dbuf = tr->tr_num_databuf_new - tr->tr_num_databuf_rm;
|
||||
reserved = mbuf + dbuf;
|
||||
/* Account for header blocks */
|
||||
reserved += DIV_ROUND_UP(mbuf, buf_limit(sdp));
|
||||
reserved += DIV_ROUND_UP(dbuf, databuf_limit(sdp));
|
||||
blocks = tr->tr_num_buf_new - tr->tr_num_buf_rm;
|
||||
reserved += blocks + DIV_ROUND_UP(blocks, buf_limit(sdp));
|
||||
blocks = tr->tr_num_databuf_new - tr->tr_num_databuf_rm;
|
||||
reserved += blocks + DIV_ROUND_UP(blocks, databuf_limit(sdp));
|
||||
}
|
||||
|
||||
if (sdp->sd_log_committed_revoke > 0)
|
||||
reserved += gfs2_struct2blk(sdp, sdp->sd_log_committed_revoke);
|
||||
/* One for the overall header */
|
||||
if (reserved)
|
||||
reserved++;
|
||||
return reserved;
|
||||
}
|
||||
|
||||
static unsigned int current_tail(struct gfs2_sbd *sdp)
|
||||
static void log_pull_tail(struct gfs2_sbd *sdp)
|
||||
{
|
||||
struct gfs2_trans *tr;
|
||||
unsigned int tail;
|
||||
|
||||
spin_lock(&sdp->sd_ail_lock);
|
||||
|
||||
if (list_empty(&sdp->sd_ail1_list)) {
|
||||
tail = sdp->sd_log_head;
|
||||
} else {
|
||||
tr = list_last_entry(&sdp->sd_ail1_list, struct gfs2_trans,
|
||||
tr_list);
|
||||
tail = tr->tr_first;
|
||||
}
|
||||
|
||||
spin_unlock(&sdp->sd_ail_lock);
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail)
|
||||
{
|
||||
unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
|
||||
unsigned int new_tail = sdp->sd_log_flush_tail;
|
||||
unsigned int dist;
|
||||
|
||||
if (new_tail == sdp->sd_log_tail)
|
||||
return;
|
||||
dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
|
||||
ail2_empty(sdp, new_tail);
|
||||
|
||||
atomic_add(dist, &sdp->sd_log_blks_free);
|
||||
trace_gfs2_log_blocks(sdp, dist);
|
||||
gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
|
||||
sdp->sd_jdesc->jd_blocks);
|
||||
|
||||
gfs2_log_release(sdp, dist);
|
||||
sdp->sd_log_tail = new_tail;
|
||||
}
|
||||
|
||||
@@ -698,7 +788,7 @@ void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_write_revokes - Add as many revokes to the system transaction as we can
|
||||
* gfs2_flush_revokes - Add as many revokes to the system transaction as we can
|
||||
* @sdp: The GFS2 superblock
|
||||
*
|
||||
* Our usual strategy is to defer writing revokes as much as we can in the hope
|
||||
@@ -709,38 +799,14 @@ void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
|
||||
* been written back. This will basically come at no cost now, and will save
|
||||
* us from having to keep track of those blocks on the AIL2 list later.
|
||||
*/
|
||||
void gfs2_write_revokes(struct gfs2_sbd *sdp)
|
||||
void gfs2_flush_revokes(struct gfs2_sbd *sdp)
|
||||
{
|
||||
/* number of revokes we still have room for */
|
||||
int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64);
|
||||
unsigned int max_revokes = atomic_read(&sdp->sd_log_revokes_available);
|
||||
|
||||
gfs2_log_lock(sdp);
|
||||
while (sdp->sd_log_num_revoke > max_revokes)
|
||||
max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64);
|
||||
max_revokes -= sdp->sd_log_num_revoke;
|
||||
if (!sdp->sd_log_num_revoke) {
|
||||
atomic_dec(&sdp->sd_log_blks_free);
|
||||
/* If no blocks have been reserved, we need to also
|
||||
* reserve a block for the header */
|
||||
if (!sdp->sd_log_blks_reserved) {
|
||||
atomic_dec(&sdp->sd_log_blks_free);
|
||||
trace_gfs2_log_blocks(sdp, -2);
|
||||
} else {
|
||||
trace_gfs2_log_blocks(sdp, -1);
|
||||
}
|
||||
}
|
||||
gfs2_ail1_empty(sdp, max_revokes);
|
||||
gfs2_log_unlock(sdp);
|
||||
|
||||
if (!sdp->sd_log_num_revoke) {
|
||||
atomic_inc(&sdp->sd_log_blks_free);
|
||||
if (!sdp->sd_log_blks_reserved) {
|
||||
atomic_inc(&sdp->sd_log_blks_free);
|
||||
trace_gfs2_log_blocks(sdp, 2);
|
||||
} else {
|
||||
trace_gfs2_log_blocks(sdp, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -769,7 +835,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
u64 dblock;
|
||||
|
||||
if (gfs2_withdrawn(sdp))
|
||||
goto out;
|
||||
return;
|
||||
|
||||
page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
|
||||
lh = page_address(page);
|
||||
@@ -822,10 +888,8 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
sb->s_blocksize - LH_V1_SIZE - 4);
|
||||
lh->lh_crc = cpu_to_be32(crc);
|
||||
|
||||
gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
|
||||
gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags);
|
||||
out:
|
||||
log_flush_wait(sdp);
|
||||
gfs2_log_write(sdp, jd, page, sb->s_blocksize, 0, dblock);
|
||||
gfs2_log_submit_bio(&jd->jd_log_bio, REQ_OP_WRITE | op_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -838,25 +902,24 @@ out:
|
||||
|
||||
static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
|
||||
{
|
||||
unsigned int tail;
|
||||
int op_flags = REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC;
|
||||
enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state);
|
||||
|
||||
gfs2_assert_withdraw(sdp, (state != SFS_FROZEN));
|
||||
tail = current_tail(sdp);
|
||||
|
||||
if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) {
|
||||
gfs2_ordered_wait(sdp);
|
||||
log_flush_wait(sdp);
|
||||
op_flags = REQ_SYNC | REQ_META | REQ_PRIO;
|
||||
}
|
||||
sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
|
||||
gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++, tail,
|
||||
sdp->sd_log_flush_head, flags, op_flags);
|
||||
sdp->sd_log_idle = (sdp->sd_log_flush_tail == sdp->sd_log_flush_head);
|
||||
gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++,
|
||||
sdp->sd_log_flush_tail, sdp->sd_log_flush_head,
|
||||
flags, op_flags);
|
||||
gfs2_log_incr_head(sdp);
|
||||
|
||||
if (sdp->sd_log_tail != tail)
|
||||
log_pull_tail(sdp, tail);
|
||||
log_flush_wait(sdp);
|
||||
log_pull_tail(sdp);
|
||||
gfs2_log_update_head(sdp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -956,10 +1019,15 @@ static void trans_drain(struct gfs2_trans *tr)
|
||||
void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
{
|
||||
struct gfs2_trans *tr = NULL;
|
||||
unsigned int reserved_blocks = 0, used_blocks = 0;
|
||||
enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state);
|
||||
unsigned int first_log_head;
|
||||
unsigned int reserved_revokes = 0;
|
||||
|
||||
down_write(&sdp->sd_log_flush_lock);
|
||||
trace_gfs2_log_flush(sdp, 1, flags);
|
||||
|
||||
repeat:
|
||||
/*
|
||||
* Do this check while holding the log_flush_lock to prevent new
|
||||
* buffers from being added to the ail via gfs2_pin()
|
||||
@@ -970,28 +1038,47 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
/* Log might have been flushed while we waited for the flush lock */
|
||||
if (gl && !test_bit(GLF_LFLUSH, &gl->gl_flags))
|
||||
goto out;
|
||||
trace_gfs2_log_flush(sdp, 1, flags);
|
||||
|
||||
first_log_head = sdp->sd_log_head;
|
||||
sdp->sd_log_flush_head = first_log_head;
|
||||
|
||||
tr = sdp->sd_log_tr;
|
||||
if (tr || sdp->sd_log_num_revoke) {
|
||||
if (reserved_blocks)
|
||||
gfs2_log_release(sdp, reserved_blocks);
|
||||
reserved_blocks = sdp->sd_log_blks_reserved;
|
||||
reserved_revokes = sdp->sd_log_num_revoke;
|
||||
if (tr) {
|
||||
sdp->sd_log_tr = NULL;
|
||||
tr->tr_first = first_log_head;
|
||||
if (unlikely (state == SFS_FROZEN)) {
|
||||
if (gfs2_assert_withdraw_delayed(sdp,
|
||||
!tr->tr_num_buf_new && !tr->tr_num_databuf_new))
|
||||
goto out_withdraw;
|
||||
}
|
||||
}
|
||||
} else if (!reserved_blocks) {
|
||||
unsigned int taboo_blocks = GFS2_LOG_FLUSH_MIN_BLOCKS;
|
||||
|
||||
reserved_blocks = GFS2_LOG_FLUSH_MIN_BLOCKS;
|
||||
if (current == sdp->sd_logd_process)
|
||||
taboo_blocks = 0;
|
||||
|
||||
if (!__gfs2_log_try_reserve(sdp, reserved_blocks, taboo_blocks)) {
|
||||
up_write(&sdp->sd_log_flush_lock);
|
||||
__gfs2_log_reserve(sdp, reserved_blocks, taboo_blocks);
|
||||
down_write(&sdp->sd_log_flush_lock);
|
||||
goto repeat;
|
||||
}
|
||||
BUG_ON(sdp->sd_log_num_revoke);
|
||||
}
|
||||
|
||||
if (flags & GFS2_LOG_HEAD_FLUSH_SHUTDOWN)
|
||||
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
|
||||
|
||||
sdp->sd_log_flush_head = sdp->sd_log_head;
|
||||
tr = sdp->sd_log_tr;
|
||||
if (tr) {
|
||||
sdp->sd_log_tr = NULL;
|
||||
tr->tr_first = sdp->sd_log_flush_head;
|
||||
if (unlikely (state == SFS_FROZEN))
|
||||
if (gfs2_assert_withdraw_delayed(sdp,
|
||||
!tr->tr_num_buf_new && !tr->tr_num_databuf_new))
|
||||
goto out_withdraw;
|
||||
}
|
||||
|
||||
if (unlikely(state == SFS_FROZEN))
|
||||
if (gfs2_assert_withdraw_delayed(sdp, !sdp->sd_log_num_revoke))
|
||||
if (gfs2_assert_withdraw_delayed(sdp, !reserved_revokes))
|
||||
goto out_withdraw;
|
||||
if (gfs2_assert_withdraw_delayed(sdp,
|
||||
sdp->sd_log_num_revoke == sdp->sd_log_committed_revoke))
|
||||
goto out_withdraw;
|
||||
|
||||
gfs2_ordered_write(sdp);
|
||||
if (gfs2_withdrawn(sdp))
|
||||
@@ -999,16 +1086,13 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
lops_before_commit(sdp, tr);
|
||||
if (gfs2_withdrawn(sdp))
|
||||
goto out_withdraw;
|
||||
gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE);
|
||||
gfs2_log_submit_bio(&sdp->sd_jdesc->jd_log_bio, REQ_OP_WRITE);
|
||||
if (gfs2_withdrawn(sdp))
|
||||
goto out_withdraw;
|
||||
|
||||
if (sdp->sd_log_head != sdp->sd_log_flush_head) {
|
||||
log_flush_wait(sdp);
|
||||
log_write_header(sdp, flags);
|
||||
} else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
|
||||
atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
|
||||
trace_gfs2_log_blocks(sdp, -1);
|
||||
} else if (sdp->sd_log_tail != sdp->sd_log_flush_tail && !sdp->sd_log_idle) {
|
||||
log_write_header(sdp, flags);
|
||||
}
|
||||
if (gfs2_withdrawn(sdp))
|
||||
@@ -1016,9 +1100,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
lops_after_commit(sdp, tr);
|
||||
|
||||
gfs2_log_lock(sdp);
|
||||
sdp->sd_log_head = sdp->sd_log_flush_head;
|
||||
sdp->sd_log_blks_reserved = 0;
|
||||
sdp->sd_log_committed_revoke = 0;
|
||||
|
||||
spin_lock(&sdp->sd_ail_lock);
|
||||
if (tr && !list_empty(&tr->tr_ail1_list)) {
|
||||
@@ -1033,10 +1115,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
empty_ail1_list(sdp);
|
||||
if (gfs2_withdrawn(sdp))
|
||||
goto out_withdraw;
|
||||
atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
|
||||
trace_gfs2_log_blocks(sdp, -1);
|
||||
log_write_header(sdp, flags);
|
||||
sdp->sd_log_head = sdp->sd_log_flush_head;
|
||||
}
|
||||
if (flags & (GFS2_LOG_HEAD_FLUSH_SHUTDOWN |
|
||||
GFS2_LOG_HEAD_FLUSH_FREEZE))
|
||||
@@ -1046,12 +1125,22 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
|
||||
}
|
||||
|
||||
out_end:
|
||||
trace_gfs2_log_flush(sdp, 0, flags);
|
||||
used_blocks = log_distance(sdp, sdp->sd_log_flush_head, first_log_head);
|
||||
reserved_revokes += atomic_read(&sdp->sd_log_revokes_available);
|
||||
atomic_set(&sdp->sd_log_revokes_available, sdp->sd_ldptrs);
|
||||
gfs2_assert_withdraw(sdp, reserved_revokes % sdp->sd_inptrs == sdp->sd_ldptrs);
|
||||
if (reserved_revokes > sdp->sd_ldptrs)
|
||||
reserved_blocks += (reserved_revokes - sdp->sd_ldptrs) / sdp->sd_inptrs;
|
||||
out:
|
||||
if (used_blocks != reserved_blocks) {
|
||||
gfs2_assert_withdraw_delayed(sdp, used_blocks < reserved_blocks);
|
||||
gfs2_log_release(sdp, reserved_blocks - used_blocks);
|
||||
}
|
||||
up_write(&sdp->sd_log_flush_lock);
|
||||
gfs2_trans_free(sdp, tr);
|
||||
if (gfs2_withdrawing(sdp))
|
||||
gfs2_withdraw(sdp);
|
||||
trace_gfs2_log_flush(sdp, 0, flags);
|
||||
return;
|
||||
|
||||
out_withdraw:
|
||||
@@ -1087,8 +1176,8 @@ static void gfs2_merge_trans(struct gfs2_sbd *sdp, struct gfs2_trans *new)
|
||||
old->tr_num_databuf_new += new->tr_num_databuf_new;
|
||||
old->tr_num_buf_rm += new->tr_num_buf_rm;
|
||||
old->tr_num_databuf_rm += new->tr_num_databuf_rm;
|
||||
old->tr_revokes += new->tr_revokes;
|
||||
old->tr_num_revoke += new->tr_num_revoke;
|
||||
old->tr_num_revoke_rm += new->tr_num_revoke_rm;
|
||||
|
||||
list_splice_tail_init(&new->tr_databuf, &old->tr_databuf);
|
||||
list_splice_tail_init(&new->tr_buf, &old->tr_buf);
|
||||
@@ -1110,20 +1199,17 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
|
||||
if (sdp->sd_log_tr) {
|
||||
gfs2_merge_trans(sdp, tr);
|
||||
} else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) {
|
||||
gfs2_assert_withdraw(sdp, test_bit(TR_ALLOCED, &tr->tr_flags));
|
||||
gfs2_assert_withdraw(sdp, !test_bit(TR_ONSTACK, &tr->tr_flags));
|
||||
sdp->sd_log_tr = tr;
|
||||
set_bit(TR_ATTACHED, &tr->tr_flags);
|
||||
}
|
||||
|
||||
sdp->sd_log_committed_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
|
||||
reserved = calc_reserved(sdp);
|
||||
maxres = sdp->sd_log_blks_reserved + tr->tr_reserved;
|
||||
gfs2_assert_withdraw(sdp, maxres >= reserved);
|
||||
unused = maxres - reserved;
|
||||
atomic_add(unused, &sdp->sd_log_blks_free);
|
||||
trace_gfs2_log_blocks(sdp, unused);
|
||||
gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
|
||||
sdp->sd_jdesc->jd_blocks);
|
||||
if (unused)
|
||||
gfs2_log_release(sdp, unused);
|
||||
sdp->sd_log_blks_reserved = reserved;
|
||||
|
||||
gfs2_log_unlock(sdp);
|
||||
@@ -1166,15 +1252,11 @@ static void gfs2_log_shutdown(struct gfs2_sbd *sdp)
|
||||
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
|
||||
gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
|
||||
|
||||
sdp->sd_log_flush_head = sdp->sd_log_head;
|
||||
|
||||
log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT | GFS2_LFC_SHUTDOWN);
|
||||
log_pull_tail(sdp);
|
||||
|
||||
gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);
|
||||
gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list));
|
||||
|
||||
sdp->sd_log_head = sdp->sd_log_flush_head;
|
||||
sdp->sd_log_tail = sdp->sd_log_head;
|
||||
}
|
||||
|
||||
static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp)
|
||||
@@ -1208,7 +1290,6 @@ int gfs2_logd(void *data)
|
||||
struct gfs2_sbd *sdp = data;
|
||||
unsigned long t = 1;
|
||||
DEFINE_WAIT(wait);
|
||||
bool did_flush;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
|
||||
@@ -1227,12 +1308,10 @@ int gfs2_logd(void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
did_flush = false;
|
||||
if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
|
||||
gfs2_ail1_empty(sdp, 0);
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_LOGD_JFLUSH_REQD);
|
||||
did_flush = true;
|
||||
GFS2_LFC_LOGD_JFLUSH_REQD);
|
||||
}
|
||||
|
||||
if (gfs2_ail_flush_reqd(sdp)) {
|
||||
@@ -1240,13 +1319,9 @@ int gfs2_logd(void *data)
|
||||
gfs2_ail1_wait(sdp);
|
||||
gfs2_ail1_empty(sdp, 0);
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_LOGD_AIL_FLUSH_REQD);
|
||||
did_flush = true;
|
||||
GFS2_LFC_LOGD_AIL_FLUSH_REQD);
|
||||
}
|
||||
|
||||
if (!gfs2_ail_flush_reqd(sdp) || did_flush)
|
||||
wake_up(&sdp->sd_log_waitq);
|
||||
|
||||
t = gfs2_tune_get(sdp, gt_logd_secs) * HZ;
|
||||
|
||||
try_to_freeze();
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
#include "incore.h"
|
||||
#include "inode.h"
|
||||
|
||||
/*
|
||||
* The minimum amount of log space required for a log flush is one block for
|
||||
* revokes and one block for the log header. Log flushes other than
|
||||
* GFS2_LOG_HEAD_FLUSH_NORMAL may write one or two more log headers.
|
||||
*/
|
||||
#define GFS2_LOG_FLUSH_MIN_BLOCKS 4
|
||||
|
||||
/**
|
||||
* gfs2_log_lock - acquire the right to mess with the log manager
|
||||
* @sdp: the filesystem
|
||||
@@ -43,7 +50,9 @@ static inline void gfs2_log_pointers_init(struct gfs2_sbd *sdp,
|
||||
if (++value == sdp->sd_jdesc->jd_blocks) {
|
||||
value = 0;
|
||||
}
|
||||
sdp->sd_log_head = sdp->sd_log_tail = value;
|
||||
sdp->sd_log_tail = value;
|
||||
sdp->sd_log_flush_tail = value;
|
||||
sdp->sd_log_head = value;
|
||||
}
|
||||
|
||||
static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip)
|
||||
@@ -64,8 +73,13 @@ static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip)
|
||||
extern void gfs2_ordered_del_inode(struct gfs2_inode *ip);
|
||||
extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct);
|
||||
extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
|
||||
extern bool gfs2_log_is_empty(struct gfs2_sbd *sdp);
|
||||
extern void gfs2_log_release_revokes(struct gfs2_sbd *sdp, unsigned int revokes);
|
||||
extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks);
|
||||
extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
|
||||
extern bool gfs2_log_try_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
unsigned int *extra_revokes);
|
||||
extern void gfs2_log_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
|
||||
unsigned int *extra_revokes);
|
||||
extern void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
u64 seq, u32 tail, u32 lblock, u32 flags,
|
||||
int op_flags);
|
||||
@@ -78,6 +92,6 @@ extern void log_flush_wait(struct gfs2_sbd *sdp);
|
||||
extern int gfs2_logd(void *data);
|
||||
extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
|
||||
extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl);
|
||||
extern void gfs2_write_revokes(struct gfs2_sbd *sdp);
|
||||
extern void gfs2_flush_revokes(struct gfs2_sbd *sdp);
|
||||
|
||||
#endif /* __LOG_DOT_H__ */
|
||||
|
||||
@@ -76,15 +76,20 @@ static void maybe_release_space(struct gfs2_bufdata *bd)
|
||||
unsigned int index = bd->bd_bh->b_blocknr - gl->gl_name.ln_number;
|
||||
struct gfs2_bitmap *bi = rgd->rd_bits + index;
|
||||
|
||||
rgrp_lock_local(rgd);
|
||||
if (bi->bi_clone == NULL)
|
||||
return;
|
||||
goto out;
|
||||
if (sdp->sd_args.ar_discard)
|
||||
gfs2_rgrp_send_discards(sdp, rgd->rd_data0, bd->bd_bh, bi, 1, NULL);
|
||||
memcpy(bi->bi_clone + bi->bi_offset,
|
||||
bd->bd_bh->b_data + bi->bi_offset, bi->bi_bytes);
|
||||
clear_bit(GBF_FULL, &bi->bi_flags);
|
||||
rgd->rd_free_clone = rgd->rd_free;
|
||||
BUG_ON(rgd->rd_free_clone < rgd->rd_reserved);
|
||||
rgd->rd_extfail_pt = rgd->rd_free;
|
||||
|
||||
out:
|
||||
rgrp_unlock_local(rgd);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,17 +327,18 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno,
|
||||
* then add the page segment to that.
|
||||
*/
|
||||
|
||||
void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
|
||||
unsigned size, unsigned offset, u64 blkno)
|
||||
void gfs2_log_write(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
struct page *page, unsigned size, unsigned offset,
|
||||
u64 blkno)
|
||||
{
|
||||
struct bio *bio;
|
||||
int ret;
|
||||
|
||||
bio = gfs2_log_get_bio(sdp, blkno, &sdp->sd_log_bio, REQ_OP_WRITE,
|
||||
bio = gfs2_log_get_bio(sdp, blkno, &jd->jd_log_bio, REQ_OP_WRITE,
|
||||
gfs2_end_log_write, false);
|
||||
ret = bio_add_page(bio, page, size, offset);
|
||||
if (ret == 0) {
|
||||
bio = gfs2_log_get_bio(sdp, blkno, &sdp->sd_log_bio,
|
||||
bio = gfs2_log_get_bio(sdp, blkno, &jd->jd_log_bio,
|
||||
REQ_OP_WRITE, gfs2_end_log_write, true);
|
||||
ret = bio_add_page(bio, page, size, offset);
|
||||
WARN_ON(ret == 0);
|
||||
@@ -355,7 +361,8 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
|
||||
|
||||
dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head);
|
||||
gfs2_log_incr_head(sdp);
|
||||
gfs2_log_write(sdp, bh->b_page, bh->b_size, bh_offset(bh), dblock);
|
||||
gfs2_log_write(sdp, sdp->sd_jdesc, bh->b_page, bh->b_size,
|
||||
bh_offset(bh), dblock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,14 +376,14 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
|
||||
* the page may be freed at any time.
|
||||
*/
|
||||
|
||||
void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
|
||||
static void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
|
||||
{
|
||||
struct super_block *sb = sdp->sd_vfs;
|
||||
u64 dblock;
|
||||
|
||||
dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head);
|
||||
gfs2_log_incr_head(sdp);
|
||||
gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
|
||||
gfs2_log_write(sdp, sdp->sd_jdesc, page, sb->s_blocksize, 0, dblock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,7 +852,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
|
||||
struct page *page;
|
||||
unsigned int length;
|
||||
|
||||
gfs2_write_revokes(sdp);
|
||||
gfs2_flush_revokes(sdp);
|
||||
if (!sdp->sd_log_num_revoke)
|
||||
return;
|
||||
|
||||
@@ -857,7 +864,6 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
|
||||
sdp->sd_log_num_revoke--;
|
||||
|
||||
if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) {
|
||||
|
||||
gfs2_log_write_page(sdp, page);
|
||||
page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
|
||||
mh = page_address(page);
|
||||
|
||||
@@ -10,37 +10,24 @@
|
||||
#include <linux/list.h>
|
||||
#include "incore.h"
|
||||
|
||||
#define BUF_OFFSET \
|
||||
((sizeof(struct gfs2_log_descriptor) + sizeof(__be64) - 1) & \
|
||||
~(sizeof(__be64) - 1))
|
||||
#define DATABUF_OFFSET \
|
||||
((sizeof(struct gfs2_log_descriptor) + (2 * sizeof(__be64) - 1)) & \
|
||||
~(2 * sizeof(__be64) - 1))
|
||||
|
||||
extern const struct gfs2_log_operations *gfs2_log_ops[];
|
||||
extern void gfs2_log_incr_head(struct gfs2_sbd *sdp);
|
||||
extern u64 gfs2_log_bmap(struct gfs2_jdesc *jd, unsigned int lbn);
|
||||
extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
|
||||
unsigned size, unsigned offset, u64 blkno);
|
||||
extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
|
||||
extern void gfs2_log_write(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
struct page *page, unsigned size, unsigned offset,
|
||||
u64 blkno);
|
||||
extern void gfs2_log_submit_bio(struct bio **biop, int opf);
|
||||
extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
|
||||
extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
|
||||
struct gfs2_log_header_host *head, bool keep_cache);
|
||||
static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
|
||||
{
|
||||
unsigned int limit;
|
||||
|
||||
limit = (sdp->sd_sb.sb_bsize - BUF_OFFSET) / sizeof(__be64);
|
||||
return limit;
|
||||
return sdp->sd_ldptrs;
|
||||
}
|
||||
|
||||
static inline unsigned int databuf_limit(struct gfs2_sbd *sdp)
|
||||
{
|
||||
unsigned int limit;
|
||||
|
||||
limit = (sdp->sd_sb.sb_bsize - DATABUF_OFFSET) / (2 * sizeof(__be64));
|
||||
return limit;
|
||||
return sdp->sd_ldptrs / 2;
|
||||
}
|
||||
|
||||
static inline void lops_before_commit(struct gfs2_sbd *sdp,
|
||||
|
||||
@@ -98,7 +98,7 @@ static int __init init_gfs2_fs(void)
|
||||
error = -ENOMEM;
|
||||
gfs2_glock_cachep = kmem_cache_create("gfs2_glock",
|
||||
sizeof(struct gfs2_glock),
|
||||
0, 0,
|
||||
0, SLAB_RECLAIM_ACCOUNT,
|
||||
gfs2_init_glock_once);
|
||||
if (!gfs2_glock_cachep)
|
||||
goto fail_cachep1;
|
||||
@@ -134,7 +134,7 @@ static int __init init_gfs2_fs(void)
|
||||
|
||||
gfs2_quotad_cachep = kmem_cache_create("gfs2_quotad",
|
||||
sizeof(struct gfs2_quota_data),
|
||||
0, 0, NULL);
|
||||
0, SLAB_RECLAIM_ACCOUNT, NULL);
|
||||
if (!gfs2_quotad_cachep)
|
||||
goto fail_cachep6;
|
||||
|
||||
|
||||
@@ -136,8 +136,6 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
|
||||
|
||||
init_rwsem(&sdp->sd_log_flush_lock);
|
||||
atomic_set(&sdp->sd_log_in_flight, 0);
|
||||
atomic_set(&sdp->sd_reserving_log, 0);
|
||||
init_waitqueue_head(&sdp->sd_reserving_log_wait);
|
||||
init_waitqueue_head(&sdp->sd_log_flush_wait);
|
||||
atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN);
|
||||
mutex_init(&sdp->sd_freeze_mutex);
|
||||
@@ -171,7 +169,8 @@ static int gfs2_check_sb(struct gfs2_sbd *sdp, int silent)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sb->sb_fs_format != GFS2_FORMAT_FS ||
|
||||
if (sb->sb_fs_format < GFS2_FS_FORMAT_MIN ||
|
||||
sb->sb_fs_format > GFS2_FS_FORMAT_MAX ||
|
||||
sb->sb_multihost_format != GFS2_FORMAT_MULTI) {
|
||||
fs_warn(sdp, "Unknown on-disk format, unable to mount\n");
|
||||
return -EINVAL;
|
||||
@@ -179,7 +178,7 @@ static int gfs2_check_sb(struct gfs2_sbd *sdp, int silent)
|
||||
|
||||
if (sb->sb_bsize < 512 || sb->sb_bsize > PAGE_SIZE ||
|
||||
(sb->sb_bsize & (sb->sb_bsize - 1))) {
|
||||
pr_warn("Invalid superblock size\n");
|
||||
pr_warn("Invalid block size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -317,6 +316,13 @@ static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent)
|
||||
sizeof(struct gfs2_meta_header))
|
||||
* GFS2_NBBY; /* not the rgrp bitmap, subsequent bitmaps only */
|
||||
|
||||
/*
|
||||
* We always keep at least one block reserved for revokes in
|
||||
* transactions. This greatly simplifies allocating additional
|
||||
* revoke blocks.
|
||||
*/
|
||||
atomic_set(&sdp->sd_log_revokes_available, sdp->sd_ldptrs);
|
||||
|
||||
/* Compute maximum reservation required to add a entry to a directory */
|
||||
|
||||
hash_blocks = DIV_ROUND_UP(sizeof(u64) * BIT(GFS2_DIR_MAX_DEPTH),
|
||||
@@ -488,6 +494,19 @@ static int init_sb(struct gfs2_sbd *sdp, int silent)
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch(sdp->sd_sb.sb_fs_format) {
|
||||
case GFS2_FS_FORMAT_MAX:
|
||||
sb->s_xattr = gfs2_xattr_handlers_max;
|
||||
break;
|
||||
|
||||
case GFS2_FS_FORMAT_MIN:
|
||||
sb->s_xattr = gfs2_xattr_handlers_min;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Set up the buffer cache and SB for real */
|
||||
if (sdp->sd_sb.sb_bsize < bdev_logical_block_size(sb->s_bdev)) {
|
||||
ret = -EINVAL;
|
||||
@@ -1032,13 +1051,14 @@ hostdata_error:
|
||||
}
|
||||
|
||||
if (lm->lm_mount == NULL) {
|
||||
fs_info(sdp, "Now mounting FS...\n");
|
||||
fs_info(sdp, "Now mounting FS (format %u)...\n", sdp->sd_sb.sb_fs_format);
|
||||
complete_all(&sdp->sd_locking_init);
|
||||
return 0;
|
||||
}
|
||||
ret = lm->lm_mount(sdp, table);
|
||||
if (ret == 0)
|
||||
fs_info(sdp, "Joined cluster. Now mounting FS...\n");
|
||||
fs_info(sdp, "Joined cluster. Now mounting FS (format %u)...\n",
|
||||
sdp->sd_sb.sb_fs_format);
|
||||
complete_all(&sdp->sd_locking_init);
|
||||
return ret;
|
||||
}
|
||||
@@ -1084,6 +1104,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
int silent = fc->sb_flags & SB_SILENT;
|
||||
struct gfs2_sbd *sdp;
|
||||
struct gfs2_holder mount_gh;
|
||||
struct gfs2_holder freeze_gh;
|
||||
int error;
|
||||
|
||||
sdp = init_sbd(sb);
|
||||
@@ -1107,7 +1128,6 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_op = &gfs2_super_ops;
|
||||
sb->s_d_op = &gfs2_dops;
|
||||
sb->s_export_op = &gfs2_export_ops;
|
||||
sb->s_xattr = gfs2_xattr_handlers;
|
||||
sb->s_qcop = &gfs2_quotactl_ops;
|
||||
sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
|
||||
sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
|
||||
@@ -1156,6 +1176,10 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (error)
|
||||
goto fail_locking;
|
||||
|
||||
/* Turn rgrplvb on by default if fs format is recent enough */
|
||||
if (!sdp->sd_args.ar_got_rgrplvb && sdp->sd_sb.sb_fs_format > 1801)
|
||||
sdp->sd_args.ar_rgrplvb = 1;
|
||||
|
||||
error = wait_on_journal(sdp);
|
||||
if (error)
|
||||
goto fail_sb;
|
||||
@@ -1195,25 +1219,18 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
goto fail_per_node;
|
||||
}
|
||||
|
||||
if (sb_rdonly(sb)) {
|
||||
struct gfs2_holder freeze_gh;
|
||||
error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
|
||||
if (error)
|
||||
goto fail_per_node;
|
||||
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED,
|
||||
LM_FLAG_NOEXP | GL_EXACT,
|
||||
&freeze_gh);
|
||||
if (error) {
|
||||
fs_err(sdp, "can't make FS RO: %d\n", error);
|
||||
goto fail_per_node;
|
||||
}
|
||||
gfs2_glock_dq_uninit(&freeze_gh);
|
||||
} else {
|
||||
if (!sb_rdonly(sb))
|
||||
error = gfs2_make_fs_rw(sdp);
|
||||
if (error) {
|
||||
fs_err(sdp, "can't make FS RW: %d\n", error);
|
||||
goto fail_per_node;
|
||||
}
|
||||
}
|
||||
|
||||
gfs2_freeze_unlock(&freeze_gh);
|
||||
if (error) {
|
||||
fs_err(sdp, "can't make FS RW: %d\n", error);
|
||||
goto fail_per_node;
|
||||
}
|
||||
gfs2_glock_dq_uninit(&mount_gh);
|
||||
gfs2_online_uevent(sdp);
|
||||
return 0;
|
||||
@@ -1456,6 +1473,7 @@ static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
break;
|
||||
case Opt_rgrplvb:
|
||||
args->ar_rgrplvb = result.boolean;
|
||||
args->ar_got_rgrplvb = 1;
|
||||
break;
|
||||
case Opt_loccookie:
|
||||
args->ar_loccookie = result.boolean;
|
||||
@@ -1514,6 +1532,12 @@ static int gfs2_reconfigure(struct fs_context *fc)
|
||||
fc->sb_flags |= SB_RDONLY;
|
||||
|
||||
if ((sb->s_flags ^ fc->sb_flags) & SB_RDONLY) {
|
||||
struct gfs2_holder freeze_gh;
|
||||
|
||||
error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
|
||||
if (error)
|
||||
return -EINVAL;
|
||||
|
||||
if (fc->sb_flags & SB_RDONLY) {
|
||||
error = gfs2_make_fs_ro(sdp);
|
||||
if (error)
|
||||
@@ -1523,6 +1547,7 @@ static int gfs2_reconfigure(struct fs_context *fc)
|
||||
if (error)
|
||||
errorfc(fc, "unable to remount read-write");
|
||||
}
|
||||
gfs2_freeze_unlock(&freeze_gh);
|
||||
}
|
||||
sdp->sd_args = *newargs;
|
||||
|
||||
|
||||
@@ -470,9 +470,7 @@ void gfs2_recover_func(struct work_struct *work)
|
||||
|
||||
/* Acquire a shared hold on the freeze lock */
|
||||
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED,
|
||||
LM_FLAG_NOEXP | LM_FLAG_PRIORITY |
|
||||
GL_EXACT, &thaw_gh);
|
||||
error = gfs2_freeze_lock(sdp, &thaw_gh, LM_FLAG_PRIORITY);
|
||||
if (error)
|
||||
goto fail_gunlock_ji;
|
||||
|
||||
@@ -507,22 +505,24 @@ void gfs2_recover_func(struct work_struct *work)
|
||||
|
||||
/* We take the sd_log_flush_lock here primarily to prevent log
|
||||
* flushes and simultaneous journal replays from stomping on
|
||||
* each other wrt sd_log_bio. */
|
||||
* each other wrt jd_log_bio. */
|
||||
down_read(&sdp->sd_log_flush_lock);
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
lops_before_scan(jd, &head, pass);
|
||||
error = foreach_descriptor(jd, head.lh_tail,
|
||||
head.lh_blkno, pass);
|
||||
lops_after_scan(jd, error, pass);
|
||||
if (error)
|
||||
if (error) {
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
goto fail_gunlock_thaw;
|
||||
}
|
||||
}
|
||||
|
||||
recover_local_statfs(jd, &head);
|
||||
clean_journal(jd, &head);
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
|
||||
gfs2_glock_dq_uninit(&thaw_gh);
|
||||
gfs2_freeze_unlock(&thaw_gh);
|
||||
t_rep = ktime_get();
|
||||
fs_info(sdp, "jid=%u: Journal replayed in %lldms [jlck:%lldms, "
|
||||
"jhead:%lldms, tlck:%lldms, replay:%lldms]\n",
|
||||
@@ -544,7 +544,7 @@ void gfs2_recover_func(struct work_struct *work)
|
||||
goto done;
|
||||
|
||||
fail_gunlock_thaw:
|
||||
gfs2_glock_dq_uninit(&thaw_gh);
|
||||
gfs2_freeze_unlock(&thaw_gh);
|
||||
fail_gunlock_ji:
|
||||
if (jlocked) {
|
||||
gfs2_glock_dq_uninit(&ji_gh);
|
||||
|
||||
442
fs/gfs2/rgrp.c
442
fs/gfs2/rgrp.c
@@ -36,6 +36,24 @@
|
||||
#define BFITNOENT ((u32)~0)
|
||||
#define NO_BLOCK ((u64)~0)
|
||||
|
||||
struct gfs2_rbm {
|
||||
struct gfs2_rgrpd *rgd;
|
||||
u32 offset; /* The offset is bitmap relative */
|
||||
int bii; /* Bitmap index */
|
||||
};
|
||||
|
||||
static inline struct gfs2_bitmap *rbm_bi(const struct gfs2_rbm *rbm)
|
||||
{
|
||||
return rbm->rgd->rd_bits + rbm->bii;
|
||||
}
|
||||
|
||||
static inline u64 gfs2_rbm_to_block(const struct gfs2_rbm *rbm)
|
||||
{
|
||||
BUG_ON(rbm->offset >= rbm->rgd->rd_data);
|
||||
return rbm->rgd->rd_data0 + (rbm_bi(rbm)->bi_start * GFS2_NBBY) +
|
||||
rbm->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* These routines are used by the resource group routines (rgrp.c)
|
||||
* to keep track of block allocation. Each block is represented by two
|
||||
@@ -61,7 +79,7 @@ static const char valid_change[16] = {
|
||||
};
|
||||
|
||||
static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
|
||||
const struct gfs2_inode *ip, bool nowrap);
|
||||
struct gfs2_blkreserv *rs, bool nowrap);
|
||||
|
||||
|
||||
/**
|
||||
@@ -175,7 +193,7 @@ static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state)
|
||||
|
||||
/**
|
||||
* rs_cmp - multi-block reservation range compare
|
||||
* @blk: absolute file system block number of the new reservation
|
||||
* @start: start of the new reservation
|
||||
* @len: number of blocks in the new reservation
|
||||
* @rs: existing reservation to compare against
|
||||
*
|
||||
@@ -183,13 +201,11 @@ static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state)
|
||||
* -1 if the block range is before the start of the reservation
|
||||
* 0 if the block range overlaps with the reservation
|
||||
*/
|
||||
static inline int rs_cmp(u64 blk, u32 len, struct gfs2_blkreserv *rs)
|
||||
static inline int rs_cmp(u64 start, u32 len, struct gfs2_blkreserv *rs)
|
||||
{
|
||||
u64 startblk = gfs2_rbm_to_block(&rs->rs_rbm);
|
||||
|
||||
if (blk >= startblk + rs->rs_free)
|
||||
if (start >= rs->rs_start + rs->rs_requested)
|
||||
return 1;
|
||||
if (blk + len - 1 < startblk)
|
||||
if (rs->rs_start >= start + len)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -277,29 +293,38 @@ static int gfs2_rbm_from_block(struct gfs2_rbm *rbm, u64 block)
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_rbm_incr - increment an rbm structure
|
||||
* gfs2_rbm_add - add a number of blocks to an rbm
|
||||
* @rbm: The rbm with rgd already set correctly
|
||||
* @blocks: The number of blocks to add to rpm
|
||||
*
|
||||
* This function takes an existing rbm structure and increments it to the next
|
||||
* viable block offset.
|
||||
*
|
||||
* Returns: If incrementing the offset would cause the rbm to go past the
|
||||
* end of the rgrp, true is returned, otherwise false.
|
||||
* This function takes an existing rbm structure and adds a number of blocks to
|
||||
* it.
|
||||
*
|
||||
* Returns: True if the new rbm would point past the end of the rgrp.
|
||||
*/
|
||||
|
||||
static bool gfs2_rbm_incr(struct gfs2_rbm *rbm)
|
||||
static bool gfs2_rbm_add(struct gfs2_rbm *rbm, u32 blocks)
|
||||
{
|
||||
if (rbm->offset + 1 < rbm_bi(rbm)->bi_blocks) { /* in the same bitmap */
|
||||
rbm->offset++;
|
||||
struct gfs2_rgrpd *rgd = rbm->rgd;
|
||||
struct gfs2_bitmap *bi = rgd->rd_bits + rbm->bii;
|
||||
|
||||
if (rbm->offset + blocks < bi->bi_blocks) {
|
||||
rbm->offset += blocks;
|
||||
return false;
|
||||
}
|
||||
if (rbm->bii == rbm->rgd->rd_length - 1) /* at the last bitmap */
|
||||
return true;
|
||||
blocks -= bi->bi_blocks - rbm->offset;
|
||||
|
||||
rbm->offset = 0;
|
||||
rbm->bii++;
|
||||
return false;
|
||||
for(;;) {
|
||||
bi++;
|
||||
if (bi == rgd->rd_bits + rgd->rd_length)
|
||||
return true;
|
||||
if (blocks < bi->bi_blocks) {
|
||||
rbm->offset = blocks;
|
||||
rbm->bii = bi - rgd->rd_bits;
|
||||
return false;
|
||||
}
|
||||
blocks -= bi->bi_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +333,8 @@ static bool gfs2_rbm_incr(struct gfs2_rbm *rbm)
|
||||
* @n_unaligned: Number of unaligned blocks to check
|
||||
* @len: Decremented for each block found (terminate on zero)
|
||||
*
|
||||
* Returns: true if a non-free block is encountered
|
||||
* Returns: true if a non-free block is encountered or the end of the resource
|
||||
* group is reached.
|
||||
*/
|
||||
|
||||
static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, u32 n_unaligned, u32 *len)
|
||||
@@ -323,7 +349,7 @@ static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, u32 n_unaligned, u32 *le
|
||||
(*len)--;
|
||||
if (*len == 0)
|
||||
return true;
|
||||
if (gfs2_rbm_incr(rbm))
|
||||
if (gfs2_rbm_add(rbm, 1))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -595,10 +621,11 @@ static void dump_rs(struct seq_file *seq, const struct gfs2_blkreserv *rs,
|
||||
{
|
||||
struct gfs2_inode *ip = container_of(rs, struct gfs2_inode, i_res);
|
||||
|
||||
gfs2_print_dbg(seq, "%s B: n:%llu s:%llu b:%u f:%u\n", fs_id_buf,
|
||||
gfs2_print_dbg(seq, "%s B: n:%llu s:%llu f:%u\n",
|
||||
fs_id_buf,
|
||||
(unsigned long long)ip->i_no_addr,
|
||||
(unsigned long long)gfs2_rbm_to_block(&rs->rs_rbm),
|
||||
rs->rs_rbm.offset, rs->rs_free);
|
||||
(unsigned long long)rs->rs_start,
|
||||
rs->rs_requested);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -613,33 +640,22 @@ static void __rs_deltree(struct gfs2_blkreserv *rs)
|
||||
if (!gfs2_rs_active(rs))
|
||||
return;
|
||||
|
||||
rgd = rs->rs_rbm.rgd;
|
||||
rgd = rs->rs_rgd;
|
||||
trace_gfs2_rs(rs, TRACE_RS_TREEDEL);
|
||||
rb_erase(&rs->rs_node, &rgd->rd_rstree);
|
||||
RB_CLEAR_NODE(&rs->rs_node);
|
||||
|
||||
if (rs->rs_free) {
|
||||
u64 last_block = gfs2_rbm_to_block(&rs->rs_rbm) +
|
||||
rs->rs_free - 1;
|
||||
struct gfs2_rbm last_rbm = { .rgd = rs->rs_rbm.rgd, };
|
||||
struct gfs2_bitmap *start, *last;
|
||||
if (rs->rs_requested) {
|
||||
/* return requested blocks to the rgrp */
|
||||
BUG_ON(rs->rs_rgd->rd_requested < rs->rs_requested);
|
||||
rs->rs_rgd->rd_requested -= rs->rs_requested;
|
||||
|
||||
/* return reserved blocks to the rgrp */
|
||||
BUG_ON(rs->rs_rbm.rgd->rd_reserved < rs->rs_free);
|
||||
rs->rs_rbm.rgd->rd_reserved -= rs->rs_free;
|
||||
/* The rgrp extent failure point is likely not to increase;
|
||||
it will only do so if the freed blocks are somehow
|
||||
contiguous with a span of free blocks that follows. Still,
|
||||
it will force the number to be recalculated later. */
|
||||
rgd->rd_extfail_pt += rs->rs_free;
|
||||
rs->rs_free = 0;
|
||||
if (gfs2_rbm_from_block(&last_rbm, last_block))
|
||||
return;
|
||||
start = rbm_bi(&rs->rs_rbm);
|
||||
last = rbm_bi(&last_rbm);
|
||||
do
|
||||
clear_bit(GBF_FULL, &start->bi_flags);
|
||||
while (start++ != last);
|
||||
rgd->rd_extfail_pt += rs->rs_requested;
|
||||
rs->rs_requested = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,11 +668,11 @@ void gfs2_rs_deltree(struct gfs2_blkreserv *rs)
|
||||
{
|
||||
struct gfs2_rgrpd *rgd;
|
||||
|
||||
rgd = rs->rs_rbm.rgd;
|
||||
rgd = rs->rs_rgd;
|
||||
if (rgd) {
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
__rs_deltree(rs);
|
||||
BUG_ON(rs->rs_free);
|
||||
BUG_ON(rs->rs_requested);
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
}
|
||||
}
|
||||
@@ -904,6 +920,7 @@ static int read_rindex_entry(struct gfs2_inode *ip)
|
||||
rgd->rd_data = be32_to_cpu(buf.ri_data);
|
||||
rgd->rd_bitbytes = be32_to_cpu(buf.ri_bitbytes);
|
||||
spin_lock_init(&rgd->rd_rsspin);
|
||||
mutex_init(&rgd->rd_mutex);
|
||||
|
||||
error = compute_bitstructs(rgd);
|
||||
if (error)
|
||||
@@ -1149,6 +1166,23 @@ static u32 count_unlinked(struct gfs2_rgrpd *rgd)
|
||||
return count;
|
||||
}
|
||||
|
||||
static void rgrp_set_bitmap_flags(struct gfs2_rgrpd *rgd)
|
||||
{
|
||||
struct gfs2_bitmap *bi;
|
||||
int x;
|
||||
|
||||
if (rgd->rd_free) {
|
||||
for (x = 0; x < rgd->rd_length; x++) {
|
||||
bi = rgd->rd_bits + x;
|
||||
clear_bit(GBF_FULL, &bi->bi_flags);
|
||||
}
|
||||
} else {
|
||||
for (x = 0; x < rgd->rd_length; x++) {
|
||||
bi = rgd->rd_bits + x;
|
||||
set_bit(GBF_FULL, &bi->bi_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_rgrp_bh_get - Read in a RG's header and bitmaps
|
||||
@@ -1192,11 +1226,11 @@ static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
|
||||
}
|
||||
|
||||
if (!(rgd->rd_flags & GFS2_RDF_UPTODATE)) {
|
||||
for (x = 0; x < length; x++)
|
||||
clear_bit(GBF_FULL, &rgd->rd_bits[x].bi_flags);
|
||||
gfs2_rgrp_in(rgd, (rgd->rd_bits[0].bi_bh)->b_data);
|
||||
rgrp_set_bitmap_flags(rgd);
|
||||
rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK);
|
||||
rgd->rd_free_clone = rgd->rd_free;
|
||||
BUG_ON(rgd->rd_reserved);
|
||||
/* max out the rgrp allocation failure point */
|
||||
rgd->rd_extfail_pt = rgd->rd_free;
|
||||
}
|
||||
@@ -1244,7 +1278,11 @@ static int update_rgrp_lvb(struct gfs2_rgrpd *rgd)
|
||||
if (rgd->rd_rgl->rl_unlinked == 0)
|
||||
rgd->rd_flags &= ~GFS2_RDF_CHECK;
|
||||
rgd->rd_free = be32_to_cpu(rgd->rd_rgl->rl_free);
|
||||
rgrp_set_bitmap_flags(rgd);
|
||||
rgd->rd_free_clone = rgd->rd_free;
|
||||
BUG_ON(rgd->rd_reserved);
|
||||
/* max out the rgrp allocation failure point */
|
||||
rgd->rd_extfail_pt = rgd->rd_free;
|
||||
rgd->rd_dinodes = be32_to_cpu(rgd->rd_rgl->rl_dinodes);
|
||||
rgd->rd_igeneration = be64_to_cpu(rgd->rd_rgl->rl_igeneration);
|
||||
return 0;
|
||||
@@ -1404,7 +1442,8 @@ int gfs2_fitrim(struct file *filp, void __user *argp)
|
||||
|
||||
while (1) {
|
||||
|
||||
ret = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &gh);
|
||||
ret = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &gh);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -1412,9 +1451,11 @@ int gfs2_fitrim(struct file *filp, void __user *argp)
|
||||
/* Trim each bitmap in the rgrp */
|
||||
for (x = 0; x < rgd->rd_length; x++) {
|
||||
struct gfs2_bitmap *bi = rgd->rd_bits + x;
|
||||
rgrp_lock_local(rgd);
|
||||
ret = gfs2_rgrp_send_discards(sdp,
|
||||
rgd->rd_data0, NULL, bi, minlen,
|
||||
&amt);
|
||||
rgrp_unlock_local(rgd);
|
||||
if (ret) {
|
||||
gfs2_glock_dq_uninit(&gh);
|
||||
goto out;
|
||||
@@ -1426,9 +1467,11 @@ int gfs2_fitrim(struct file *filp, void __user *argp)
|
||||
ret = gfs2_trans_begin(sdp, RES_RG_HDR, 0);
|
||||
if (ret == 0) {
|
||||
bh = rgd->rd_bits[0].bi_bh;
|
||||
rgrp_lock_local(rgd);
|
||||
rgd->rd_flags |= GFS2_RGF_TRIMMED;
|
||||
gfs2_trans_add_meta(rgd->rd_gl, bh);
|
||||
gfs2_rgrp_out(rgd, bh->b_data);
|
||||
rgrp_unlock_local(rgd);
|
||||
gfs2_trans_end(sdp);
|
||||
}
|
||||
}
|
||||
@@ -1458,8 +1501,7 @@ static void rs_insert(struct gfs2_inode *ip)
|
||||
struct rb_node **newn, *parent = NULL;
|
||||
int rc;
|
||||
struct gfs2_blkreserv *rs = &ip->i_res;
|
||||
struct gfs2_rgrpd *rgd = rs->rs_rbm.rgd;
|
||||
u64 fsblock = gfs2_rbm_to_block(&rs->rs_rbm);
|
||||
struct gfs2_rgrpd *rgd = rs->rs_rgd;
|
||||
|
||||
BUG_ON(gfs2_rs_active(rs));
|
||||
|
||||
@@ -1470,7 +1512,7 @@ static void rs_insert(struct gfs2_inode *ip)
|
||||
rb_entry(*newn, struct gfs2_blkreserv, rs_node);
|
||||
|
||||
parent = *newn;
|
||||
rc = rs_cmp(fsblock, rs->rs_free, cur);
|
||||
rc = rs_cmp(rs->rs_start, rs->rs_requested, cur);
|
||||
if (rc > 0)
|
||||
newn = &((*newn)->rb_right);
|
||||
else if (rc < 0)
|
||||
@@ -1486,7 +1528,7 @@ static void rs_insert(struct gfs2_inode *ip)
|
||||
rb_insert_color(&rs->rs_node, &rgd->rd_rstree);
|
||||
|
||||
/* Do our rgrp accounting for the reservation */
|
||||
rgd->rd_reserved += rs->rs_free; /* blocks reserved */
|
||||
rgd->rd_requested += rs->rs_requested; /* blocks requested */
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
trace_gfs2_rs(rs, TRACE_RS_INSERT);
|
||||
}
|
||||
@@ -1507,9 +1549,9 @@ static inline u32 rgd_free(struct gfs2_rgrpd *rgd, struct gfs2_blkreserv *rs)
|
||||
{
|
||||
u32 tot_reserved, tot_free;
|
||||
|
||||
if (WARN_ON_ONCE(rgd->rd_reserved < rs->rs_free))
|
||||
if (WARN_ON_ONCE(rgd->rd_requested < rs->rs_requested))
|
||||
return 0;
|
||||
tot_reserved = rgd->rd_reserved - rs->rs_free;
|
||||
tot_reserved = rgd->rd_requested - rs->rs_requested;
|
||||
|
||||
if (rgd->rd_free_clone < tot_reserved)
|
||||
tot_reserved = 0;
|
||||
@@ -1534,17 +1576,26 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
|
||||
u64 goal;
|
||||
struct gfs2_blkreserv *rs = &ip->i_res;
|
||||
u32 extlen;
|
||||
u32 free_blocks = rgd_free(rgd, rs);
|
||||
u32 free_blocks, blocks_available;
|
||||
int ret;
|
||||
struct inode *inode = &ip->i_inode;
|
||||
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
free_blocks = rgd_free(rgd, rs);
|
||||
if (rgd->rd_free_clone < rgd->rd_requested)
|
||||
free_blocks = 0;
|
||||
blocks_available = rgd->rd_free_clone - rgd->rd_reserved;
|
||||
if (rgd == rs->rs_rgd)
|
||||
blocks_available += rs->rs_reserved;
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
extlen = 1;
|
||||
else {
|
||||
extlen = max_t(u32, atomic_read(&ip->i_sizehint), ap->target);
|
||||
extlen = clamp(extlen, (u32)RGRP_RSRV_MINBLKS, free_blocks);
|
||||
}
|
||||
if ((rgd->rd_free_clone < rgd->rd_reserved) || (free_blocks < extlen))
|
||||
if (free_blocks < extlen || blocks_available < extlen)
|
||||
return;
|
||||
|
||||
/* Find bitmap block that contains bits for goal block */
|
||||
@@ -1556,10 +1607,10 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
|
||||
if (WARN_ON(gfs2_rbm_from_block(&rbm, goal)))
|
||||
return;
|
||||
|
||||
ret = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, &extlen, ip, true);
|
||||
ret = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, &extlen, &ip->i_res, true);
|
||||
if (ret == 0) {
|
||||
rs->rs_rbm = rbm;
|
||||
rs->rs_free = extlen;
|
||||
rs->rs_start = gfs2_rbm_to_block(&rbm);
|
||||
rs->rs_requested = extlen;
|
||||
rs_insert(ip);
|
||||
} else {
|
||||
if (goal == rgd->rd_last_alloc + rgd->rd_data0)
|
||||
@@ -1572,7 +1623,7 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
|
||||
* @rgd: The resource group
|
||||
* @block: The starting block
|
||||
* @length: The required length
|
||||
* @ip: Ignore any reservations for this inode
|
||||
* @ignore_rs: Reservation to ignore
|
||||
*
|
||||
* If the block does not appear in any reservation, then return the
|
||||
* block number unchanged. If it does appear in the reservation, then
|
||||
@@ -1582,7 +1633,7 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
|
||||
|
||||
static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
|
||||
u32 length,
|
||||
const struct gfs2_inode *ip)
|
||||
struct gfs2_blkreserv *ignore_rs)
|
||||
{
|
||||
struct gfs2_blkreserv *rs;
|
||||
struct rb_node *n;
|
||||
@@ -1602,8 +1653,8 @@ static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
|
||||
}
|
||||
|
||||
if (n) {
|
||||
while ((rs_cmp(block, length, rs) == 0) && (&ip->i_res != rs)) {
|
||||
block = gfs2_rbm_to_block(&rs->rs_rbm) + rs->rs_free;
|
||||
while (rs_cmp(block, length, rs) == 0 && rs != ignore_rs) {
|
||||
block = rs->rs_start + rs->rs_requested;
|
||||
n = n->rb_right;
|
||||
if (n == NULL)
|
||||
break;
|
||||
@@ -1618,7 +1669,7 @@ static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
|
||||
/**
|
||||
* gfs2_reservation_check_and_update - Check for reservations during block alloc
|
||||
* @rbm: The current position in the resource group
|
||||
* @ip: The inode for which we are searching for blocks
|
||||
* @rs: Our own reservation
|
||||
* @minext: The minimum extent length
|
||||
* @maxext: A pointer to the maximum extent structure
|
||||
*
|
||||
@@ -1632,20 +1683,19 @@ static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
|
||||
*/
|
||||
|
||||
static int gfs2_reservation_check_and_update(struct gfs2_rbm *rbm,
|
||||
const struct gfs2_inode *ip,
|
||||
struct gfs2_blkreserv *rs,
|
||||
u32 minext,
|
||||
struct gfs2_extent *maxext)
|
||||
{
|
||||
u64 block = gfs2_rbm_to_block(rbm);
|
||||
u32 extlen = 1;
|
||||
u64 nblock;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we have a minimum extent length, then skip over any extent
|
||||
* which is less than the min extent length in size.
|
||||
*/
|
||||
if (minext) {
|
||||
if (minext > 1) {
|
||||
extlen = gfs2_free_extlen(rbm, minext);
|
||||
if (extlen <= maxext->len)
|
||||
goto fail;
|
||||
@@ -1655,7 +1705,7 @@ static int gfs2_reservation_check_and_update(struct gfs2_rbm *rbm,
|
||||
* Check the extent which has been found against the reservations
|
||||
* and skip if parts of it are already reserved
|
||||
*/
|
||||
nblock = gfs2_next_unreserved_block(rbm->rgd, block, extlen, ip);
|
||||
nblock = gfs2_next_unreserved_block(rbm->rgd, block, extlen, rs);
|
||||
if (nblock == block) {
|
||||
if (!minext || extlen >= minext)
|
||||
return 0;
|
||||
@@ -1664,12 +1714,15 @@ static int gfs2_reservation_check_and_update(struct gfs2_rbm *rbm,
|
||||
maxext->len = extlen;
|
||||
maxext->rbm = *rbm;
|
||||
}
|
||||
fail:
|
||||
nblock = block + extlen;
|
||||
} else {
|
||||
u64 len = nblock - block;
|
||||
if (len >= (u64)1 << 32)
|
||||
return -E2BIG;
|
||||
extlen = len;
|
||||
}
|
||||
ret = gfs2_rbm_from_block(rbm, nblock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fail:
|
||||
if (gfs2_rbm_add(rbm, extlen))
|
||||
return -E2BIG;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1677,9 +1730,9 @@ fail:
|
||||
* gfs2_rbm_find - Look for blocks of a particular state
|
||||
* @rbm: Value/result starting position and final position
|
||||
* @state: The state which we want to find
|
||||
* @minext: Pointer to the requested extent length (NULL for a single block)
|
||||
* @minext: Pointer to the requested extent length
|
||||
* This is updated to be the actual reservation size.
|
||||
* @ip: If set, check for reservations
|
||||
* @rs: Our own reservation (NULL to skip checking for reservations)
|
||||
* @nowrap: Stop looking at the end of the rgrp, rather than wrapping
|
||||
* around until we've reached the starting point.
|
||||
*
|
||||
@@ -1693,7 +1746,7 @@ fail:
|
||||
*/
|
||||
|
||||
static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
|
||||
const struct gfs2_inode *ip, bool nowrap)
|
||||
struct gfs2_blkreserv *rs, bool nowrap)
|
||||
{
|
||||
bool scan_from_start = rbm->bii == 0 && rbm->offset == 0;
|
||||
struct buffer_head *bh;
|
||||
@@ -1714,8 +1767,7 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
|
||||
|
||||
while(1) {
|
||||
bi = rbm_bi(rbm);
|
||||
if ((ip == NULL || !gfs2_rs_active(&ip->i_res)) &&
|
||||
test_bit(GBF_FULL, &bi->bi_flags) &&
|
||||
if (test_bit(GBF_FULL, &bi->bi_flags) &&
|
||||
(state == GFS2_BLKST_FREE))
|
||||
goto next_bitmap;
|
||||
|
||||
@@ -1731,11 +1783,10 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
|
||||
goto next_bitmap;
|
||||
}
|
||||
rbm->offset = offset;
|
||||
if (ip == NULL)
|
||||
if (!rs)
|
||||
return 0;
|
||||
|
||||
ret = gfs2_reservation_check_and_update(rbm, ip,
|
||||
minext ? *minext : 0,
|
||||
ret = gfs2_reservation_check_and_update(rbm, rs, *minext,
|
||||
&maxext);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
@@ -1767,7 +1818,7 @@ next_iter:
|
||||
break;
|
||||
}
|
||||
|
||||
if (minext == NULL || state != GFS2_BLKST_FREE)
|
||||
if (state != GFS2_BLKST_FREE)
|
||||
return -ENOSPC;
|
||||
|
||||
/* If the extent was too small, and it's smaller than the smallest
|
||||
@@ -1775,7 +1826,7 @@ next_iter:
|
||||
useless to search this rgrp again for this amount or more. */
|
||||
if (wrapped && (scan_from_start || rbm->bii > last_bii) &&
|
||||
*minext < rbm->rgd->rd_extfail_pt)
|
||||
rbm->rgd->rd_extfail_pt = *minext;
|
||||
rbm->rgd->rd_extfail_pt = *minext - 1;
|
||||
|
||||
/* If the maximum extent we found is big enough to fulfill the
|
||||
minimum requirements, use it anyway. */
|
||||
@@ -1938,7 +1989,7 @@ static bool gfs2_rgrp_used_recently(const struct gfs2_blkreserv *rs,
|
||||
u64 tdiff;
|
||||
|
||||
tdiff = ktime_to_ns(ktime_sub(ktime_get_real(),
|
||||
rs->rs_rbm.rgd->rd_gl->gl_dstamp));
|
||||
rs->rs_rgd->rd_gl->gl_dstamp));
|
||||
|
||||
return tdiff > (msecs * 1000 * 1000);
|
||||
}
|
||||
@@ -1993,8 +2044,7 @@ static inline int fast_to_acquire(struct gfs2_rgrpd *rgd)
|
||||
* We try our best to find an rgrp that has at least ap->target blocks
|
||||
* available. After a couple of passes (loops == 2), the prospects of finding
|
||||
* such an rgrp diminish. At this stage, we return the first rgrp that has
|
||||
* at least ap->min_target blocks available. Either way, we set ap->allowed to
|
||||
* the number of blocks available in the chosen rgrp.
|
||||
* at least ap->min_target blocks available.
|
||||
*
|
||||
* Returns: 0 on success,
|
||||
* -ENOMEM if a suitable rgrp can't be found
|
||||
@@ -2006,56 +2056,64 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap)
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||
struct gfs2_rgrpd *begin = NULL;
|
||||
struct gfs2_blkreserv *rs = &ip->i_res;
|
||||
int error = 0, rg_locked, flags = 0;
|
||||
int error = 0, flags = LM_FLAG_NODE_SCOPE;
|
||||
bool rg_locked;
|
||||
u64 last_unlinked = NO_BLOCK;
|
||||
u32 target = ap->target;
|
||||
int loops = 0;
|
||||
u32 free_blocks, skip = 0;
|
||||
u32 free_blocks, blocks_available, skip = 0;
|
||||
|
||||
BUG_ON(rs->rs_reserved);
|
||||
|
||||
if (sdp->sd_args.ar_rgrplvb)
|
||||
flags |= GL_SKIP;
|
||||
if (gfs2_assert_warn(sdp, ap->target))
|
||||
if (gfs2_assert_warn(sdp, target))
|
||||
return -EINVAL;
|
||||
if (gfs2_rs_active(rs)) {
|
||||
begin = rs->rs_rbm.rgd;
|
||||
} else if (rs->rs_rbm.rgd &&
|
||||
rgrp_contains_block(rs->rs_rbm.rgd, ip->i_goal)) {
|
||||
begin = rs->rs_rbm.rgd;
|
||||
begin = rs->rs_rgd;
|
||||
} else if (rs->rs_rgd &&
|
||||
rgrp_contains_block(rs->rs_rgd, ip->i_goal)) {
|
||||
begin = rs->rs_rgd;
|
||||
} else {
|
||||
check_and_update_goal(ip);
|
||||
rs->rs_rbm.rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
|
||||
rs->rs_rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
|
||||
}
|
||||
if (S_ISDIR(ip->i_inode.i_mode) && (ap->aflags & GFS2_AF_ORLOV))
|
||||
skip = gfs2_orlov_skip(ip);
|
||||
if (rs->rs_rbm.rgd == NULL)
|
||||
if (rs->rs_rgd == NULL)
|
||||
return -EBADSLT;
|
||||
|
||||
while (loops < 3) {
|
||||
rg_locked = 1;
|
||||
struct gfs2_rgrpd *rgd;
|
||||
|
||||
if (!gfs2_glock_is_locked_by_me(rs->rs_rbm.rgd->rd_gl)) {
|
||||
rg_locked = 0;
|
||||
rg_locked = gfs2_glock_is_locked_by_me(rs->rs_rgd->rd_gl);
|
||||
if (rg_locked) {
|
||||
rgrp_lock_local(rs->rs_rgd);
|
||||
} else {
|
||||
if (skip && skip--)
|
||||
goto next_rgrp;
|
||||
if (!gfs2_rs_active(rs)) {
|
||||
if (loops == 0 &&
|
||||
!fast_to_acquire(rs->rs_rbm.rgd))
|
||||
!fast_to_acquire(rs->rs_rgd))
|
||||
goto next_rgrp;
|
||||
if ((loops < 2) &&
|
||||
gfs2_rgrp_used_recently(rs, 1000) &&
|
||||
gfs2_rgrp_congested(rs->rs_rbm.rgd, loops))
|
||||
gfs2_rgrp_congested(rs->rs_rgd, loops))
|
||||
goto next_rgrp;
|
||||
}
|
||||
error = gfs2_glock_nq_init(rs->rs_rbm.rgd->rd_gl,
|
||||
error = gfs2_glock_nq_init(rs->rs_rgd->rd_gl,
|
||||
LM_ST_EXCLUSIVE, flags,
|
||||
&ip->i_rgd_gh);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
rgrp_lock_local(rs->rs_rgd);
|
||||
if (!gfs2_rs_active(rs) && (loops < 2) &&
|
||||
gfs2_rgrp_congested(rs->rs_rbm.rgd, loops))
|
||||
gfs2_rgrp_congested(rs->rs_rgd, loops))
|
||||
goto skip_rgrp;
|
||||
if (sdp->sd_args.ar_rgrplvb) {
|
||||
error = update_rgrp_lvb(rs->rs_rbm.rgd);
|
||||
error = update_rgrp_lvb(rs->rs_rgd);
|
||||
if (unlikely(error)) {
|
||||
rgrp_unlock_local(rs->rs_rgd);
|
||||
gfs2_glock_dq_uninit(&ip->i_rgd_gh);
|
||||
return error;
|
||||
}
|
||||
@@ -2063,36 +2121,46 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap)
|
||||
}
|
||||
|
||||
/* Skip unusable resource groups */
|
||||
if ((rs->rs_rbm.rgd->rd_flags & (GFS2_RGF_NOALLOC |
|
||||
if ((rs->rs_rgd->rd_flags & (GFS2_RGF_NOALLOC |
|
||||
GFS2_RDF_ERROR)) ||
|
||||
(loops == 0 && ap->target > rs->rs_rbm.rgd->rd_extfail_pt))
|
||||
(loops == 0 && target > rs->rs_rgd->rd_extfail_pt))
|
||||
goto skip_rgrp;
|
||||
|
||||
if (sdp->sd_args.ar_rgrplvb)
|
||||
gfs2_rgrp_bh_get(rs->rs_rbm.rgd);
|
||||
gfs2_rgrp_bh_get(rs->rs_rgd);
|
||||
|
||||
/* Get a reservation if we don't already have one */
|
||||
if (!gfs2_rs_active(rs))
|
||||
rg_mblk_search(rs->rs_rbm.rgd, ip, ap);
|
||||
rg_mblk_search(rs->rs_rgd, ip, ap);
|
||||
|
||||
/* Skip rgrps when we can't get a reservation on first pass */
|
||||
if (!gfs2_rs_active(rs) && (loops < 1))
|
||||
goto check_rgrp;
|
||||
|
||||
/* If rgrp has enough free space, use it */
|
||||
free_blocks = rgd_free(rs->rs_rbm.rgd, rs);
|
||||
if (free_blocks >= ap->target ||
|
||||
(loops == 2 && ap->min_target &&
|
||||
free_blocks >= ap->min_target)) {
|
||||
ap->allowed = free_blocks;
|
||||
return 0;
|
||||
rgd = rs->rs_rgd;
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
free_blocks = rgd_free(rgd, rs);
|
||||
blocks_available = rgd->rd_free_clone - rgd->rd_reserved;
|
||||
if (free_blocks < target || blocks_available < target) {
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
goto check_rgrp;
|
||||
}
|
||||
rs->rs_reserved = ap->target;
|
||||
if (rs->rs_reserved > blocks_available)
|
||||
rs->rs_reserved = blocks_available;
|
||||
rgd->rd_reserved += rs->rs_reserved;
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
rgrp_unlock_local(rs->rs_rgd);
|
||||
return 0;
|
||||
check_rgrp:
|
||||
/* Check for unlinked inodes which can be reclaimed */
|
||||
if (rs->rs_rbm.rgd->rd_flags & GFS2_RDF_CHECK)
|
||||
try_rgrp_unlink(rs->rs_rbm.rgd, &last_unlinked,
|
||||
if (rs->rs_rgd->rd_flags & GFS2_RDF_CHECK)
|
||||
try_rgrp_unlink(rs->rs_rgd, &last_unlinked,
|
||||
ip->i_no_addr);
|
||||
skip_rgrp:
|
||||
rgrp_unlock_local(rs->rs_rgd);
|
||||
|
||||
/* Drop reservation, if we couldn't use reserved rgrp */
|
||||
if (gfs2_rs_active(rs))
|
||||
gfs2_rs_deltree(rs);
|
||||
@@ -2102,7 +2170,7 @@ skip_rgrp:
|
||||
gfs2_glock_dq_uninit(&ip->i_rgd_gh);
|
||||
next_rgrp:
|
||||
/* Find the next rgrp, and continue looking */
|
||||
if (gfs2_select_rgrp(&rs->rs_rbm.rgd, begin))
|
||||
if (gfs2_select_rgrp(&rs->rs_rgd, begin))
|
||||
continue;
|
||||
if (skip)
|
||||
continue;
|
||||
@@ -2119,9 +2187,12 @@ next_rgrp:
|
||||
return error;
|
||||
}
|
||||
/* Flushing the log may release space */
|
||||
if (loops == 2)
|
||||
if (loops == 2) {
|
||||
if (ap->min_target)
|
||||
target = ap->min_target;
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_INPLACE_RESERVE);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
@@ -2136,6 +2207,17 @@ next_rgrp:
|
||||
|
||||
void gfs2_inplace_release(struct gfs2_inode *ip)
|
||||
{
|
||||
struct gfs2_blkreserv *rs = &ip->i_res;
|
||||
|
||||
if (rs->rs_reserved) {
|
||||
struct gfs2_rgrpd *rgd = rs->rs_rgd;
|
||||
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
BUG_ON(rgd->rd_reserved < rs->rs_reserved);
|
||||
rgd->rd_reserved -= rs->rs_reserved;
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
rs->rs_reserved = 0;
|
||||
}
|
||||
if (gfs2_holder_initialized(&ip->i_rgd_gh))
|
||||
gfs2_glock_dq_uninit(&ip->i_rgd_gh);
|
||||
}
|
||||
@@ -2205,7 +2287,7 @@ static void rgblk_free(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd,
|
||||
bi_prev = bi;
|
||||
}
|
||||
gfs2_setbit(&rbm, false, new_state);
|
||||
gfs2_rbm_incr(&rbm);
|
||||
gfs2_rbm_add(&rbm, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2223,11 +2305,12 @@ void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_rgrpd *rgd,
|
||||
struct gfs2_blkreserv *trs;
|
||||
const struct rb_node *n;
|
||||
|
||||
gfs2_print_dbg(seq, "%s R: n:%llu f:%02x b:%u/%u i:%u r:%u e:%u\n",
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
gfs2_print_dbg(seq, "%s R: n:%llu f:%02x b:%u/%u i:%u q:%u r:%u e:%u\n",
|
||||
fs_id_buf,
|
||||
(unsigned long long)rgd->rd_addr, rgd->rd_flags,
|
||||
rgd->rd_free, rgd->rd_free_clone, rgd->rd_dinodes,
|
||||
rgd->rd_reserved, rgd->rd_extfail_pt);
|
||||
rgd->rd_requested, rgd->rd_reserved, rgd->rd_extfail_pt);
|
||||
if (rgd->rd_sbd->sd_args.ar_rgrplvb) {
|
||||
struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl;
|
||||
|
||||
@@ -2236,7 +2319,6 @@ void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_rgrpd *rgd,
|
||||
be32_to_cpu(rgl->rl_free),
|
||||
be32_to_cpu(rgl->rl_dinodes));
|
||||
}
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
for (n = rb_first(&rgd->rd_rstree); n; n = rb_next(&trs->rs_node)) {
|
||||
trs = rb_entry(n, struct gfs2_blkreserv, rs_node);
|
||||
dump_rs(seq, trs, fs_id_buf);
|
||||
@@ -2273,29 +2355,29 @@ static void gfs2_adjust_reservation(struct gfs2_inode *ip,
|
||||
{
|
||||
struct gfs2_blkreserv *rs = &ip->i_res;
|
||||
struct gfs2_rgrpd *rgd = rbm->rgd;
|
||||
unsigned rlen;
|
||||
u64 block;
|
||||
int ret;
|
||||
|
||||
spin_lock(&rgd->rd_rsspin);
|
||||
BUG_ON(rs->rs_reserved < len);
|
||||
rs->rs_reserved -= len;
|
||||
if (gfs2_rs_active(rs)) {
|
||||
if (gfs2_rbm_eq(&rs->rs_rbm, rbm)) {
|
||||
block = gfs2_rbm_to_block(rbm);
|
||||
ret = gfs2_rbm_from_block(&rs->rs_rbm, block + len);
|
||||
rlen = min(rs->rs_free, len);
|
||||
rs->rs_free -= rlen;
|
||||
rgd->rd_reserved -= rlen;
|
||||
u64 start = gfs2_rbm_to_block(rbm);
|
||||
|
||||
if (rs->rs_start == start) {
|
||||
unsigned int rlen;
|
||||
|
||||
rs->rs_start += len;
|
||||
rlen = min(rs->rs_requested, len);
|
||||
rs->rs_requested -= rlen;
|
||||
rgd->rd_requested -= rlen;
|
||||
trace_gfs2_rs(rs, TRACE_RS_CLAIM);
|
||||
if (rs->rs_free && !ret)
|
||||
goto out;
|
||||
if (rs->rs_start < rgd->rd_data0 + rgd->rd_data &&
|
||||
rs->rs_requested)
|
||||
return;
|
||||
/* We used up our block reservation, so we should
|
||||
reserve more blocks next time. */
|
||||
atomic_add(RGRP_RSRV_ADDBLKS, &ip->i_sizehint);
|
||||
}
|
||||
__rs_deltree(rs);
|
||||
}
|
||||
out:
|
||||
spin_unlock(&rgd->rd_rsspin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2315,15 +2397,13 @@ static void gfs2_set_alloc_start(struct gfs2_rbm *rbm,
|
||||
u64 goal;
|
||||
|
||||
if (gfs2_rs_active(&ip->i_res)) {
|
||||
*rbm = ip->i_res.rs_rbm;
|
||||
return;
|
||||
goal = ip->i_res.rs_start;
|
||||
} else {
|
||||
if (!dinode && rgrp_contains_block(rbm->rgd, ip->i_goal))
|
||||
goal = ip->i_goal;
|
||||
else
|
||||
goal = rbm->rgd->rd_last_alloc + rbm->rgd->rd_data0;
|
||||
}
|
||||
|
||||
if (!dinode && rgrp_contains_block(rbm->rgd, ip->i_goal))
|
||||
goal = ip->i_goal;
|
||||
else
|
||||
goal = rbm->rgd->rd_last_alloc + rbm->rgd->rd_data0;
|
||||
|
||||
if (WARN_ON_ONCE(gfs2_rbm_from_block(rbm, goal))) {
|
||||
rbm->bii = 0;
|
||||
rbm->offset = 0;
|
||||
@@ -2346,17 +2426,21 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
|
||||
{
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||
struct buffer_head *dibh;
|
||||
struct gfs2_rbm rbm = { .rgd = ip->i_res.rs_rbm.rgd, };
|
||||
unsigned int ndata;
|
||||
struct gfs2_rbm rbm = { .rgd = ip->i_res.rs_rgd, };
|
||||
u64 block; /* block, within the file system scope */
|
||||
int error;
|
||||
u32 minext = 1;
|
||||
int error = -ENOSPC;
|
||||
|
||||
gfs2_set_alloc_start(&rbm, ip, dinode);
|
||||
error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, NULL, ip, false);
|
||||
BUG_ON(ip->i_res.rs_reserved < *nblocks);
|
||||
|
||||
rgrp_lock_local(rbm.rgd);
|
||||
if (gfs2_rs_active(&ip->i_res)) {
|
||||
gfs2_set_alloc_start(&rbm, ip, dinode);
|
||||
error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, &minext, &ip->i_res, false);
|
||||
}
|
||||
if (error == -ENOSPC) {
|
||||
gfs2_set_alloc_start(&rbm, ip, dinode);
|
||||
error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, NULL, NULL, false);
|
||||
error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, &minext, NULL, false);
|
||||
}
|
||||
|
||||
/* Since all blocks are reserved in advance, this shouldn't happen */
|
||||
@@ -2371,14 +2455,8 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
|
||||
gfs2_alloc_extent(&rbm, dinode, nblocks);
|
||||
block = gfs2_rbm_to_block(&rbm);
|
||||
rbm.rgd->rd_last_alloc = block - rbm.rgd->rd_data0;
|
||||
if (gfs2_rs_active(&ip->i_res))
|
||||
gfs2_adjust_reservation(ip, &rbm, *nblocks);
|
||||
ndata = *nblocks;
|
||||
if (dinode)
|
||||
ndata--;
|
||||
|
||||
if (!dinode) {
|
||||
ip->i_goal = block + ndata - 1;
|
||||
ip->i_goal = block + *nblocks - 1;
|
||||
error = gfs2_meta_inode_buffer(ip, &dibh);
|
||||
if (error == 0) {
|
||||
struct gfs2_dinode *di =
|
||||
@@ -2389,12 +2467,20 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
|
||||
brelse(dibh);
|
||||
}
|
||||
}
|
||||
if (rbm.rgd->rd_free < *nblocks) {
|
||||
spin_lock(&rbm.rgd->rd_rsspin);
|
||||
gfs2_adjust_reservation(ip, &rbm, *nblocks);
|
||||
if (rbm.rgd->rd_free < *nblocks || rbm.rgd->rd_reserved < *nblocks) {
|
||||
fs_warn(sdp, "nblocks=%u\n", *nblocks);
|
||||
spin_unlock(&rbm.rgd->rd_rsspin);
|
||||
goto rgrp_error;
|
||||
}
|
||||
|
||||
BUG_ON(rbm.rgd->rd_reserved < *nblocks);
|
||||
BUG_ON(rbm.rgd->rd_free_clone < *nblocks);
|
||||
BUG_ON(rbm.rgd->rd_free < *nblocks);
|
||||
rbm.rgd->rd_reserved -= *nblocks;
|
||||
rbm.rgd->rd_free_clone -= *nblocks;
|
||||
rbm.rgd->rd_free -= *nblocks;
|
||||
spin_unlock(&rbm.rgd->rd_rsspin);
|
||||
if (dinode) {
|
||||
rbm.rgd->rd_dinodes++;
|
||||
*generation = rbm.rgd->rd_igeneration++;
|
||||
@@ -2404,6 +2490,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
|
||||
|
||||
gfs2_trans_add_meta(rbm.rgd->rd_gl, rbm.rgd->rd_bits[0].bi_bh);
|
||||
gfs2_rgrp_out(rbm.rgd, rbm.rgd->rd_bits[0].bi_bh->b_data);
|
||||
rgrp_unlock_local(rbm.rgd);
|
||||
|
||||
gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0);
|
||||
if (dinode)
|
||||
@@ -2411,13 +2498,13 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
|
||||
|
||||
gfs2_quota_change(ip, *nblocks, ip->i_inode.i_uid, ip->i_inode.i_gid);
|
||||
|
||||
rbm.rgd->rd_free_clone -= *nblocks;
|
||||
trace_gfs2_block_alloc(ip, rbm.rgd, block, *nblocks,
|
||||
dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
|
||||
*bn = block;
|
||||
return 0;
|
||||
|
||||
rgrp_error:
|
||||
rgrp_unlock_local(rbm.rgd);
|
||||
gfs2_rgrp_error(rbm.rgd);
|
||||
return -EIO;
|
||||
}
|
||||
@@ -2437,12 +2524,14 @@ void __gfs2_free_blocks(struct gfs2_inode *ip, struct gfs2_rgrpd *rgd,
|
||||
{
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||
|
||||
rgrp_lock_local(rgd);
|
||||
rgblk_free(sdp, rgd, bstart, blen, GFS2_BLKST_FREE);
|
||||
trace_gfs2_block_alloc(ip, rgd, bstart, blen, GFS2_BLKST_FREE);
|
||||
rgd->rd_free += blen;
|
||||
rgd->rd_flags &= ~GFS2_RGF_TRIMMED;
|
||||
gfs2_trans_add_meta(rgd->rd_gl, rgd->rd_bits[0].bi_bh);
|
||||
gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
|
||||
rgrp_unlock_local(rgd);
|
||||
|
||||
/* Directories keep their data in the metadata address space */
|
||||
if (meta || ip->i_depth || gfs2_is_jdata(ip))
|
||||
@@ -2478,17 +2567,20 @@ void gfs2_unlink_di(struct inode *inode)
|
||||
rgd = gfs2_blk2rgrpd(sdp, blkno, true);
|
||||
if (!rgd)
|
||||
return;
|
||||
rgrp_lock_local(rgd);
|
||||
rgblk_free(sdp, rgd, blkno, 1, GFS2_BLKST_UNLINKED);
|
||||
trace_gfs2_block_alloc(ip, rgd, blkno, 1, GFS2_BLKST_UNLINKED);
|
||||
gfs2_trans_add_meta(rgd->rd_gl, rgd->rd_bits[0].bi_bh);
|
||||
gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
|
||||
be32_add_cpu(&rgd->rd_rgl->rl_unlinked, 1);
|
||||
rgrp_unlock_local(rgd);
|
||||
}
|
||||
|
||||
void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
|
||||
{
|
||||
struct gfs2_sbd *sdp = rgd->rd_sbd;
|
||||
|
||||
rgrp_lock_local(rgd);
|
||||
rgblk_free(sdp, rgd, ip->i_no_addr, 1, GFS2_BLKST_FREE);
|
||||
if (!rgd->rd_dinodes)
|
||||
gfs2_consist_rgrpd(rgd);
|
||||
@@ -2497,6 +2589,7 @@ void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
|
||||
|
||||
gfs2_trans_add_meta(rgd->rd_gl, rgd->rd_bits[0].bi_bh);
|
||||
gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
|
||||
rgrp_unlock_local(rgd);
|
||||
be32_add_cpu(&rgd->rd_rgl->rl_unlinked, -1);
|
||||
|
||||
gfs2_statfs_change(sdp, 0, +1, -1);
|
||||
@@ -2511,6 +2604,10 @@ void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
|
||||
* @no_addr: The block number to check
|
||||
* @type: The block type we are looking for
|
||||
*
|
||||
* The inode glock of @no_addr must be held. The @type to check for is either
|
||||
* GFS2_BLKST_DINODE or GFS2_BLKST_UNLINKED; checking for type GFS2_BLKST_FREE
|
||||
* or GFS2_BLKST_USED would make no sense.
|
||||
*
|
||||
* Returns: 0 if the block type matches the expected type
|
||||
* -ESTALE if it doesn't match
|
||||
* or -ve errno if something went wrong while checking
|
||||
@@ -2534,6 +2631,13 @@ int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 no_addr, unsigned int type)
|
||||
rbm.rgd = rgd;
|
||||
error = gfs2_rbm_from_block(&rbm, no_addr);
|
||||
if (!WARN_ON_ONCE(error)) {
|
||||
/*
|
||||
* No need to take the local resource group lock here; the
|
||||
* inode glock of @no_addr provides the necessary
|
||||
* synchronization in case the block is an inode. (In case
|
||||
* the block is not an inode, the block type will not match
|
||||
* the @type we are looking for.)
|
||||
*/
|
||||
if (gfs2_testbit(&rbm, false) != type)
|
||||
error = -ESTALE;
|
||||
}
|
||||
@@ -2578,7 +2682,7 @@ void gfs2_rlist_add(struct gfs2_inode *ip, struct gfs2_rgrp_list *rlist,
|
||||
return;
|
||||
rgd = gfs2_blk2rgrpd(sdp, block, 1);
|
||||
} else {
|
||||
rgd = ip->i_res.rs_rbm.rgd;
|
||||
rgd = ip->i_res.rs_rgd;
|
||||
if (!rgd || !rgrp_contains_block(rgd, block))
|
||||
rgd = gfs2_blk2rgrpd(sdp, block, 1);
|
||||
}
|
||||
@@ -2633,9 +2737,8 @@ void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist)
|
||||
sizeof(struct gfs2_holder),
|
||||
GFP_NOFS | __GFP_NOFAIL);
|
||||
for (x = 0; x < rlist->rl_rgrps; x++)
|
||||
gfs2_holder_init(rlist->rl_rgd[x]->rd_gl,
|
||||
LM_ST_EXCLUSIVE, 0,
|
||||
&rlist->rl_ghs[x]);
|
||||
gfs2_holder_init(rlist->rl_rgd[x]->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &rlist->rl_ghs[x]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2658,3 +2761,14 @@ void gfs2_rlist_free(struct gfs2_rgrp_list *rlist)
|
||||
}
|
||||
}
|
||||
|
||||
void rgrp_lock_local(struct gfs2_rgrpd *rgd)
|
||||
{
|
||||
BUG_ON(!gfs2_glock_is_held_excl(rgd->rd_gl) &&
|
||||
!test_bit(SDF_NORECOVERY, &rgd->rd_sbd->sd_flags));
|
||||
mutex_lock(&rgd->rd_mutex);
|
||||
}
|
||||
|
||||
void rgrp_unlock_local(struct gfs2_rgrpd *rgd)
|
||||
{
|
||||
mutex_unlock(&rgd->rd_mutex);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ extern int gfs2_fitrim(struct file *filp, void __user *argp);
|
||||
/* This is how to tell if a reservation is in the rgrp tree: */
|
||||
static inline bool gfs2_rs_active(const struct gfs2_blkreserv *rs)
|
||||
{
|
||||
return rs && !RB_EMPTY_NODE(&rs->rs_node);
|
||||
return !RB_EMPTY_NODE(&rs->rs_node);
|
||||
}
|
||||
|
||||
static inline int rgrp_contains_block(struct gfs2_rgrpd *rgd, u64 block)
|
||||
@@ -88,4 +88,8 @@ static inline int rgrp_contains_block(struct gfs2_rgrpd *rgd, u64 block)
|
||||
}
|
||||
|
||||
extern void check_and_update_goal(struct gfs2_inode *ip);
|
||||
|
||||
extern void rgrp_lock_local(struct gfs2_rgrpd *rgd);
|
||||
extern void rgrp_unlock_local(struct gfs2_rgrpd *rgd);
|
||||
|
||||
#endif /* __RGRP_DOT_H__ */
|
||||
|
||||
@@ -81,19 +81,12 @@ void gfs2_jindex_free(struct gfs2_sbd *sdp)
|
||||
static struct gfs2_jdesc *jdesc_find_i(struct list_head *head, unsigned int jid)
|
||||
{
|
||||
struct gfs2_jdesc *jd;
|
||||
int found = 0;
|
||||
|
||||
list_for_each_entry(jd, head, jd_list) {
|
||||
if (jd->jd_jid == jid) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (jd->jd_jid == jid)
|
||||
return jd;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
jd = NULL;
|
||||
|
||||
return jd;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid)
|
||||
@@ -165,7 +158,6 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
|
||||
{
|
||||
struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode);
|
||||
struct gfs2_glock *j_gl = ip->i_gl;
|
||||
struct gfs2_holder freeze_gh;
|
||||
struct gfs2_log_header_host head;
|
||||
int error;
|
||||
|
||||
@@ -173,12 +165,6 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED,
|
||||
LM_FLAG_NOEXP | GL_EXACT,
|
||||
&freeze_gh);
|
||||
if (error)
|
||||
goto fail_threads;
|
||||
|
||||
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
|
||||
if (gfs2_withdrawn(sdp)) {
|
||||
error = -EIO;
|
||||
@@ -205,13 +191,9 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
|
||||
|
||||
set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
|
||||
|
||||
gfs2_glock_dq_uninit(&freeze_gh);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gfs2_glock_dq_uninit(&freeze_gh);
|
||||
fail_threads:
|
||||
if (sdp->sd_quotad_process)
|
||||
kthread_stop(sdp->sd_quotad_process);
|
||||
sdp->sd_quotad_process = NULL;
|
||||
@@ -452,7 +434,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
|
||||
}
|
||||
|
||||
if (error)
|
||||
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
|
||||
gfs2_freeze_unlock(&sdp->sd_freeze_gh);
|
||||
|
||||
out:
|
||||
while (!list_empty(&list)) {
|
||||
@@ -607,30 +589,9 @@ out:
|
||||
|
||||
int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
|
||||
{
|
||||
struct gfs2_holder freeze_gh;
|
||||
int error = 0;
|
||||
int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
|
||||
|
||||
gfs2_holder_mark_uninitialized(&freeze_gh);
|
||||
if (sdp->sd_freeze_gl &&
|
||||
!gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl)) {
|
||||
if (!log_write_allowed) {
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl,
|
||||
LM_ST_SHARED, LM_FLAG_TRY |
|
||||
LM_FLAG_NOEXP | GL_EXACT,
|
||||
&freeze_gh);
|
||||
if (error == GLR_TRYFAILED)
|
||||
error = 0;
|
||||
} else {
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl,
|
||||
LM_ST_SHARED,
|
||||
LM_FLAG_NOEXP | GL_EXACT,
|
||||
&freeze_gh);
|
||||
if (error && !gfs2_withdrawn(sdp))
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
gfs2_flush_delete_work(sdp);
|
||||
if (!log_write_allowed && current == sdp->sd_quotad_process)
|
||||
fs_warn(sdp, "The quotad daemon is withdrawing.\n");
|
||||
@@ -650,18 +611,15 @@ int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
|
||||
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_SHUTDOWN |
|
||||
GFS2_LFC_MAKE_FS_RO);
|
||||
wait_event(sdp->sd_reserving_log_wait,
|
||||
atomic_read(&sdp->sd_reserving_log) == 0);
|
||||
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) ==
|
||||
sdp->sd_jdesc->jd_blocks);
|
||||
wait_event_timeout(sdp->sd_log_waitq,
|
||||
gfs2_log_is_empty(sdp),
|
||||
HZ * 5);
|
||||
gfs2_assert_warn(sdp, gfs2_log_is_empty(sdp));
|
||||
} else {
|
||||
wait_event_timeout(sdp->sd_reserving_log_wait,
|
||||
atomic_read(&sdp->sd_reserving_log) == 0,
|
||||
wait_event_timeout(sdp->sd_log_waitq,
|
||||
gfs2_log_is_empty(sdp),
|
||||
HZ * 5);
|
||||
}
|
||||
if (gfs2_holder_initialized(&freeze_gh))
|
||||
gfs2_glock_dq_uninit(&freeze_gh);
|
||||
|
||||
gfs2_quota_cleanup(sdp);
|
||||
|
||||
if (!log_write_allowed)
|
||||
@@ -770,10 +728,8 @@ void gfs2_freeze_func(struct work_struct *work)
|
||||
struct super_block *sb = sdp->sd_vfs;
|
||||
|
||||
atomic_inc(&sb->s_active);
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED,
|
||||
LM_FLAG_NOEXP | GL_EXACT, &freeze_gh);
|
||||
error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
|
||||
if (error) {
|
||||
fs_info(sdp, "GFS2: couldn't get freeze lock : %d\n", error);
|
||||
gfs2_assert_withdraw(sdp, 0);
|
||||
} else {
|
||||
atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN);
|
||||
@@ -783,7 +739,7 @@ void gfs2_freeze_func(struct work_struct *work)
|
||||
error);
|
||||
gfs2_assert_withdraw(sdp, 0);
|
||||
}
|
||||
gfs2_glock_dq_uninit(&freeze_gh);
|
||||
gfs2_freeze_unlock(&freeze_gh);
|
||||
}
|
||||
deactivate_super(sb);
|
||||
clear_bit_unlock(SDF_FS_FROZEN, &sdp->sd_flags);
|
||||
@@ -851,7 +807,7 @@ static int gfs2_unfreeze(struct super_block *sb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
|
||||
gfs2_freeze_unlock(&sdp->sd_freeze_gh);
|
||||
mutex_unlock(&sdp->sd_freeze_mutex);
|
||||
return wait_on_bit(&sdp->sd_flags, SDF_FS_FROZEN, TASK_INTERRUPTIBLE);
|
||||
}
|
||||
@@ -1227,7 +1183,8 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip)
|
||||
goto out_qs;
|
||||
}
|
||||
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &gh);
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &gh);
|
||||
if (error)
|
||||
goto out_qs;
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <linux/dcache.h>
|
||||
#include "incore.h"
|
||||
|
||||
/* Supported fs format version range */
|
||||
#define GFS2_FS_FORMAT_MIN (1801)
|
||||
#define GFS2_FS_FORMAT_MAX (1802)
|
||||
|
||||
extern void gfs2_lm_unmount(struct gfs2_sbd *sdp);
|
||||
|
||||
static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
|
||||
@@ -54,7 +58,9 @@ extern struct file_system_type gfs2meta_fs_type;
|
||||
extern const struct export_operations gfs2_export_ops;
|
||||
extern const struct super_operations gfs2_super_ops;
|
||||
extern const struct dentry_operations gfs2_dops;
|
||||
extern const struct xattr_handler *gfs2_xattr_handlers[];
|
||||
|
||||
extern const struct xattr_handler *gfs2_xattr_handlers_max[];
|
||||
extern const struct xattr_handler **gfs2_xattr_handlers_min;
|
||||
|
||||
#endif /* __SUPER_DOT_H__ */
|
||||
|
||||
|
||||
@@ -560,6 +560,7 @@ TRACE_EVENT(gfs2_block_alloc,
|
||||
__field( u8, block_state )
|
||||
__field( u64, rd_addr )
|
||||
__field( u32, rd_free_clone )
|
||||
__field( u32, rd_requested )
|
||||
__field( u32, rd_reserved )
|
||||
),
|
||||
|
||||
@@ -571,17 +572,20 @@ TRACE_EVENT(gfs2_block_alloc,
|
||||
__entry->block_state = block_state;
|
||||
__entry->rd_addr = rgd->rd_addr;
|
||||
__entry->rd_free_clone = rgd->rd_free_clone;
|
||||
__entry->rd_requested = rgd->rd_requested;
|
||||
__entry->rd_reserved = rgd->rd_reserved;
|
||||
),
|
||||
|
||||
TP_printk("%u,%u bmap %llu alloc %llu/%lu %s rg:%llu rf:%u rr:%lu",
|
||||
TP_printk("%u,%u bmap %llu alloc %llu/%lu %s rg:%llu rf:%u rq:%u rr:%u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
(unsigned long long)__entry->inum,
|
||||
(unsigned long long)__entry->start,
|
||||
(unsigned long)__entry->len,
|
||||
block_state_name(__entry->block_state),
|
||||
(unsigned long long)__entry->rd_addr,
|
||||
__entry->rd_free_clone, (unsigned long)__entry->rd_reserved)
|
||||
__entry->rd_free_clone,
|
||||
__entry->rd_requested,
|
||||
__entry->rd_reserved)
|
||||
);
|
||||
|
||||
/* Keep track of multi-block reservations as they are allocated/freed */
|
||||
@@ -595,33 +599,40 @@ TRACE_EVENT(gfs2_rs,
|
||||
__field( dev_t, dev )
|
||||
__field( u64, rd_addr )
|
||||
__field( u32, rd_free_clone )
|
||||
__field( u32, rd_requested )
|
||||
__field( u32, rd_reserved )
|
||||
__field( u64, inum )
|
||||
__field( u64, start )
|
||||
__field( u32, free )
|
||||
__field( u32, requested )
|
||||
__field( u32, reserved )
|
||||
__field( u8, func )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = rs->rs_rbm.rgd->rd_sbd->sd_vfs->s_dev;
|
||||
__entry->rd_addr = rs->rs_rbm.rgd->rd_addr;
|
||||
__entry->rd_free_clone = rs->rs_rbm.rgd->rd_free_clone;
|
||||
__entry->rd_reserved = rs->rs_rbm.rgd->rd_reserved;
|
||||
__entry->dev = rs->rs_rgd->rd_sbd->sd_vfs->s_dev;
|
||||
__entry->rd_addr = rs->rs_rgd->rd_addr;
|
||||
__entry->rd_free_clone = rs->rs_rgd->rd_free_clone;
|
||||
__entry->rd_requested = rs->rs_rgd->rd_requested;
|
||||
__entry->rd_reserved = rs->rs_rgd->rd_reserved;
|
||||
__entry->inum = container_of(rs, struct gfs2_inode,
|
||||
i_res)->i_no_addr;
|
||||
__entry->start = gfs2_rbm_to_block(&rs->rs_rbm);
|
||||
__entry->free = rs->rs_free;
|
||||
__entry->start = rs->rs_start;
|
||||
__entry->requested = rs->rs_requested;
|
||||
__entry->reserved = rs->rs_reserved;
|
||||
__entry->func = func;
|
||||
),
|
||||
|
||||
TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%lu rr:%lu %s f:%lu",
|
||||
TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%u rq:%u rr:%u %s q:%u r:%u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
(unsigned long long)__entry->inum,
|
||||
(unsigned long long)__entry->start,
|
||||
(unsigned long long)__entry->rd_addr,
|
||||
(unsigned long)__entry->rd_free_clone,
|
||||
(unsigned long)__entry->rd_reserved,
|
||||
rs_func_name(__entry->func), (unsigned long)__entry->free)
|
||||
__entry->rd_free_clone,
|
||||
__entry->rd_requested,
|
||||
__entry->rd_reserved,
|
||||
rs_func_name(__entry->func),
|
||||
__entry->requested,
|
||||
__entry->reserved)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_GFS2_H */
|
||||
|
||||
102
fs/gfs2/trans.c
102
fs/gfs2/trans.c
@@ -31,17 +31,17 @@ static void gfs2_print_trans(struct gfs2_sbd *sdp, const struct gfs2_trans *tr)
|
||||
fs_warn(sdp, "blocks=%u revokes=%u reserved=%u touched=%u\n",
|
||||
tr->tr_blocks, tr->tr_revokes, tr->tr_reserved,
|
||||
test_bit(TR_TOUCHED, &tr->tr_flags));
|
||||
fs_warn(sdp, "Buf %u/%u Databuf %u/%u Revoke %u/%u\n",
|
||||
fs_warn(sdp, "Buf %u/%u Databuf %u/%u Revoke %u\n",
|
||||
tr->tr_num_buf_new, tr->tr_num_buf_rm,
|
||||
tr->tr_num_databuf_new, tr->tr_num_databuf_rm,
|
||||
tr->tr_num_revoke, tr->tr_num_revoke_rm);
|
||||
tr->tr_num_revoke);
|
||||
}
|
||||
|
||||
int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
|
||||
unsigned int revokes)
|
||||
int __gfs2_trans_begin(struct gfs2_trans *tr, struct gfs2_sbd *sdp,
|
||||
unsigned int blocks, unsigned int revokes,
|
||||
unsigned long ip)
|
||||
{
|
||||
struct gfs2_trans *tr;
|
||||
int error;
|
||||
unsigned int extra_revokes;
|
||||
|
||||
if (current->journal_info) {
|
||||
gfs2_print_trans(sdp, current->journal_info);
|
||||
@@ -52,39 +52,72 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
|
||||
if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
|
||||
return -EROFS;
|
||||
|
||||
tr = kmem_cache_zalloc(gfs2_trans_cachep, GFP_NOFS);
|
||||
if (!tr)
|
||||
return -ENOMEM;
|
||||
|
||||
tr->tr_ip = _RET_IP_;
|
||||
tr->tr_ip = ip;
|
||||
tr->tr_blocks = blocks;
|
||||
tr->tr_revokes = revokes;
|
||||
tr->tr_reserved = 1;
|
||||
set_bit(TR_ALLOCED, &tr->tr_flags);
|
||||
if (blocks)
|
||||
tr->tr_reserved += 6 + blocks;
|
||||
if (revokes)
|
||||
tr->tr_reserved += gfs2_struct2blk(sdp, revokes);
|
||||
tr->tr_reserved = GFS2_LOG_FLUSH_MIN_BLOCKS;
|
||||
if (blocks) {
|
||||
/*
|
||||
* The reserved blocks are either used for data or metadata.
|
||||
* We can have mixed data and metadata, each with its own log
|
||||
* descriptor block; see calc_reserved().
|
||||
*/
|
||||
tr->tr_reserved += blocks + 1 + DIV_ROUND_UP(blocks - 1, databuf_limit(sdp));
|
||||
}
|
||||
INIT_LIST_HEAD(&tr->tr_databuf);
|
||||
INIT_LIST_HEAD(&tr->tr_buf);
|
||||
INIT_LIST_HEAD(&tr->tr_list);
|
||||
INIT_LIST_HEAD(&tr->tr_ail1_list);
|
||||
INIT_LIST_HEAD(&tr->tr_ail2_list);
|
||||
|
||||
if (gfs2_assert_warn(sdp, tr->tr_reserved <= sdp->sd_jdesc->jd_blocks))
|
||||
return -EINVAL;
|
||||
|
||||
sb_start_intwrite(sdp->sd_vfs);
|
||||
|
||||
error = gfs2_log_reserve(sdp, tr->tr_reserved);
|
||||
if (error)
|
||||
goto fail;
|
||||
/*
|
||||
* Try the reservations under sd_log_flush_lock to prevent log flushes
|
||||
* from creating inconsistencies between the number of allocated and
|
||||
* reserved revokes. If that fails, do a full-block allocation outside
|
||||
* of the lock to avoid stalling log flushes. Then, allot the
|
||||
* appropriate number of blocks to revokes, use as many revokes locally
|
||||
* as needed, and "release" the surplus into the revokes pool.
|
||||
*/
|
||||
|
||||
down_read(&sdp->sd_log_flush_lock);
|
||||
if (gfs2_log_try_reserve(sdp, tr, &extra_revokes))
|
||||
goto reserved;
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
gfs2_log_reserve(sdp, tr, &extra_revokes);
|
||||
down_read(&sdp->sd_log_flush_lock);
|
||||
|
||||
reserved:
|
||||
gfs2_log_release_revokes(sdp, extra_revokes);
|
||||
if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) {
|
||||
gfs2_log_release_revokes(sdp, tr->tr_revokes);
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
gfs2_log_release(sdp, tr->tr_reserved);
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
current->journal_info = tr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fail:
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
kmem_cache_free(gfs2_trans_cachep, tr);
|
||||
int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
|
||||
unsigned int revokes)
|
||||
{
|
||||
struct gfs2_trans *tr;
|
||||
int error;
|
||||
|
||||
tr = kmem_cache_zalloc(gfs2_trans_cachep, GFP_NOFS);
|
||||
if (!tr)
|
||||
return -ENOMEM;
|
||||
error = __gfs2_trans_begin(tr, sdp, blocks, revokes, _RET_IP_);
|
||||
if (error)
|
||||
kmem_cache_free(gfs2_trans_cachep, tr);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -92,37 +125,39 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
|
||||
{
|
||||
struct gfs2_trans *tr = current->journal_info;
|
||||
s64 nbuf;
|
||||
int alloced = test_bit(TR_ALLOCED, &tr->tr_flags);
|
||||
|
||||
current->journal_info = NULL;
|
||||
|
||||
if (!test_bit(TR_TOUCHED, &tr->tr_flags)) {
|
||||
gfs2_log_release_revokes(sdp, tr->tr_revokes);
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
gfs2_log_release(sdp, tr->tr_reserved);
|
||||
if (alloced) {
|
||||
if (!test_bit(TR_ONSTACK, &tr->tr_flags))
|
||||
gfs2_trans_free(sdp, tr);
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
}
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
return;
|
||||
}
|
||||
|
||||
gfs2_log_release_revokes(sdp, tr->tr_revokes - tr->tr_num_revoke);
|
||||
|
||||
nbuf = tr->tr_num_buf_new + tr->tr_num_databuf_new;
|
||||
nbuf -= tr->tr_num_buf_rm;
|
||||
nbuf -= tr->tr_num_databuf_rm;
|
||||
|
||||
if (gfs2_assert_withdraw(sdp, (nbuf <= tr->tr_blocks) &&
|
||||
(tr->tr_num_revoke <= tr->tr_revokes)))
|
||||
if (gfs2_assert_withdraw(sdp, nbuf <= tr->tr_blocks) ||
|
||||
gfs2_assert_withdraw(sdp, tr->tr_num_revoke <= tr->tr_revokes))
|
||||
gfs2_print_trans(sdp, tr);
|
||||
|
||||
gfs2_log_commit(sdp, tr);
|
||||
if (alloced && !test_bit(TR_ATTACHED, &tr->tr_flags))
|
||||
if (!test_bit(TR_ONSTACK, &tr->tr_flags) &&
|
||||
!test_bit(TR_ATTACHED, &tr->tr_flags))
|
||||
gfs2_trans_free(sdp, tr);
|
||||
up_read(&sdp->sd_log_flush_lock);
|
||||
|
||||
if (sdp->sd_vfs->s_flags & SB_SYNCHRONOUS)
|
||||
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
|
||||
GFS2_LFC_TRANS_END);
|
||||
if (alloced)
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
sb_end_intwrite(sdp->sd_vfs);
|
||||
}
|
||||
|
||||
static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl,
|
||||
@@ -262,7 +297,6 @@ void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
|
||||
void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
|
||||
{
|
||||
struct gfs2_bufdata *bd, *tmp;
|
||||
struct gfs2_trans *tr = current->journal_info;
|
||||
unsigned int n = len;
|
||||
|
||||
gfs2_log_lock(sdp);
|
||||
@@ -274,7 +308,7 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
|
||||
if (bd->bd_gl)
|
||||
gfs2_glock_remove_revoke(bd->bd_gl);
|
||||
kmem_cache_free(gfs2_bufdata_cachep, bd);
|
||||
tr->tr_num_revoke_rm++;
|
||||
gfs2_log_release_revokes(sdp, 1);
|
||||
if (--n == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,16 @@ struct gfs2_glock;
|
||||
* block, or all of the blocks in the rg, whichever is smaller */
|
||||
static inline unsigned int gfs2_rg_blocks(const struct gfs2_inode *ip, unsigned requested)
|
||||
{
|
||||
struct gfs2_rgrpd *rgd = ip->i_res.rs_rbm.rgd;
|
||||
struct gfs2_rgrpd *rgd = ip->i_res.rs_rgd;
|
||||
|
||||
if (requested < rgd->rd_length)
|
||||
return requested + 1;
|
||||
return rgd->rd_length;
|
||||
}
|
||||
|
||||
extern int __gfs2_trans_begin(struct gfs2_trans *tr, struct gfs2_sbd *sdp,
|
||||
unsigned int blocks, unsigned int revokes,
|
||||
unsigned long ip);
|
||||
extern int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
|
||||
unsigned int revokes);
|
||||
|
||||
|
||||
@@ -91,12 +91,39 @@ out_unlock:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* gfs2_freeze_lock - hold the freeze glock
|
||||
* @sdp: the superblock
|
||||
* @freeze_gh: pointer to the requested holder
|
||||
* @caller_flags: any additional flags needed by the caller
|
||||
*/
|
||||
int gfs2_freeze_lock(struct gfs2_sbd *sdp, struct gfs2_holder *freeze_gh,
|
||||
int caller_flags)
|
||||
{
|
||||
int flags = LM_FLAG_NOEXP | GL_EXACT | caller_flags;
|
||||
int error;
|
||||
|
||||
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, flags,
|
||||
freeze_gh);
|
||||
if (error && error != GLR_TRYFAILED)
|
||||
fs_err(sdp, "can't lock the freeze lock: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
void gfs2_freeze_unlock(struct gfs2_holder *freeze_gh)
|
||||
{
|
||||
if (gfs2_holder_initialized(freeze_gh))
|
||||
gfs2_glock_dq_uninit(freeze_gh);
|
||||
}
|
||||
|
||||
static void signal_our_withdraw(struct gfs2_sbd *sdp)
|
||||
{
|
||||
struct gfs2_glock *gl = sdp->sd_live_gh.gh_gl;
|
||||
struct gfs2_glock *live_gl = sdp->sd_live_gh.gh_gl;
|
||||
struct inode *inode = sdp->sd_jdesc->jd_inode;
|
||||
struct gfs2_inode *ip = GFS2_I(inode);
|
||||
struct gfs2_glock *i_gl = ip->i_gl;
|
||||
u64 no_formal_ino = ip->i_no_formal_ino;
|
||||
int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
|
||||
int ret = 0;
|
||||
int tries;
|
||||
|
||||
@@ -117,8 +144,21 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
|
||||
* therefore we need to clear SDF_JOURNAL_LIVE manually.
|
||||
*/
|
||||
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
|
||||
if (!sb_rdonly(sdp->sd_vfs))
|
||||
ret = gfs2_make_fs_ro(sdp);
|
||||
if (!sb_rdonly(sdp->sd_vfs)) {
|
||||
struct gfs2_holder freeze_gh;
|
||||
|
||||
gfs2_holder_mark_uninitialized(&freeze_gh);
|
||||
if (sdp->sd_freeze_gl &&
|
||||
!gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl)) {
|
||||
ret = gfs2_freeze_lock(sdp, &freeze_gh,
|
||||
log_write_allowed ? 0 : LM_FLAG_TRY);
|
||||
if (ret == GLR_TRYFAILED)
|
||||
ret = 0;
|
||||
}
|
||||
if (!ret)
|
||||
ret = gfs2_make_fs_ro(sdp);
|
||||
gfs2_freeze_unlock(&freeze_gh);
|
||||
}
|
||||
|
||||
if (sdp->sd_lockstruct.ls_ops->lm_lock == NULL) { /* lock_nolock */
|
||||
if (!ret)
|
||||
@@ -141,7 +181,8 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
|
||||
atomic_set(&sdp->sd_freeze_state, SFS_FROZEN);
|
||||
thaw_super(sdp->sd_vfs);
|
||||
} else {
|
||||
wait_on_bit(&gl->gl_flags, GLF_DEMOTE, TASK_UNINTERRUPTIBLE);
|
||||
wait_on_bit(&i_gl->gl_flags, GLF_DEMOTE,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -161,15 +202,15 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
|
||||
* on other nodes to be successful, otherwise we remain the owner of
|
||||
* the glock as far as dlm is concerned.
|
||||
*/
|
||||
if (gl->gl_ops->go_free) {
|
||||
set_bit(GLF_FREEING, &gl->gl_flags);
|
||||
wait_on_bit(&gl->gl_flags, GLF_FREEING, TASK_UNINTERRUPTIBLE);
|
||||
if (i_gl->gl_ops->go_free) {
|
||||
set_bit(GLF_FREEING, &i_gl->gl_flags);
|
||||
wait_on_bit(&i_gl->gl_flags, GLF_FREEING, TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue the "live" glock, but keep a reference so it's never freed.
|
||||
*/
|
||||
gfs2_glock_hold(gl);
|
||||
gfs2_glock_hold(live_gl);
|
||||
gfs2_glock_dq_wait(&sdp->sd_live_gh);
|
||||
/*
|
||||
* We enqueue the "live" glock in EX so that all other nodes
|
||||
@@ -208,7 +249,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
|
||||
gfs2_glock_nq(&sdp->sd_live_gh);
|
||||
}
|
||||
|
||||
gfs2_glock_queue_put(gl); /* drop the extra reference we acquired */
|
||||
gfs2_glock_queue_put(live_gl); /* drop extra reference we acquired */
|
||||
clear_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags);
|
||||
|
||||
/*
|
||||
|
||||
@@ -149,6 +149,9 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function,
|
||||
|
||||
extern int check_journal_clean(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
|
||||
bool verbose);
|
||||
extern int gfs2_freeze_lock(struct gfs2_sbd *sdp,
|
||||
struct gfs2_holder *freeze_gh, int caller_flags);
|
||||
extern void gfs2_freeze_unlock(struct gfs2_holder *freeze_gh);
|
||||
|
||||
#define gfs2_io_error(sdp) \
|
||||
gfs2_io_error_i((sdp), __func__, __FILE__, __LINE__)
|
||||
|
||||
@@ -70,6 +70,20 @@ static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool gfs2_eatype_valid(struct gfs2_sbd *sdp, u8 type)
|
||||
{
|
||||
switch(sdp->sd_sb.sb_fs_format) {
|
||||
case GFS2_FS_FORMAT_MAX:
|
||||
return true;
|
||||
|
||||
case GFS2_FS_FORMAT_MIN:
|
||||
return type <= GFS2_EATYPE_SECURITY;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
struct gfs2_ea_header *ea,
|
||||
struct gfs2_ea_header *prev, void *private);
|
||||
@@ -77,6 +91,7 @@ typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
ea_call_t ea_call, void *data)
|
||||
{
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||
struct gfs2_ea_header *ea, *prev = NULL;
|
||||
int error = 0;
|
||||
|
||||
@@ -89,9 +104,8 @@ static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
if (!(bh->b_data <= (char *)ea && (char *)GFS2_EA2NEXT(ea) <=
|
||||
bh->b_data + bh->b_size))
|
||||
goto fail;
|
||||
if (!GFS2_EATYPE_VALID(ea->ea_type))
|
||||
if (!gfs2_eatype_valid(sdp, ea->ea_type))
|
||||
goto fail;
|
||||
|
||||
error = ea_call(ip, bh, ea, prev, data);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -259,7 +273,8 @@ static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &rg_gh);
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &rg_gh);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -344,6 +359,7 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
|
||||
void *private)
|
||||
{
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||
struct ea_list *ei = private;
|
||||
struct gfs2_ea_request *er = ei->ei_er;
|
||||
unsigned int ea_size;
|
||||
@@ -353,6 +369,8 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
if (ea->ea_type == GFS2_EATYPE_UNUSED)
|
||||
return 0;
|
||||
|
||||
BUG_ON(ea->ea_type > GFS2_EATYPE_SECURITY &&
|
||||
sdp->sd_sb.sb_fs_format == GFS2_FS_FORMAT_MIN);
|
||||
switch (ea->ea_type) {
|
||||
case GFS2_EATYPE_USR:
|
||||
prefix = "user.";
|
||||
@@ -366,8 +384,12 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
|
||||
prefix = "security.";
|
||||
l = 9;
|
||||
break;
|
||||
case GFS2_EATYPE_TRUSTED:
|
||||
prefix = "trusted.";
|
||||
l = 8;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ea_size = l + ea->ea_name_len + 1;
|
||||
@@ -1386,7 +1408,8 @@ static int ea_dealloc_block(struct gfs2_inode *ip)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &gh);
|
||||
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
|
||||
LM_FLAG_NODE_SCOPE, &gh);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -1464,7 +1487,25 @@ static const struct xattr_handler gfs2_xattr_security_handler = {
|
||||
.set = gfs2_xattr_set,
|
||||
};
|
||||
|
||||
const struct xattr_handler *gfs2_xattr_handlers[] = {
|
||||
static bool
|
||||
gfs2_xattr_trusted_list(struct dentry *dentry)
|
||||
{
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
static const struct xattr_handler gfs2_xattr_trusted_handler = {
|
||||
.prefix = XATTR_TRUSTED_PREFIX,
|
||||
.flags = GFS2_EATYPE_TRUSTED,
|
||||
.list = gfs2_xattr_trusted_list,
|
||||
.get = gfs2_xattr_get,
|
||||
.set = gfs2_xattr_set,
|
||||
};
|
||||
|
||||
const struct xattr_handler *gfs2_xattr_handlers_max[] = {
|
||||
/* GFS2_FS_FORMAT_MAX */
|
||||
&gfs2_xattr_trusted_handler,
|
||||
|
||||
/* GFS2_FS_FORMAT_MIN */
|
||||
&gfs2_xattr_user_handler,
|
||||
&gfs2_xattr_security_handler,
|
||||
&posix_acl_access_xattr_handler,
|
||||
@@ -1472,3 +1513,4 @@ const struct xattr_handler *gfs2_xattr_handlers[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
const struct xattr_handler **gfs2_xattr_handlers_min = gfs2_xattr_handlers_max + 1;
|
||||
|
||||
103
include/dt-bindings/input/cros-ec-keyboard.h
Normal file
103
include/dt-bindings/input/cros-ec-keyboard.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This header provides the constants of the standard Chrome OS key matrix
|
||||
* for cros-ec keyboard-controller bindings.
|
||||
*
|
||||
* Copyright (c) 2021 Google, Inc
|
||||
*/
|
||||
|
||||
#ifndef _CROS_EC_KEYBOARD_H
|
||||
#define _CROS_EC_KEYBOARD_H
|
||||
|
||||
#define CROS_STD_TOP_ROW_KEYMAP \
|
||||
MATRIX_KEY(0x00, 0x02, KEY_F1) \
|
||||
MATRIX_KEY(0x03, 0x02, KEY_F2) \
|
||||
MATRIX_KEY(0x02, 0x02, KEY_F3) \
|
||||
MATRIX_KEY(0x01, 0x02, KEY_F4) \
|
||||
MATRIX_KEY(0x03, 0x04, KEY_F5) \
|
||||
MATRIX_KEY(0x02, 0x04, KEY_F6) \
|
||||
MATRIX_KEY(0x01, 0x04, KEY_F7) \
|
||||
MATRIX_KEY(0x02, 0x09, KEY_F8) \
|
||||
MATRIX_KEY(0x01, 0x09, KEY_F9) \
|
||||
MATRIX_KEY(0x00, 0x04, KEY_F10)
|
||||
|
||||
#define CROS_STD_MAIN_KEYMAP \
|
||||
MATRIX_KEY(0x00, 0x01, KEY_LEFTMETA) \
|
||||
MATRIX_KEY(0x00, 0x03, KEY_B) \
|
||||
MATRIX_KEY(0x00, 0x05, KEY_RO) \
|
||||
MATRIX_KEY(0x00, 0x06, KEY_N) \
|
||||
MATRIX_KEY(0x00, 0x08, KEY_EQUAL) \
|
||||
MATRIX_KEY(0x00, 0x0a, KEY_RIGHTALT) \
|
||||
MATRIX_KEY(0x01, 0x01, KEY_ESC) \
|
||||
MATRIX_KEY(0x01, 0x03, KEY_G) \
|
||||
MATRIX_KEY(0x01, 0x06, KEY_H) \
|
||||
MATRIX_KEY(0x01, 0x08, KEY_APOSTROPHE) \
|
||||
MATRIX_KEY(0x01, 0x0b, KEY_BACKSPACE) \
|
||||
MATRIX_KEY(0x01, 0x0c, KEY_HENKAN) \
|
||||
\
|
||||
MATRIX_KEY(0x02, 0x00, KEY_LEFTCTRL) \
|
||||
MATRIX_KEY(0x02, 0x01, KEY_TAB) \
|
||||
MATRIX_KEY(0x02, 0x03, KEY_T) \
|
||||
MATRIX_KEY(0x02, 0x05, KEY_RIGHTBRACE) \
|
||||
MATRIX_KEY(0x02, 0x06, KEY_Y) \
|
||||
MATRIX_KEY(0x02, 0x07, KEY_102ND) \
|
||||
MATRIX_KEY(0x02, 0x08, KEY_LEFTBRACE) \
|
||||
MATRIX_KEY(0x02, 0x0a, KEY_YEN) \
|
||||
\
|
||||
MATRIX_KEY(0x03, 0x00, KEY_LEFTMETA) \
|
||||
MATRIX_KEY(0x03, 0x01, KEY_GRAVE) \
|
||||
MATRIX_KEY(0x03, 0x03, KEY_5) \
|
||||
MATRIX_KEY(0x03, 0x06, KEY_6) \
|
||||
MATRIX_KEY(0x03, 0x08, KEY_MINUS) \
|
||||
MATRIX_KEY(0x03, 0x09, KEY_SLEEP) \
|
||||
MATRIX_KEY(0x03, 0x0b, KEY_BACKSLASH) \
|
||||
MATRIX_KEY(0x03, 0x0c, KEY_MUHENKAN) \
|
||||
\
|
||||
MATRIX_KEY(0x04, 0x00, KEY_RIGHTCTRL) \
|
||||
MATRIX_KEY(0x04, 0x01, KEY_A) \
|
||||
MATRIX_KEY(0x04, 0x02, KEY_D) \
|
||||
MATRIX_KEY(0x04, 0x03, KEY_F) \
|
||||
MATRIX_KEY(0x04, 0x04, KEY_S) \
|
||||
MATRIX_KEY(0x04, 0x05, KEY_K) \
|
||||
MATRIX_KEY(0x04, 0x06, KEY_J) \
|
||||
MATRIX_KEY(0x04, 0x08, KEY_SEMICOLON) \
|
||||
MATRIX_KEY(0x04, 0x09, KEY_L) \
|
||||
MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH) \
|
||||
MATRIX_KEY(0x04, 0x0b, KEY_ENTER) \
|
||||
\
|
||||
MATRIX_KEY(0x05, 0x01, KEY_Z) \
|
||||
MATRIX_KEY(0x05, 0x02, KEY_C) \
|
||||
MATRIX_KEY(0x05, 0x03, KEY_V) \
|
||||
MATRIX_KEY(0x05, 0x04, KEY_X) \
|
||||
MATRIX_KEY(0x05, 0x05, KEY_COMMA) \
|
||||
MATRIX_KEY(0x05, 0x06, KEY_M) \
|
||||
MATRIX_KEY(0x05, 0x07, KEY_LEFTSHIFT) \
|
||||
MATRIX_KEY(0x05, 0x08, KEY_SLASH) \
|
||||
MATRIX_KEY(0x05, 0x09, KEY_DOT) \
|
||||
MATRIX_KEY(0x05, 0x0b, KEY_SPACE) \
|
||||
\
|
||||
MATRIX_KEY(0x06, 0x01, KEY_1) \
|
||||
MATRIX_KEY(0x06, 0x02, KEY_3) \
|
||||
MATRIX_KEY(0x06, 0x03, KEY_4) \
|
||||
MATRIX_KEY(0x06, 0x04, KEY_2) \
|
||||
MATRIX_KEY(0x06, 0x05, KEY_8) \
|
||||
MATRIX_KEY(0x06, 0x06, KEY_7) \
|
||||
MATRIX_KEY(0x06, 0x08, KEY_0) \
|
||||
MATRIX_KEY(0x06, 0x09, KEY_9) \
|
||||
MATRIX_KEY(0x06, 0x0a, KEY_LEFTALT) \
|
||||
MATRIX_KEY(0x06, 0x0b, KEY_DOWN) \
|
||||
MATRIX_KEY(0x06, 0x0c, KEY_RIGHT) \
|
||||
\
|
||||
MATRIX_KEY(0x07, 0x01, KEY_Q) \
|
||||
MATRIX_KEY(0x07, 0x02, KEY_E) \
|
||||
MATRIX_KEY(0x07, 0x03, KEY_R) \
|
||||
MATRIX_KEY(0x07, 0x04, KEY_W) \
|
||||
MATRIX_KEY(0x07, 0x05, KEY_I) \
|
||||
MATRIX_KEY(0x07, 0x06, KEY_U) \
|
||||
MATRIX_KEY(0x07, 0x07, KEY_RIGHTSHIFT) \
|
||||
MATRIX_KEY(0x07, 0x08, KEY_P) \
|
||||
MATRIX_KEY(0x07, 0x09, KEY_O) \
|
||||
MATRIX_KEY(0x07, 0x0b, KEY_UP) \
|
||||
MATRIX_KEY(0x07, 0x0c, KEY_LEFT)
|
||||
|
||||
#endif /* _CROS_EC_KEYBOARD_H */
|
||||
@@ -150,7 +150,7 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
|
||||
* @info: return information about attribute after parsing report
|
||||
*
|
||||
* Parses report and returns the attribute information such as report id,
|
||||
* field index, units and exponet etc.
|
||||
* field index, units and exponent etc.
|
||||
*/
|
||||
int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
|
||||
u8 type,
|
||||
@@ -167,7 +167,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
|
||||
* @is_signed: If true then fields < 32 bits will be sign-extended
|
||||
*
|
||||
* Issues a synchronous or asynchronous read request for an input attribute.
|
||||
* Returns data upto 32 bits.
|
||||
* Return: data up to 32 bits.
|
||||
*/
|
||||
|
||||
enum sensor_hub_read_flags {
|
||||
@@ -205,8 +205,9 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
* @buffer: buffer to copy output
|
||||
*
|
||||
* Used to get a field in feature report. For example this can get polling
|
||||
* interval, sensitivity, activate/deactivate state. On success it returns
|
||||
* number of bytes copied to buffer. On failure, it returns value < 0.
|
||||
* interval, sensitivity, activate/deactivate state.
|
||||
* Return: On success, it returns the number of bytes copied to buffer.
|
||||
* On failure, it returns value < 0.
|
||||
*/
|
||||
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, int buffer_size, void *buffer);
|
||||
|
||||
@@ -918,7 +918,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
|
||||
/**
|
||||
* hid_device_io_start - enable HID input during probe, remove
|
||||
*
|
||||
* @hid - the device
|
||||
* @hid: the device
|
||||
*
|
||||
* This should only be called during probe or remove and only be
|
||||
* called by the thread calling probe or remove. It will allow
|
||||
@@ -936,7 +936,7 @@ static inline void hid_device_io_start(struct hid_device *hid) {
|
||||
/**
|
||||
* hid_device_io_stop - disable HID input during probe, remove
|
||||
*
|
||||
* @hid - the device
|
||||
* @hid: the device
|
||||
*
|
||||
* Should only be called after hid_device_io_start. It will prevent
|
||||
* incoming packets from going to the driver for the duration of
|
||||
@@ -1010,6 +1010,13 @@ static inline void hid_map_usage(struct hid_input *hidinput,
|
||||
/**
|
||||
* hid_map_usage_clear - map usage input bits and clear the input bit
|
||||
*
|
||||
* @hidinput: hidinput which we are interested in
|
||||
* @usage: usage to fill in
|
||||
* @bit: pointer to input->{}bit (out parameter)
|
||||
* @max: maximal valid usage->code to consider later (out parameter)
|
||||
* @type: input event type (EV_KEY, EV_REL, ...)
|
||||
* @c: code which corresponds to this usage and type
|
||||
*
|
||||
* The same as hid_map_usage, except the @c bit is also cleared in supported
|
||||
* bits (@bit).
|
||||
*/
|
||||
@@ -1084,7 +1091,7 @@ static inline void hid_hw_request(struct hid_device *hdev,
|
||||
* @rtype: HID report type
|
||||
* @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
|
||||
*
|
||||
* @return: count of data transfered, negative if error
|
||||
* Return: count of data transferred, negative if error
|
||||
*
|
||||
* Same behavior as hid_hw_request, but with raw buffers instead.
|
||||
*/
|
||||
@@ -1106,7 +1113,7 @@ static inline int hid_hw_raw_request(struct hid_device *hdev,
|
||||
* @buf: raw data to transfer
|
||||
* @len: length of buf
|
||||
*
|
||||
* @return: count of data transfered, negative if error
|
||||
* Return: count of data transferred, negative if error
|
||||
*/
|
||||
static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
|
||||
size_t len)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user