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:
Linus Torvalds 2016-05-20 21:26:15 -07:00
commit 3aa2fc1667
33 changed files with 1160 additions and 519 deletions

View file

@ -768,6 +768,7 @@ D: Z85230 driver
D: Former security contact point (please use vendor-sec@lst.de) D: Former security contact point (please use vendor-sec@lst.de)
D: ex 2.2 maintainer D: ex 2.2 maintainer
D: 2.1.x modular sound D: 2.1.x modular sound
D: Assigned major/minor numbers maintainer at lanana.org
S: c/o Red Hat UK Ltd S: c/o Red Hat UK Ltd
S: Alexandra House S: Alexandra House
S: Alexandra Terrace S: Alexandra Terrace

View file

@ -1,20 +1,17 @@
LINUX ALLOCATED DEVICES (2.6+ version) LINUX ALLOCATED DEVICES (4.x+ version)
Maintained by Alan Cox <device@lanana.org>
Last revised: 6th April 2009
This list is the Linux Device List, the official registry of allocated This list is the Linux Device List, the official registry of allocated
device numbers and /dev directory nodes for the Linux operating device numbers and /dev directory nodes for the Linux operating
system. system.
The latest version of this list is available from The LaTeX version of this document is no longer maintained, nor is
http://www.lanana.org/docs/device-list/ or the document that used to reside at lanana.org. This version in the
ftp://ftp.kernel.org/pub/linux/docs/device-list/. This version may be mainline Linux kernel is the master document. Updates shall be sent
newer than the one distributed with the Linux kernel. as patches to the kernel maintainers (see the SubmittingPatches document).
Specifically explore the sections titled "CHAR and MISC DRIVERS", and
The LaTeX version of this document is no longer maintained. "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 This document is included by reference into the Filesystem Hierarchy
Standard (FHS). The FHS is available from http://www.pathname.com/fhs/. 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 platform only. Allocations marked (68k/Atari) apply to Linux/68k on
the Atari platform only. the Atari platform only.
The symbol {2.6} means the allocation is obsolete and scheduled for This document is in the public domain. The authors requests, however,
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,
that semantically altered versions are not distributed without 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. 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 **** **** 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 To have a major number allocated, or a minor number in situations
where that applies (e.g. busmice), please contact me with the where that applies (e.g. busmice), please submit a patch and send to
appropriate device information. Also, if you have additional the authors as indicated above.
information regarding any of the devices listed below, or if I have
made a mistake, I would greatly appreciate a note.
I do, however, make a few requests about the nature of your report. Keep the description of the device *in the same format
This is necessary for me to be able to keep this list up to date and as this list*. The reason for this is that it is the only way we have
correct in a timely manner. First of all, *please* send it to the found to ensure we have all the requisite information to publish your
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
device and avoid conflicts. device and avoid conflicts.
Third, please don't assume that the distributed version of the list is Finally, sometimes we have to play "namespace police." Please don't be
up to date. Due to the number of registrations I have to maintain it offended. We often get submissions for /dev names that would be bound
in "batch mode", so there is likely additional registrations that to cause conflicts down the road. We are trying to avoid getting in a
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
situation where we would have to suffer an incompatible forward 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 device names and numbers in any way public, at least to the point
where it would be at all difficult to get them changed. 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 129 = /dev/ipath_sma Device used by Subnet Management Agent
130 = /dev/ipath_diag Device used by diagnostics programs 130 = /dev/ipath_diag Device used by diagnostics programs
234-239 UNASSIGNED 234-254 char RESERVED FOR DYNAMIC ASSIGNMENT
Character devices that request a dynamic allocation of major number will
240-254 char LOCAL/EXPERIMENTAL USE take numbers starting from 254 and downward.
240-254 block LOCAL/EXPERIMENTAL USE 240-254 block LOCAL/EXPERIMENTAL USE
Allocated for local/experimental use. For devices not Allocated for local/experimental use. For devices not

121
Documentation/isa.txt Normal file
View 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.

View file

@ -6067,6 +6067,13 @@ F: include/linux/irqdomain.h
F: kernel/irq/irqdomain.c F: kernel/irq/irqdomain.c
F: kernel/irq/msi.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 ISAPNP
M: Jaroslav Kysela <perex@perex.cz> M: Jaroslav Kysela <perex@perex.cz>
S: Maintained S: Maintained

View file

@ -2445,8 +2445,6 @@ config ISA_DMA_API
Enables ISA-style DMA support for devices requiring such controllers. Enables ISA-style DMA support for devices requiring such controllers.
If unsure, say Y. If unsure, say Y.
if X86_32
config ISA config ISA
bool "ISA support" bool "ISA support"
---help--- ---help---
@ -2456,6 +2454,8 @@ config ISA
(MCA) or VESA. ISA is an older system, now being displaced by PCI; (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. newer boards don't support it. If you have ISA, say Y, otherwise N.
if X86_32
config EISA config EISA
bool "EISA support" bool "EISA support"
depends on ISA depends on ISA

View file

@ -4,6 +4,7 @@
* GPL LICENSE SUMMARY * GPL LICENSE SUMMARY
* *
* Copyright(c) 2014 Intel Mobile Communications GmbH * 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 * 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 * 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 devcd_entry {
struct device devcd_dev; struct device devcd_dev;
const void *data; void *data;
size_t datalen; size_t datalen;
struct module *owner; struct module *owner;
ssize_t (*read)(char *buffer, loff_t offset, size_t count, ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen); void *data, size_t datalen);
void (*free)(const void *data); void (*free)(void *data);
struct delayed_work del_wk; struct delayed_work del_wk;
struct device *failing_dev; 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, 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) if (offset > datalen)
return -EINVAL; return -EINVAL;
@ -188,6 +189,11 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
return count; return count;
} }
static void devcd_freev(void *data)
{
vfree(data);
}
/** /**
* dev_coredumpv - create device coredump with vmalloc data * dev_coredumpv - create device coredump with vmalloc data
* @dev: the struct device for the crashed device * @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 * 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. * 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) 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); 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; 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_coredumpm - create device coredump with read/free methods
* @dev: the struct device for the crashed device * @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. * function will be called to free the data.
*/ */
void dev_coredumpm(struct device *dev, struct module *owner, 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, ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen), void *data, size_t datalen),
void (*free)(const void *data)) void (*free)(void *data))
{ {
static atomic_t devcd_count = ATOMIC_INIT(0); static atomic_t devcd_count = ATOMIC_INIT(0);
struct devcd_entry *devcd; struct devcd_entry *devcd;
@ -291,6 +335,27 @@ void dev_coredumpm(struct device *dev, struct module *owner,
} }
EXPORT_SYMBOL_GPL(dev_coredumpm); 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) static int __init devcoredump_init(void)
{ {
return class_register(&devcd_class); return class_register(&devcd_class);

View file

@ -125,9 +125,7 @@ static void fw_cfg_io_cleanup(void)
# define FW_CFG_CTRL_OFF 0x00 # define FW_CFG_CTRL_OFF 0x00
# define FW_CFG_DATA_OFF 0x01 # define FW_CFG_DATA_OFF 0x01
# else # else
# warning "QEMU FW_CFG may not be available on this architecture!" # error "QEMU FW_CFG not available on this architecture!"
# define FW_CFG_CTRL_OFF 0x00
# define FW_CFG_DATA_OFF 0x01
# endif # endif
#endif #endif

View file

@ -530,30 +530,35 @@ menu "Port-mapped I/O GPIO drivers"
config GPIO_104_DIO_48E config GPIO_104_DIO_48E
tristate "ACCES 104-DIO-48E GPIO support" tristate "ACCES 104-DIO-48E GPIO support"
depends on ISA
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
help help
Enables GPIO support for the ACCES 104-DIO-48E family. The base port Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
address for the device may be configured via the dio_48e_base module 104-DIO-24E). The base port addresses for the devices may be
parameter. The interrupt line number for the device may be configured configured via the base module parameter. The interrupt line numbers
via the dio_48e_irq module parameter. for the devices may be configured via the irq module parameter.
config GPIO_104_IDIO_16 config GPIO_104_IDIO_16
tristate "ACCES 104-IDIO-16 GPIO support" tristate "ACCES 104-IDIO-16 GPIO support"
depends on ISA
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
help help
Enables GPIO support for the ACCES 104-IDIO-16 family. The base port Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
address for the device may be set via the idio_16_base module 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, 104-IDO-8). The
parameter. The interrupt line number for the device may be set via the base port addresses for the devices may be configured via the base
idio_16_irq module parameter. module parameter. The interrupt line numbers for the devices may be
configured via the irq module parameter.
config GPIO_104_IDI_48 config GPIO_104_IDI_48
tristate "ACCES 104-IDI-48 GPIO support" tristate "ACCES 104-IDI-48 GPIO support"
depends on ISA
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
help help
Enables GPIO support for the ACCES 104-IDI-48 family. The base port Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A,
address for the device may be configured via the idi_48_base module 104-IDI-48AC, 104-IDI-48B, 104-IDI-48BC). The base port addresses for
parameter. The interrupt line number for the device may be configured the devices may be configured via the base module parameter. The
via the idi_48_irq module parameter. interrupt line numbers for the devices may be configured via the irq
module parameter.
config GPIO_F7188X config GPIO_F7188X
tristate "F71869, F71869A, F71882FG, F71889F and F81866 GPIO support" tristate "F71869, F71869A, F71882FG, F71889F and F81866 GPIO support"
@ -622,12 +627,13 @@ config GPIO_TS5500
config GPIO_WS16C48 config GPIO_WS16C48
tristate "WinSystems WS16C48 GPIO support" tristate "WinSystems WS16C48 GPIO support"
depends on ISA
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
help help
Enables GPIO support for the WinSystems WS16C48. The base port address Enables GPIO support for the WinSystems WS16C48. The base port
for the device may be configured via the ws16c48_base module addresses for the devices may be configured via the base module
parameter. The interrupt line number for the device may be configured parameter. The interrupt line numbers for the devices may be
via the ws16c48_irq module parameter. configured via the irq module parameter.
endmenu endmenu

View file

@ -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 * Copyright (C) 2016 William Breathitt Gray
* *
* This program is free software; you can redistribute it and/or modify * 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 * WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * 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/bitops.h>
#include <linux/device.h> #include <linux/device.h>
@ -19,18 +22,23 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
#include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
static unsigned dio_48e_base; #define DIO48E_EXTENT 16
module_param(dio_48e_base, uint, 0); #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT)
MODULE_PARM_DESC(dio_48e_base, "ACCES 104-DIO-48E base address");
static unsigned dio_48e_irq; static unsigned int base[MAX_NUM_DIO48E];
module_param(dio_48e_irq, uint, 0); static unsigned int num_dio48e;
MODULE_PARM_DESC(dio_48e_irq, "ACCES 104-DIO-48E interrupt line number"); 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 * 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; 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; struct dio48e_gpio *dio48egpio;
const unsigned base = dio_48e_base;
const unsigned extent = 16;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
int err; int err;
const unsigned irq = dio_48e_irq;
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
if (!dio48egpio) if (!dio48egpio)
return -ENOMEM; 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", dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base, base + extent); base[id], base[id] + DIO48E_EXTENT);
return -EBUSY; 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.direction_output = dio48e_gpio_direction_output;
dio48egpio->chip.get = dio48e_gpio_get; dio48egpio->chip.get = dio48e_gpio_get;
dio48egpio->chip.set = dio48e_gpio_set; dio48egpio->chip.set = dio48e_gpio_set;
dio48egpio->base = base; dio48egpio->base = base[id];
dio48egpio->irq = irq; dio48egpio->irq = irq[id];
spin_lock_init(&dio48egpio->lock); spin_lock_init(&dio48egpio->lock);
@ -338,19 +342,19 @@ static int __init dio48e_probe(struct platform_device *pdev)
} }
/* initialize all GPIO as output */ /* initialize all GPIO as output */
outb(0x80, base + 3); outb(0x80, base[id] + 3);
outb(0x00, base); outb(0x00, base[id]);
outb(0x00, base + 1); outb(0x00, base[id] + 1);
outb(0x00, base + 2); outb(0x00, base[id] + 2);
outb(0x00, base + 3); outb(0x00, base[id] + 3);
outb(0x80, base + 7); outb(0x80, base[id] + 7);
outb(0x00, base + 4); outb(0x00, base[id] + 4);
outb(0x00, base + 5); outb(0x00, base[id] + 5);
outb(0x00, base + 6); outb(0x00, base[id] + 6);
outb(0x00, base + 7); outb(0x00, base[id] + 7);
/* disable IRQ by default */ /* disable IRQ by default */
inb(base + 0xB); inb(base[id] + 0xB);
err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0, err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0,
handle_edge_irq, IRQ_TYPE_NONE); handle_edge_irq, IRQ_TYPE_NONE);
@ -359,7 +363,7 @@ static int __init dio48e_probe(struct platform_device *pdev)
goto err_gpiochip_remove; 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) { if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err); dev_err(dev, "IRQ handler registering failed (%d)\n", err);
goto err_gpiochip_remove; goto err_gpiochip_remove;
@ -372,9 +376,9 @@ static int __init dio48e_probe(struct platform_device *pdev)
return err; 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); free_irq(dio48egpio->irq, dio48egpio);
gpiochip_remove(&dio48egpio->chip); gpiochip_remove(&dio48egpio->chip);
@ -382,48 +386,14 @@ static int dio48e_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device *dio48e_device; static struct isa_driver dio48e_driver = {
.probe = dio48e_probe,
static struct platform_driver dio48e_driver = {
.driver = { .driver = {
.name = "104-dio-48e" .name = "104-dio-48e"
}, },
.remove = dio48e_remove .remove = dio48e_remove
}; };
module_isa_driver(dio48e_driver, num_dio48e);
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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");

View file

@ -10,6 +10,9 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of * WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * 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/bitops.h>
#include <linux/device.h> #include <linux/device.h>
@ -19,18 +22,23 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
#include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
static unsigned idi_48_base; #define IDI_48_EXTENT 8
module_param(idi_48_base, uint, 0); #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
MODULE_PARM_DESC(idi_48_base, "ACCES 104-IDI-48 base address");
static unsigned idi_48_irq; static unsigned int base[MAX_NUM_IDI_48];
module_param(idi_48_irq, uint, 0); static unsigned int num_idi_48;
MODULE_PARM_DESC(idi_48_irq, "ACCES 104-IDI-48 interrupt line number"); 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 * 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; 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; struct idi_48_gpio *idi48gpio;
const unsigned base = idi_48_base;
const unsigned extent = 8;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
int err; int err;
const unsigned irq = idi_48_irq;
idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL); idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL);
if (!idi48gpio) if (!idi48gpio)
return -ENOMEM; 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", 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; 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.get_direction = idi_48_gpio_get_direction;
idi48gpio->chip.direction_input = idi_48_gpio_direction_input; idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
idi48gpio->chip.get = idi_48_gpio_get; idi48gpio->chip.get = idi_48_gpio_get;
idi48gpio->base = base; idi48gpio->base = base[id];
idi48gpio->irq = irq; idi48gpio->irq = irq[id];
spin_lock_init(&idi48gpio->lock); spin_lock_init(&idi48gpio->lock);
@ -253,8 +257,8 @@ static int __init idi_48_probe(struct platform_device *pdev)
} }
/* Disable IRQ by default */ /* Disable IRQ by default */
outb(0, base + 7); outb(0, base[id] + 7);
inb(base + 7); inb(base[id] + 7);
err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0, err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0,
handle_edge_irq, IRQ_TYPE_NONE); handle_edge_irq, IRQ_TYPE_NONE);
@ -263,7 +267,7 @@ static int __init idi_48_probe(struct platform_device *pdev)
goto err_gpiochip_remove; 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); idi48gpio);
if (err) { if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", 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; 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); free_irq(idi48gpio->irq, idi48gpio);
gpiochip_remove(&idi48gpio->chip); gpiochip_remove(&idi48gpio->chip);
@ -287,48 +291,14 @@ static int idi_48_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device *idi_48_device; static struct isa_driver idi_48_driver = {
.probe = idi_48_probe,
static struct platform_driver idi_48_driver = {
.driver = { .driver = {
.name = "104-idi-48" .name = "104-idi-48"
}, },
.remove = idi_48_remove .remove = idi_48_remove
}; };
module_isa_driver(idi_48_driver, num_idi_48);
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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver"); MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");

View file

@ -10,6 +10,9 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of * WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * 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/bitops.h>
#include <linux/device.h> #include <linux/device.h>
@ -19,18 +22,23 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
#include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
static unsigned idio_16_base; #define IDIO_16_EXTENT 8
module_param(idio_16_base, uint, 0); #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address");
static unsigned idio_16_irq; static unsigned int base[MAX_NUM_IDIO_16];
module_param(idio_16_irq, uint, 0); static unsigned int num_idio_16;
MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number"); 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 * 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; 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; struct idio_16_gpio *idio16gpio;
const unsigned base = idio_16_base;
const unsigned extent = 8;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
int err; int err;
const unsigned irq = idio_16_irq;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
if (!idio16gpio) if (!idio16gpio)
return -ENOMEM; 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", 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; 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.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get; idio16gpio->chip.get = idio_16_gpio_get;
idio16gpio->chip.set = idio_16_gpio_set; idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->base = base; idio16gpio->base = base[id];
idio16gpio->irq = irq; idio16gpio->irq = irq[id];
idio16gpio->out_state = 0xFFFF; idio16gpio->out_state = 0xFFFF;
spin_lock_init(&idio16gpio->lock); spin_lock_init(&idio16gpio->lock);
@ -230,8 +234,8 @@ static int __init idio_16_probe(struct platform_device *pdev)
} }
/* Disable IRQ by default */ /* Disable IRQ by default */
outb(0, base + 2); outb(0, base[id] + 2);
outb(0, base + 1); outb(0, base[id] + 1);
err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
handle_edge_irq, IRQ_TYPE_NONE); handle_edge_irq, IRQ_TYPE_NONE);
@ -240,7 +244,7 @@ static int __init idio_16_probe(struct platform_device *pdev)
goto err_gpiochip_remove; 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) { if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err); dev_err(dev, "IRQ handler registering failed (%d)\n", err);
goto err_gpiochip_remove; goto err_gpiochip_remove;
@ -253,9 +257,9 @@ static int __init idio_16_probe(struct platform_device *pdev)
return err; 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); free_irq(idio16gpio->irq, idio16gpio);
gpiochip_remove(&idio16gpio->chip); gpiochip_remove(&idio16gpio->chip);
@ -263,48 +267,15 @@ static int idio_16_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device *idio_16_device; static struct isa_driver idio_16_driver = {
.probe = idio_16_probe,
static struct platform_driver idio_16_driver = {
.driver = { .driver = {
.name = "104-idio-16" .name = "104-idio-16"
}, },
.remove = idio_16_remove .remove = idio_16_remove
}; };
static void __exit idio_16_exit(void) module_isa_driver(idio_16_driver, num_idio_16);
{
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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");

View file

@ -19,18 +19,23 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
#include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
static unsigned ws16c48_base; #define WS16C48_EXTENT 16
module_param(ws16c48_base, uint, 0); #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address");
static unsigned ws16c48_irq; static unsigned int base[MAX_NUM_WS16C48];
module_param(ws16c48_irq, uint, 0); static unsigned int num_ws16c48;
MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number"); 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 * 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; 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; struct ws16c48_gpio *ws16c48gpio;
const unsigned base = ws16c48_base;
const unsigned extent = 16;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
int err; int err;
const unsigned irq = ws16c48_irq;
ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
if (!ws16c48gpio) if (!ws16c48gpio)
return -ENOMEM; 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", dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base, base + extent); base[id], base[id] + WS16C48_EXTENT);
return -EBUSY; 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.direction_output = ws16c48_gpio_direction_output;
ws16c48gpio->chip.get = ws16c48_gpio_get; ws16c48gpio->chip.get = ws16c48_gpio_get;
ws16c48gpio->chip.set = ws16c48_gpio_set; ws16c48gpio->chip.set = ws16c48_gpio_set;
ws16c48gpio->base = base; ws16c48gpio->base = base[id];
ws16c48gpio->irq = irq; ws16c48gpio->irq = irq[id];
spin_lock_init(&ws16c48gpio->lock); spin_lock_init(&ws16c48gpio->lock);
@ -342,11 +343,11 @@ static int __init ws16c48_probe(struct platform_device *pdev)
} }
/* Disable IRQ by default */ /* Disable IRQ by default */
outb(0x80, base + 7); outb(0x80, base[id] + 7);
outb(0, base + 8); outb(0, base[id] + 8);
outb(0, base + 9); outb(0, base[id] + 9);
outb(0, base + 10); outb(0, base[id] + 10);
outb(0xC0, base + 7); outb(0xC0, base[id] + 7);
err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
handle_edge_irq, IRQ_TYPE_NONE); handle_edge_irq, IRQ_TYPE_NONE);
@ -355,7 +356,7 @@ static int __init ws16c48_probe(struct platform_device *pdev)
goto err_gpiochip_remove; 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); ws16c48gpio);
if (err) { if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", 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; 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); free_irq(ws16c48gpio->irq, ws16c48gpio);
gpiochip_remove(&ws16c48gpio->chip); gpiochip_remove(&ws16c48gpio->chip);
@ -379,48 +380,15 @@ static int ws16c48_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device *ws16c48_device; static struct isa_driver ws16c48_driver = {
.probe = ws16c48_probe,
static struct platform_driver ws16c48_driver = {
.driver = { .driver = {
.name = "ws16c48" .name = "ws16c48"
}, },
.remove = ws16c48_remove .remove = ws16c48_remove
}; };
static void __exit ws16c48_exit(void) module_isa_driver(ws16c48_driver, num_ws16c48);
{
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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");

View file

@ -210,7 +210,7 @@ config MCP4922
config STX104 config STX104
tristate "Apex Embedded Systems STX104 DAC driver" tristate "Apex Embedded Systems STX104 DAC driver"
depends on ISA depends on X86 && ISA
help help
Say yes here to build support for the 2-channel DAC on the Apex 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 Embedded Systems STX104 integrated analog PC/104 card. The base port

View file

@ -33,16 +33,9 @@
} }
#define STX104_EXTENT 16 #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 int base[max_num_isa_dev(STX104_EXTENT)];
static unsigned num_stx104; static unsigned int num_stx104;
module_param_array(base, uint, &num_stx104, 0); module_param_array(base, uint, &num_stx104, 0);
MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); 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) module_isa_driver(stx104_driver, num_stx104);
{
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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver"); MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");

View file

@ -71,7 +71,7 @@
#include "iwl-csr.h" #include "iwl-csr.h"
static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, 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; const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
ssize_t bytes_read; 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; 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; const struct iwl_mvm_dump_ptrs *fw_error_dump = data;

View file

@ -3,7 +3,7 @@
# #
config PNPBIOS config PNPBIOS
bool "Plug and Play BIOS support" bool "Plug and Play BIOS support"
depends on ISA && X86 depends on ISA && X86_32
default n default n
---help--- ---help---
Linux uses the PNPBIOS as defined in "Plug and Play BIOS Linux uses the PNPBIOS as defined in "Plug and Play BIOS

View file

@ -738,7 +738,7 @@ config ALIM7101_WDT
config EBC_C384_WDT config EBC_C384_WDT
tristate "WinSystems EBC-C384 Watchdog Timer" tristate "WinSystems EBC-C384 Watchdog Timer"
depends on X86 depends on X86 && ISA
select WATCHDOG_CORE select WATCHDOG_CORE
help help
Enables watchdog timer support for the watchdog timer on the Enables watchdog timer support for the watchdog timer on the

View file

@ -16,10 +16,10 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
@ -95,9 +95,8 @@ static const struct watchdog_info ebc_c384_wdt_info = {
.identity = MODULE_NAME .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; struct watchdog_device *wdd;
if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) { 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", dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n",
timeout, WATCHDOG_TIMEOUT); timeout, WATCHDOG_TIMEOUT);
platform_set_drvdata(pdev, wdd); dev_set_drvdata(dev, wdd);
return watchdog_register_device(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); watchdog_unregister_device(wdd);
return 0; return 0;
} }
static struct platform_driver ebc_c384_wdt_driver = { static struct isa_driver ebc_c384_wdt_driver = {
.probe = ebc_c384_wdt_probe,
.driver = { .driver = {
.name = MODULE_NAME .name = MODULE_NAME
}, },
.remove = ebc_c384_wdt_remove .remove = ebc_c384_wdt_remove
}; };
static struct platform_device *ebc_c384_wdt_device;
static int __init ebc_c384_wdt_init(void) static int __init ebc_c384_wdt_init(void)
{ {
int err;
if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC")) if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC"))
return -ENODEV; return -ENODEV;
ebc_c384_wdt_device = platform_device_alloc(MODULE_NAME, -1); return isa_register_driver(&ebc_c384_wdt_driver, 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;
} }
static void __exit ebc_c384_wdt_exit(void) static void __exit ebc_c384_wdt_exit(void)
{ {
platform_device_unregister(ebc_c384_wdt_device); isa_unregister_driver(&ebc_c384_wdt_driver);
platform_driver_unregister(&ebc_c384_wdt_driver);
} }
module_init(ebc_c384_wdt_init); 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_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver"); MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" MODULE_NAME); MODULE_ALIAS("isa:" MODULE_NAME);

View file

@ -91,6 +91,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
break; 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) { if (i == 0) {
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;

View file

@ -22,6 +22,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/device.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, static ssize_t default_read_file(struct file *file, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
@ -35,27 +41,293 @@ static ssize_t default_write_file(struct file *file, const char __user *buf,
return count; return count;
} }
const struct file_operations debugfs_file_operations = { const struct file_operations debugfs_noop_file_operations = {
.read = default_read_file, .read = default_read_file,
.write = default_write_file, .write = default_write_file,
.open = simple_open, .open = simple_open,
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
static struct dentry *debugfs_create_mode(const char *name, umode_t mode, /**
struct dentry *parent, void *value, * debugfs_use_file_start - mark the beginning of file data access
const struct file_operations *fops, * @dentry: the dentry object whose data is being accessed.
const struct file_operations *fops_ro, * @srcu_idx: a pointer to some memory to store a SRCU index in.
const struct file_operations *fops_wo) *
* 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,
const struct file_operations *fops_wo)
{ {
/* if there are no write bits set, make read only */ /* if there are no write bits set, make read only */
if (!(mode & S_IWUGO)) 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 there are no read bits set, make write only */
if (!(mode & S_IRUGO)) 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) 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; *val = *(u8 *)data;
return 0; return 0;
} }
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n"); DEFINE_DEBUGFS_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_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 * 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 *debugfs_create_u8(const char *name, umode_t mode,
struct dentry *parent, u8 *value) 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); &fops_u8_ro, &fops_u8_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_u8); EXPORT_SYMBOL_GPL(debugfs_create_u8);
@ -114,9 +386,9 @@ static int debugfs_u16_get(void *data, u64 *val)
*val = *(u16 *)data; *val = *(u16 *)data;
return 0; return 0;
} }
DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n"); DEFINE_DEBUGFS_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_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 * 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 *debugfs_create_u16(const char *name, umode_t mode,
struct dentry *parent, u16 *value) 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); &fops_u16_ro, &fops_u16_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_u16); EXPORT_SYMBOL_GPL(debugfs_create_u16);
@ -160,9 +432,9 @@ static int debugfs_u32_get(void *data, u64 *val)
*val = *(u32 *)data; *val = *(u32 *)data;
return 0; return 0;
} }
DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n"); DEFINE_DEBUGFS_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_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 * 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 *debugfs_create_u32(const char *name, umode_t mode,
struct dentry *parent, u32 *value) 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); &fops_u32_ro, &fops_u32_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_u32); EXPORT_SYMBOL_GPL(debugfs_create_u32);
@ -207,9 +479,9 @@ static int debugfs_u64_get(void *data, u64 *val)
*val = *(u64 *)data; *val = *(u64 *)data;
return 0; return 0;
} }
DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n"); DEFINE_DEBUGFS_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_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 * 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 *debugfs_create_u64(const char *name, umode_t mode,
struct dentry *parent, u64 *value) 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); &fops_u64_ro, &fops_u64_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_u64); EXPORT_SYMBOL_GPL(debugfs_create_u64);
@ -254,9 +526,10 @@ static int debugfs_ulong_get(void *data, u64 *val)
*val = *(unsigned long *)data; *val = *(unsigned long *)data;
return 0; return 0;
} }
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set,
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n"); "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_wo, NULL, 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 * 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 *debugfs_create_ulong(const char *name, umode_t mode,
struct dentry *parent, unsigned long *value) struct dentry *parent, unsigned long *value)
{ {
return debugfs_create_mode(name, mode, parent, value, &fops_ulong, return debugfs_create_mode_unsafe(name, mode, parent, value,
&fops_ulong_ro, &fops_ulong_wo); &fops_ulong, &fops_ulong_ro,
&fops_ulong_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_ulong); EXPORT_SYMBOL_GPL(debugfs_create_ulong);
DEFINE_SIMPLE_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n"); DEFINE_DEBUGFS_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_DEBUGFS_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_wo, NULL, debugfs_u8_set, "0x%02llx\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set, "0x%04llx\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set,
DEFINE_SIMPLE_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n"); "0x%04llx\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_x16_wo, NULL, 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_DEBUGFS_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set,
DEFINE_SIMPLE_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n"); "0x%08llx\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_x32_wo, NULL, 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_DEBUGFS_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set,
DEFINE_SIMPLE_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n"); "0x%016llx\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_x64_wo, NULL, 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 * 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 *debugfs_create_x8(const char *name, umode_t mode,
struct dentry *parent, u8 *value) 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); &fops_x8_ro, &fops_x8_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_x8); 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 *debugfs_create_x16(const char *name, umode_t mode,
struct dentry *parent, u16 *value) 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); &fops_x16_ro, &fops_x16_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_x16); 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 *debugfs_create_x32(const char *name, umode_t mode,
struct dentry *parent, u32 *value) 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); &fops_x32_ro, &fops_x32_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_x32); 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 *debugfs_create_x64(const char *name, umode_t mode,
struct dentry *parent, u64 *value) 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); &fops_x64_ro, &fops_x64_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_x64); EXPORT_SYMBOL_GPL(debugfs_create_x64);
@ -398,10 +675,10 @@ static int debugfs_size_t_get(void *data, u64 *val)
*val = *(size_t *)data; *val = *(size_t *)data;
return 0; 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 */ "%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_DEBUGFS_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_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 * 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 *debugfs_create_size_t(const char *name, umode_t mode,
struct dentry *parent, size_t *value) struct dentry *parent, size_t *value)
{ {
return debugfs_create_mode(name, mode, parent, value, &fops_size_t, return debugfs_create_mode_unsafe(name, mode, parent, value,
&fops_size_t_ro, &fops_size_t_wo); &fops_size_t, &fops_size_t_ro,
&fops_size_t_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_size_t); 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); *val = atomic_read((atomic_t *)data);
return 0; 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"); debugfs_atomic_t_set, "%lld\n");
DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, "%lld\n"); DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL,
DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%lld\n"); "%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 * 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 *debugfs_create_atomic_t(const char *name, umode_t mode,
struct dentry *parent, atomic_t *value) struct dentry *parent, atomic_t *value)
{ {
return debugfs_create_mode(name, mode, parent, value, &fops_atomic_t, return debugfs_create_mode_unsafe(name, mode, parent, value,
&fops_atomic_t_ro, &fops_atomic_t_wo); &fops_atomic_t, &fops_atomic_t_ro,
&fops_atomic_t_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_atomic_t); 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) size_t count, loff_t *ppos)
{ {
char buf[3]; 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'; buf[0] = 'Y';
else else
buf[0] = 'N'; buf[0] = 'N';
@ -477,6 +766,7 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
char buf[32]; char buf[32];
size_t buf_size; size_t buf_size;
bool bv; bool bv;
int r, srcu_idx;
bool *val = file->private_data; bool *val = file->private_data;
buf_size = min(count, (sizeof(buf)-1)); 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; return -EFAULT;
buf[buf_size] = '\0'; buf[buf_size] = '\0';
if (strtobool(buf, &bv) == 0) if (strtobool(buf, &bv) == 0) {
*val = bv; 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; 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 *debugfs_create_bool(const char *name, umode_t mode,
struct dentry *parent, bool *value) 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); &fops_bool_ro, &fops_bool_wo);
} }
EXPORT_SYMBOL_GPL(debugfs_create_bool); 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) size_t count, loff_t *ppos)
{ {
struct debugfs_blob_wrapper *blob = file->private_data; struct debugfs_blob_wrapper *blob = file->private_data;
return simple_read_from_buffer(user_buf, count, ppos, blob->data, ssize_t r;
blob->size); 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 = { 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 dentry *parent,
struct debugfs_blob_wrapper *blob) 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); 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->array = array;
data->elements = elements; 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); EXPORT_SYMBOL_GPL(debugfs_create_u32_array);

View file

@ -27,9 +27,14 @@
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/srcu.h>
#include "internal.h"
#define DEBUGFS_DEFAULT_MODE 0700 #define DEBUGFS_DEFAULT_MODE 0700
DEFINE_SRCU(debugfs_srcu);
static struct vfsmount *debugfs_mount; static struct vfsmount *debugfs_mount;
static int debugfs_mount_count; static int debugfs_mount_count;
static bool debugfs_registered; static bool debugfs_registered;
@ -39,7 +44,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
struct inode *inode = new_inode(sb); struct inode *inode = new_inode(sb);
if (inode) { if (inode) {
inode->i_ino = get_next_ino(); 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; return inode;
} }
@ -294,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
return 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 * debugfs_create_file - create a file in the debugfs filesystem
* @name: a pointer to a string containing the name of the file to create. * @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, struct dentry *parent, void *data,
const struct file_operations *fops) const struct file_operations *fops)
{ {
struct dentry *dentry;
struct inode *inode;
if (!(mode & S_IFMT)) return __debugfs_create_file(name, mode, parent, data,
mode |= S_IFREG; fops ? &debugfs_full_proxy_file_operations :
BUG_ON(!S_ISREG(mode)); &debugfs_noop_file_operations,
dentry = start_creating(name, parent); fops);
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);
} }
EXPORT_SYMBOL_GPL(debugfs_create_file); 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 * 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. * @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_flags |= S_AUTOMOUNT;
inode->i_private = data; inode->i_private = data;
dentry->d_fsdata = (void *)f; dentry->d_fsdata = (void *)f;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
inc_nlink(d_inode(dentry->d_parent));
fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
return end_creating(dentry); return end_creating(dentry);
} }
EXPORT_SYMBOL(debugfs_create_automount); EXPORT_SYMBOL(debugfs_create_automount);
@ -565,6 +629,8 @@ void debugfs_remove(struct dentry *dentry)
inode_unlock(d_inode(parent)); inode_unlock(d_inode(parent));
if (!ret) if (!ret)
simple_release_fs(&debugfs_mount, &debugfs_mount_count); simple_release_fs(&debugfs_mount, &debugfs_mount_count);
synchronize_srcu(&debugfs_srcu);
} }
EXPORT_SYMBOL_GPL(debugfs_remove); EXPORT_SYMBOL_GPL(debugfs_remove);
@ -642,6 +708,8 @@ void debugfs_remove_recursive(struct dentry *dentry)
if (!__debugfs_remove(child, parent)) if (!__debugfs_remove(child, parent))
simple_release_fs(&debugfs_mount, &debugfs_mount_count); simple_release_fs(&debugfs_mount, &debugfs_mount_count);
inode_unlock(d_inode(parent)); inode_unlock(d_inode(parent));
synchronize_srcu(&debugfs_srcu);
} }
EXPORT_SYMBOL_GPL(debugfs_remove_recursive); EXPORT_SYMBOL_GPL(debugfs_remove_recursive);

26
fs/debugfs/internal.h Normal file
View 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_ */

View file

@ -753,7 +753,8 @@ int kernfs_add_one(struct kernfs_node *kn)
ps_iattr = parent->iattr; ps_iattr = parent->iattr;
if (ps_iattr) { if (ps_iattr) {
struct iattr *ps_iattrs = &ps_iattr->ia_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); mutex_unlock(&kernfs_mutex);
@ -1279,8 +1280,9 @@ static void __kernfs_remove(struct kernfs_node *kn)
/* update timestamps on the parent */ /* update timestamps on the parent */
if (ps_iattr) { if (ps_iattr) {
ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; ktime_get_real_ts(&ps_iattr->ia_iattr.ia_ctime);
ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; ps_iattr->ia_iattr.ia_mtime =
ps_iattr->ia_iattr.ia_ctime;
} }
kernfs_put(pos); kernfs_put(pos);

View file

@ -190,15 +190,16 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
char *buf; char *buf;
buf = of->prealloc_buf; buf = of->prealloc_buf;
if (!buf) if (buf)
mutex_lock(&of->prealloc_mutex);
else
buf = kmalloc(len, GFP_KERNEL); buf = kmalloc(len, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
/* /*
* @of->mutex nests outside active ref and is used both to ensure that * @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 * the ops aren't called concurrently for the same open file.
* to provide exclusive access to ->prealloc_buf (when that exists).
*/ */
mutex_lock(&of->mutex); mutex_lock(&of->mutex);
if (!kernfs_get_active(of->kn)) { if (!kernfs_get_active(of->kn)) {
@ -214,21 +215,23 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
else else
len = -EINVAL; len = -EINVAL;
kernfs_put_active(of->kn);
mutex_unlock(&of->mutex);
if (len < 0) if (len < 0)
goto out_unlock; goto out_free;
if (copy_to_user(user_buf, buf, len)) { if (copy_to_user(user_buf, buf, len)) {
len = -EFAULT; len = -EFAULT;
goto out_unlock; goto out_free;
} }
*ppos += len; *ppos += len;
out_unlock:
kernfs_put_active(of->kn);
mutex_unlock(&of->mutex);
out_free: out_free:
if (buf != of->prealloc_buf) if (buf == of->prealloc_buf)
mutex_unlock(&of->prealloc_mutex);
else
kfree(buf); kfree(buf);
return len; return len;
} }
@ -284,15 +287,22 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
} }
buf = of->prealloc_buf; buf = of->prealloc_buf;
if (!buf) if (buf)
mutex_lock(&of->prealloc_mutex);
else
buf = kmalloc(len + 1, GFP_KERNEL); buf = kmalloc(len + 1, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; 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 * @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 * the ops aren't called concurrently for the same open file.
* to provide exclusive access to ->prealloc_buf (when that exists).
*/ */
mutex_lock(&of->mutex); mutex_lock(&of->mutex);
if (!kernfs_get_active(of->kn)) { 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; 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); ops = kernfs_ops(of->kn);
if (ops->write) if (ops->write)
len = ops->write(of, buf, len, *ppos); len = ops->write(of, buf, len, *ppos);
else else
len = -EINVAL; len = -EINVAL;
kernfs_put_active(of->kn);
mutex_unlock(&of->mutex);
if (len > 0) if (len > 0)
*ppos += len; *ppos += len;
out_unlock:
kernfs_put_active(of->kn);
mutex_unlock(&of->mutex);
out_free: out_free:
if (buf != of->prealloc_buf) if (buf == of->prealloc_buf)
mutex_unlock(&of->prealloc_mutex);
else
kfree(buf); kfree(buf);
return len; return len;
} }
@ -687,6 +693,7 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
error = -ENOMEM; error = -ENOMEM;
if (!of->prealloc_buf) if (!of->prealloc_buf)
goto err_free; goto err_free;
mutex_init(&of->prealloc_mutex);
} }
/* /*

View file

@ -54,7 +54,10 @@ static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn)
iattrs->ia_mode = kn->mode; iattrs->ia_mode = kn->mode;
iattrs->ia_uid = GLOBAL_ROOT_UID; iattrs->ia_uid = GLOBAL_ROOT_UID;
iattrs->ia_gid = GLOBAL_ROOT_GID; 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); simple_xattrs_init(&kn->iattr->xattrs);
out_unlock: 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) static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
{ {
inode->i_mode = 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) 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_uid = iattr->ia_uid;
inode->i_gid = iattr->ia_gid; inode->i_gid = iattr->ia_gid;
inode->i_atime = iattr->ia_atime; inode->i_atime = timespec_trunc(iattr->ia_atime, sb->s_time_gran);
inode->i_mtime = iattr->ia_mtime; inode->i_mtime = timespec_trunc(iattr->ia_mtime, sb->s_time_gran);
inode->i_ctime = iattr->ia_ctime; inode->i_ctime = timespec_trunc(iattr->ia_ctime, sb->s_time_gran);
} }
static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)

View file

@ -19,9 +19,11 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/compiler.h>
struct device; struct device;
struct file_operations; struct file_operations;
struct srcu_struct;
struct debugfs_blob_wrapper { struct debugfs_blob_wrapper {
void *data; void *data;
@ -41,14 +43,16 @@ struct debugfs_regset32 {
extern struct dentry *arch_debugfs_dir; extern struct dentry *arch_debugfs_dir;
extern struct srcu_struct debugfs_srcu;
#if defined(CONFIG_DEBUG_FS) #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 *debugfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data, struct dentry *parent, void *data,
const struct file_operations *fops); 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 *debugfs_create_file_size(const char *name, umode_t mode,
struct dentry *parent, void *data, 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(struct dentry *dentry);
void debugfs_remove_recursive(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 *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, const char *new_name); 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 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, static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, char *new_name) struct dentry *new_dir, char *new_name)
{ {

View file

@ -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 #ifndef __DEVCOREDUMP_H
#define __DEVCOREDUMP_H #define __DEVCOREDUMP_H
@ -5,17 +24,62 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/vmalloc.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 #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); gfp_t gfp);
void dev_coredumpm(struct device *dev, struct module *owner, 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, ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen), void *data, size_t datalen),
void (*free)(const void *data)); void (*free)(void *data));
void dev_coredumpsg(struct device *dev, struct scatterlist *table,
size_t datalen, gfp_t gfp);
#else #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) size_t datalen, gfp_t gfp)
{ {
vfree(data); vfree(data);
@ -23,13 +87,19 @@ static inline void dev_coredumpv(struct device *dev, const void *data,
static inline void static inline void
dev_coredumpm(struct device *dev, struct module *owner, 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, ssize_t (*read)(char *buffer, loff_t offset, size_t count,
const void *data, size_t datalen), void *data, size_t datalen),
void (*free)(const void *data)) void (*free)(void *data))
{ {
free(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 /* CONFIG_DEV_COREDUMP */
#endif /* __DEVCOREDUMP_H */ #endif /* __DEVCOREDUMP_H */

View file

@ -1288,8 +1288,11 @@ do { \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \ dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0) } while (0)
#else #else
#define dev_dbg_ratelimited(dev, fmt, ...) \ #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 #endif
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG

View file

@ -2427,6 +2427,8 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
/* fs/char_dev.c */ /* fs/char_dev.c */
#define CHRDEV_MAJOR_HASH_SIZE 255 #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 alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
extern int register_chrdev_region(dev_t, unsigned, const char *); extern int register_chrdev_region(dev_t, unsigned, const char *);
extern int __register_chrdev(unsigned int major, unsigned int baseminor, extern int __register_chrdev(unsigned int major, unsigned int baseminor,

View file

@ -36,4 +36,36 @@ static inline void isa_unregister_driver(struct isa_driver *d)
} }
#endif #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 */ #endif /* __LINUX_ISA_H */

View file

@ -179,6 +179,7 @@ struct kernfs_open_file {
/* private fields, do not use outside kernfs proper */ /* private fields, do not use outside kernfs proper */
struct mutex mutex; struct mutex mutex;
struct mutex prealloc_mutex;
int event; int event;
struct list_head list; struct list_head list;
char *prealloc_buf; char *prealloc_buf;

View file

@ -257,6 +257,7 @@ config PAGE_OWNER
config DEBUG_FS config DEBUG_FS
bool "Debug Filesystem" bool "Debug Filesystem"
select SRCU
help help
debugfs is a virtual file system that kernel developers use to put debugfs is a virtual file system that kernel developers use to put
debugging files into. Enable this option to be able to read and debugging files into. Enable this option to be able to read and

View 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)