diff options
Diffstat (limited to 'drivers/media/dvb-core/dvbdev.c')
| -rw-r--r-- | drivers/media/dvb-core/dvbdev.c | 411 |
1 files changed, 283 insertions, 128 deletions
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 41aad0f99d73..8b980d371a45 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -1,20 +1,10 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later /* * dvbdev.c * * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> * & Marcus Metzler <marcus@convergence.de> * for convergence integrated media GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * 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. - * */ #define pr_fmt(fmt) "dvbdev: " fmt @@ -24,18 +14,20 @@ #include <linux/string.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/i2c.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/mutex.h> -#include "dvbdev.h" +#include <media/dvbdev.h> /* Due to enum tuner_pad_index */ #include <media/tuner.h> static DEFINE_MUTEX(dvbdev_mutex); +static LIST_HEAD(dvbdevfops_list); static int dvbdev_debug; module_param(dvbdev_debug, int, 0644); @@ -51,8 +43,15 @@ static LIST_HEAD(dvb_adapter_list); static DEFINE_MUTEX(dvbdev_register_lock); static const char * const dnames[] = { - "video", "audio", "sec", "frontend", "demux", "dvr", "ca", - "net", "osd" + [DVB_DEVICE_VIDEO] = "video", + [DVB_DEVICE_AUDIO] = "audio", + [DVB_DEVICE_SEC] = "sec", + [DVB_DEVICE_FRONTEND] = "frontend", + [DVB_DEVICE_DEMUX] = "demux", + [DVB_DEVICE_DVR] = "dvr", + [DVB_DEVICE_CA] = "ca", + [DVB_DEVICE_NET] = "net", + [DVB_DEVICE_OSD] = "osd" }; #ifdef CONFIG_DVB_DYNAMIC_MINORS @@ -60,8 +59,23 @@ static const char * const dnames[] = { #define DVB_MAX_IDS MAX_DVB_MINORS #else #define DVB_MAX_IDS 4 -#define nums2minor(num, type, id) ((num << 6) | (id << 4) | type) -#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64) + +static const u8 minor_type[] = { + [DVB_DEVICE_VIDEO] = 0, + [DVB_DEVICE_AUDIO] = 1, + [DVB_DEVICE_SEC] = 2, + [DVB_DEVICE_FRONTEND] = 3, + [DVB_DEVICE_DEMUX] = 4, + [DVB_DEVICE_DVR] = 5, + [DVB_DEVICE_CA] = 6, + [DVB_DEVICE_NET] = 7, + [DVB_DEVICE_OSD] = 8, +}; + +#define nums2minor(num, type, id) \ + (((num) << 6) | ((id) << 4) | minor_type[type]) + +#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS * 64) #endif static struct class *dvb_class; @@ -72,10 +86,15 @@ static DECLARE_RWSEM(minor_rwsem); static int dvb_device_open(struct inode *inode, struct file *file) { struct dvb_device *dvbdev; + unsigned int minor = iminor(inode); + + if (minor >= MAX_DVB_MINORS) + return -ENODEV; mutex_lock(&dvbdev_mutex); down_read(&minor_rwsem); - dvbdev = dvb_minors[iminor(inode)]; + + dvbdev = dvb_minors[minor]; if (dvbdev && dvbdev->fops) { int err = 0; @@ -84,12 +103,14 @@ static int dvb_device_open(struct inode *inode, struct file *file) new_fops = fops_get(dvbdev->fops); if (!new_fops) goto fail; - file->private_data = dvbdev; + file->private_data = dvb_device_get(dvbdev); replace_fops(file, new_fops); if (file->f_op->open) err = file->f_op->open(inode, file); up_read(&minor_rwsem); mutex_unlock(&dvbdev_mutex); + if (err) + dvb_device_put(dvbdev); return err; } fail: @@ -98,9 +119,7 @@ fail: return -ENODEV; } - -static const struct file_operations dvb_device_fops = -{ +static const struct file_operations dvb_device_fops = { .owner = THIS_MODULE, .open = dvb_device_open, .llseek = noop_llseek, @@ -133,7 +152,6 @@ int dvb_generic_open(struct inode *inode, struct file *file) } EXPORT_SYMBOL(dvb_generic_open); - int dvb_generic_release(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; @@ -141,18 +159,19 @@ int dvb_generic_release(struct inode *inode, struct file *file) if (!dvbdev) return -ENODEV; - if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) dvbdev->readers++; - } else { + else dvbdev->writers++; - } dvbdev->users++; + + dvb_device_put(dvbdev); + return 0; } EXPORT_SYMBOL(dvb_generic_release); - long dvb_generic_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -168,13 +187,13 @@ long dvb_generic_ioctl(struct file *file, } EXPORT_SYMBOL(dvb_generic_ioctl); - -static int dvbdev_get_free_id (struct dvb_adapter *adap, int type) +static int dvbdev_get_free_id(struct dvb_adapter *adap, int type) { u32 id = 0; while (id < DVB_MAX_IDS) { struct dvb_device *dev; + list_for_each_entry(dev, &adap->device_list, list_head) if (dev->type == type && dev->id == id) goto skip; @@ -218,6 +237,7 @@ static void dvb_media_device_free(struct dvb_device *dvbdev) if (dvbdev->adapter->conn) { media_device_unregister_entity(dvbdev->adapter->conn); + kfree(dvbdev->adapter->conn); dvbdev->adapter->conn = NULL; kfree(dvbdev->adapter->conn_pads); dvbdev->adapter->conn_pads = NULL; @@ -227,9 +247,9 @@ static void dvb_media_device_free(struct dvb_device *dvbdev) #if defined(CONFIG_MEDIA_CONTROLLER_DVB) static int dvb_create_tsout_entity(struct dvb_device *dvbdev, - const char *name, int npads) + const char *name, int npads) { - int i, ret = 0; + int i; dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads), GFP_KERNEL); @@ -246,6 +266,7 @@ static int dvb_create_tsout_entity(struct dvb_device *dvbdev, for (i = 0; i < npads; i++) { struct media_pad *pads = &dvbdev->tsout_pads[i]; struct media_entity *entity = &dvbdev->tsout_entity[i]; + int ret; entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i); if (!entity->name) @@ -316,8 +337,11 @@ static int dvb_create_media_entity(struct dvb_device *dvbdev, if (npads) { dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads), GFP_KERNEL); - if (!dvbdev->pads) + if (!dvbdev->pads) { + kfree(dvbdev->entity); + dvbdev->entity = NULL; return -ENOMEM; + } } switch (type) { @@ -365,7 +389,7 @@ static int dvb_create_media_entity(struct dvb_device *dvbdev, static int dvb_register_media_device(struct dvb_device *dvbdev, int type, int minor, - unsigned demux_sink_pads) + unsigned int demux_sink_pads) { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) struct media_link *link; @@ -417,8 +441,10 @@ static int dvb_register_media_device(struct dvb_device *dvbdev, if (!dvbdev->entity) return 0; - link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + link = media_create_intf_link(dvbdev->entity, + &dvbdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; #endif @@ -426,18 +452,20 @@ static int dvb_register_media_device(struct dvb_device *dvbdev, } int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, - const struct dvb_device *template, void *priv, int type, - int demux_sink_pads) + const struct dvb_device *template, void *priv, + enum dvb_device_type type, int demux_sink_pads) { struct dvb_device *dvbdev; - struct file_operations *dvbdevfops; + struct file_operations *dvbdevfops = NULL; + struct dvbdevfops_node *node = NULL, *new_node = NULL; struct device *clsdev; int minor; int id, ret; mutex_lock(&dvbdev_register_lock); - if ((id = dvbdev_get_free_id (adap, type)) < 0){ + id = dvbdev_get_free_id(adap, type); + if (id < 0) { mutex_unlock(&dvbdev_register_lock); *pdvbdev = NULL; pr_err("%s: couldn't find free device id\n", __func__); @@ -445,85 +473,127 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, } *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL); - - if (!dvbdev){ + if (!dvbdev) { mutex_unlock(&dvbdev_register_lock); return -ENOMEM; } - dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); + /* + * When a device of the same type is probe()d more than once, + * the first allocated fops are used. This prevents memory leaks + * that can occur when the same device is probe()d repeatedly. + */ + list_for_each_entry(node, &dvbdevfops_list, list_head) { + if (node->fops->owner == adap->module && + node->type == type && node->template == template) { + dvbdevfops = node->fops; + break; + } + } + + if (!dvbdevfops) { + dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL); + if (!dvbdevfops) { + kfree(dvbdev); + *pdvbdev = NULL; + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } - if (!dvbdevfops){ - kfree (dvbdev); - mutex_unlock(&dvbdev_register_lock); - return -ENOMEM; + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) { + kfree(dvbdevfops); + kfree(dvbdev); + *pdvbdev = NULL; + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } + + new_node->fops = dvbdevfops; + new_node->type = type; + new_node->template = template; + list_add_tail(&new_node->list_head, &dvbdevfops_list); } memcpy(dvbdev, template, sizeof(struct dvb_device)); + kref_init(&dvbdev->ref); dvbdev->type = type; dvbdev->id = id; dvbdev->adapter = adap; dvbdev->priv = priv; dvbdev->fops = dvbdevfops; - init_waitqueue_head (&dvbdev->wait_queue); - - memcpy(dvbdevfops, template->fops, sizeof(struct file_operations)); + init_waitqueue_head(&dvbdev->wait_queue); dvbdevfops->owner = adap->module; - - list_add_tail (&dvbdev->list_head, &adap->device_list); - + list_add_tail(&dvbdev->list_head, &adap->device_list); down_write(&minor_rwsem); #ifdef CONFIG_DVB_DYNAMIC_MINORS for (minor = 0; minor < MAX_DVB_MINORS; minor++) - if (dvb_minors[minor] == NULL) + if (!dvb_minors[minor]) break; - - if (minor == MAX_DVB_MINORS) { - kfree(dvbdevfops); +#else + minor = nums2minor(adap->num, type, id); +#endif + if (minor >= MAX_DVB_MINORS) { + if (new_node) { + list_del(&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } + list_del(&dvbdev->list_head); kfree(dvbdev); + *pdvbdev = NULL; up_write(&minor_rwsem); mutex_unlock(&dvbdev_register_lock); return -EINVAL; } -#else - minor = nums2minor(adap->num, type, id); -#endif dvbdev->minor = minor; - dvb_minors[minor] = dvbdev; + dvb_minors[minor] = dvb_device_get(dvbdev); up_write(&minor_rwsem); - ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads); if (ret) { pr_err("%s: dvb_register_media_device failed to create the mediagraph\n", - __func__); - + __func__); + if (new_node) { + list_del(&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } dvb_media_device_free(dvbdev); - kfree(dvbdevfops); + list_del(&dvbdev->list_head); kfree(dvbdev); - up_write(&minor_rwsem); + *pdvbdev = NULL; mutex_unlock(&dvbdev_register_lock); return ret; } - mutex_unlock(&dvbdev_register_lock); - clsdev = device_create(dvb_class, adap->device, MKDEV(DVB_MAJOR, minor), dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); if (IS_ERR(clsdev)) { - pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", - __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); + pr_err("%s: failed to create device dvb%d.%s%d (%pe)\n", + __func__, adap->num, dnames[type], id, clsdev); + if (new_node) { + list_del(&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } + dvb_media_device_free(dvbdev); + list_del(&dvbdev->list_head); + kfree(dvbdev); + *pdvbdev = NULL; + mutex_unlock(&dvbdev_register_lock); return PTR_ERR(clsdev); } + dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, minor, minor); + mutex_unlock(&dvbdev_register_lock); return 0; } EXPORT_SYMBOL(dvb_register_device); - void dvb_remove_device(struct dvb_device *dvbdev) { if (!dvbdev) @@ -531,36 +601,44 @@ void dvb_remove_device(struct dvb_device *dvbdev) down_write(&minor_rwsem); dvb_minors[dvbdev->minor] = NULL; + dvb_device_put(dvbdev); up_write(&minor_rwsem); dvb_media_device_free(dvbdev); device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); - list_del (&dvbdev->list_head); + list_del(&dvbdev->list_head); } EXPORT_SYMBOL(dvb_remove_device); - -void dvb_free_device(struct dvb_device *dvbdev) +static void dvb_free_device(struct kref *ref) { - if (!dvbdev) - return; + struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref); - kfree (dvbdev->fops); - kfree (dvbdev); + kfree(dvbdev); } -EXPORT_SYMBOL(dvb_free_device); +struct dvb_device *dvb_device_get(struct dvb_device *dvbdev) +{ + kref_get(&dvbdev->ref); + return dvbdev; +} +EXPORT_SYMBOL(dvb_device_get); + +void dvb_device_put(struct dvb_device *dvbdev) +{ + if (dvbdev) + kref_put(&dvbdev->ref, dvb_free_device); +} void dvb_unregister_device(struct dvb_device *dvbdev) { dvb_remove_device(dvbdev); - dvb_free_device(dvbdev); + dvb_device_put(dvbdev); } EXPORT_SYMBOL(dvb_unregister_device); - #ifdef CONFIG_MEDIA_CONTROLLER_DVB static int dvb_create_io_intf_links(struct dvb_adapter *adap, @@ -576,7 +654,8 @@ static int dvb_create_io_intf_links(struct dvb_adapter *adap, if (strncmp(entity->name, name, strlen(name))) continue; link = media_create_intf_link(entity, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -592,10 +671,10 @@ int dvb_create_media_graph(struct dvb_adapter *adap, struct media_entity *demux = NULL, *ca = NULL; struct media_link *link; struct media_interface *intf; - unsigned demux_pad = 0; - unsigned dvr_pad = 0; - unsigned ntuner = 0, ndemod = 0; - int ret; + unsigned int demux_pad = 0; + unsigned int dvr_pad = 0; + unsigned int ntuner = 0, ndemod = 0; + int ret, pad_source, pad_sink; static const char *connector_name = "Television"; if (!mdev) @@ -655,7 +734,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap, if (ret) return ret; - if (!ntuner) + if (!ntuner) { ret = media_create_pad_links(mdev, MEDIA_ENT_F_CONN_RF, conn, 0, @@ -663,22 +742,32 @@ int dvb_create_media_graph(struct dvb_adapter *adap, demod, 0, MEDIA_LNK_FL_ENABLED, false); - else + } else { + pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK, + PAD_SIGNAL_ANALOG); + if (pad_sink < 0) + return -EINVAL; ret = media_create_pad_links(mdev, MEDIA_ENT_F_CONN_RF, conn, 0, MEDIA_ENT_F_TUNER, - tuner, TUNER_PAD_RF_INPUT, + tuner, pad_sink, MEDIA_LNK_FL_ENABLED, false); + } if (ret) return ret; } if (ntuner && ndemod) { + /* NOTE: first found tuner source pad presumed correct */ + pad_source = media_get_pad_index(tuner, MEDIA_PAD_FL_SOURCE, + PAD_SIGNAL_ANALOG); + if (pad_source < 0) + return -EINVAL; ret = media_create_pad_links(mdev, MEDIA_ENT_F_TUNER, - tuner, TUNER_PAD_OUTPUT, + tuner, pad_source, MEDIA_ENT_F_DTV_DEMOD, demod, 0, MEDIA_LNK_FL_ENABLED, false); @@ -708,18 +797,18 @@ int dvb_create_media_graph(struct dvb_adapter *adap, media_device_for_each_entity(entity, mdev) { if (entity->function == MEDIA_ENT_F_IO_DTV) { if (!strncmp(entity->name, DVR_TSOUT, - strlen(DVR_TSOUT))) { + strlen(DVR_TSOUT))) { ret = media_create_pad_link(demux, - ++dvr_pad, - entity, 0, 0); + ++dvr_pad, + entity, 0, 0); if (ret) return ret; } if (!strncmp(entity->name, DEMUX_TSOUT, - strlen(DEMUX_TSOUT))) { + strlen(DEMUX_TSOUT))) { ret = media_create_pad_link(demux, - ++demux_pad, - entity, 0, 0); + ++demux_pad, + entity, 0, 0); if (ret) return ret; } @@ -731,14 +820,16 @@ int dvb_create_media_graph(struct dvb_adapter *adap, media_device_for_each_intf(intf, mdev) { if (intf->type == MEDIA_INTF_T_DVB_CA && ca) { link = media_create_intf_link(ca, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) { link = media_create_intf_link(tuner, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -750,7 +841,8 @@ int dvb_create_media_graph(struct dvb_adapter *adap, */ if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) { link = media_create_intf_link(demux, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -774,8 +866,10 @@ EXPORT_SYMBOL_GPL(dvb_create_media_graph); static int dvbdev_check_free_adapter_num(int num) { struct list_head *entry; + list_for_each(entry, &dvb_adapter_list) { struct dvb_adapter *adap; + adap = list_entry(entry, struct dvb_adapter, list_head); if (adap->num == num) return 0; @@ -783,7 +877,7 @@ static int dvbdev_check_free_adapter_num(int num) return 1; } -static int dvbdev_get_free_adapter_num (void) +static int dvbdev_get_free_adapter_num(void) { int num = 0; @@ -796,7 +890,6 @@ static int dvbdev_get_free_adapter_num (void) return -ENFILE; } - int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct module *module, struct device *device, short *adapter_nums) @@ -823,8 +916,8 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, return -ENFILE; } - memset (adap, 0, sizeof(struct dvb_adapter)); - INIT_LIST_HEAD (&adap->device_list); + memset(adap, 0, sizeof(struct dvb_adapter)); + INIT_LIST_HEAD(&adap->device_list); pr_info("DVB: registering new adapter (%s)\n", name); @@ -834,9 +927,13 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, adap->device = device; adap->mfe_shared = 0; adap->mfe_dvbdev = NULL; - mutex_init (&adap->mfe_lock); + mutex_init(&adap->mfe_lock); - list_add_tail (&adap->list_head, &dvb_adapter_list); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + mutex_init(&adap->mdev_lock); +#endif + + list_add_tail(&adap->list_head, &dvb_adapter_list); mutex_unlock(&dvbdev_register_lock); @@ -844,27 +941,28 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, } EXPORT_SYMBOL(dvb_register_adapter); - int dvb_unregister_adapter(struct dvb_adapter *adap) { mutex_lock(&dvbdev_register_lock); - list_del (&adap->list_head); + list_del(&adap->list_head); mutex_unlock(&dvbdev_register_lock); return 0; } EXPORT_SYMBOL(dvb_unregister_adapter); -/* if the miracle happens and "generic_usercopy()" is included into - the kernel, then this can vanish. please don't make the mistake and - define this as video_usercopy(). this will introduce a dependecy - to the v4l "videodev.o" module, which is unnecessary for some - cards (ie. the budget dvb-cards don't need the v4l module...) */ +/* + * if the miracle happens and "generic_usercopy()" is included into + * the kernel, then this can vanish. please don't make the mistake and + * define this as video_usercopy(). this will introduce a dependency + * to the v4l "videodev.o" module, which is unnecessary for some + * cards (ie. the budget dvb-cards don't need the v4l module...) + */ int dvb_usercopy(struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct file *file, - unsigned int cmd, void *arg)) + unsigned int cmd, unsigned long arg, + int (*func)(struct file *file, + unsigned int cmd, void *arg)) { - char sbuf[128]; + char sbuf[128] = {}; void *mbuf = NULL; void *parg = NULL; int err = -EINVAL; @@ -876,7 +974,7 @@ int dvb_usercopy(struct file *file, * For this command, the pointer is actually an integer * argument. */ - parg = (void *) arg; + parg = (void *)arg; break; case _IOC_READ: /* some v4l ioctls are marked wrong ... */ case _IOC_WRITE: @@ -886,7 +984,7 @@ int dvb_usercopy(struct file *file, } else { /* too big to allocate from stack */ mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (NULL == mbuf) + if (!mbuf) return -ENOMEM; parg = mbuf; } @@ -898,15 +996,15 @@ int dvb_usercopy(struct file *file, } /* call driver */ - if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) + err = func(file, cmd, parg); + if (err == -ENOIOCTLCMD) err = -ENOTTY; if (err < 0) goto out; /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) - { + switch (_IOC_DIR(cmd)) { case _IOC_READ: case (_IOC_WRITE | _IOC_READ): if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) @@ -919,9 +1017,58 @@ out: return err; } -static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) +#if IS_ENABLED(CONFIG_I2C) +struct i2c_client *dvb_module_probe(const char *module_name, + const char *name, + struct i2c_adapter *adap, + unsigned char addr, + void *platform_data) { - struct dvb_device *dvbdev = dev_get_drvdata(dev); + struct i2c_client *client; + struct i2c_board_info *board_info; + + board_info = kzalloc(sizeof(*board_info), GFP_KERNEL); + if (!board_info) + return NULL; + + if (name) + strscpy(board_info->type, name, I2C_NAME_SIZE); + else + strscpy(board_info->type, module_name, I2C_NAME_SIZE); + + board_info->addr = addr; + board_info->platform_data = platform_data; + request_module(module_name); + client = i2c_new_client_device(adap, board_info); + if (!i2c_client_has_driver(client)) { + kfree(board_info); + return NULL; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + client = NULL; + } + + kfree(board_info); + return client; +} +EXPORT_SYMBOL_GPL(dvb_module_probe); + +void dvb_module_release(struct i2c_client *client) +{ + if (!client) + return; + + module_put(client->dev.driver->owner); + i2c_unregister_device(client); +} +EXPORT_SYMBOL_GPL(dvb_module_release); +#endif + +static int dvb_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + const struct dvb_device *dvbdev = dev_get_drvdata(dev); add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num); add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]); @@ -929,32 +1076,33 @@ static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -static char *dvb_devnode(struct device *dev, umode_t *mode) +static char *dvb_devnode(const struct device *dev, umode_t *mode) { - struct dvb_device *dvbdev = dev_get_drvdata(dev); + const struct dvb_device *dvbdev = dev_get_drvdata(dev); return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d", dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id); } - static int __init init_dvbdev(void) { int retval; dev_t dev = MKDEV(DVB_MAJOR, 0); - if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) { + retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB"); + if (retval != 0) { pr_err("dvb-core: unable to get major %d\n", DVB_MAJOR); return retval; } cdev_init(&dvb_device_cdev, &dvb_device_fops); - if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) { + retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS); + if (retval != 0) { pr_err("dvb-core: unable register character device\n"); goto error; } - dvb_class = class_create(THIS_MODULE, "dvb"); + dvb_class = class_create("dvb"); if (IS_ERR(dvb_class)) { retval = PTR_ERR(dvb_class); goto error; @@ -969,12 +1117,19 @@ error: return retval; } - static void __exit exit_dvbdev(void) { + struct dvbdevfops_node *node, *next; + class_destroy(dvb_class); cdev_del(&dvb_device_cdev); unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); + + list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) { + list_del(&node->list_head); + kfree(node->fops); + kfree(node); + } } subsys_initcall(init_dvbdev); |
