driver core update for 4.7-rc1
Here's the "big" driver core update for 4.7-rc1. Mostly just debugfs changes, the long-known and messy races with removing debugfs files should be fixed thanks to the great work of Nicolai Stange. We also have some isa updates in here (the x86 maintainers told me to take it through this tree), a new warning when we run out of dynamic char major numbers, and a few other assorted changes, details in the shortlog. All have been in linux-next for some time with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlc/0mwACgkQMUfUDdst+ynjXACgjNxR5nMUiM8ZuuD0i4Xj7VXd hnIAoM08+XDCv41noGdAcKv+2WZVZWMC =i+0H -----END PGP SIGNATURE----- Merge tag 'driver-core-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core updates from Greg KH: "Here's the "big" driver core update for 4.7-rc1. Mostly just debugfs changes, the long-known and messy races with removing debugfs files should be fixed thanks to the great work of Nicolai Stange. We also have some isa updates in here (the x86 maintainers told me to take it through this tree), a new warning when we run out of dynamic char major numbers, and a few other assorted changes, details in the shortlog. All have been in linux-next for some time with no reported issues" * tag 'driver-core-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (32 commits) Revert "base: dd: don't remove driver_data in -EPROBE_DEFER case" gpio: ws16c48: Utilize the ISA bus driver gpio: 104-idio-16: Utilize the ISA bus driver gpio: 104-idi-48: Utilize the ISA bus driver gpio: 104-dio-48e: Utilize the ISA bus driver watchdog: ebc-c384_wdt: Utilize the ISA bus driver iio: stx104: Utilize the module_isa_driver and max_num_isa_dev macros iio: stx104: Add X86 dependency to STX104 Kconfig option Documentation: Add ISA bus driver documentation isa: Implement the max_num_isa_dev macro isa: Implement the module_isa_driver macro pnp: pnpbios: Add explicit X86_32 dependency to PNPBIOS isa: Decouple X86_32 dependency from the ISA Kconfig option driver-core: use 'dev' argument in dev_dbg_ratelimited stub base: dd: don't remove driver_data in -EPROBE_DEFER case kernfs: Move faulting copy_user operations outside of the mutex devcoredump: add scatterlist support debugfs: unproxify files created through debugfs_create_u32_array() debugfs: unproxify files created through debugfs_create_blob() debugfs: unproxify files created through debugfs_create_bool() ...
This commit is contained in:
commit
3aa2fc1667
33 changed files with 1160 additions and 519 deletions
1
CREDITS
1
CREDITS
|
@ -768,6 +768,7 @@ D: Z85230 driver
|
|||
D: Former security contact point (please use vendor-sec@lst.de)
|
||||
D: ex 2.2 maintainer
|
||||
D: 2.1.x modular sound
|
||||
D: Assigned major/minor numbers maintainer at lanana.org
|
||||
S: c/o Red Hat UK Ltd
|
||||
S: Alexandra House
|
||||
S: Alexandra Terrace
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
|
||||
LINUX ALLOCATED DEVICES (2.6+ version)
|
||||
|
||||
Maintained by Alan Cox <device@lanana.org>
|
||||
|
||||
Last revised: 6th April 2009
|
||||
LINUX ALLOCATED DEVICES (4.x+ version)
|
||||
|
||||
This list is the Linux Device List, the official registry of allocated
|
||||
device numbers and /dev directory nodes for the Linux operating
|
||||
system.
|
||||
|
||||
The latest version of this list is available from
|
||||
http://www.lanana.org/docs/device-list/ or
|
||||
ftp://ftp.kernel.org/pub/linux/docs/device-list/. This version may be
|
||||
newer than the one distributed with the Linux kernel.
|
||||
|
||||
The LaTeX version of this document is no longer maintained.
|
||||
The LaTeX version of this document is no longer maintained, nor is
|
||||
the document that used to reside at lanana.org. This version in the
|
||||
mainline Linux kernel is the master document. Updates shall be sent
|
||||
as patches to the kernel maintainers (see the SubmittingPatches document).
|
||||
Specifically explore the sections titled "CHAR and MISC DRIVERS", and
|
||||
"BLOCK LAYER" in the MAINTAINERS file to find the right maintainers
|
||||
to involve for character and block devices.
|
||||
|
||||
This document is included by reference into the Filesystem Hierarchy
|
||||
Standard (FHS). The FHS is available from http://www.pathname.com/fhs/.
|
||||
|
@ -23,60 +20,33 @@ Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
|
|||
platform only. Allocations marked (68k/Atari) apply to Linux/68k on
|
||||
the Atari platform only.
|
||||
|
||||
The symbol {2.6} means the allocation is obsolete and scheduled for
|
||||
removal once kernel version 2.6 (or equivalent) is released. Some of these
|
||||
allocations have already been removed.
|
||||
|
||||
This document is in the public domain. The author requests, however,
|
||||
This document is in the public domain. The authors requests, however,
|
||||
that semantically altered versions are not distributed without
|
||||
permission of the author, assuming the author can be contacted without
|
||||
permission of the authors, assuming the authors can be contacted without
|
||||
an unreasonable effort.
|
||||
|
||||
In particular, please don't sent patches for this list to Linus, at
|
||||
least not without contacting me first.
|
||||
|
||||
I do not have any information about these devices beyond what appears
|
||||
on this list. Any such information requests will be deleted without
|
||||
reply.
|
||||
|
||||
|
||||
**** DEVICE DRIVERS AUTHORS PLEASE READ THIS ****
|
||||
|
||||
Linux now has extensive support for dynamic allocation of device numbering
|
||||
and can use sysfs and udev (systemd) to handle the naming needs. There are
|
||||
still some exceptions in the serial and boot device area. Before asking
|
||||
for a device number make sure you actually need one.
|
||||
|
||||
To have a major number allocated, or a minor number in situations
|
||||
where that applies (e.g. busmice), please contact me with the
|
||||
appropriate device information. Also, if you have additional
|
||||
information regarding any of the devices listed below, or if I have
|
||||
made a mistake, I would greatly appreciate a note.
|
||||
where that applies (e.g. busmice), please submit a patch and send to
|
||||
the authors as indicated above.
|
||||
|
||||
I do, however, make a few requests about the nature of your report.
|
||||
This is necessary for me to be able to keep this list up to date and
|
||||
correct in a timely manner. First of all, *please* send it to the
|
||||
correct address... <device@lanana.org>. I receive hundreds of email
|
||||
messages a day, so mail sent to other addresses may very well get lost
|
||||
in the avalanche. Please put in a descriptive subject, so I can find
|
||||
your mail again should I need to. Too many people send me email
|
||||
saying just "device number request" in the subject.
|
||||
|
||||
Second, please include a description of the device *in the same format
|
||||
as this list*. The reason for this is that it is the only way I have
|
||||
found to ensure I have all the requisite information to publish your
|
||||
Keep the description of the device *in the same format
|
||||
as this list*. The reason for this is that it is the only way we have
|
||||
found to ensure we have all the requisite information to publish your
|
||||
device and avoid conflicts.
|
||||
|
||||
Third, please don't assume that the distributed version of the list is
|
||||
up to date. Due to the number of registrations I have to maintain it
|
||||
in "batch mode", so there is likely additional registrations that
|
||||
haven't been listed yet.
|
||||
|
||||
Fourth, remember that Linux now has extensive support for dynamic allocation
|
||||
of device numbering and can use sysfs and udev to handle the naming needs.
|
||||
There are still some exceptions in the serial and boot device area. Before
|
||||
asking for a device number make sure you actually need one.
|
||||
|
||||
Finally, sometimes I have to play "namespace police." Please don't be
|
||||
offended. I often get submissions for /dev names that would be bound
|
||||
to cause conflicts down the road. I am trying to avoid getting in a
|
||||
Finally, sometimes we have to play "namespace police." Please don't be
|
||||
offended. We often get submissions for /dev names that would be bound
|
||||
to cause conflicts down the road. We are trying to avoid getting in a
|
||||
situation where we would have to suffer an incompatible forward
|
||||
change. Therefore, please consult with me *before* you make your
|
||||
change. Therefore, please consult with us *before* you make your
|
||||
device names and numbers in any way public, at least to the point
|
||||
where it would be at all difficult to get them changed.
|
||||
|
||||
|
@ -3099,9 +3069,9 @@ Your cooperation is appreciated.
|
|||
129 = /dev/ipath_sma Device used by Subnet Management Agent
|
||||
130 = /dev/ipath_diag Device used by diagnostics programs
|
||||
|
||||
234-239 UNASSIGNED
|
||||
|
||||
240-254 char LOCAL/EXPERIMENTAL USE
|
||||
234-254 char RESERVED FOR DYNAMIC ASSIGNMENT
|
||||
Character devices that request a dynamic allocation of major number will
|
||||
take numbers starting from 254 and downward.
|
||||
|
||||
240-254 block LOCAL/EXPERIMENTAL USE
|
||||
Allocated for local/experimental use. For devices not
|
||||
|
|
121
Documentation/isa.txt
Normal file
121
Documentation/isa.txt
Normal file
|
@ -0,0 +1,121 @@
|
|||
ISA Drivers
|
||||
-----------
|
||||
|
||||
The following text is adapted from the commit message of the initial
|
||||
commit of the ISA bus driver authored by Rene Herman.
|
||||
|
||||
During the recent "isa drivers using platform devices" discussion it was
|
||||
pointed out that (ALSA) ISA drivers ran into the problem of not having
|
||||
the option to fail driver load (device registration rather) upon not
|
||||
finding their hardware due to a probe() error not being passed up
|
||||
through the driver model. In the course of that, I suggested a separate
|
||||
ISA bus might be best; Russell King agreed and suggested this bus could
|
||||
use the .match() method for the actual device discovery.
|
||||
|
||||
The attached does this. For this old non (generically) discoverable ISA
|
||||
hardware only the driver itself can do discovery so as a difference with
|
||||
the platform_bus, this isa_bus also distributes match() up to the
|
||||
driver.
|
||||
|
||||
As another difference: these devices only exist in the driver model due
|
||||
to the driver creating them because it might want to drive them, meaning
|
||||
that all device creation has been made internal as well.
|
||||
|
||||
The usage model this provides is nice, and has been acked from the ALSA
|
||||
side by Takashi Iwai and Jaroslav Kysela. The ALSA driver module_init's
|
||||
now (for oldisa-only drivers) become:
|
||||
|
||||
static int __init alsa_card_foo_init(void)
|
||||
{
|
||||
return isa_register_driver(&snd_foo_isa_driver, SNDRV_CARDS);
|
||||
}
|
||||
|
||||
static void __exit alsa_card_foo_exit(void)
|
||||
{
|
||||
isa_unregister_driver(&snd_foo_isa_driver);
|
||||
}
|
||||
|
||||
Quite like the other bus models therefore. This removes a lot of
|
||||
duplicated init code from the ALSA ISA drivers.
|
||||
|
||||
The passed in isa_driver struct is the regular driver struct embedding a
|
||||
struct device_driver, the normal probe/remove/shutdown/suspend/resume
|
||||
callbacks, and as indicated that .match callback.
|
||||
|
||||
The "SNDRV_CARDS" you see being passed in is a "unsigned int ndev"
|
||||
parameter, indicating how many devices to create and call our methods
|
||||
with.
|
||||
|
||||
The platform_driver callbacks are called with a platform_device param;
|
||||
the isa_driver callbacks are being called with a "struct device *dev,
|
||||
unsigned int id" pair directly -- with the device creation completely
|
||||
internal to the bus it's much cleaner to not leak isa_dev's by passing
|
||||
them in at all. The id is the only thing we ever want other then the
|
||||
struct device * anyways, and it makes for nicer code in the callbacks as
|
||||
well.
|
||||
|
||||
With this additional .match() callback ISA drivers have all options. If
|
||||
ALSA would want to keep the old non-load behaviour, it could stick all
|
||||
of the old .probe in .match, which would only keep them registered after
|
||||
everything was found to be present and accounted for. If it wanted the
|
||||
behaviour of always loading as it inadvertently did for a bit after the
|
||||
changeover to platform devices, it could just not provide a .match() and
|
||||
do everything in .probe() as before.
|
||||
|
||||
If it, as Takashi Iwai already suggested earlier as a way of following
|
||||
the model from saner buses more closely, wants to load when a later bind
|
||||
could conceivably succeed, it could use .match() for the prerequisites
|
||||
(such as checking the user wants the card enabled and that port/irq/dma
|
||||
values have been passed in) and .probe() for everything else. This is
|
||||
the nicest model.
|
||||
|
||||
To the code...
|
||||
|
||||
This exports only two functions; isa_{,un}register_driver().
|
||||
|
||||
isa_register_driver() register's the struct device_driver, and then
|
||||
loops over the passed in ndev creating devices and registering them.
|
||||
This causes the bus match method to be called for them, which is:
|
||||
|
||||
int isa_bus_match(struct device *dev, struct device_driver *driver)
|
||||
{
|
||||
struct isa_driver *isa_driver = to_isa_driver(driver);
|
||||
|
||||
if (dev->platform_data == isa_driver) {
|
||||
if (!isa_driver->match ||
|
||||
isa_driver->match(dev, to_isa_dev(dev)->id))
|
||||
return 1;
|
||||
dev->platform_data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
The first thing this does is check if this device is in fact one of this
|
||||
driver's devices by seeing if the device's platform_data pointer is set
|
||||
to this driver. Platform devices compare strings, but we don't need to
|
||||
do that with everything being internal, so isa_register_driver() abuses
|
||||
dev->platform_data as a isa_driver pointer which we can then check here.
|
||||
I believe platform_data is available for this, but if rather not, moving
|
||||
the isa_driver pointer to the private struct isa_dev is ofcourse fine as
|
||||
well.
|
||||
|
||||
Then, if the the driver did not provide a .match, it matches. If it did,
|
||||
the driver match() method is called to determine a match.
|
||||
|
||||
If it did _not_ match, dev->platform_data is reset to indicate this to
|
||||
isa_register_driver which can then unregister the device again.
|
||||
|
||||
If during all this, there's any error, or no devices matched at all
|
||||
everything is backed out again and the error, or -ENODEV, is returned.
|
||||
|
||||
isa_unregister_driver() just unregisters the matched devices and the
|
||||
driver itself.
|
||||
|
||||
module_isa_driver is a helper macro for ISA drivers which do not do
|
||||
anything special in module init/exit. This eliminates a lot of
|
||||
boilerplate code. Each module may only use this macro once, and calling
|
||||
it replaces module_init and module_exit.
|
||||
|
||||
max_num_isa_dev is a macro to determine the maximum possible number of
|
||||
ISA devices which may be registered in the I/O port address space given
|
||||
the address extent of the ISA devices.
|
|
@ -6067,6 +6067,13 @@ F: include/linux/irqdomain.h
|
|||
F: kernel/irq/irqdomain.c
|
||||
F: kernel/irq/msi.c
|
||||
|
||||
ISA
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/isa.txt
|
||||
F: drivers/base/isa.c
|
||||
F: include/linux/isa.h
|
||||
|
||||
ISAPNP
|
||||
M: Jaroslav Kysela <perex@perex.cz>
|
||||
S: Maintained
|
||||
|
|
|
@ -2445,8 +2445,6 @@ config ISA_DMA_API
|
|||
Enables ISA-style DMA support for devices requiring such controllers.
|
||||
If unsure, say Y.
|
||||
|
||||
if X86_32
|
||||
|
||||
config ISA
|
||||
bool "ISA support"
|
||||
---help---
|
||||
|
@ -2456,6 +2454,8 @@ config ISA
|
|||
(MCA) or VESA. ISA is an older system, now being displaced by PCI;
|
||||
newer boards don't support it. If you have ISA, say Y, otherwise N.
|
||||
|
||||
if X86_32
|
||||
|
||||
config EISA
|
||||
bool "EISA support"
|
||||
depends on ISA
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2015 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
|
@ -41,12 +42,12 @@ static bool devcd_disabled;
|
|||
|
||||
struct devcd_entry {
|
||||
struct device devcd_dev;
|
||||
const void *data;
|
||||
void *data;
|
||||
size_t datalen;
|
||||
struct module *owner;
|
||||
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen);
|
||||
void (*free)(const void *data);
|
||||
void *data, size_t datalen);
|
||||
void (*free)(void *data);
|
||||
struct delayed_work del_wk;
|
||||
struct device *failing_dev;
|
||||
};
|
||||
|
@ -174,7 +175,7 @@ static struct class devcd_class = {
|
|||
};
|
||||
|
||||
static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen)
|
||||
void *data, size_t datalen)
|
||||
{
|
||||
if (offset > datalen)
|
||||
return -EINVAL;
|
||||
|
@ -188,6 +189,11 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void devcd_freev(void *data)
|
||||
{
|
||||
vfree(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_coredumpv - create device coredump with vmalloc data
|
||||
* @dev: the struct device for the crashed device
|
||||
|
@ -198,10 +204,10 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
|
|||
* This function takes ownership of the vmalloc'ed data and will free
|
||||
* it when it is no longer used. See dev_coredumpm() for more information.
|
||||
*/
|
||||
void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
|
||||
void dev_coredumpv(struct device *dev, void *data, size_t datalen,
|
||||
gfp_t gfp)
|
||||
{
|
||||
dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree);
|
||||
dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_coredumpv);
|
||||
|
||||
|
@ -212,6 +218,44 @@ static int devcd_match_failing(struct device *dev, const void *failing)
|
|||
return devcd->failing_dev == failing;
|
||||
}
|
||||
|
||||
/**
|
||||
* devcd_free_sgtable - free all the memory of the given scatterlist table
|
||||
* (i.e. both pages and scatterlist instances)
|
||||
* NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
|
||||
* using the sg_chain function then that function should be called only once
|
||||
* on the chained table
|
||||
* @table: pointer to sg_table to free
|
||||
*/
|
||||
static void devcd_free_sgtable(void *data)
|
||||
{
|
||||
_devcd_free_sgtable(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* devcd_read_from_table - copy data from sg_table to a given buffer
|
||||
* and return the number of bytes read
|
||||
* @buffer: the buffer to copy the data to it
|
||||
* @buf_len: the length of the buffer
|
||||
* @data: the scatterlist table to copy from
|
||||
* @offset: start copy from @offset@ bytes from the head of the data
|
||||
* in the given scatterlist
|
||||
* @data_len: the length of the data in the sg_table
|
||||
*/
|
||||
static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
|
||||
size_t buf_len, void *data,
|
||||
size_t data_len)
|
||||
{
|
||||
struct scatterlist *table = data;
|
||||
|
||||
if (offset > data_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset + buf_len > data_len)
|
||||
buf_len = data_len - offset;
|
||||
return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
|
||||
offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_coredumpm - create device coredump with read/free methods
|
||||
* @dev: the struct device for the crashed device
|
||||
|
@ -228,10 +272,10 @@ static int devcd_match_failing(struct device *dev, const void *failing)
|
|||
* function will be called to free the data.
|
||||
*/
|
||||
void dev_coredumpm(struct device *dev, struct module *owner,
|
||||
const void *data, size_t datalen, gfp_t gfp,
|
||||
void *data, size_t datalen, gfp_t gfp,
|
||||
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen),
|
||||
void (*free)(const void *data))
|
||||
void *data, size_t datalen),
|
||||
void (*free)(void *data))
|
||||
{
|
||||
static atomic_t devcd_count = ATOMIC_INIT(0);
|
||||
struct devcd_entry *devcd;
|
||||
|
@ -291,6 +335,27 @@ void dev_coredumpm(struct device *dev, struct module *owner,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dev_coredumpm);
|
||||
|
||||
/**
|
||||
* dev_coredumpmsg - create device coredump that uses scatterlist as data
|
||||
* parameter
|
||||
* @dev: the struct device for the crashed device
|
||||
* @table: the dump data
|
||||
* @datalen: length of the data
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* Creates a new device coredump for the given device. If a previous one hasn't
|
||||
* been read yet, the new coredump is discarded. The data lifetime is determined
|
||||
* by the device coredump framework and when it is no longer needed
|
||||
* it will free the data.
|
||||
*/
|
||||
void dev_coredumpsg(struct device *dev, struct scatterlist *table,
|
||||
size_t datalen, gfp_t gfp)
|
||||
{
|
||||
dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable,
|
||||
devcd_free_sgtable);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_coredumpsg);
|
||||
|
||||
static int __init devcoredump_init(void)
|
||||
{
|
||||
return class_register(&devcd_class);
|
||||
|
|
|
@ -125,9 +125,7 @@ static void fw_cfg_io_cleanup(void)
|
|||
# define FW_CFG_CTRL_OFF 0x00
|
||||
# define FW_CFG_DATA_OFF 0x01
|
||||
# else
|
||||
# warning "QEMU FW_CFG may not be available on this architecture!"
|
||||
# define FW_CFG_CTRL_OFF 0x00
|
||||
# define FW_CFG_DATA_OFF 0x01
|
||||
# error "QEMU FW_CFG not available on this architecture!"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -530,30 +530,35 @@ menu "Port-mapped I/O GPIO drivers"
|
|||
|
||||
config GPIO_104_DIO_48E
|
||||
tristate "ACCES 104-DIO-48E GPIO support"
|
||||
depends on ISA
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-DIO-48E family. The base port
|
||||
address for the device may be configured via the dio_48e_base module
|
||||
parameter. The interrupt line number for the device may be configured
|
||||
via the dio_48e_irq module parameter.
|
||||
Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
|
||||
104-DIO-24E). The base port addresses for the devices may be
|
||||
configured via the base module parameter. The interrupt line numbers
|
||||
for the devices may be configured via the irq module parameter.
|
||||
|
||||
config GPIO_104_IDIO_16
|
||||
tristate "ACCES 104-IDIO-16 GPIO support"
|
||||
depends on ISA
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-IDIO-16 family. The base port
|
||||
address for the device may be set via the idio_16_base module
|
||||
parameter. The interrupt line number for the device may be set via the
|
||||
idio_16_irq module parameter.
|
||||
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
|
||||
104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, 104-IDO-8). The
|
||||
base port addresses for the devices may be configured via the base
|
||||
module parameter. The interrupt line numbers for the devices may be
|
||||
configured via the irq module parameter.
|
||||
|
||||
config GPIO_104_IDI_48
|
||||
tristate "ACCES 104-IDI-48 GPIO support"
|
||||
depends on ISA
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-IDI-48 family. The base port
|
||||
address for the device may be configured via the idi_48_base module
|
||||
parameter. The interrupt line number for the device may be configured
|
||||
via the idi_48_irq module parameter.
|
||||
Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A,
|
||||
104-IDI-48AC, 104-IDI-48B, 104-IDI-48BC). The base port addresses for
|
||||
the devices may be configured via the base module parameter. The
|
||||
interrupt line numbers for the devices may be configured via the irq
|
||||
module parameter.
|
||||
|
||||
config GPIO_F7188X
|
||||
tristate "F71869, F71869A, F71882FG, F71889F and F81866 GPIO support"
|
||||
|
@ -622,12 +627,13 @@ config GPIO_TS5500
|
|||
|
||||
config GPIO_WS16C48
|
||||
tristate "WinSystems WS16C48 GPIO support"
|
||||
depends on ISA
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the WinSystems WS16C48. The base port address
|
||||
for the device may be configured via the ws16c48_base module
|
||||
parameter. The interrupt line number for the device may be configured
|
||||
via the ws16c48_irq module parameter.
|
||||
Enables GPIO support for the WinSystems WS16C48. The base port
|
||||
addresses for the devices may be configured via the base module
|
||||
parameter. The interrupt line numbers for the devices may be
|
||||
configured via the irq module parameter.
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* GPIO driver for the ACCES 104-DIO-48E
|
||||
* GPIO driver for the ACCES 104-DIO-48E series
|
||||
* Copyright (C) 2016 William Breathitt Gray
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -10,6 +10,9 @@
|
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the following ACCES devices: 104-DIO-48E and
|
||||
* 104-DIO-24E.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -19,18 +22,23 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static unsigned dio_48e_base;
|
||||
module_param(dio_48e_base, uint, 0);
|
||||
MODULE_PARM_DESC(dio_48e_base, "ACCES 104-DIO-48E base address");
|
||||
static unsigned dio_48e_irq;
|
||||
module_param(dio_48e_irq, uint, 0);
|
||||
MODULE_PARM_DESC(dio_48e_irq, "ACCES 104-DIO-48E interrupt line number");
|
||||
#define DIO48E_EXTENT 16
|
||||
#define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT)
|
||||
|
||||
static unsigned int base[MAX_NUM_DIO48E];
|
||||
static unsigned int num_dio48e;
|
||||
module_param_array(base, uint, &num_dio48e, 0);
|
||||
MODULE_PARM_DESC(base, "ACCES 104-DIO-48E base addresses");
|
||||
|
||||
static unsigned int irq[MAX_NUM_DIO48E];
|
||||
module_param_array(irq, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
|
||||
|
||||
/**
|
||||
* struct dio48e_gpio - GPIO device private data structure
|
||||
|
@ -294,23 +302,19 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init dio48e_probe(struct platform_device *pdev)
|
||||
static int dio48e_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dio48e_gpio *dio48egpio;
|
||||
const unsigned base = dio_48e_base;
|
||||
const unsigned extent = 16;
|
||||
const char *const name = dev_name(dev);
|
||||
int err;
|
||||
const unsigned irq = dio_48e_irq;
|
||||
|
||||
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
|
||||
if (!dio48egpio)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base, extent, name)) {
|
||||
if (!devm_request_region(dev, base[id], DIO48E_EXTENT, name)) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
base, base + extent);
|
||||
base[id], base[id] + DIO48E_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -324,8 +328,8 @@ static int __init dio48e_probe(struct platform_device *pdev)
|
|||
dio48egpio->chip.direction_output = dio48e_gpio_direction_output;
|
||||
dio48egpio->chip.get = dio48e_gpio_get;
|
||||
dio48egpio->chip.set = dio48e_gpio_set;
|
||||
dio48egpio->base = base;
|
||||
dio48egpio->irq = irq;
|
||||
dio48egpio->base = base[id];
|
||||
dio48egpio->irq = irq[id];
|
||||
|
||||
spin_lock_init(&dio48egpio->lock);
|
||||
|
||||
|
@ -338,19 +342,19 @@ static int __init dio48e_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* initialize all GPIO as output */
|
||||
outb(0x80, base + 3);
|
||||
outb(0x00, base);
|
||||
outb(0x00, base + 1);
|
||||
outb(0x00, base + 2);
|
||||
outb(0x00, base + 3);
|
||||
outb(0x80, base + 7);
|
||||
outb(0x00, base + 4);
|
||||
outb(0x00, base + 5);
|
||||
outb(0x00, base + 6);
|
||||
outb(0x00, base + 7);
|
||||
outb(0x80, base[id] + 3);
|
||||
outb(0x00, base[id]);
|
||||
outb(0x00, base[id] + 1);
|
||||
outb(0x00, base[id] + 2);
|
||||
outb(0x00, base[id] + 3);
|
||||
outb(0x80, base[id] + 7);
|
||||
outb(0x00, base[id] + 4);
|
||||
outb(0x00, base[id] + 5);
|
||||
outb(0x00, base[id] + 6);
|
||||
outb(0x00, base[id] + 7);
|
||||
|
||||
/* disable IRQ by default */
|
||||
inb(base + 0xB);
|
||||
inb(base[id] + 0xB);
|
||||
|
||||
err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0,
|
||||
handle_edge_irq, IRQ_TYPE_NONE);
|
||||
|
@ -359,7 +363,7 @@ static int __init dio48e_probe(struct platform_device *pdev)
|
|||
goto err_gpiochip_remove;
|
||||
}
|
||||
|
||||
err = request_irq(irq, dio48e_irq_handler, 0, name, dio48egpio);
|
||||
err = request_irq(irq[id], dio48e_irq_handler, 0, name, dio48egpio);
|
||||
if (err) {
|
||||
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
|
||||
goto err_gpiochip_remove;
|
||||
|
@ -372,9 +376,9 @@ static int __init dio48e_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int dio48e_remove(struct platform_device *pdev)
|
||||
static int dio48e_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct dio48e_gpio *const dio48egpio = platform_get_drvdata(pdev);
|
||||
struct dio48e_gpio *const dio48egpio = dev_get_drvdata(dev);
|
||||
|
||||
free_irq(dio48egpio->irq, dio48egpio);
|
||||
gpiochip_remove(&dio48egpio->chip);
|
||||
|
@ -382,48 +386,14 @@ static int dio48e_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *dio48e_device;
|
||||
|
||||
static struct platform_driver dio48e_driver = {
|
||||
static struct isa_driver dio48e_driver = {
|
||||
.probe = dio48e_probe,
|
||||
.driver = {
|
||||
.name = "104-dio-48e"
|
||||
},
|
||||
.remove = dio48e_remove
|
||||
};
|
||||
|
||||
static void __exit dio48e_exit(void)
|
||||
{
|
||||
platform_device_unregister(dio48e_device);
|
||||
platform_driver_unregister(&dio48e_driver);
|
||||
}
|
||||
|
||||
static int __init dio48e_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
dio48e_device = platform_device_alloc(dio48e_driver.driver.name, -1);
|
||||
if (!dio48e_device)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(dio48e_device);
|
||||
if (err)
|
||||
goto err_platform_device;
|
||||
|
||||
err = platform_driver_probe(&dio48e_driver, dio48e_probe);
|
||||
if (err)
|
||||
goto err_platform_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
err_platform_driver:
|
||||
platform_device_del(dio48e_device);
|
||||
err_platform_device:
|
||||
platform_device_put(dio48e_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(dio48e_init);
|
||||
module_exit(dio48e_exit);
|
||||
module_isa_driver(dio48e_driver, num_dio48e);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the following ACCES devices: 104-IDI-48A,
|
||||
* 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -19,18 +22,23 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static unsigned idi_48_base;
|
||||
module_param(idi_48_base, uint, 0);
|
||||
MODULE_PARM_DESC(idi_48_base, "ACCES 104-IDI-48 base address");
|
||||
static unsigned idi_48_irq;
|
||||
module_param(idi_48_irq, uint, 0);
|
||||
MODULE_PARM_DESC(idi_48_irq, "ACCES 104-IDI-48 interrupt line number");
|
||||
#define IDI_48_EXTENT 8
|
||||
#define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
|
||||
|
||||
static unsigned int base[MAX_NUM_IDI_48];
|
||||
static unsigned int num_idi_48;
|
||||
module_param_array(base, uint, &num_idi_48, 0);
|
||||
MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
|
||||
|
||||
static unsigned int irq[MAX_NUM_IDI_48];
|
||||
module_param_array(irq, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
|
||||
|
||||
/**
|
||||
* struct idi_48_gpio - GPIO device private data structure
|
||||
|
@ -211,23 +219,19 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init idi_48_probe(struct platform_device *pdev)
|
||||
static int idi_48_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct idi_48_gpio *idi48gpio;
|
||||
const unsigned base = idi_48_base;
|
||||
const unsigned extent = 8;
|
||||
const char *const name = dev_name(dev);
|
||||
int err;
|
||||
const unsigned irq = idi_48_irq;
|
||||
|
||||
idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL);
|
||||
if (!idi48gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base, extent, name)) {
|
||||
if (!devm_request_region(dev, base[id], IDI_48_EXTENT, name)) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
base, base + extent);
|
||||
base[id], base[id] + IDI_48_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -239,8 +243,8 @@ static int __init idi_48_probe(struct platform_device *pdev)
|
|||
idi48gpio->chip.get_direction = idi_48_gpio_get_direction;
|
||||
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
|
||||
idi48gpio->chip.get = idi_48_gpio_get;
|
||||
idi48gpio->base = base;
|
||||
idi48gpio->irq = irq;
|
||||
idi48gpio->base = base[id];
|
||||
idi48gpio->irq = irq[id];
|
||||
|
||||
spin_lock_init(&idi48gpio->lock);
|
||||
|
||||
|
@ -253,8 +257,8 @@ static int __init idi_48_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Disable IRQ by default */
|
||||
outb(0, base + 7);
|
||||
inb(base + 7);
|
||||
outb(0, base[id] + 7);
|
||||
inb(base[id] + 7);
|
||||
|
||||
err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0,
|
||||
handle_edge_irq, IRQ_TYPE_NONE);
|
||||
|
@ -263,7 +267,7 @@ static int __init idi_48_probe(struct platform_device *pdev)
|
|||
goto err_gpiochip_remove;
|
||||
}
|
||||
|
||||
err = request_irq(irq, idi_48_irq_handler, IRQF_SHARED, name,
|
||||
err = request_irq(irq[id], idi_48_irq_handler, IRQF_SHARED, name,
|
||||
idi48gpio);
|
||||
if (err) {
|
||||
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
|
||||
|
@ -277,9 +281,9 @@ static int __init idi_48_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int idi_48_remove(struct platform_device *pdev)
|
||||
static int idi_48_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct idi_48_gpio *const idi48gpio = platform_get_drvdata(pdev);
|
||||
struct idi_48_gpio *const idi48gpio = dev_get_drvdata(dev);
|
||||
|
||||
free_irq(idi48gpio->irq, idi48gpio);
|
||||
gpiochip_remove(&idi48gpio->chip);
|
||||
|
@ -287,48 +291,14 @@ static int idi_48_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *idi_48_device;
|
||||
|
||||
static struct platform_driver idi_48_driver = {
|
||||
static struct isa_driver idi_48_driver = {
|
||||
.probe = idi_48_probe,
|
||||
.driver = {
|
||||
.name = "104-idi-48"
|
||||
},
|
||||
.remove = idi_48_remove
|
||||
};
|
||||
|
||||
static void __exit idi_48_exit(void)
|
||||
{
|
||||
platform_device_unregister(idi_48_device);
|
||||
platform_driver_unregister(&idi_48_driver);
|
||||
}
|
||||
|
||||
static int __init idi_48_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
idi_48_device = platform_device_alloc(idi_48_driver.driver.name, -1);
|
||||
if (!idi_48_device)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(idi_48_device);
|
||||
if (err)
|
||||
goto err_platform_device;
|
||||
|
||||
err = platform_driver_probe(&idi_48_driver, idi_48_probe);
|
||||
if (err)
|
||||
goto err_platform_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
err_platform_driver:
|
||||
platform_device_del(idi_48_device);
|
||||
err_platform_device:
|
||||
platform_device_put(idi_48_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(idi_48_init);
|
||||
module_exit(idi_48_exit);
|
||||
module_isa_driver(idi_48_driver, num_idi_48);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the following ACCES devices: 104-IDIO-16,
|
||||
* 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -19,18 +22,23 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static unsigned idio_16_base;
|
||||
module_param(idio_16_base, uint, 0);
|
||||
MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address");
|
||||
static unsigned idio_16_irq;
|
||||
module_param(idio_16_irq, uint, 0);
|
||||
MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number");
|
||||
#define IDIO_16_EXTENT 8
|
||||
#define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
|
||||
|
||||
static unsigned int base[MAX_NUM_IDIO_16];
|
||||
static unsigned int num_idio_16;
|
||||
module_param_array(base, uint, &num_idio_16, 0);
|
||||
MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
|
||||
|
||||
static unsigned int irq[MAX_NUM_IDIO_16];
|
||||
module_param_array(irq, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
|
||||
|
||||
/**
|
||||
* struct idio_16_gpio - GPIO device private data structure
|
||||
|
@ -185,23 +193,19 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init idio_16_probe(struct platform_device *pdev)
|
||||
static int idio_16_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct idio_16_gpio *idio16gpio;
|
||||
const unsigned base = idio_16_base;
|
||||
const unsigned extent = 8;
|
||||
const char *const name = dev_name(dev);
|
||||
int err;
|
||||
const unsigned irq = idio_16_irq;
|
||||
|
||||
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
|
||||
if (!idio16gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base, extent, name)) {
|
||||
if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
base, base + extent);
|
||||
base[id], base[id] + IDIO_16_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -215,8 +219,8 @@ static int __init idio_16_probe(struct platform_device *pdev)
|
|||
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
|
||||
idio16gpio->chip.get = idio_16_gpio_get;
|
||||
idio16gpio->chip.set = idio_16_gpio_set;
|
||||
idio16gpio->base = base;
|
||||
idio16gpio->irq = irq;
|
||||
idio16gpio->base = base[id];
|
||||
idio16gpio->irq = irq[id];
|
||||
idio16gpio->out_state = 0xFFFF;
|
||||
|
||||
spin_lock_init(&idio16gpio->lock);
|
||||
|
@ -230,8 +234,8 @@ static int __init idio_16_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Disable IRQ by default */
|
||||
outb(0, base + 2);
|
||||
outb(0, base + 1);
|
||||
outb(0, base[id] + 2);
|
||||
outb(0, base[id] + 1);
|
||||
|
||||
err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
|
||||
handle_edge_irq, IRQ_TYPE_NONE);
|
||||
|
@ -240,7 +244,7 @@ static int __init idio_16_probe(struct platform_device *pdev)
|
|||
goto err_gpiochip_remove;
|
||||
}
|
||||
|
||||
err = request_irq(irq, idio_16_irq_handler, 0, name, idio16gpio);
|
||||
err = request_irq(irq[id], idio_16_irq_handler, 0, name, idio16gpio);
|
||||
if (err) {
|
||||
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
|
||||
goto err_gpiochip_remove;
|
||||
|
@ -253,9 +257,9 @@ static int __init idio_16_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int idio_16_remove(struct platform_device *pdev)
|
||||
static int idio_16_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = platform_get_drvdata(pdev);
|
||||
struct idio_16_gpio *const idio16gpio = dev_get_drvdata(dev);
|
||||
|
||||
free_irq(idio16gpio->irq, idio16gpio);
|
||||
gpiochip_remove(&idio16gpio->chip);
|
||||
|
@ -263,48 +267,15 @@ static int idio_16_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *idio_16_device;
|
||||
|
||||
static struct platform_driver idio_16_driver = {
|
||||
static struct isa_driver idio_16_driver = {
|
||||
.probe = idio_16_probe,
|
||||
.driver = {
|
||||
.name = "104-idio-16"
|
||||
},
|
||||
.remove = idio_16_remove
|
||||
};
|
||||
|
||||
static void __exit idio_16_exit(void)
|
||||
{
|
||||
platform_device_unregister(idio_16_device);
|
||||
platform_driver_unregister(&idio_16_driver);
|
||||
}
|
||||
|
||||
static int __init idio_16_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
idio_16_device = platform_device_alloc(idio_16_driver.driver.name, -1);
|
||||
if (!idio_16_device)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(idio_16_device);
|
||||
if (err)
|
||||
goto err_platform_device;
|
||||
|
||||
err = platform_driver_probe(&idio_16_driver, idio_16_probe);
|
||||
if (err)
|
||||
goto err_platform_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
err_platform_driver:
|
||||
platform_device_del(idio_16_device);
|
||||
err_platform_device:
|
||||
platform_device_put(idio_16_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(idio_16_init);
|
||||
module_exit(idio_16_exit);
|
||||
module_isa_driver(idio_16_driver, num_idio_16);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
|
||||
|
|
|
@ -19,18 +19,23 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static unsigned ws16c48_base;
|
||||
module_param(ws16c48_base, uint, 0);
|
||||
MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address");
|
||||
static unsigned ws16c48_irq;
|
||||
module_param(ws16c48_irq, uint, 0);
|
||||
MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number");
|
||||
#define WS16C48_EXTENT 16
|
||||
#define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
|
||||
|
||||
static unsigned int base[MAX_NUM_WS16C48];
|
||||
static unsigned int num_ws16c48;
|
||||
module_param_array(base, uint, &num_ws16c48, 0);
|
||||
MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses");
|
||||
|
||||
static unsigned int irq[MAX_NUM_WS16C48];
|
||||
module_param_array(irq, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
|
||||
|
||||
/**
|
||||
* struct ws16c48_gpio - GPIO device private data structure
|
||||
|
@ -298,23 +303,19 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init ws16c48_probe(struct platform_device *pdev)
|
||||
static int ws16c48_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ws16c48_gpio *ws16c48gpio;
|
||||
const unsigned base = ws16c48_base;
|
||||
const unsigned extent = 16;
|
||||
const char *const name = dev_name(dev);
|
||||
int err;
|
||||
const unsigned irq = ws16c48_irq;
|
||||
|
||||
ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
|
||||
if (!ws16c48gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base, extent, name)) {
|
||||
if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
base, base + extent);
|
||||
base[id], base[id] + WS16C48_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -328,8 +329,8 @@ static int __init ws16c48_probe(struct platform_device *pdev)
|
|||
ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
|
||||
ws16c48gpio->chip.get = ws16c48_gpio_get;
|
||||
ws16c48gpio->chip.set = ws16c48_gpio_set;
|
||||
ws16c48gpio->base = base;
|
||||
ws16c48gpio->irq = irq;
|
||||
ws16c48gpio->base = base[id];
|
||||
ws16c48gpio->irq = irq[id];
|
||||
|
||||
spin_lock_init(&ws16c48gpio->lock);
|
||||
|
||||
|
@ -342,11 +343,11 @@ static int __init ws16c48_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Disable IRQ by default */
|
||||
outb(0x80, base + 7);
|
||||
outb(0, base + 8);
|
||||
outb(0, base + 9);
|
||||
outb(0, base + 10);
|
||||
outb(0xC0, base + 7);
|
||||
outb(0x80, base[id] + 7);
|
||||
outb(0, base[id] + 8);
|
||||
outb(0, base[id] + 9);
|
||||
outb(0, base[id] + 10);
|
||||
outb(0xC0, base[id] + 7);
|
||||
|
||||
err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
|
||||
handle_edge_irq, IRQ_TYPE_NONE);
|
||||
|
@ -355,7 +356,7 @@ static int __init ws16c48_probe(struct platform_device *pdev)
|
|||
goto err_gpiochip_remove;
|
||||
}
|
||||
|
||||
err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name,
|
||||
err = request_irq(irq[id], ws16c48_irq_handler, IRQF_SHARED, name,
|
||||
ws16c48gpio);
|
||||
if (err) {
|
||||
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
|
||||
|
@ -369,9 +370,9 @@ static int __init ws16c48_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ws16c48_remove(struct platform_device *pdev)
|
||||
static int ws16c48_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev);
|
||||
struct ws16c48_gpio *const ws16c48gpio = dev_get_drvdata(dev);
|
||||
|
||||
free_irq(ws16c48gpio->irq, ws16c48gpio);
|
||||
gpiochip_remove(&ws16c48gpio->chip);
|
||||
|
@ -379,48 +380,15 @@ static int ws16c48_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *ws16c48_device;
|
||||
|
||||
static struct platform_driver ws16c48_driver = {
|
||||
static struct isa_driver ws16c48_driver = {
|
||||
.probe = ws16c48_probe,
|
||||
.driver = {
|
||||
.name = "ws16c48"
|
||||
},
|
||||
.remove = ws16c48_remove
|
||||
};
|
||||
|
||||
static void __exit ws16c48_exit(void)
|
||||
{
|
||||
platform_device_unregister(ws16c48_device);
|
||||
platform_driver_unregister(&ws16c48_driver);
|
||||
}
|
||||
|
||||
static int __init ws16c48_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1);
|
||||
if (!ws16c48_device)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(ws16c48_device);
|
||||
if (err)
|
||||
goto err_platform_device;
|
||||
|
||||
err = platform_driver_probe(&ws16c48_driver, ws16c48_probe);
|
||||
if (err)
|
||||
goto err_platform_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
err_platform_driver:
|
||||
platform_device_del(ws16c48_device);
|
||||
err_platform_device:
|
||||
platform_device_put(ws16c48_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(ws16c48_init);
|
||||
module_exit(ws16c48_exit);
|
||||
module_isa_driver(ws16c48_driver, num_ws16c48);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
|
||||
|
|
|
@ -210,7 +210,7 @@ config MCP4922
|
|||
|
||||
config STX104
|
||||
tristate "Apex Embedded Systems STX104 DAC driver"
|
||||
depends on ISA
|
||||
depends on X86 && ISA
|
||||
help
|
||||
Say yes here to build support for the 2-channel DAC on the Apex
|
||||
Embedded Systems STX104 integrated analog PC/104 card. The base port
|
||||
|
|
|
@ -33,16 +33,9 @@
|
|||
}
|
||||
|
||||
#define STX104_EXTENT 16
|
||||
/**
|
||||
* The highest base address possible for an ISA device is 0x3FF; this results in
|
||||
* 1024 possible base addresses. Dividing the number of possible base addresses
|
||||
* by the address extent taken by each device results in the maximum number of
|
||||
* devices on a system.
|
||||
*/
|
||||
#define MAX_NUM_STX104 (1024 / STX104_EXTENT)
|
||||
|
||||
static unsigned base[MAX_NUM_STX104];
|
||||
static unsigned num_stx104;
|
||||
static unsigned int base[max_num_isa_dev(STX104_EXTENT)];
|
||||
static unsigned int num_stx104;
|
||||
module_param_array(base, uint, &num_stx104, 0);
|
||||
MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
|
||||
|
||||
|
@ -134,18 +127,7 @@ static struct isa_driver stx104_driver = {
|
|||
}
|
||||
};
|
||||
|
||||
static void __exit stx104_exit(void)
|
||||
{
|
||||
isa_unregister_driver(&stx104_driver);
|
||||
}
|
||||
|
||||
static int __init stx104_init(void)
|
||||
{
|
||||
return isa_register_driver(&stx104_driver, num_stx104);
|
||||
}
|
||||
|
||||
module_init(stx104_init);
|
||||
module_exit(stx104_exit);
|
||||
module_isa_driver(stx104_driver, num_stx104);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
#include "iwl-csr.h"
|
||||
|
||||
static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen)
|
||||
void *data, size_t datalen)
|
||||
{
|
||||
const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
|
||||
ssize_t bytes_read;
|
||||
|
@ -104,7 +104,7 @@ static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
|
|||
return bytes_read + bytes_read_trans;
|
||||
}
|
||||
|
||||
static void iwl_mvm_free_coredump(const void *data)
|
||||
static void iwl_mvm_free_coredump(void *data)
|
||||
{
|
||||
const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
config PNPBIOS
|
||||
bool "Plug and Play BIOS support"
|
||||
depends on ISA && X86
|
||||
depends on ISA && X86_32
|
||||
default n
|
||||
---help---
|
||||
Linux uses the PNPBIOS as defined in "Plug and Play BIOS
|
||||
|
|
|
@ -738,7 +738,7 @@ config ALIM7101_WDT
|
|||
|
||||
config EBC_C384_WDT
|
||||
tristate "WinSystems EBC-C384 Watchdog Timer"
|
||||
depends on X86
|
||||
depends on X86 && ISA
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Enables watchdog timer support for the watchdog timer on the
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
|
@ -95,9 +95,8 @@ static const struct watchdog_info ebc_c384_wdt_info = {
|
|||
.identity = MODULE_NAME
|
||||
};
|
||||
|
||||
static int __init ebc_c384_wdt_probe(struct platform_device *pdev)
|
||||
static int ebc_c384_wdt_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct watchdog_device *wdd;
|
||||
|
||||
if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) {
|
||||
|
@ -122,61 +121,39 @@ static int __init ebc_c384_wdt_probe(struct platform_device *pdev)
|
|||
dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n",
|
||||
timeout, WATCHDOG_TIMEOUT);
|
||||
|
||||
platform_set_drvdata(pdev, wdd);
|
||||
dev_set_drvdata(dev, wdd);
|
||||
|
||||
return watchdog_register_device(wdd);
|
||||
}
|
||||
|
||||
static int ebc_c384_wdt_remove(struct platform_device *pdev)
|
||||
static int ebc_c384_wdt_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct watchdog_device *wdd = platform_get_drvdata(pdev);
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
|
||||
watchdog_unregister_device(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ebc_c384_wdt_driver = {
|
||||
static struct isa_driver ebc_c384_wdt_driver = {
|
||||
.probe = ebc_c384_wdt_probe,
|
||||
.driver = {
|
||||
.name = MODULE_NAME
|
||||
},
|
||||
.remove = ebc_c384_wdt_remove
|
||||
};
|
||||
|
||||
static struct platform_device *ebc_c384_wdt_device;
|
||||
|
||||
static int __init ebc_c384_wdt_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC"))
|
||||
return -ENODEV;
|
||||
|
||||
ebc_c384_wdt_device = platform_device_alloc(MODULE_NAME, -1);
|
||||
if (!ebc_c384_wdt_device)
|
||||
return -ENOMEM;
|
||||
|
||||
err = platform_device_add(ebc_c384_wdt_device);
|
||||
if (err)
|
||||
goto err_platform_device;
|
||||
|
||||
err = platform_driver_probe(&ebc_c384_wdt_driver, ebc_c384_wdt_probe);
|
||||
if (err)
|
||||
goto err_platform_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
err_platform_driver:
|
||||
platform_device_del(ebc_c384_wdt_device);
|
||||
err_platform_device:
|
||||
platform_device_put(ebc_c384_wdt_device);
|
||||
return err;
|
||||
return isa_register_driver(&ebc_c384_wdt_driver, 1);
|
||||
}
|
||||
|
||||
static void __exit ebc_c384_wdt_exit(void)
|
||||
{
|
||||
platform_device_unregister(ebc_c384_wdt_device);
|
||||
platform_driver_unregister(&ebc_c384_wdt_driver);
|
||||
isa_unregister_driver(&ebc_c384_wdt_driver);
|
||||
}
|
||||
|
||||
module_init(ebc_c384_wdt_init);
|
||||
|
@ -185,4 +162,4 @@ module_exit(ebc_c384_wdt_exit);
|
|||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" MODULE_NAME);
|
||||
MODULE_ALIAS("isa:" MODULE_NAME);
|
||||
|
|
|
@ -91,6 +91,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
|
|||
break;
|
||||
}
|
||||
|
||||
if (i < CHRDEV_MAJOR_DYN_END)
|
||||
pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range",
|
||||
name, i);
|
||||
|
||||
if (i == 0) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <asm/poll.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
struct poll_table_struct;
|
||||
|
||||
static ssize_t default_read_file(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -35,14 +41,278 @@ static ssize_t default_write_file(struct file *file, const char __user *buf,
|
|||
return count;
|
||||
}
|
||||
|
||||
const struct file_operations debugfs_file_operations = {
|
||||
const struct file_operations debugfs_noop_file_operations = {
|
||||
.read = default_read_file,
|
||||
.write = default_write_file,
|
||||
.open = simple_open,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
|
||||
/**
|
||||
* debugfs_use_file_start - mark the beginning of file data access
|
||||
* @dentry: the dentry object whose data is being accessed.
|
||||
* @srcu_idx: a pointer to some memory to store a SRCU index in.
|
||||
*
|
||||
* Up to a matching call to debugfs_use_file_finish(), any
|
||||
* successive call into the file removing functions debugfs_remove()
|
||||
* and debugfs_remove_recursive() will block. Since associated private
|
||||
* file data may only get freed after a successful return of any of
|
||||
* the removal functions, you may safely access it after a successful
|
||||
* call to debugfs_use_file_start() without worrying about
|
||||
* lifetime issues.
|
||||
*
|
||||
* If -%EIO is returned, the file has already been removed and thus,
|
||||
* it is not safe to access any of its data. If, on the other hand,
|
||||
* it is allowed to access the file data, zero is returned.
|
||||
*
|
||||
* Regardless of the return code, any call to
|
||||
* debugfs_use_file_start() must be followed by a matching call
|
||||
* to debugfs_use_file_finish().
|
||||
*/
|
||||
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
|
||||
__acquires(&debugfs_srcu)
|
||||
{
|
||||
*srcu_idx = srcu_read_lock(&debugfs_srcu);
|
||||
barrier();
|
||||
if (d_unlinked(dentry))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_use_file_start);
|
||||
|
||||
/**
|
||||
* debugfs_use_file_finish - mark the end of file data access
|
||||
* @srcu_idx: the SRCU index "created" by a former call to
|
||||
* debugfs_use_file_start().
|
||||
*
|
||||
* Allow any ongoing concurrent call into debugfs_remove() or
|
||||
* debugfs_remove_recursive() blocked by a former call to
|
||||
* debugfs_use_file_start() to proceed and return to its caller.
|
||||
*/
|
||||
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
|
||||
{
|
||||
srcu_read_unlock(&debugfs_srcu, srcu_idx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
|
||||
|
||||
#define F_DENTRY(filp) ((filp)->f_path.dentry)
|
||||
|
||||
#define REAL_FOPS_DEREF(dentry) \
|
||||
((const struct file_operations *)(dentry)->d_fsdata)
|
||||
|
||||
static int open_proxy_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = NULL;
|
||||
int srcu_idx, r;
|
||||
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx);
|
||||
if (r) {
|
||||
r = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
real_fops = REAL_FOPS_DEREF(dentry);
|
||||
real_fops = fops_get(real_fops);
|
||||
if (!real_fops) {
|
||||
/* Huh? Module did not clean up after itself at exit? */
|
||||
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
||||
dentry);
|
||||
r = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
replace_fops(filp, real_fops);
|
||||
|
||||
if (real_fops->open)
|
||||
r = real_fops->open(inode, filp);
|
||||
|
||||
out:
|
||||
fops_put(real_fops);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct file_operations debugfs_open_proxy_file_operations = {
|
||||
.open = open_proxy_open,
|
||||
};
|
||||
|
||||
#define PROTO(args...) args
|
||||
#define ARGS(args...) args
|
||||
|
||||
#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
|
||||
static ret_type full_proxy_ ## name(proto) \
|
||||
{ \
|
||||
const struct dentry *dentry = F_DENTRY(filp); \
|
||||
const struct file_operations *real_fops = \
|
||||
REAL_FOPS_DEREF(dentry); \
|
||||
int srcu_idx; \
|
||||
ret_type r; \
|
||||
\
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx); \
|
||||
if (likely(!r)) \
|
||||
r = real_fops->name(args); \
|
||||
debugfs_use_file_finish(srcu_idx); \
|
||||
return r; \
|
||||
}
|
||||
|
||||
FULL_PROXY_FUNC(llseek, loff_t, filp,
|
||||
PROTO(struct file *filp, loff_t offset, int whence),
|
||||
ARGS(filp, offset, whence));
|
||||
|
||||
FULL_PROXY_FUNC(read, ssize_t, filp,
|
||||
PROTO(struct file *filp, char __user *buf, size_t size,
|
||||
loff_t *ppos),
|
||||
ARGS(filp, buf, size, ppos));
|
||||
|
||||
FULL_PROXY_FUNC(write, ssize_t, filp,
|
||||
PROTO(struct file *filp, const char __user *buf, size_t size,
|
||||
loff_t *ppos),
|
||||
ARGS(filp, buf, size, ppos));
|
||||
|
||||
FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
|
||||
PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
|
||||
ARGS(filp, cmd, arg));
|
||||
|
||||
static unsigned int full_proxy_poll(struct file *filp,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
|
||||
int srcu_idx;
|
||||
unsigned int r = 0;
|
||||
|
||||
if (debugfs_use_file_start(dentry, &srcu_idx)) {
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return POLLHUP;
|
||||
}
|
||||
|
||||
r = real_fops->poll(filp, wait);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int full_proxy_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
|
||||
const struct file_operations *proxy_fops = filp->f_op;
|
||||
int r = 0;
|
||||
|
||||
/*
|
||||
* We must not protect this against removal races here: the
|
||||
* original releaser should be called unconditionally in order
|
||||
* not to leak any resources. Releasers must not assume that
|
||||
* ->i_private is still being meaningful here.
|
||||
*/
|
||||
if (real_fops->release)
|
||||
r = real_fops->release(inode, filp);
|
||||
|
||||
replace_fops(filp, d_inode(dentry)->i_fop);
|
||||
kfree((void *)proxy_fops);
|
||||
fops_put(real_fops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __full_proxy_fops_init(struct file_operations *proxy_fops,
|
||||
const struct file_operations *real_fops)
|
||||
{
|
||||
proxy_fops->release = full_proxy_release;
|
||||
if (real_fops->llseek)
|
||||
proxy_fops->llseek = full_proxy_llseek;
|
||||
if (real_fops->read)
|
||||
proxy_fops->read = full_proxy_read;
|
||||
if (real_fops->write)
|
||||
proxy_fops->write = full_proxy_write;
|
||||
if (real_fops->poll)
|
||||
proxy_fops->poll = full_proxy_poll;
|
||||
if (real_fops->unlocked_ioctl)
|
||||
proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
|
||||
}
|
||||
|
||||
static int full_proxy_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = NULL;
|
||||
struct file_operations *proxy_fops = NULL;
|
||||
int srcu_idx, r;
|
||||
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx);
|
||||
if (r) {
|
||||
r = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
real_fops = REAL_FOPS_DEREF(dentry);
|
||||
real_fops = fops_get(real_fops);
|
||||
if (!real_fops) {
|
||||
/* Huh? Module did not cleanup after itself at exit? */
|
||||
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
||||
dentry);
|
||||
r = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
|
||||
if (!proxy_fops) {
|
||||
r = -ENOMEM;
|
||||
goto free_proxy;
|
||||
}
|
||||
__full_proxy_fops_init(proxy_fops, real_fops);
|
||||
replace_fops(filp, proxy_fops);
|
||||
|
||||
if (real_fops->open) {
|
||||
r = real_fops->open(inode, filp);
|
||||
|
||||
if (filp->f_op != proxy_fops) {
|
||||
/* No protection against file removal anymore. */
|
||||
WARN(1, "debugfs file owner replaced proxy fops: %pd",
|
||||
dentry);
|
||||
goto free_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
free_proxy:
|
||||
kfree(proxy_fops);
|
||||
fops_put(real_fops);
|
||||
out:
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct file_operations debugfs_full_proxy_file_operations = {
|
||||
.open = full_proxy_open,
|
||||
};
|
||||
|
||||
ssize_t debugfs_attr_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
ssize_t ret;
|
||||
int srcu_idx;
|
||||
|
||||
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!ret))
|
||||
ret = simple_attr_read(file, buf, len, ppos);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_attr_read);
|
||||
|
||||
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
ssize_t ret;
|
||||
int srcu_idx;
|
||||
|
||||
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!ret))
|
||||
ret = simple_attr_write(file, buf, len, ppos);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_attr_write);
|
||||
|
||||
static struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *value,
|
||||
const struct file_operations *fops,
|
||||
const struct file_operations *fops_ro,
|
||||
|
@ -50,12 +320,14 @@ static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
|
|||
{
|
||||
/* if there are no write bits set, make read only */
|
||||
if (!(mode & S_IWUGO))
|
||||
return debugfs_create_file(name, mode, parent, value, fops_ro);
|
||||
return debugfs_create_file_unsafe(name, mode, parent, value,
|
||||
fops_ro);
|
||||
/* if there are no read bits set, make write only */
|
||||
if (!(mode & S_IRUGO))
|
||||
return debugfs_create_file(name, mode, parent, value, fops_wo);
|
||||
return debugfs_create_file_unsafe(name, mode, parent, value,
|
||||
fops_wo);
|
||||
|
||||
return debugfs_create_file(name, mode, parent, value, fops);
|
||||
return debugfs_create_file_unsafe(name, mode, parent, value, fops);
|
||||
}
|
||||
|
||||
static int debugfs_u8_set(void *data, u64 val)
|
||||
|
@ -68,9 +340,9 @@ static int debugfs_u8_get(void *data, u64 *val)
|
|||
*val = *(u8 *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
|
||||
|
@ -99,7 +371,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
|
|||
struct dentry *debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_u8,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,
|
||||
&fops_u8_ro, &fops_u8_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u8);
|
||||
|
@ -114,9 +386,9 @@ static int debugfs_u16_get(void *data, u64 *val)
|
|||
*val = *(u16 *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
|
||||
|
@ -145,7 +417,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
|
|||
struct dentry *debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_u16,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,
|
||||
&fops_u16_ro, &fops_u16_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u16);
|
||||
|
@ -160,9 +432,9 @@ static int debugfs_u32_get(void *data, u64 *val)
|
|||
*val = *(u32 *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
|
||||
|
@ -191,7 +463,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
|
|||
struct dentry *debugfs_create_u32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_u32,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,
|
||||
&fops_u32_ro, &fops_u32_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u32);
|
||||
|
@ -207,9 +479,9 @@ static int debugfs_u64_get(void *data, u64 *val)
|
|||
*val = *(u64 *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value
|
||||
|
@ -238,7 +510,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
|
|||
struct dentry *debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_u64,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,
|
||||
&fops_u64_ro, &fops_u64_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u64);
|
||||
|
@ -254,9 +526,10 @@ static int debugfs_ulong_get(void *data, u64 *val)
|
|||
*val = *(unsigned long *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set,
|
||||
"%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_ulong - create a debugfs file that is used to read and write
|
||||
|
@ -286,26 +559,30 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
|
|||
struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
|
||||
struct dentry *parent, unsigned long *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_ulong,
|
||||
&fops_ulong_ro, &fops_ulong_wo);
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value,
|
||||
&fops_ulong, &fops_ulong_ro,
|
||||
&fops_ulong_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_ulong);
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%02llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%02llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%02llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%02llx\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set, "0x%04llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%04llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set,
|
||||
"0x%04llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%04llx\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%08llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set,
|
||||
"0x%08llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set, "0x%016llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set,
|
||||
"0x%016llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
|
||||
|
||||
/*
|
||||
* debugfs_create_x{8,16,32,64} - create a debugfs file that is used to read and write an unsigned {8,16,32,64}-bit value
|
||||
|
@ -328,7 +605,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
|
|||
struct dentry *debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_x8,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,
|
||||
&fops_x8_ro, &fops_x8_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x8);
|
||||
|
@ -346,7 +623,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x8);
|
|||
struct dentry *debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_x16,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,
|
||||
&fops_x16_ro, &fops_x16_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x16);
|
||||
|
@ -364,7 +641,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x16);
|
|||
struct dentry *debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_x32,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,
|
||||
&fops_x32_ro, &fops_x32_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x32);
|
||||
|
@ -382,7 +659,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x32);
|
|||
struct dentry *debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_x64,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,
|
||||
&fops_x64_ro, &fops_x64_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x64);
|
||||
|
@ -398,10 +675,10 @@ static int debugfs_size_t_get(void *data, u64 *val)
|
|||
*val = *(size_t *)data;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_size_t, debugfs_size_t_get, debugfs_size_t_set,
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t, debugfs_size_t_get, debugfs_size_t_set,
|
||||
"%llu\n"); /* %llu and %zu are more or less the same */
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_ro, debugfs_size_t_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_ro, debugfs_size_t_get, NULL, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_size_t - create a debugfs file that is used to read and write an size_t value
|
||||
|
@ -416,8 +693,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
|
|||
struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_size_t,
|
||||
&fops_size_t_ro, &fops_size_t_wo);
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value,
|
||||
&fops_size_t, &fops_size_t_ro,
|
||||
&fops_size_t_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_size_t);
|
||||
|
||||
|
@ -431,10 +709,12 @@ static int debugfs_atomic_t_get(void *data, u64 *val)
|
|||
*val = atomic_read((atomic_t *)data);
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get,
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get,
|
||||
debugfs_atomic_t_set, "%lld\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, "%lld\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%lld\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL,
|
||||
"%lld\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set,
|
||||
"%lld\n");
|
||||
|
||||
/**
|
||||
* debugfs_create_atomic_t - create a debugfs file that is used to read and
|
||||
|
@ -450,8 +730,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%lld\n");
|
|||
struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_atomic_t,
|
||||
&fops_atomic_t_ro, &fops_atomic_t_wo);
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value,
|
||||
&fops_atomic_t, &fops_atomic_t_ro,
|
||||
&fops_atomic_t_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
|
||||
|
||||
|
@ -459,9 +740,17 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
|
|||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[3];
|
||||
bool *val = file->private_data;
|
||||
bool val;
|
||||
int r, srcu_idx;
|
||||
|
||||
if (*val)
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
val = *(bool *)file->private_data;
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (val)
|
||||
buf[0] = 'Y';
|
||||
else
|
||||
buf[0] = 'N';
|
||||
|
@ -477,6 +766,7 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
|
|||
char buf[32];
|
||||
size_t buf_size;
|
||||
bool bv;
|
||||
int r, srcu_idx;
|
||||
bool *val = file->private_data;
|
||||
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
|
@ -484,8 +774,14 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
|
|||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
if (strtobool(buf, &bv) == 0)
|
||||
if (strtobool(buf, &bv) == 0) {
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
*val = bv;
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -537,7 +833,7 @@ static const struct file_operations fops_bool_wo = {
|
|||
struct dentry *debugfs_create_bool(const char *name, umode_t mode,
|
||||
struct dentry *parent, bool *value)
|
||||
{
|
||||
return debugfs_create_mode(name, mode, parent, value, &fops_bool,
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool,
|
||||
&fops_bool_ro, &fops_bool_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_bool);
|
||||
|
@ -546,8 +842,15 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf,
|
|||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct debugfs_blob_wrapper *blob = file->private_data;
|
||||
return simple_read_from_buffer(user_buf, count, ppos, blob->data,
|
||||
ssize_t r;
|
||||
int srcu_idx;
|
||||
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
|
||||
blob->size);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_blob = {
|
||||
|
@ -584,7 +887,7 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode,
|
|||
struct dentry *parent,
|
||||
struct debugfs_blob_wrapper *blob)
|
||||
{
|
||||
return debugfs_create_file(name, mode, parent, blob, &fops_blob);
|
||||
return debugfs_create_file_unsafe(name, mode, parent, blob, &fops_blob);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_blob);
|
||||
|
||||
|
@ -689,7 +992,8 @@ struct dentry *debugfs_create_u32_array(const char *name, umode_t mode,
|
|||
data->array = array;
|
||||
data->elements = elements;
|
||||
|
||||
return debugfs_create_file(name, mode, parent, data, &u32_array_fops);
|
||||
return debugfs_create_file_unsafe(name, mode, parent, data,
|
||||
&u32_array_fops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u32_array);
|
||||
|
||||
|
|
|
@ -27,9 +27,14 @@
|
|||
#include <linux/parser.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define DEBUGFS_DEFAULT_MODE 0700
|
||||
|
||||
DEFINE_SRCU(debugfs_srcu);
|
||||
|
||||
static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
|
@ -39,7 +44,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
|
|||
struct inode *inode = new_inode(sb);
|
||||
if (inode) {
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_atime = inode->i_mtime =
|
||||
inode->i_ctime = current_fs_time(sb);
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
@ -294,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
|
|||
return dentry;
|
||||
}
|
||||
|
||||
static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *proxy_fops,
|
||||
const struct file_operations *real_fops)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
if (!(mode & S_IFMT))
|
||||
mode |= S_IFREG;
|
||||
BUG_ON(!S_ISREG(mode));
|
||||
dentry = start_creating(name, parent);
|
||||
|
||||
if (IS_ERR(dentry))
|
||||
return NULL;
|
||||
|
||||
inode = debugfs_get_inode(dentry->d_sb);
|
||||
if (unlikely(!inode))
|
||||
return failed_creating(dentry);
|
||||
|
||||
inode->i_mode = mode;
|
||||
inode->i_private = data;
|
||||
|
||||
inode->i_fop = proxy_fops;
|
||||
dentry->d_fsdata = (void *)real_fops;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
fsnotify_create(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* debugfs_create_file - create a file in the debugfs filesystem
|
||||
* @name: a pointer to a string containing the name of the file to create.
|
||||
|
@ -324,30 +361,53 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
|
|||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
if (!(mode & S_IFMT))
|
||||
mode |= S_IFREG;
|
||||
BUG_ON(!S_ISREG(mode));
|
||||
dentry = start_creating(name, parent);
|
||||
|
||||
if (IS_ERR(dentry))
|
||||
return NULL;
|
||||
|
||||
inode = debugfs_get_inode(dentry->d_sb);
|
||||
if (unlikely(!inode))
|
||||
return failed_creating(dentry);
|
||||
|
||||
inode->i_mode = mode;
|
||||
inode->i_fop = fops ? fops : &debugfs_file_operations;
|
||||
inode->i_private = data;
|
||||
d_instantiate(dentry, inode);
|
||||
fsnotify_create(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
return __debugfs_create_file(name, mode, parent, data,
|
||||
fops ? &debugfs_full_proxy_file_operations :
|
||||
&debugfs_noop_file_operations,
|
||||
fops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_file);
|
||||
|
||||
/**
|
||||
* debugfs_create_file_unsafe - create a file in the debugfs filesystem
|
||||
* @name: a pointer to a string containing the name of the file to create.
|
||||
* @mode: the permission that the file should have.
|
||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
||||
* directory dentry if set. If this parameter is NULL, then the
|
||||
* file will be created in the root of the debugfs filesystem.
|
||||
* @data: a pointer to something that the caller will want to get to later
|
||||
* on. The inode.i_private pointer will point to this value on
|
||||
* the open() call.
|
||||
* @fops: a pointer to a struct file_operations that should be used for
|
||||
* this file.
|
||||
*
|
||||
* debugfs_create_file_unsafe() is completely analogous to
|
||||
* debugfs_create_file(), the only difference being that the fops
|
||||
* handed it will not get protected against file removals by the
|
||||
* debugfs core.
|
||||
*
|
||||
* It is your responsibility to protect your struct file_operation
|
||||
* methods against file removals by means of debugfs_use_file_start()
|
||||
* and debugfs_use_file_finish(). ->open() is still protected by
|
||||
* debugfs though.
|
||||
*
|
||||
* Any struct file_operations defined by means of
|
||||
* DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and
|
||||
* thus, may be used here.
|
||||
*/
|
||||
struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
|
||||
return __debugfs_create_file(name, mode, parent, data,
|
||||
fops ? &debugfs_open_proxy_file_operations :
|
||||
&debugfs_noop_file_operations,
|
||||
fops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
|
||||
|
||||
/**
|
||||
* debugfs_create_file_size - create a file in the debugfs filesystem
|
||||
* @name: a pointer to a string containing the name of the file to create.
|
||||
|
@ -461,7 +521,11 @@ struct dentry *debugfs_create_automount(const char *name,
|
|||
inode->i_flags |= S_AUTOMOUNT;
|
||||
inode->i_private = data;
|
||||
dentry->d_fsdata = (void *)f;
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
inc_nlink(d_inode(dentry->d_parent));
|
||||
fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
}
|
||||
EXPORT_SYMBOL(debugfs_create_automount);
|
||||
|
@ -565,6 +629,8 @@ void debugfs_remove(struct dentry *dentry)
|
|||
inode_unlock(d_inode(parent));
|
||||
if (!ret)
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
|
||||
synchronize_srcu(&debugfs_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_remove);
|
||||
|
||||
|
@ -642,6 +708,8 @@ void debugfs_remove_recursive(struct dentry *dentry)
|
|||
if (!__debugfs_remove(child, parent))
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
inode_unlock(d_inode(parent));
|
||||
|
||||
synchronize_srcu(&debugfs_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
|
||||
|
||||
|
|
26
fs/debugfs/internal.h
Normal file
26
fs/debugfs/internal.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* internal.h - declarations internal to debugfs
|
||||
*
|
||||
* Copyright (C) 2016 Nicolai Stange <nicstange@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DEBUGFS_INTERNAL_H_
|
||||
#define _DEBUGFS_INTERNAL_H_
|
||||
|
||||
struct file_operations;
|
||||
|
||||
/* declared over in file.c */
|
||||
extern const struct file_operations debugfs_noop_file_operations;
|
||||
extern const struct file_operations debugfs_open_proxy_file_operations;
|
||||
extern const struct file_operations debugfs_full_proxy_file_operations;
|
||||
|
||||
struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops);
|
||||
|
||||
#endif /* _DEBUGFS_INTERNAL_H_ */
|
|
@ -753,7 +753,8 @@ int kernfs_add_one(struct kernfs_node *kn)
|
|||
ps_iattr = parent->iattr;
|
||||
if (ps_iattr) {
|
||||
struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
|
||||
ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
|
||||
ktime_get_real_ts(&ps_iattrs->ia_ctime);
|
||||
ps_iattrs->ia_mtime = ps_iattrs->ia_ctime;
|
||||
}
|
||||
|
||||
mutex_unlock(&kernfs_mutex);
|
||||
|
@ -1279,8 +1280,9 @@ static void __kernfs_remove(struct kernfs_node *kn)
|
|||
|
||||
/* update timestamps on the parent */
|
||||
if (ps_iattr) {
|
||||
ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME;
|
||||
ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME;
|
||||
ktime_get_real_ts(&ps_iattr->ia_iattr.ia_ctime);
|
||||
ps_iattr->ia_iattr.ia_mtime =
|
||||
ps_iattr->ia_iattr.ia_ctime;
|
||||
}
|
||||
|
||||
kernfs_put(pos);
|
||||
|
|
|
@ -190,15 +190,16 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
|
|||
char *buf;
|
||||
|
||||
buf = of->prealloc_buf;
|
||||
if (!buf)
|
||||
if (buf)
|
||||
mutex_lock(&of->prealloc_mutex);
|
||||
else
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* @of->mutex nests outside active ref and is used both to ensure that
|
||||
* the ops aren't called concurrently for the same open file, and
|
||||
* to provide exclusive access to ->prealloc_buf (when that exists).
|
||||
* the ops aren't called concurrently for the same open file.
|
||||
*/
|
||||
mutex_lock(&of->mutex);
|
||||
if (!kernfs_get_active(of->kn)) {
|
||||
|
@ -214,21 +215,23 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
|
|||
else
|
||||
len = -EINVAL;
|
||||
|
||||
kernfs_put_active(of->kn);
|
||||
mutex_unlock(&of->mutex);
|
||||
|
||||
if (len < 0)
|
||||
goto out_unlock;
|
||||
goto out_free;
|
||||
|
||||
if (copy_to_user(user_buf, buf, len)) {
|
||||
len = -EFAULT;
|
||||
goto out_unlock;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
*ppos += len;
|
||||
|
||||
out_unlock:
|
||||
kernfs_put_active(of->kn);
|
||||
mutex_unlock(&of->mutex);
|
||||
out_free:
|
||||
if (buf != of->prealloc_buf)
|
||||
if (buf == of->prealloc_buf)
|
||||
mutex_unlock(&of->prealloc_mutex);
|
||||
else
|
||||
kfree(buf);
|
||||
return len;
|
||||
}
|
||||
|
@ -284,15 +287,22 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
|
|||
}
|
||||
|
||||
buf = of->prealloc_buf;
|
||||
if (!buf)
|
||||
if (buf)
|
||||
mutex_lock(&of->prealloc_mutex);
|
||||
else
|
||||
buf = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buf, user_buf, len)) {
|
||||
len = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
buf[len] = '\0'; /* guarantee string termination */
|
||||
|
||||
/*
|
||||
* @of->mutex nests outside active ref and is used both to ensure that
|
||||
* the ops aren't called concurrently for the same open file, and
|
||||
* to provide exclusive access to ->prealloc_buf (when that exists).
|
||||
* the ops aren't called concurrently for the same open file.
|
||||
*/
|
||||
mutex_lock(&of->mutex);
|
||||
if (!kernfs_get_active(of->kn)) {
|
||||
|
@ -301,26 +311,22 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
if (copy_from_user(buf, user_buf, len)) {
|
||||
len = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
buf[len] = '\0'; /* guarantee string termination */
|
||||
|
||||
ops = kernfs_ops(of->kn);
|
||||
if (ops->write)
|
||||
len = ops->write(of, buf, len, *ppos);
|
||||
else
|
||||
len = -EINVAL;
|
||||
|
||||
kernfs_put_active(of->kn);
|
||||
mutex_unlock(&of->mutex);
|
||||
|
||||
if (len > 0)
|
||||
*ppos += len;
|
||||
|
||||
out_unlock:
|
||||
kernfs_put_active(of->kn);
|
||||
mutex_unlock(&of->mutex);
|
||||
out_free:
|
||||
if (buf != of->prealloc_buf)
|
||||
if (buf == of->prealloc_buf)
|
||||
mutex_unlock(&of->prealloc_mutex);
|
||||
else
|
||||
kfree(buf);
|
||||
return len;
|
||||
}
|
||||
|
@ -687,6 +693,7 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
|
|||
error = -ENOMEM;
|
||||
if (!of->prealloc_buf)
|
||||
goto err_free;
|
||||
mutex_init(&of->prealloc_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -54,7 +54,10 @@ static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn)
|
|||
iattrs->ia_mode = kn->mode;
|
||||
iattrs->ia_uid = GLOBAL_ROOT_UID;
|
||||
iattrs->ia_gid = GLOBAL_ROOT_GID;
|
||||
iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
|
||||
|
||||
ktime_get_real_ts(&iattrs->ia_atime);
|
||||
iattrs->ia_mtime = iattrs->ia_atime;
|
||||
iattrs->ia_ctime = iattrs->ia_atime;
|
||||
|
||||
simple_xattrs_init(&kn->iattr->xattrs);
|
||||
out_unlock:
|
||||
|
@ -236,16 +239,18 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
|
|||
static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
|
||||
{
|
||||
inode->i_mode = mode;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_atime = inode->i_mtime =
|
||||
inode->i_ctime = current_fs_time(inode->i_sb);
|
||||
}
|
||||
|
||||
static inline void set_inode_attr(struct inode *inode, struct iattr *iattr)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
inode->i_uid = iattr->ia_uid;
|
||||
inode->i_gid = iattr->ia_gid;
|
||||
inode->i_atime = iattr->ia_atime;
|
||||
inode->i_mtime = iattr->ia_mtime;
|
||||
inode->i_ctime = iattr->ia_ctime;
|
||||
inode->i_atime = timespec_trunc(iattr->ia_atime, sb->s_time_gran);
|
||||
inode->i_mtime = timespec_trunc(iattr->ia_mtime, sb->s_time_gran);
|
||||
inode->i_ctime = timespec_trunc(iattr->ia_ctime, sb->s_time_gran);
|
||||
}
|
||||
|
||||
static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#include <linux/seq_file.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
struct device;
|
||||
struct file_operations;
|
||||
struct srcu_struct;
|
||||
|
||||
struct debugfs_blob_wrapper {
|
||||
void *data;
|
||||
|
@ -41,14 +43,16 @@ struct debugfs_regset32 {
|
|||
|
||||
extern struct dentry *arch_debugfs_dir;
|
||||
|
||||
extern struct srcu_struct debugfs_srcu;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
||||
/* declared over in file.c */
|
||||
extern const struct file_operations debugfs_file_operations;
|
||||
|
||||
struct dentry *debugfs_create_file(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops);
|
||||
struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops);
|
||||
|
||||
struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
|
@ -68,6 +72,31 @@ struct dentry *debugfs_create_automount(const char *name,
|
|||
void debugfs_remove(struct dentry *dentry);
|
||||
void debugfs_remove_recursive(struct dentry *dentry);
|
||||
|
||||
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
|
||||
__acquires(&debugfs_srcu);
|
||||
|
||||
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
|
||||
|
||||
ssize_t debugfs_attr_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *ppos);
|
||||
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos);
|
||||
|
||||
#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
|
||||
static int __fops ## _open(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
__simple_attr_check_format(__fmt, 0ull); \
|
||||
return simple_attr_open(inode, file, __get, __set, __fmt); \
|
||||
} \
|
||||
static const struct file_operations __fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = __fops ## _open, \
|
||||
.release = simple_attr_release, \
|
||||
.read = debugfs_attr_read, \
|
||||
.write = debugfs_attr_write, \
|
||||
.llseek = generic_file_llseek, \
|
||||
}
|
||||
|
||||
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
||||
struct dentry *new_dir, const char *new_name);
|
||||
|
||||
|
@ -176,6 +205,20 @@ static inline void debugfs_remove(struct dentry *dentry)
|
|||
static inline void debugfs_remove_recursive(struct dentry *dentry)
|
||||
{ }
|
||||
|
||||
static inline int debugfs_use_file_start(const struct dentry *dentry,
|
||||
int *srcu_idx)
|
||||
__acquires(&debugfs_srcu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void debugfs_use_file_finish(int srcu_idx)
|
||||
__releases(&debugfs_srcu)
|
||||
{ }
|
||||
|
||||
#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
|
||||
static const struct file_operations __fops = { 0 }
|
||||
|
||||
static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
||||
struct dentry *new_dir, char *new_name)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* This file is provided under the GPLv2 license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2015 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called COPYING.
|
||||
*/
|
||||
#ifndef __DEVCOREDUMP_H
|
||||
#define __DEVCOREDUMP_H
|
||||
|
||||
|
@ -5,17 +24,62 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* _devcd_free_sgtable - free all the memory of the given scatterlist table
|
||||
* (i.e. both pages and scatterlist instances)
|
||||
* NOTE: if two tables allocated and chained using the sg_chain function then
|
||||
* this function should be called only once on the first table
|
||||
* @table: pointer to sg_table to free
|
||||
*/
|
||||
static inline void _devcd_free_sgtable(struct scatterlist *table)
|
||||
{
|
||||
int i;
|
||||
struct page *page;
|
||||
struct scatterlist *iter;
|
||||
struct scatterlist *delete_iter;
|
||||
|
||||
/* free pages */
|
||||
iter = table;
|
||||
for_each_sg(table, iter, sg_nents(table), i) {
|
||||
page = sg_page(iter);
|
||||
if (page)
|
||||
__free_page(page);
|
||||
}
|
||||
|
||||
/* then free all chained tables */
|
||||
iter = table;
|
||||
delete_iter = table; /* always points on a head of a table */
|
||||
while (!sg_is_last(iter)) {
|
||||
iter++;
|
||||
if (sg_is_chain(iter)) {
|
||||
iter = sg_chain_ptr(iter);
|
||||
kfree(delete_iter);
|
||||
delete_iter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
/* free the last table */
|
||||
kfree(delete_iter);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_DEV_COREDUMP
|
||||
void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
|
||||
void dev_coredumpv(struct device *dev, void *data, size_t datalen,
|
||||
gfp_t gfp);
|
||||
|
||||
void dev_coredumpm(struct device *dev, struct module *owner,
|
||||
const void *data, size_t datalen, gfp_t gfp,
|
||||
void *data, size_t datalen, gfp_t gfp,
|
||||
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen),
|
||||
void (*free)(const void *data));
|
||||
void *data, size_t datalen),
|
||||
void (*free)(void *data));
|
||||
|
||||
void dev_coredumpsg(struct device *dev, struct scatterlist *table,
|
||||
size_t datalen, gfp_t gfp);
|
||||
#else
|
||||
static inline void dev_coredumpv(struct device *dev, const void *data,
|
||||
static inline void dev_coredumpv(struct device *dev, void *data,
|
||||
size_t datalen, gfp_t gfp)
|
||||
{
|
||||
vfree(data);
|
||||
|
@ -23,13 +87,19 @@ static inline void dev_coredumpv(struct device *dev, const void *data,
|
|||
|
||||
static inline void
|
||||
dev_coredumpm(struct device *dev, struct module *owner,
|
||||
const void *data, size_t datalen, gfp_t gfp,
|
||||
void *data, size_t datalen, gfp_t gfp,
|
||||
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
|
||||
const void *data, size_t datalen),
|
||||
void (*free)(const void *data))
|
||||
void *data, size_t datalen),
|
||||
void (*free)(void *data))
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table,
|
||||
size_t datalen, gfp_t gfp)
|
||||
{
|
||||
_devcd_free_sgtable(table);
|
||||
}
|
||||
#endif /* CONFIG_DEV_COREDUMP */
|
||||
|
||||
#endif /* __DEVCOREDUMP_H */
|
||||
|
|
|
@ -1289,7 +1289,10 @@ do { \
|
|||
} while (0)
|
||||
#else
|
||||
#define dev_dbg_ratelimited(dev, fmt, ...) \
|
||||
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
|
||||
do { \
|
||||
if (0) \
|
||||
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
|
|
|
@ -2427,6 +2427,8 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
|
|||
|
||||
/* fs/char_dev.c */
|
||||
#define CHRDEV_MAJOR_HASH_SIZE 255
|
||||
/* Marks the bottom of the first segment of free char majors */
|
||||
#define CHRDEV_MAJOR_DYN_END 234
|
||||
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
|
||||
extern int register_chrdev_region(dev_t, unsigned, const char *);
|
||||
extern int __register_chrdev(unsigned int major, unsigned int baseminor,
|
||||
|
|
|
@ -36,4 +36,36 @@ static inline void isa_unregister_driver(struct isa_driver *d)
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* module_isa_driver() - Helper macro for registering a ISA driver
|
||||
* @__isa_driver: isa_driver struct
|
||||
* @__num_isa_dev: number of devices to register
|
||||
*
|
||||
* Helper macro for ISA drivers which do not do anything special in module
|
||||
* init/exit. This eliminates a lot of boilerplate code. Each module may only
|
||||
* use this macro once, and calling it replaces module_init and module_exit.
|
||||
*/
|
||||
#define module_isa_driver(__isa_driver, __num_isa_dev) \
|
||||
static int __init __isa_driver##_init(void) \
|
||||
{ \
|
||||
return isa_register_driver(&(__isa_driver), __num_isa_dev); \
|
||||
} \
|
||||
module_init(__isa_driver##_init); \
|
||||
static void __exit __isa_driver##_exit(void) \
|
||||
{ \
|
||||
isa_unregister_driver(&(__isa_driver)); \
|
||||
} \
|
||||
module_exit(__isa_driver##_exit);
|
||||
|
||||
/**
|
||||
* max_num_isa_dev() - Maximum possible number registered of an ISA device
|
||||
* @__ida_dev_ext: ISA device address extent
|
||||
*
|
||||
* The highest base address possible for an ISA device is 0x3FF; this results in
|
||||
* 1024 possible base addresses. Dividing the number of possible base addresses
|
||||
* by the address extent taken by each device results in the maximum number of
|
||||
* devices on a system.
|
||||
*/
|
||||
#define max_num_isa_dev(__isa_dev_ext) (1024 / __isa_dev_ext)
|
||||
|
||||
#endif /* __LINUX_ISA_H */
|
||||
|
|
|
@ -179,6 +179,7 @@ struct kernfs_open_file {
|
|||
|
||||
/* private fields, do not use outside kernfs proper */
|
||||
struct mutex mutex;
|
||||
struct mutex prealloc_mutex;
|
||||
int event;
|
||||
struct list_head list;
|
||||
char *prealloc_buf;
|
||||
|
|
|
@ -257,6 +257,7 @@ config PAGE_OWNER
|
|||
|
||||
config DEBUG_FS
|
||||
bool "Debug Filesystem"
|
||||
select SRCU
|
||||
help
|
||||
debugfs is a virtual file system that kernel developers use to put
|
||||
debugging files into. Enable this option to be able to read and
|
||||
|
|
67
scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci
Normal file
67
scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci
Normal file
|
@ -0,0 +1,67 @@
|
|||
/// Use DEFINE_DEBUGFS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTRIBUTE
|
||||
/// for debugfs files.
|
||||
///
|
||||
//# Rationale: DEFINE_SIMPLE_ATTRIBUTE + debugfs_create_file()
|
||||
//# imposes some significant overhead as compared to
|
||||
//# DEFINE_DEBUGFS_ATTRIBUTE + debugfs_create_file_unsafe().
|
||||
//
|
||||
// Copyright (C): 2016 Nicolai Stange
|
||||
// Options: --no-includes
|
||||
//
|
||||
|
||||
virtual context
|
||||
virtual patch
|
||||
virtual org
|
||||
virtual report
|
||||
|
||||
@dsa@
|
||||
declarer name DEFINE_SIMPLE_ATTRIBUTE;
|
||||
identifier dsa_fops;
|
||||
expression dsa_get, dsa_set, dsa_fmt;
|
||||
position p;
|
||||
@@
|
||||
DEFINE_SIMPLE_ATTRIBUTE@p(dsa_fops, dsa_get, dsa_set, dsa_fmt);
|
||||
|
||||
@dcf@
|
||||
expression name, mode, parent, data;
|
||||
identifier dsa.dsa_fops;
|
||||
@@
|
||||
debugfs_create_file(name, mode, parent, data, &dsa_fops)
|
||||
|
||||
|
||||
@context_dsa depends on context && dcf@
|
||||
declarer name DEFINE_DEBUGFS_ATTRIBUTE;
|
||||
identifier dsa.dsa_fops;
|
||||
expression dsa.dsa_get, dsa.dsa_set, dsa.dsa_fmt;
|
||||
@@
|
||||
* DEFINE_SIMPLE_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
|
||||
|
||||
|
||||
@patch_dcf depends on patch expression@
|
||||
expression name, mode, parent, data;
|
||||
identifier dsa.dsa_fops;
|
||||
@@
|
||||
- debugfs_create_file(name, mode, parent, data, &dsa_fops)
|
||||
+ debugfs_create_file_unsafe(name, mode, parent, data, &dsa_fops)
|
||||
|
||||
@patch_dsa depends on patch_dcf && patch@
|
||||
identifier dsa.dsa_fops;
|
||||
expression dsa.dsa_get, dsa.dsa_set, dsa.dsa_fmt;
|
||||
@@
|
||||
- DEFINE_SIMPLE_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
|
||||
+ DEFINE_DEBUGFS_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
|
||||
|
||||
|
||||
@script:python depends on org && dcf@
|
||||
fops << dsa.dsa_fops;
|
||||
p << dsa.p;
|
||||
@@
|
||||
msg="%s should be defined with DEFINE_DEBUGFS_ATTRIBUTE" % (fops)
|
||||
coccilib.org.print_todo(p[0], msg)
|
||||
|
||||
@script:python depends on report && dcf@
|
||||
fops << dsa.dsa_fops;
|
||||
p << dsa.p;
|
||||
@@
|
||||
msg="WARNING: %s should be defined with DEFINE_DEBUGFS_ATTRIBUTE" % (fops)
|
||||
coccilib.report.print_report(p[0], msg)
|
Loading…
Reference in a new issue