1
0
Files
kernel-49/sound/core/seq/seq_device.c
Greg Kroah-Hartman 3510d8203b Merge 4.9.288 into android-4.9-q
Changes in 4.9.288
	ALSA: seq: Fix a potential UAF by wrong private_free call order
	s390: fix strrchr() implementation
	xhci: Enable trust tx length quirk for Fresco FL11 USB controller
	cb710: avoid NULL pointer subtraction
	efi/cper: use stack buffer for error record decoding
	efi: Change down_interruptible() in virt_efi_reset_system() to down_trylock()
	Input: xpad - add support for another USB ID of Nacon GC-100
	USB: serial: qcserial: add EM9191 QDL support
	USB: serial: option: add Telit LE910Cx composition 0x1204
	nvmem: Fix shift-out-of-bound (UBSAN) with byte size cells
	iio: adc128s052: Fix the error handling path of 'adc128_probe()'
	iio: light: opt3001: Fixed timeout error when 0 lux
	iio: ssp_sensors: add more range checking in ssp_parse_dataframe()
	iio: ssp_sensors: fix error code in ssp_print_mcu_debug()
	net: arc: select CRC32
	net: korina: select CRC32
	net: encx24j600: check error in devm_regmap_init_encx24j600
	ethernet: s2io: fix setting mac address during resume
	nfc: fix error handling of nfc_proto_register()
	NFC: digital: fix possible memory leak in digital_tg_listen_mdaa()
	NFC: digital: fix possible memory leak in digital_in_send_sdd_req()
	pata_legacy: fix a couple uninitialized variable bugs
	drm/msm: Fix null pointer dereference on pointer edp
	drm/msm/dsi: fix off by one in dsi_bus_clk_enable error handling
	r8152: select CRC32 and CRYPTO/CRYPTO_HASH/CRYPTO_SHA256
	xtensa: xtfpga: use CONFIG_USE_OF instead of CONFIG_OF
	xtensa: xtfpga: Try software restart before simulating CPU reset
	NFSD: Keep existing listeners on portlist error
	netfilter: ipvs: make global sysctl readonly in non-init netns
	NIOS2: irqflags: rename a redefined register name
	can: rcar_can: fix suspend/resume
	can: peak_usb: pcan_usb_fd_decode_status(): fix back to ERROR_ACTIVE state notification
	can: peak_pci: peak_pci_remove(): fix UAF
	ocfs2: fix data corruption after conversion from inline format
	ocfs2: mount fails with buffer overflow in strlen
	elfcore: correct reference to CONFIG_UML
	vfs: check fd has read access in kernel_read_file_from_fd()
	ALSA: usb-audio: Provide quirk for Sennheiser GSP670 Headset
	ASoC: DAPM: Fix missing kctl change notifications
	nfc: nci: fix the UAF of rf_conn_info object
	isdn: cpai: check ctr->cnr to avoid array index out of bound
	netfilter: Kconfig: use 'default y' instead of 'm' for bool config option
	ARM: dts: spear3xx: Fix gmac node
	isdn: mISDN: Fix sleeping function called from invalid context
	platform/x86: intel_scu_ipc: Update timeout value in comment
	ALSA: hda: avoid write to STATESTS if controller is in reset
	net: mdiobus: Fix memory leak in __mdiobus_register
	tracing: Have all levels of checks prevent recursion
	ARM: 9122/1: select HAVE_FUTEX_CMPXCHG
	Linux 4.9.288

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I8aaac774905fc04e20f969573f54832c3f18ad14
2021-11-16 23:25:29 +03:00

317 lines
7.7 KiB
C

