aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2013-12-13 06:51:25 -0500
committerMauro Carvalho Chehab <m.chehab@samsung.com>2013-12-18 08:30:32 -0500
commit21326c461e10431767e817e858e66113336d361c (patch)
treee213e31a44f6d245c17ab8ff615878d4081e20f3
parent5df2def55073596f6f50eb3337ece6a4a60dfcf9 (diff)
[media] radio-raremono: add support for 'Thanko's Raremono' AM/FM/SW USB device
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Cc: Dinesh Ram <dinesh.ram@cern.ch> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r--drivers/media/radio/Kconfig14
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-raremono.c387
3 files changed, 402 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e92cec6fc480..192f36f2f4aa 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -129,6 +129,20 @@ config USB_KEENE
129 To compile this driver as a module, choose M here: the 129 To compile this driver as a module, choose M here: the
130 module will be called radio-keene. 130 module will be called radio-keene.
131 131
132config USB_RAREMONO
133 tristate "Thanko's Raremono AM/FM/SW radio support"
134 depends on USB && VIDEO_V4L2
135 ---help---
136 The 'Thanko's Raremono' device contains the Si4734 chip from Silicon Labs Inc.
137 It is one of the very few or perhaps the only consumer USB radio device
138 to receive the AM/FM/SW bands.
139
140 Say Y here if you want to connect this type of AM/FM/SW receiver
141 to your computer's USB port.
142
143 To compile this driver as a module, choose M here: the
144 module will be called radio-raremono.
145
132config USB_MA901 146config USB_MA901
133 tristate "Masterkit MA901 USB FM radio support" 147 tristate "Masterkit MA901 USB FM radio support"
134 depends on USB && VIDEO_V4L2 148 depends on USB && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index eb1a3a034622..120e791199b2 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
32obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o 32obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
33obj-$(CONFIG_RADIO_WL128X) += wl128x/ 33obj-$(CONFIG_RADIO_WL128X) += wl128x/
34obj-$(CONFIG_RADIO_TEA575X) += tea575x.o 34obj-$(CONFIG_RADIO_TEA575X) += tea575x.o
35obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o
35 36
36shark2-objs := radio-shark2.o radio-tea5777.o 37shark2-objs := radio-shark2.o radio-tea5777.o
37 38
diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c
new file mode 100644
index 000000000000..7b3bdbb1be73
--- /dev/null
+++ b/drivers/media/radio/radio-raremono.c
@@ -0,0 +1,387 @@
1/*
2 * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 *
4 * This program is free software; you may redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15 * SOFTWARE.
16 */
17
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/slab.h>
22#include <linux/input.h>
23#include <linux/usb.h>
24#include <linux/hid.h>
25#include <linux/mutex.h>
26#include <linux/videodev2.h>
27#include <asm/unaligned.h>
28#include <media/v4l2-device.h>
29#include <media/v4l2-ioctl.h>
30#include <media/v4l2-ctrls.h>
31#include <media/v4l2-event.h>
32
33/*
34 * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver:
35 *
36 * http://www.raremono.jp/product/484.html/
37 *
38 * The USB protocol has been reversed engineered using wireshark, initially
39 * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil
40 * <hverkuil@xs4all.nl>.
41 *
42 * Sadly the firmware used in this product hides lots of goodies since the
43 * si4734 has more features than are supported by the firmware. Oh well...
44 */
45
46/* driver and module definitions */
47MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
48MODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver");
49MODULE_LICENSE("GPL v2");
50
51/*
52 * The Device announces itself as Cygnal Integrated Products, Inc.
53 *
54 * The vendor and product IDs (and in fact all other lsusb information as
55 * well) are identical to the si470x Silicon Labs USB FM Radio Reference
56 * Design board, even though this card has a si4734 device. Clearly the
57 * designer of this product never bothered to change the USB IDs.
58 */
59
60/* USB Device ID List */
61static struct usb_device_id usb_raremono_device_table[] = {
62 {USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
63 { } /* Terminating entry */
64};
65
66MODULE_DEVICE_TABLE(usb, usb_raremono_device_table);
67
68#define BUFFER_LENGTH 64
69
70/* Timeout is set to a high value, could probably be reduced. Need more tests */
71#define USB_TIMEOUT 10000
72
73/* Frequency limits in KHz */
74#define FM_FREQ_RANGE_LOW 64000
75#define FM_FREQ_RANGE_HIGH 108000
76
77#define AM_FREQ_RANGE_LOW 520
78#define AM_FREQ_RANGE_HIGH 1710
79
80#define SW_FREQ_RANGE_LOW 2300
81#define SW_FREQ_RANGE_HIGH 26100
82
83enum { BAND_FM, BAND_AM, BAND_SW };
84
85static const struct v4l2_frequency_band bands[] = {
86 /* Band FM */
87 {
88 .type = V4L2_TUNER_RADIO,
89 .index = 0,
90 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
91 V4L2_TUNER_CAP_FREQ_BANDS,
92 .rangelow = FM_FREQ_RANGE_LOW * 16,
93 .rangehigh = FM_FREQ_RANGE_HIGH * 16,
94 .modulation = V4L2_BAND_MODULATION_FM,
95 },
96 /* Band AM */
97 {
98 .type = V4L2_TUNER_RADIO,
99 .index = 1,
100 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
101 .rangelow = AM_FREQ_RANGE_LOW * 16,
102 .rangehigh = AM_FREQ_RANGE_HIGH * 16,
103 .modulation = V4L2_BAND_MODULATION_AM,
104 },
105 /* Band SW */
106 {
107 .type = V4L2_TUNER_RADIO,
108 .index = 2,
109 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
110 .rangelow = SW_FREQ_RANGE_LOW * 16,
111 .rangehigh = SW_FREQ_RANGE_HIGH * 16,
112 .modulation = V4L2_BAND_MODULATION_AM,
113 },
114};
115
116struct raremono_device {
117 struct usb_device *usbdev;
118 struct usb_interface *intf;
119 struct video_device vdev;
120 struct v4l2_device v4l2_dev;
121 struct mutex lock;
122
123 u8 *buffer;
124 u32 band;
125 unsigned curfreq;
126};
127
128static inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev)
129{
130 return container_of(v4l2_dev, struct raremono_device, v4l2_dev);
131}
132
133/* Set frequency. */
134static int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq)
135{
136 unsigned band_offset;
137 int ret;
138
139 switch (band) {
140 case BAND_FM:
141 band_offset = 1;
142 freq /= 10;
143 break;
144 case BAND_AM:
145 band_offset = 0;
146 break;
147 default:
148 band_offset = 2;
149 break;
150 }
151 radio->buffer[0] = 0x04 + band_offset;
152 radio->buffer[1] = freq >> 8;
153 radio->buffer[2] = freq & 0xff;
154
155 ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
156 HID_REQ_SET_REPORT,
157 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
158 0x0300 + radio->buffer[0], 2,
159 radio->buffer, 3, USB_TIMEOUT);
160
161 if (ret < 0) {
162 dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
163 return ret;
164 }
165 radio->curfreq = (band == BAND_FM) ? freq * 10 : freq;
166 return 0;
167}
168
169/* Handle unplugging the device.
170 * We call video_unregister_device in any case.
171 * The last function called in this procedure is
172 * usb_raremono_device_release.
173 */
174static void usb_raremono_disconnect(struct usb_interface *intf)
175{
176 struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf));
177
178 dev_info(&intf->dev, "Thanko's Raremono disconnected\n");
179
180 mutex_lock(&radio->lock);
181 usb_set_intfdata(intf, NULL);
182 video_unregister_device(&radio->vdev);
183 v4l2_device_disconnect(&radio->v4l2_dev);
184 mutex_unlock(&radio->lock);
185 v4l2_device_put(&radio->v4l2_dev);
186}
187
188/*
189 * Linux Video interface
190 */
191static int vidioc_querycap(struct file *file, void *priv,
192 struct v4l2_capability *v)
193{
194 struct raremono_device *radio = video_drvdata(file);
195
196 strlcpy(v->driver, "radio-raremono", sizeof(v->driver));
197 strlcpy(v->card, "Thanko's Raremono", sizeof(v->card));
198 usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
199 v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
200 v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
201 return 0;
202}
203
204static int vidioc_enum_freq_bands(struct file *file, void *priv,
205 struct v4l2_frequency_band *band)
206{
207 if (band->tuner != 0)
208 return -EINVAL;
209
210 if (band->index >= ARRAY_SIZE(bands))
211 return -EINVAL;
212
213 *band = bands[band->index];
214
215 return 0;
216}
217
218static int vidioc_g_tuner(struct file *file, void *priv,
219 struct v4l2_tuner *v)
220{
221 struct raremono_device *radio = video_drvdata(file);
222 int ret;
223
224 if (v->index > 0)
225 return -EINVAL;
226
227 strlcpy(v->name, "AM/FM/SW", sizeof(v->name));
228 v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
229 V4L2_TUNER_CAP_FREQ_BANDS;
230 v->rangelow = AM_FREQ_RANGE_LOW * 16;
231 v->rangehigh = FM_FREQ_RANGE_HIGH * 16;
232 v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
233 v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ?
234 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
235 memset(radio->buffer, 1, BUFFER_LENGTH);
236 ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
237 1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
238
239 if (ret < 0) {
240 dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
241 return ret;
242 }
243 v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4;
244 return 0;
245}
246
247static int vidioc_s_tuner(struct file *file, void *priv,
248 const struct v4l2_tuner *v)
249{
250 return v->index ? -EINVAL : 0;
251}
252
253static int vidioc_s_frequency(struct file *file, void *priv,
254 const struct v4l2_frequency *f)
255{
256 struct raremono_device *radio = video_drvdata(file);
257 u32 freq = f->frequency;
258 unsigned band;
259
260 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
261 return -EINVAL;
262
263 if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8)
264 band = BAND_FM;
265 else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8)
266 band = BAND_AM;
267 else
268 band = BAND_SW;
269
270 freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh);
271 return raremono_cmd_main(radio, band, freq / 16);
272}
273
274static int vidioc_g_frequency(struct file *file, void *priv,
275 struct v4l2_frequency *f)
276{
277 struct raremono_device *radio = video_drvdata(file);
278
279 if (f->tuner != 0)
280 return -EINVAL;
281 f->type = V4L2_TUNER_RADIO;
282 f->frequency = radio->curfreq * 16;
283 return 0;
284}
285
286/* File system interface */
287static const struct v4l2_file_operations usb_raremono_fops = {
288 .owner = THIS_MODULE,
289 .open = v4l2_fh_open,
290 .release = v4l2_fh_release,
291 .unlocked_ioctl = video_ioctl2,
292};
293
294static const struct v4l2_ioctl_ops usb_raremono_ioctl_ops = {
295 .vidioc_querycap = vidioc_querycap,
296 .vidioc_g_tuner = vidioc_g_tuner,
297 .vidioc_s_tuner = vidioc_s_tuner,
298 .vidioc_g_frequency = vidioc_g_frequency,
299 .vidioc_s_frequency = vidioc_s_frequency,
300 .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
301};
302
303/* check if the device is present and register with v4l and usb if it is */
304static int usb_raremono_probe(struct usb_interface *intf,
305 const struct usb_device_id *id)
306{
307 struct raremono_device *radio;
308 int retval = 0;
309
310 radio = devm_kzalloc(&intf->dev, sizeof(struct raremono_device), GFP_KERNEL);
311 if (radio)
312 radio->buffer = devm_kmalloc(&intf->dev, BUFFER_LENGTH, GFP_KERNEL);
313
314 if (!radio || !radio->buffer)
315 return -ENOMEM;
316
317 radio->usbdev = interface_to_usbdev(intf);
318 radio->intf = intf;
319
320 /*
321 * This device uses the same USB IDs as the si470x SiLabs reference
322 * design. So do an additional check: attempt to read the device ID
323 * from the si470x: the lower 12 bits are 0x0242 for the si470x. The
324 * Raremono always returns 0x0800 (the meaning of that is unknown, but
325 * at least it works).
326 *
327 * We use this check to determine which device we are dealing with.
328 */
329 msleep(20);
330 retval = usb_control_msg(radio->usbdev,
331 usb_rcvctrlpipe(radio->usbdev, 0),
332 HID_REQ_GET_REPORT,
333 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
334 1, 2,
335 radio->buffer, 3, 500);
336 if (retval != 3 ||
337 (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) {
338 dev_info(&intf->dev, "this is not Thanko's Raremono.\n");
339 return -ENODEV;
340 }
341
342 dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n",
343 id->idVendor, id->idProduct);
344
345 retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
346 if (retval < 0) {
347 dev_err(&intf->dev, "couldn't register v4l2_device\n");
348 return retval;
349 }
350
351 mutex_init(&radio->lock);
352
353 strlcpy(radio->vdev.name, radio->v4l2_dev.name,
354 sizeof(radio->vdev.name));
355 radio->vdev.v4l2_dev = &radio->v4l2_dev;
356 radio->vdev.fops = &usb_raremono_fops;
357 radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops;
358 radio->vdev.lock = &radio->lock;
359 radio->vdev.release = video_device_release_empty;
360
361 usb_set_intfdata(intf, &radio->v4l2_dev);
362
363 video_set_drvdata(&radio->vdev, radio);
364 set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
365
366 raremono_cmd_main(radio, BAND_FM, 95160);
367
368 retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
369 if (retval == 0) {
370 dev_info(&intf->dev, "V4L2 device registered as %s\n",
371 video_device_node_name(&radio->vdev));
372 return 0;
373 }
374 dev_err(&intf->dev, "could not register video device\n");
375 v4l2_device_unregister(&radio->v4l2_dev);
376 return retval;
377}
378
379/* USB subsystem interface */
380static struct usb_driver usb_raremono_driver = {
381 .name = "radio-raremono",
382 .probe = usb_raremono_probe,
383 .disconnect = usb_raremono_disconnect,
384 .id_table = usb_raremono_device_table,
385};
386
387module_usb_driver(usb_raremono_driver);