Commit 3088fba8 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] radio-aztech: Convert to radio-isa

Tested with actual hardware and the Keene USB FM Transmitter.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent cc3c6df1
...@@ -256,15 +256,11 @@ config RADIO_RTRACK2_PORT ...@@ -256,15 +256,11 @@ config RADIO_RTRACK2_PORT
config RADIO_AZTECH config RADIO_AZTECH
tristate "Aztech/Packard Bell Radio" tristate "Aztech/Packard Bell Radio"
depends on ISA && VIDEO_V4L2 depends on ISA && VIDEO_V4L2
select RADIO_ISA
---help--- ---help---
Choose Y here if you have one of these FM radio cards, and then fill Choose Y here if you have one of these FM radio cards, and then fill
in the port address below. in the port address below.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
<file:Documentation/video4linux/API.html>.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called radio-aztech. module will be called radio-aztech.
......
/* radio-aztech.c - Aztech radio card driver for Linux 2.2 /*
* radio-aztech.c - Aztech radio card driver
* *
* Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
* Adapted to support the Video for Linux API by * Adapted to support the Video for Linux API by
* Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
...@@ -10,19 +12,7 @@ ...@@ -10,19 +12,7 @@
* Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
* William McGrath (wmcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
* *
* The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
* along with more information on the card itself.
*
* History:
* 1999-02-24 Russell Kroll <rkroll@exploits.org>
* Fine tuning/VIDEO_TUNER_LOW
* Range expanded to 87-108 MHz (from 87.9-107.8)
*
* Notable changes from the original source:
* - includes stripped down to the essentials
* - for loops used as delays replaced with udelay()
* - #defines removed, changed to static values
* - tuning structure changed - no more character arrays, other changes
*/ */
#include <linux/module.h> /* Modules */ #include <linux/module.h> /* Modules */
...@@ -33,124 +23,69 @@ ...@@ -33,124 +23,69 @@
#include <linux/io.h> /* outb, outb_p */ #include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include "radio-isa.h"
MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Aztech radio card."); MODULE_DESCRIPTION("A driver for the Aztech radio card.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.3"); MODULE_VERSION("1.0.0");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
#ifndef CONFIG_RADIO_AZTECH_PORT #ifndef CONFIG_RADIO_AZTECH_PORT
#define CONFIG_RADIO_AZTECH_PORT -1 #define CONFIG_RADIO_AZTECH_PORT -1
#endif #endif
static int io = CONFIG_RADIO_AZTECH_PORT; #define AZTECH_MAX 2
static int radio_nr = -1;
static int radio_wait_time = 1000;
module_param(io, int, 0); static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
module_param(radio_nr, int, 0); [1 ... (AZTECH_MAX - 1)] = -1 };
MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 };
static const int radio_wait_time = 1000;
struct aztech module_param_array(io, int, NULL, 0444);
{ MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
struct v4l2_device v4l2_dev; module_param_array(radio_nr, int, NULL, 0444);
struct video_device vdev; MODULE_PARM_DESC(radio_nr, "Radio device numbers");
int io;
struct aztech {
struct radio_isa_card isa;
int curvol; int curvol;
unsigned long curfreq;
int stereo;
struct mutex lock;
}; };
static struct aztech aztech_card;
static int volconvert(int level)
{
level >>= 14; /* Map 16bits down to 2 bit */
level &= 3;
/* convert to card-friendly values */
switch (level) {
case 0:
return 0;
case 1:
return 1;
case 2:
return 4;
case 3:
return 5;
}
return 0; /* Quieten gcc */
}
static void send_0_byte(struct aztech *az) static void send_0_byte(struct aztech *az)
{ {
udelay(radio_wait_time); udelay(radio_wait_time);
outb_p(2 + volconvert(az->curvol), az->io); outb_p(2 + az->curvol, az->isa.io);
outb_p(64 + 2 + volconvert(az->curvol), az->io); outb_p(64 + 2 + az->curvol, az->isa.io);
} }
static void send_1_byte(struct aztech *az) static void send_1_byte(struct aztech *az)
{ {
udelay (radio_wait_time); udelay(radio_wait_time);
outb_p(128 + 2 + volconvert(az->curvol), az->io); outb_p(128 + 2 + az->curvol, az->isa.io);
outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io); outb_p(128 + 64 + 2 + az->curvol, az->isa.io);
}
static int az_setvol(struct aztech *az, int vol)
{
mutex_lock(&az->lock);
outb(volconvert(vol), az->io);
mutex_unlock(&az->lock);
return 0;
}
/* thanks to Michael Dwyer for giving me a dose of clues in
* the signal strength department..
*
* This card has a stereo bit - bit 0 set = mono, not set = stereo
* It also has a "signal" bit - bit 1 set = bad signal, not set = good
*
*/
static int az_getsigstr(struct aztech *az)
{
int sig = 1;
mutex_lock(&az->lock);
if (inb(az->io) & 2) /* bit set = no signal present */
sig = 0;
mutex_unlock(&az->lock);
return sig;
} }
static int az_getstereo(struct aztech *az) static struct radio_isa_card *aztech_alloc(void)
{ {
int stereo = 1; struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL);
mutex_lock(&az->lock); return az ? &az->isa : NULL;
if (inb(az->io) & 1) /* bit set = mono */
stereo = 0;
mutex_unlock(&az->lock);
return stereo;
} }
static int az_setfreq(struct aztech *az, unsigned long frequency) static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq)
{ {
struct aztech *az = container_of(isa, struct aztech, isa);
int i; int i;
mutex_lock(&az->lock); freq += 171200; /* Add 10.7 MHz IF */
freq /= 800; /* Convert to 50 kHz units */
az->curfreq = frequency;
frequency += 171200; /* Add 10.7 MHz IF */
frequency /= 800; /* Convert to 50 kHz units */
send_0_byte(az); /* 0: LSB of frequency */ send_0_byte(az); /* 0: LSB of frequency */
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
if (frequency & (1 << i)) if (freq & (1 << i))
send_1_byte(az); send_1_byte(az);
else else
send_0_byte(az); send_0_byte(az);
...@@ -158,7 +93,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency) ...@@ -158,7 +93,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
send_0_byte(az); /* 14: test bit - always 0 */ send_0_byte(az); /* 14: test bit - always 0 */
send_0_byte(az); /* 15: test bit - always 0 */ send_0_byte(az); /* 15: test bit - always 0 */
send_0_byte(az); /* 16: band data 0 - always 0 */ send_0_byte(az); /* 16: band data 0 - always 0 */
if (az->stereo) /* 17: stereo (1 to enable) */ if (isa->stereo) /* 17: stereo (1 to enable) */
send_1_byte(az); send_1_byte(az);
else else
send_0_byte(az); send_0_byte(az);
...@@ -173,225 +108,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency) ...@@ -173,225 +108,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
/* latch frequency */ /* latch frequency */
udelay(radio_wait_time); udelay(radio_wait_time);
outb_p(128 + 64 + volconvert(az->curvol), az->io); outb_p(128 + 64 + az->curvol, az->isa.io);
mutex_unlock(&az->lock);
return 0;
}
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
strlcpy(v->card, "Aztech Radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct aztech *az = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
strlcpy(v->name, "FM", sizeof(v->name));
v->type = V4L2_TUNER_RADIO;
v->rangelow = 87 * 16000;
v->rangehigh = 108 * 16000;
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
v->capability = V4L2_TUNER_CAP_LOW;
if (az_getstereo(az))
v->audmode = V4L2_TUNER_MODE_STEREO;
else
v->audmode = V4L2_TUNER_MODE_MONO;
v->signal = 0xFFFF * az_getsigstr(az);
return 0;
}
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
return v->index ? -EINVAL : 0;
}
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
{
*i = 0;
return 0; return 0;
} }
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) /* thanks to Michael Dwyer for giving me a dose of clues in
{ * the signal strength department..
return i ? -EINVAL : 0; *
} * This card has a stereo bit - bit 0 set = mono, not set = stereo
*/
static int vidioc_g_audio(struct file *file, void *priv, static u32 aztech_g_rxsubchans(struct radio_isa_card *isa)
struct v4l2_audio *a)
{
a->index = 0;
strlcpy(a->name, "Radio", sizeof(a->name));
a->capability = V4L2_AUDCAP_STEREO;
return 0;
}
static int vidioc_s_audio(struct file *file, void *priv,
struct v4l2_audio *a)
{ {
return a->index ? -EINVAL : 0; if (inb(isa->io) & 1)
return V4L2_TUNER_SUB_MONO;
return V4L2_TUNER_SUB_STEREO;
} }
static int vidioc_s_frequency(struct file *file, void *priv, static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo)
struct v4l2_frequency *f)
{ {
struct aztech *az = video_drvdata(file); return aztech_s_frequency(isa, isa->freq);
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
az_setfreq(az, f->frequency);
return 0;
} }
static int vidioc_g_frequency(struct file *file, void *priv, static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
struct v4l2_frequency *f)
{ {
struct aztech *az = video_drvdata(file); struct aztech *az = container_of(isa, struct aztech, isa);
if (f->tuner != 0) if (mute)
return -EINVAL; vol = 0;
f->type = V4L2_TUNER_RADIO; az->curvol = (vol & 1) + ((vol & 2) << 1);
f->frequency = az->curfreq; outb(az->curvol, isa->io);
return 0; return 0;
} }
static int vidioc_queryctrl(struct file *file, void *priv, static const struct radio_isa_ops aztech_ops = {
struct v4l2_queryctrl *qc) .alloc = aztech_alloc,
{ .s_mute_volume = aztech_s_mute_volume,
switch (qc->id) { .s_frequency = aztech_s_frequency,
case V4L2_CID_AUDIO_MUTE: .s_stereo = aztech_s_stereo,
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); .g_rxsubchans = aztech_g_rxsubchans,
case V4L2_CID_AUDIO_VOLUME:
return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
}
return -EINVAL;
}
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct aztech *az = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (az->curvol == 0)
ctrl->value = 1;
else
ctrl->value = 0;
return 0;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = az->curvol * 6554;
return 0;
}
return -EINVAL;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct aztech *az = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value)
az_setvol(az, 0);
else
az_setvol(az, az->curvol);
return 0;
case V4L2_CID_AUDIO_VOLUME:
az_setvol(az, ctrl->value);
return 0;
}
return -EINVAL;
}
static const struct v4l2_file_operations aztech_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
}; };
static const struct v4l2_ioctl_ops aztech_ioctl_ops = { static const int aztech_ioports[] = { 0x350, 0x358 };
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner, static struct radio_isa_driver aztech_driver = {
.vidioc_s_tuner = vidioc_s_tuner, .driver = {
.vidioc_g_audio = vidioc_g_audio, .match = radio_isa_match,
.vidioc_s_audio = vidioc_s_audio, .probe = radio_isa_probe,
.vidioc_g_input = vidioc_g_input, .remove = radio_isa_remove,
.vidioc_s_input = vidioc_s_input, .driver = {
.vidioc_g_frequency = vidioc_g_frequency, .name = "radio-aztech",
.vidioc_s_frequency = vidioc_s_frequency, },
.vidioc_queryctrl = vidioc_queryctrl, },
.vidioc_g_ctrl = vidioc_g_ctrl, .io_params = io,
.vidioc_s_ctrl = vidioc_s_ctrl, .radio_nr_params = radio_nr,
.io_ports = aztech_ioports,
.num_of_io_ports = ARRAY_SIZE(aztech_ioports),
.region_size = 2,
.card = "Aztech Radio",
.ops = &aztech_ops,
.has_stereo = true,
.max_volume = 3,
}; };
static int __init aztech_init(void) static int __init aztech_init(void)
{ {
struct aztech *az = &aztech_card; return isa_register_driver(&aztech_driver.driver, AZTECH_MAX);
struct v4l2_device *v4l2_dev = &az->v4l2_dev;
int res;
strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
az->io = io;
if (az->io == -1) {
v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
return -EINVAL;
}
if (!request_region(az->io, 2, "aztech")) {
v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
return -EBUSY;
}
res = v4l2_device_register(NULL, v4l2_dev);
if (res < 0) {
release_region(az->io, 2);
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
return res;
}
mutex_init(&az->lock);
strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
az->vdev.v4l2_dev = v4l2_dev;
az->vdev.fops = &aztech_fops;
az->vdev.ioctl_ops = &aztech_ioctl_ops;
az->vdev.release = video_device_release_empty;
video_set_drvdata(&az->vdev, az);
/* mute card - prevents noisy bootups */
outb(0, az->io);
if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
v4l2_device_unregister(v4l2_dev);
release_region(az->io, 2);
return -EINVAL;
}
v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
return 0;
} }
static void __exit aztech_exit(void) static void __exit aztech_exit(void)
{ {
struct aztech *az = &aztech_card; isa_unregister_driver(&aztech_driver.driver);
video_unregister_device(&az->vdev);
v4l2_device_unregister(&az->v4l2_dev);
release_region(az->io, 2);
} }
module_init(aztech_init); module_init(aztech_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment