1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * vivid-radio-tx.c - radio transmitter support functions. |
4 | * |
5 | * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/errno.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/sched/signal.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/videodev2.h> |
13 | #include <linux/v4l2-dv-timings.h> |
14 | #include <media/v4l2-common.h> |
15 | #include <media/v4l2-event.h> |
16 | #include <media/v4l2-dv-timings.h> |
17 | |
18 | #include "vivid-core.h" |
19 | #include "vivid-ctrls.h" |
20 | #include "vivid-radio-common.h" |
21 | #include "vivid-radio-tx.h" |
22 | |
23 | ssize_t vivid_radio_tx_write(struct file *file, const char __user *buf, |
24 | size_t size, loff_t *offset) |
25 | { |
26 | struct vivid_dev *dev = video_drvdata(file); |
27 | struct v4l2_rds_data *data = dev->rds_gen.data; |
28 | ktime_t timestamp; |
29 | unsigned blk; |
30 | int i; |
31 | |
32 | if (dev->radio_tx_rds_controls) |
33 | return -EINVAL; |
34 | |
35 | if (size < sizeof(*data)) |
36 | return -EINVAL; |
37 | size = sizeof(*data) * (size / sizeof(*data)); |
38 | |
39 | if (mutex_lock_interruptible(&dev->mutex)) |
40 | return -ERESTARTSYS; |
41 | if (dev->radio_tx_rds_owner && |
42 | file->private_data != dev->radio_tx_rds_owner) { |
43 | mutex_unlock(lock: &dev->mutex); |
44 | return -EBUSY; |
45 | } |
46 | dev->radio_tx_rds_owner = file->private_data; |
47 | |
48 | retry: |
49 | timestamp = ktime_sub(ktime_get(), dev->radio_rds_init_time); |
50 | blk = ktime_divns(kt: timestamp, VIVID_RDS_NSEC_PER_BLK); |
51 | if (blk - VIVID_RDS_GEN_BLOCKS >= dev->radio_tx_rds_last_block) |
52 | dev->radio_tx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1; |
53 | |
54 | /* |
55 | * No data is available if there hasn't been time to get new data, |
56 | * or if the RDS receiver has been disabled, or if we use the data |
57 | * from the RDS transmitter and that RDS transmitter has been disabled, |
58 | * or if the signal quality is too weak. |
59 | */ |
60 | if (blk == dev->radio_tx_rds_last_block || |
61 | !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) { |
62 | mutex_unlock(lock: &dev->mutex); |
63 | if (file->f_flags & O_NONBLOCK) |
64 | return -EWOULDBLOCK; |
65 | if (msleep_interruptible(msecs: 20) && signal_pending(current)) |
66 | return -EINTR; |
67 | if (mutex_lock_interruptible(&dev->mutex)) |
68 | return -ERESTARTSYS; |
69 | goto retry; |
70 | } |
71 | |
72 | for (i = 0; i < size && blk > dev->radio_tx_rds_last_block; |
73 | dev->radio_tx_rds_last_block++) { |
74 | unsigned data_blk = dev->radio_tx_rds_last_block % VIVID_RDS_GEN_BLOCKS; |
75 | struct v4l2_rds_data rds; |
76 | |
77 | if (copy_from_user(to: &rds, from: buf + i, n: sizeof(rds))) { |
78 | i = -EFAULT; |
79 | break; |
80 | } |
81 | i += sizeof(rds); |
82 | if (!dev->radio_rds_loop) |
83 | continue; |
84 | if ((rds.block & V4L2_RDS_BLOCK_MSK) == V4L2_RDS_BLOCK_INVALID || |
85 | (rds.block & V4L2_RDS_BLOCK_ERROR)) |
86 | continue; |
87 | rds.block &= V4L2_RDS_BLOCK_MSK; |
88 | data[data_blk] = rds; |
89 | } |
90 | mutex_unlock(lock: &dev->mutex); |
91 | return i; |
92 | } |
93 | |
94 | __poll_t vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait) |
95 | { |
96 | return EPOLLOUT | EPOLLWRNORM | v4l2_ctrl_poll(file, wait); |
97 | } |
98 | |
99 | int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a) |
100 | { |
101 | struct vivid_dev *dev = video_drvdata(file); |
102 | |
103 | if (a->index > 0) |
104 | return -EINVAL; |
105 | |
106 | strscpy(a->name, "AM/FM/SW Transmitter" , sizeof(a->name)); |
107 | a->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | |
108 | V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS | |
109 | (dev->radio_tx_rds_controls ? |
110 | V4L2_TUNER_CAP_RDS_CONTROLS : |
111 | V4L2_TUNER_CAP_RDS_BLOCK_IO); |
112 | a->rangelow = AM_FREQ_RANGE_LOW; |
113 | a->rangehigh = FM_FREQ_RANGE_HIGH; |
114 | a->txsubchans = dev->radio_tx_subchans; |
115 | return 0; |
116 | } |
117 | |
118 | int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a) |
119 | { |
120 | struct vivid_dev *dev = video_drvdata(file); |
121 | |
122 | if (a->index) |
123 | return -EINVAL; |
124 | if (a->txsubchans & ~0x13) |
125 | return -EINVAL; |
126 | dev->radio_tx_subchans = a->txsubchans; |
127 | return 0; |
128 | } |
129 | |