/*
* ALSA sequencer device management
* Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*----------------------------------------------------------------
*
* This device handler separates the card driver module from sequencer
* stuff (sequencer core, synth drivers, etc), so that user can avoid
* to spend unnecessary resources e.g. if he needs only listening to
* MP3s.
*
* The card (or lowlevel) driver creates a sequencer device entry
* via snd_seq_device_new(). This is an entry pointer to communicate
* with the sequencer device "driver", which is involved with the
* actual part to communicate with the sequencer core.
* Each sequencer device entry has an id string and the corresponding
* driver with the same id is loaded when required. For example,
* lowlevel codes to access emu8000 chip on sbawe card are included in
* emu8000-synth module. To activate this module, the hardware
* resources like i/o port are passed via snd_seq_device argument.
*
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/seq_device.h>
#include <sound/seq_kernel.h>
#include <sound/initval.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/mutex.h>
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ALSA sequencer device management");
MODULE_LICENSE("GPL");
/*
* bus definition
*/
static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_seq_driver *sdrv = to_seq_drv(drv);
return strcmp(sdrv->id, sdev->id) == 0 &&
sdrv->argsize == sdev->argsize;
}
static struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
};
/*
* proc interface -- just for compatibility
*/
#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
static int print_dev_info(struct device *dev, void *data)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_info_buffer *buffer = data;
snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
dev->driver ? "loaded" : "empty",
dev->driver ? 1 : 0);
return 0;
}
static void snd_seq_device_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
}
#endif
/*
* load all registered drivers (called from seq_clientmgr.c)
*/
#ifdef CONFIG_MODULES
/* flag to block auto-loading */
static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
static int request_seq_drv(struct device *dev, void *data)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
if (!dev->driver)
request_module("snd-%s", sdev->id);
return 0;
}
static void autoload_drivers(struct work_struct *work)
{
/* avoid reentrance */
if (atomic_inc_return(&snd_seq_in_init) == 1)
bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
request_seq_drv);
atomic_dec(&snd_seq_in_init);
}
static DECLARE_WORK(autoload_work, autoload_drivers);
static void queue_autoload_drivers(void)
{
schedule_work(&autoload_work);
}
void snd_seq_autoload_init(void)
{
atomic_dec(&snd_seq_in_init);
#ifdef CONFIG_SND_SEQUENCER_MODULE
/* initial autoload only when snd-seq is a module */
queue_autoload_drivers();
#endif
}
EXPORT_SYMBOL(snd_seq_autoload_init);
void snd_seq_autoload_exit(void)
{
atomic_inc(&snd_seq_in_init);
}
EXPORT_SYMBOL(snd_seq_autoload_exit);
void snd_seq_device_load_drivers(void)
{
queue_autoload_drivers();
flush_work(&autoload_work);
}
EXPORT_SYMBOL(snd_seq_device_load_drivers);
#define cancel_autoload_drivers() cancel_work_sync(&autoload_work)
#else
#define queue_autoload_drivers() /* NOP */
#define cancel_autoload_drivers() /* NOP */
#endif
/*
* device management
*/
static int snd_seq_device_dev_free(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
cancel_autoload_drivers();
if (dev->private_free)
dev->private_free(dev);
put_device(&dev->dev);
return 0;
}
static int snd_seq_device_dev_register(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
int err;
err = device_add(&dev->dev);
if (err < 0)
return err;
if (!dev->dev.driver)
queue_autoload_drivers();
return 0;
}
static int snd_seq_device_dev_disconnect(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
device_del(&dev->dev);
return 0;
}
static void snd_seq_dev_release(struct device *dev)
{
kfree(to_seq_dev(dev));
}
/*
* register a sequencer device
* card = card info
* device = device number (if any)
* id = id of driver
* result = return pointer (NULL allowed if unnecessary)
*/
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result)
{
struct snd_seq_device *dev;
int err;
static struct snd_device_ops dops = {
.dev_free = snd_seq_device_dev_free,
.dev_register = snd_seq_device_dev_register,
.dev_disconnect = snd_seq_device_dev_disconnect,
};
if (result)
*result = NULL;
if (snd_BUG_ON(!id))
return -EINVAL;
dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* set up device info */
dev->card = card;
dev->device = device;
dev->id = id;
dev->argsize = argsize;
device_initialize(&dev->dev);
dev->dev.parent = &card->card_dev;
dev->dev.bus = &snd_seq_bus_type;
dev->dev.release = snd_seq_dev_release;
dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
/* add this device to the list */
err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
if (err < 0) {
put_device(&dev->dev);
return err;
}
if (result)
*result = dev;
return 0;
}
EXPORT_SYMBOL(snd_seq_device_new);
/*
* driver registration
*/
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
{
if (WARN_ON(!drv->driver.name || !drv->id))
return -EINVAL;
drv->driver.bus = &snd_seq_bus_type;
drv->driver.owner = mod;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
void snd_seq_driver_unregister(struct snd_seq_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
/*
* module part
*/
static int __init seq_dev_proc_init(void)
{
#ifdef CONFIG_SND_PROC_FS
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
snd_seq_root);
if (info_entry == NULL)
return -ENOMEM;
info_entry->content = SNDRV_INFO_CONTENT_TEXT;
info_entry->c.text.read = snd_seq_device_info;
if (snd_info_register(info_entry) < 0) {
snd_info_free_entry(info_entry);
return -ENOMEM;
}
#endif
return 0;
}
static int __init alsa_seq_device_init(void)
{
int err;
err = bus_register(&snd_seq_bus_type);
if (err < 0)
return err;
err = seq_dev_proc_init();
if (err < 0)
bus_unregister(&snd_seq_bus_type);
return err;
}
static void __exit alsa_seq_device_exit(void)
{
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
#ifdef CONFIG_SND_PROC_FS
snd_info_free_entry(info_entry);
#endif
bus_unregister(&snd_seq_bus_type);
}
subsys_initcall(alsa_seq_device_init)
module_exit(alsa_seq_device_exit)