linux/drivers/media/dvb-core/dvbdev.c
Brad Love 9f984cacf4 media: dvbdev: Fix tuner->demod media controller link
Fixes bug exposed by:

[a3fbc2e6bb: media: mc-entity.c: use WARN_ON, validate link pads]

The dvbdev incorrectly requests a tuner sink pad to connect to a demux
sink pad. The media controller failure percolates back and the dvb device
creation fails. Fix this by requesting a tuner source pad. Instead of
forcing that pad to be index zero, check if a negative integer error
is returned. A note is added that first source pad found is chosen.

Affected bridges cx231xx and em28xx printed the below warning[s]
when a variety of media controller dvb enabled devices were connected.
The warning returns an error causing all affected devices to fail DVB
device creation.

[  253.138332] ------------[ cut here ]------------
[  253.138339] WARNING: CPU: 0 PID: 1550 at drivers/media/mc/mc-entity.c:669 media_create_pad_link+0x1e0/0x200 [mc]
[  253.138339] Modules linked in: si2168 em28xx_dvb(+) em28xx si2157 lgdt3306a cx231xx_dvb dvb_core cx231xx_alsa cx25840 cx231xx tveeprom cx2341x i2c_mux videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc ir_rc5_decoder rc_hauppauge mceusb rc_core eda
c_mce_amd kvm nls_iso8859_1 crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel crypto_simd cryptd glue_helper efi_pstore wmi_bmof k10temp asix usbnet mii nouveau snd_hda_codec_realtek snd_hda_codec_generic input_leds ledtrig_audio snd_hda_codec_hdmi mxm_wmi snd_hda_in
tel video snd_intel_dspcfg ttm snd_hda_codec drm_kms_helper snd_hda_core drm snd_hwdep snd_seq_midi snd_seq_midi_event i2c_algo_bit snd_pcm snd_rawmidi fb_sys_fops snd_seq syscopyarea sysfillrect snd_seq_device sysimgblt snd_timer snd soundcore ccp mac_hid sch_fq_codel parport_p
c ppdev lp parport ip_tables x_tables autofs4 vfio_pci irqbypass vfio_virqfd vfio_iommu_type1 vfio hid_generic usbhid hid i2c_piix4 ahci libahci wmi gpio_amdpt
[  253.138370]  gpio_generic
[  253.138372] CPU: 0 PID: 1550 Comm: modprobe Tainted: G        W         5.7.0-rc2+ #181
[  253.138373] Hardware name: MSI MS-7A39/B350M GAMING PRO (MS-7A39), BIOS 2.G0 04/27/2018
[  253.138376] RIP: 0010:media_create_pad_link+0x1e0/0x200 [mc]
[  253.138378] Code: 26 fd ff ff 44 8b 4d d0 eb d9 0f 0b 41 b9 ea ff ff ff 44 89 c8 c3 0f 0b 41 b9 ea ff ff ff eb f2 0f 0b 41 b9 ea ff ff ff eb e8 <0f> 0b 41 b9 ea ff ff ff eb af 0f 0b 41 b9 ea ff ff ff eb a5 66 90
[  253.138379] RSP: 0018:ffffb9ecc0ee7a78 EFLAGS: 00010246
[  253.138380] RAX: ffff943f706c99d8 RBX: 0000000000000000 RCX: 0000000000000000
[  253.138381] RDX: ffff943f613e0180 RSI: 0000000000000000 RDI: ffff943f706c9958
[  253.138381] RBP: ffffb9ecc0ee7ab0 R08: 0000000000000001 R09: ffff943f613e0180
[  253.138382] R10: ffff943f613e0180 R11: ffff943f706c9400 R12: 0000000000000000
[  253.138383] R13: 0000000000000001 R14: ffff943f706c9958 R15: 0000000000000001
[  253.138384] FS:  00007f3cd29ba540(0000) GS:ffff943f8ec00000(0000) knlGS:0000000000000000
[  253.138385] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  253.138385] CR2: 000055f7de0ca830 CR3: 00000003dd208000 CR4: 00000000003406f0
[  253.138386] Call Trace:
[  253.138392]  media_create_pad_links+0x104/0x1b0 [mc]
[  253.138397]  dvb_create_media_graph+0x350/0x5f0 [dvb_core]
[  253.138402]  em28xx_dvb_init+0x5ea/0x2600 [em28xx_dvb]
[  253.138408]  em28xx_register_extension+0x63/0xc0 [em28xx]
[  253.138410]  ? 0xffffffffc039c000
[  253.138412]  em28xx_dvb_register+0x15/0x1000 [em28xx_dvb]
[  253.138416]  do_one_initcall+0x71/0x250
[  253.138418]  ? do_init_module+0x27/0x22e
[  253.138421]  ? _cond_resched+0x1a/0x50
[  253.138423]  ? kmem_cache_alloc_trace+0x1ec/0x270
[  253.138425]  ? __vunmap+0x1e3/0x240
[  253.138427]  do_init_module+0x5f/0x22e
[  253.138430]  load_module+0x2525/0x2d40
[  253.138436]  __do_sys_finit_module+0xe5/0x120
[  253.138438]  ? __do_sys_finit_module+0xe5/0x120
[  253.138442]  __x64_sys_finit_module+0x1a/0x20
[  253.138443]  do_syscall_64+0x57/0x1b0
[  253.138445]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  253.138446] RIP: 0033:0x7f3cd24dc839
[  253.138448] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 1f f6 2c 00 f7 d8 64 89 01 48
[  253.138449] RSP: 002b:00007ffe4fc514d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[  253.138450] RAX: ffffffffffffffda RBX: 000055a9237f63f0 RCX: 00007f3cd24dc839
[  253.138451] RDX: 0000000000000000 RSI: 000055a922c3ad2e RDI: 0000000000000000
[  253.138451] RBP: 000055a922c3ad2e R08: 0000000000000000 R09: 0000000000000000
[  253.138452] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
[  253.138453] R13: 000055a9237f5550 R14: 0000000000040000 R15: 000055a9237f63f0
[  253.138456] ---[ end trace a60f19c54aa96ec4 ]---

[  234.915628] ------------[ cut here ]------------
[  234.915640] WARNING: CPU: 0 PID: 1502 at drivers/media/mc/mc-entity.c:669 media_create_pad_link+0x1e0/0x200 [mc]
[  234.915641] Modules linked in: si2157 lgdt3306a cx231xx_dvb(+) dvb_core cx231xx_alsa cx25840 cx231xx tveeprom cx2341x i2c_mux videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc ir_rc5_decoder rc_hauppauge mceusb rc_core edac_mce_amd kvm nls_iso8859
_1 crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel crypto_simd cryptd glue_helper efi_pstore wmi_bmof k10temp asix usbnet mii nouveau snd_hda_codec_realtek snd_hda_codec_generic input_leds ledtrig_audio snd_hda_codec_hdmi mxm_wmi snd_hda_intel video snd_intel_dspcf
g ttm snd_hda_codec drm_kms_helper snd_hda_core drm snd_hwdep snd_seq_midi snd_seq_midi_event i2c_algo_bit snd_pcm snd_rawmidi fb_sys_fops snd_seq syscopyarea sysfillrect snd_seq_device sysimgblt snd_timer snd soundcore ccp mac_hid sch_fq_codel parport_pc ppdev lp parport ip_tab
les x_tables autofs4 vfio_pci irqbypass vfio_virqfd vfio_iommu_type1 vfio hid_generic usbhid hid i2c_piix4 ahci libahci wmi gpio_amdpt gpio_generic
[  234.915700] CPU: 0 PID: 1502 Comm: modprobe Not tainted 5.7.0-rc2+ #181
[  234.915702] Hardware name: MSI MS-7A39/B350M GAMING PRO (MS-7A39), BIOS 2.G0 04/27/2018
[  234.915709] RIP: 0010:media_create_pad_link+0x1e0/0x200 [mc]
[  234.915712] Code: 26 fd ff ff 44 8b 4d d0 eb d9 0f 0b 41 b9 ea ff ff ff 44 89 c8 c3 0f 0b 41 b9 ea ff ff ff eb f2 0f 0b 41 b9 ea ff ff ff eb e8 <0f> 0b 41 b9 ea ff ff ff eb af 0f 0b 41 b9 ea ff ff ff eb a5 66 90
[  234.915714] RSP: 0018:ffffb9ecc1b6fa50 EFLAGS: 00010246
[  234.915717] RAX: ffff943f8c94a9d8 RBX: 0000000000000000 RCX: 0000000000000000
[  234.915719] RDX: ffff943f613e0900 RSI: 0000000000000000 RDI: ffff943f8c94a958
[  234.915721] RBP: ffffb9ecc1b6fa88 R08: 0000000000000001 R09: ffff943f613e0900
[  234.915723] R10: ffff943f613e0900 R11: ffff943f6b590c00 R12: 0000000000000000
[  234.915724] R13: 0000000000000001 R14: ffff943f8c94a958 R15: 0000000000000001
[  234.915727] FS:  00007f4ca3646540(0000) GS:ffff943f8ec00000(0000) knlGS:0000000000000000
[  234.915729] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  234.915731] CR2: 00007fff7a53ba18 CR3: 00000003da614000 CR4: 00000000003406f0
[  234.915733] Call Trace:
[  234.915745]  media_create_pad_links+0x104/0x1b0 [mc]
[  234.915756]  dvb_create_media_graph+0x350/0x5f0 [dvb_core]
[  234.915766]  dvb_init.part.4+0x691/0x1360 [cx231xx_dvb]
[  234.915780]  dvb_init+0x1a/0x20 [cx231xx_dvb]
[  234.915787]  cx231xx_register_extension+0x71/0xa0 [cx231xx]
[  234.915791]  ? 0xffffffffc042f000
[  234.915796]  cx231xx_dvb_register+0x15/0x1000 [cx231xx_dvb]
[  234.915802]  do_one_initcall+0x71/0x250
[  234.915807]  ? do_init_module+0x27/0x22e
[  234.915811]  ? _cond_resched+0x1a/0x50
[  234.915816]  ? kmem_cache_alloc_trace+0x1ec/0x270
[  234.915820]  ? __vunmap+0x1e3/0x240
[  234.915826]  do_init_module+0x5f/0x22e
[  234.915831]  load_module+0x2525/0x2d40
[  234.915848]  __do_sys_finit_module+0xe5/0x120
[  234.915850]  ? __do_sys_finit_module+0xe5/0x120
[  234.915862]  __x64_sys_finit_module+0x1a/0x20
[  234.915865]  do_syscall_64+0x57/0x1b0
[  234.915870]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  234.915872] RIP: 0033:0x7f4ca3168839
[  234.915876] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 1f f6 2c 00 f7 d8 64 89 01 48
[  234.915878] RSP: 002b:00007ffcea3db3b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[  234.915881] RAX: ffffffffffffffda RBX: 000055af22c29340 RCX: 00007f4ca3168839
[  234.915882] RDX: 0000000000000000 RSI: 000055af22c38390 RDI: 0000000000000001
[  234.915884] RBP: 000055af22c38390 R08: 0000000000000000 R09: 0000000000000000
[  234.915885] R10: 0000000000000001 R11: 0000000000000246 R12: 0000000000000000
[  234.915887] R13: 000055af22c29060 R14: 0000000000040000 R15: 0000000000000000
[  234.915896] ---[ end trace a60f19c54aa96ec3 ]---

Signed-off-by: Brad Love <brad@nextdimension.cc>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-05-25 09:09:05 +02:00

1078 lines
24 KiB
C

/*
* 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
#include <linux/types.h>
#include <linux/errno.h>
#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 <media/dvbdev.h>
/* Due to enum tuner_pad_index */
#include <media/tuner.h>
static DEFINE_MUTEX(dvbdev_mutex);
static int dvbdev_debug;
module_param(dvbdev_debug, int, 0644);
MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
#define dprintk(fmt, arg...) do { \
if (dvbdev_debug) \
printk(KERN_DEBUG pr_fmt("%s: " fmt), \
__func__, ##arg); \
} while (0)
static LIST_HEAD(dvb_adapter_list);
static DEFINE_MUTEX(dvbdev_register_lock);
static const char * const dnames[] = {
[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
#define MAX_DVB_MINORS 256
#define DVB_MAX_IDS MAX_DVB_MINORS
#else
#define DVB_MAX_IDS 4
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;
static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
static int dvb_device_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev;
mutex_lock(&dvbdev_mutex);
down_read(&minor_rwsem);
dvbdev = dvb_minors[iminor(inode)];
if (dvbdev && dvbdev->fops) {
int err = 0;
const struct file_operations *new_fops;
new_fops = fops_get(dvbdev->fops);
if (!new_fops)
goto fail;
file->private_data = 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);
return err;
}
fail:
up_read(&minor_rwsem);
mutex_unlock(&dvbdev_mutex);
return -ENODEV;
}
static const struct file_operations dvb_device_fops =
{
.owner = THIS_MODULE,
.open = dvb_device_open,
.llseek = noop_llseek,
};
static struct cdev dvb_device_cdev;
int dvb_generic_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if (!dvbdev->users)
return -EBUSY;
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
if (!dvbdev->readers)
return -EBUSY;
dvbdev->readers--;
} else {
if (!dvbdev->writers)
return -EBUSY;
dvbdev->writers--;
}
dvbdev->users--;
return 0;
}
EXPORT_SYMBOL(dvb_generic_open);
int dvb_generic_release(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
dvbdev->readers++;
} else {
dvbdev->writers++;
}
dvbdev->users++;
return 0;
}
EXPORT_SYMBOL(dvb_generic_release);
long dvb_generic_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if (!dvbdev->kernel_ioctl)
return -EINVAL;
return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
}
EXPORT_SYMBOL(dvb_generic_ioctl);
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;
return id;
skip:
id++;
}
return -ENFILE;
}
static void dvb_media_device_free(struct dvb_device *dvbdev)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
if (dvbdev->entity) {
media_device_unregister_entity(dvbdev->entity);
kfree(dvbdev->entity);
kfree(dvbdev->pads);
dvbdev->entity = NULL;
dvbdev->pads = NULL;
}
if (dvbdev->tsout_entity) {
int i;
for (i = 0; i < dvbdev->tsout_num_entities; i++) {
media_device_unregister_entity(&dvbdev->tsout_entity[i]);
kfree(dvbdev->tsout_entity[i].name);
}
kfree(dvbdev->tsout_entity);
kfree(dvbdev->tsout_pads);
dvbdev->tsout_entity = NULL;
dvbdev->tsout_pads = NULL;
dvbdev->tsout_num_entities = 0;
}
if (dvbdev->intf_devnode) {
media_devnode_remove(dvbdev->intf_devnode);
dvbdev->intf_devnode = NULL;
}
if (dvbdev->adapter->conn) {
media_device_unregister_entity(dvbdev->adapter->conn);
dvbdev->adapter->conn = NULL;
kfree(dvbdev->adapter->conn_pads);
dvbdev->adapter->conn_pads = NULL;
}
#endif
}
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
const char *name, int npads)
{
int i, ret = 0;
dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads),
GFP_KERNEL);
if (!dvbdev->tsout_pads)
return -ENOMEM;
dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity),
GFP_KERNEL);
if (!dvbdev->tsout_entity)
return -ENOMEM;
dvbdev->tsout_num_entities = npads;
for (i = 0; i < npads; i++) {
struct media_pad *pads = &dvbdev->tsout_pads[i];
struct media_entity *entity = &dvbdev->tsout_entity[i];
entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i);
if (!entity->name)
return -ENOMEM;
entity->function = MEDIA_ENT_F_IO_DTV;
pads->flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(entity, 1, pads);
if (ret < 0)
return ret;
ret = media_device_register_entity(dvbdev->adapter->mdev,
entity);
if (ret < 0)
return ret;
}
return 0;
}
#define DEMUX_TSOUT "demux-tsout"
#define DVR_TSOUT "dvr-tsout"
static int dvb_create_media_entity(struct dvb_device *dvbdev,
int type, int demux_sink_pads)
{
int i, ret, npads;
switch (type) {
case DVB_DEVICE_FRONTEND:
npads = 2;
break;
case DVB_DEVICE_DVR:
ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT,
demux_sink_pads);
return ret;
case DVB_DEVICE_DEMUX:
npads = 1 + demux_sink_pads;
ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT,
demux_sink_pads);
if (ret < 0)
return ret;
break;
case DVB_DEVICE_CA:
npads = 2;
break;
case DVB_DEVICE_NET:
/*
* We should be creating entities for the MPE/ULE
* decapsulation hardware (or software implementation).
*
* However, the number of for the MPE/ULE decaps may not be
* fixed. As we don't have yet dynamic support for PADs at
* the Media Controller, let's not create the decap
* entities yet.
*/
return 0;
default:
return 0;
}
dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
if (!dvbdev->entity)
return -ENOMEM;
dvbdev->entity->name = dvbdev->name;
if (npads) {
dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads),
GFP_KERNEL);
if (!dvbdev->pads) {
kfree(dvbdev->entity);
return -ENOMEM;
}
}
switch (type) {
case DVB_DEVICE_FRONTEND:
dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
break;
case DVB_DEVICE_DEMUX:
dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
for (i = 1; i < npads; i++)
dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE;
break;
case DVB_DEVICE_CA:
dvbdev->entity->function = MEDIA_ENT_F_DTV_CA;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
break;
default:
/* Should never happen, as the first switch prevents it */
kfree(dvbdev->entity);
kfree(dvbdev->pads);
dvbdev->entity = NULL;
dvbdev->pads = NULL;
return 0;
}
if (npads) {
ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads);
if (ret)
return ret;
}
ret = media_device_register_entity(dvbdev->adapter->mdev,
dvbdev->entity);
if (ret)
return ret;
pr_info("%s: media entity '%s' registered.\n",
__func__, dvbdev->entity->name);
return 0;
}
#endif
static int dvb_register_media_device(struct dvb_device *dvbdev,
int type, int minor,
unsigned demux_sink_pads)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
struct media_link *link;
u32 intf_type;
int ret;
if (!dvbdev->adapter->mdev)
return 0;
ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads);
if (ret)
return ret;
switch (type) {
case DVB_DEVICE_FRONTEND:
intf_type = MEDIA_INTF_T_DVB_FE;
break;
case DVB_DEVICE_DEMUX:
intf_type = MEDIA_INTF_T_DVB_DEMUX;
break;
case DVB_DEVICE_DVR:
intf_type = MEDIA_INTF_T_DVB_DVR;
break;
case DVB_DEVICE_CA:
intf_type = MEDIA_INTF_T_DVB_CA;
break;
case DVB_DEVICE_NET:
intf_type = MEDIA_INTF_T_DVB_NET;
break;
default:
return 0;
}
dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev,
intf_type, 0,
DVB_MAJOR, minor);
if (!dvbdev->intf_devnode)
return -ENOMEM;
/*
* Create the "obvious" link, e. g. the ones that represent
* a direct association between an interface and an entity.
* Other links should be created elsewhere, like:
* DVB FE intf -> tuner
* DVB demux intf -> dvr
*/
if (!dvbdev->entity)
return 0;
link = media_create_intf_link(dvbdev->entity,
&dvbdev->intf_devnode->intf,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (!link)
return -ENOMEM;
#endif
return 0;
}
int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
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 device *clsdev;
int minor;
int id, ret;
mutex_lock(&dvbdev_register_lock);
if ((id = dvbdev_get_free_id (adap, type)) < 0){
mutex_unlock(&dvbdev_register_lock);
*pdvbdev = NULL;
pr_err("%s: couldn't find free device id\n", __func__);
return -ENFILE;
}
*pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);
if (!dvbdev){
mutex_unlock(&dvbdev_register_lock);
return -ENOMEM;
}
dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
if (!dvbdevfops){
kfree (dvbdev);
mutex_unlock(&dvbdev_register_lock);
return -ENOMEM;
}
memcpy(dvbdev, template, sizeof(struct dvb_device));
dvbdev->type = type;
dvbdev->id = id;
dvbdev->adapter = adap;
dvbdev->priv = priv;
dvbdev->fops = dvbdevfops;
init_waitqueue_head (&dvbdev->wait_queue);
dvbdevfops->owner = adap->module;
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)
break;
if (minor == MAX_DVB_MINORS) {
kfree(dvbdevfops);
kfree(dvbdev);
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;
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__);
dvb_media_device_free(dvbdev);
kfree(dvbdevfops);
kfree(dvbdev);
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));
return PTR_ERR(clsdev);
}
dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
adap->num, dnames[type], id, minor, minor);
return 0;
}
EXPORT_SYMBOL(dvb_register_device);
void dvb_remove_device(struct dvb_device *dvbdev)
{
if (!dvbdev)
return;
down_write(&minor_rwsem);
dvb_minors[dvbdev->minor] = NULL;
up_write(&minor_rwsem);
dvb_media_device_free(dvbdev);
device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
list_del (&dvbdev->list_head);
}
EXPORT_SYMBOL(dvb_remove_device);
void dvb_free_device(struct dvb_device *dvbdev)
{
if (!dvbdev)
return;
kfree (dvbdev->fops);
kfree (dvbdev);
}
EXPORT_SYMBOL(dvb_free_device);
void dvb_unregister_device(struct dvb_device *dvbdev)
{
dvb_remove_device(dvbdev);
dvb_free_device(dvbdev);
}
EXPORT_SYMBOL(dvb_unregister_device);
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
static int dvb_create_io_intf_links(struct dvb_adapter *adap,
struct media_interface *intf,
char *name)
{
struct media_device *mdev = adap->mdev;
struct media_entity *entity;
struct media_link *link;
media_device_for_each_entity(entity, mdev) {
if (entity->function == MEDIA_ENT_F_IO_DTV) {
if (strncmp(entity->name, name, strlen(name)))
continue;
link = media_create_intf_link(entity, intf,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (!link)
return -ENOMEM;
}
}
return 0;
}
int dvb_create_media_graph(struct dvb_adapter *adap,
bool create_rf_connector)
{
struct media_device *mdev = adap->mdev;
struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn;
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, pad_source, pad_sink;
static const char *connector_name = "Television";
if (!mdev)
return 0;
media_device_for_each_entity(entity, mdev) {
switch (entity->function) {
case MEDIA_ENT_F_TUNER:
tuner = entity;
ntuner++;
break;
case MEDIA_ENT_F_DTV_DEMOD:
demod = entity;
ndemod++;
break;
case MEDIA_ENT_F_TS_DEMUX:
demux = entity;
break;
case MEDIA_ENT_F_DTV_CA:
ca = entity;
break;
}
}
/*
* Prepare to signalize to media_create_pad_links() that multiple
* entities of the same type exists and a 1:n or n:1 links need to be
* created.
* NOTE: if both tuner and demod have multiple instances, it is up
* to the caller driver to create such links.
*/
if (ntuner > 1)
tuner = NULL;
if (ndemod > 1)
demod = NULL;
if (create_rf_connector) {
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn)
return -ENOMEM;
adap->conn = conn;
adap->conn_pads = kzalloc(sizeof(*adap->conn_pads), GFP_KERNEL);
if (!adap->conn_pads)
return -ENOMEM;
conn->flags = MEDIA_ENT_FL_CONNECTOR;
conn->function = MEDIA_ENT_F_CONN_RF;
conn->name = connector_name;
adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(conn, 1, adap->conn_pads);
if (ret)
return ret;
ret = media_device_register_entity(mdev, conn);
if (ret)
return ret;
if (!ntuner) {
ret = media_create_pad_links(mdev,
MEDIA_ENT_F_CONN_RF,
conn, 0,
MEDIA_ENT_F_DTV_DEMOD,
demod, 0,
MEDIA_LNK_FL_ENABLED,
false);
} else {
pad_sink = media_get_pad_index(tuner, true,
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, 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, false,
PAD_SIGNAL_ANALOG);
if (pad_source < 0)
return -EINVAL;
ret = media_create_pad_links(mdev,
MEDIA_ENT_F_TUNER,
tuner, pad_source,
MEDIA_ENT_F_DTV_DEMOD,
demod, 0, MEDIA_LNK_FL_ENABLED,
false);
if (ret)
return ret;
}
if (ndemod && demux) {
ret = media_create_pad_links(mdev,
MEDIA_ENT_F_DTV_DEMOD,
demod, 1,
MEDIA_ENT_F_TS_DEMUX,
demux, 0, MEDIA_LNK_FL_ENABLED,
false);
if (ret)
return ret;
}
if (demux && ca) {
ret = media_create_pad_link(demux, 1, ca,
0, MEDIA_LNK_FL_ENABLED);
if (ret)
return ret;
}
/* Create demux links for each ringbuffer/pad */
if (demux) {
media_device_for_each_entity(entity, mdev) {
if (entity->function == MEDIA_ENT_F_IO_DTV) {
if (!strncmp(entity->name, DVR_TSOUT,
strlen(DVR_TSOUT))) {
ret = media_create_pad_link(demux,
++dvr_pad,
entity, 0, 0);
if (ret)
return ret;
}
if (!strncmp(entity->name, DEMUX_TSOUT,
strlen(DEMUX_TSOUT))) {
ret = media_create_pad_link(demux,
++demux_pad,
entity, 0, 0);
if (ret)
return ret;
}
}
}
}
/* Create interface links for FE->tuner, DVR->demux and CA->ca */
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_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_IMMUTABLE);
if (!link)
return -ENOMEM;
}
#if 0
/*
* Indirect link - let's not create yet, as we don't know how
* to handle indirect links, nor if this will
* actually be needed.
*/
if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) {
link = media_create_intf_link(demux, intf,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (!link)
return -ENOMEM;
}
#endif
if (intf->type == MEDIA_INTF_T_DVB_DVR) {
ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT);
if (ret)
return ret;
}
if (intf->type == MEDIA_INTF_T_DVB_DEMUX) {
ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT);
if (ret)
return ret;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(dvb_create_media_graph);
#endif
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;
}
return 1;
}
static int dvbdev_get_free_adapter_num (void)
{
int num = 0;
while (num < DVB_MAX_ADAPTERS) {
if (dvbdev_check_free_adapter_num(num))
return num;
num++;
}
return -ENFILE;
}
int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
struct module *module, struct device *device,
short *adapter_nums)
{
int i, num;
mutex_lock(&dvbdev_register_lock);
for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
num = adapter_nums[i];
if (num >= 0 && num < DVB_MAX_ADAPTERS) {
/* use the one the driver asked for */
if (dvbdev_check_free_adapter_num(num))
break;
} else {
num = dvbdev_get_free_adapter_num();
break;
}
num = -1;
}
if (num < 0) {
mutex_unlock(&dvbdev_register_lock);
return -ENFILE;
}
memset (adap, 0, sizeof(struct dvb_adapter));
INIT_LIST_HEAD (&adap->device_list);
pr_info("DVB: registering new adapter (%s)\n", name);
adap->num = num;
adap->name = name;
adap->module = module;
adap->device = device;
adap->mfe_shared = 0;
adap->mfe_dvbdev = NULL;
mutex_init (&adap->mfe_lock);
#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);
return num;
}
EXPORT_SYMBOL(dvb_register_adapter);
int dvb_unregister_adapter(struct dvb_adapter *adap)
{
mutex_lock(&dvbdev_register_lock);
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 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))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *) arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
err = -ENOTTY;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
kfree(mbuf);
return err;
}
#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 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(struct device *dev, struct kobj_uevent_env *env)
{
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]);
add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
return 0;
}
static char *dvb_devnode(struct device *dev, umode_t *mode)
{
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) {
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) {
pr_err("dvb-core: unable register character device\n");
goto error;
}
dvb_class = class_create(THIS_MODULE, "dvb");
if (IS_ERR(dvb_class)) {
retval = PTR_ERR(dvb_class);
goto error;
}
dvb_class->dev_uevent = dvb_uevent;
dvb_class->devnode = dvb_devnode;
return 0;
error:
cdev_del(&dvb_device_cdev);
unregister_chrdev_region(dev, MAX_DVB_MINORS);
return retval;
}
static void __exit exit_dvbdev(void)
{
class_destroy(dvb_class);
cdev_del(&dvb_device_cdev);
unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
}
subsys_initcall(init_dvbdev);
module_exit(exit_dvbdev);
MODULE_DESCRIPTION("DVB Core Driver");
MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");