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

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

Tested with actual hardware (up to two cards) 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 3088fba8
...@@ -276,6 +276,7 @@ config RADIO_AZTECH_PORT ...@@ -276,6 +276,7 @@ config RADIO_AZTECH_PORT
config RADIO_GEMTEK config RADIO_GEMTEK
tristate "GemTek Radio card (or compatible) support" tristate "GemTek Radio card (or compatible) support"
depends on ISA && VIDEO_V4L2 depends on ISA && VIDEO_V4L2
select RADIO_ISA
---help--- ---help---
Choose Y here if you have this FM radio card, and then fill in the Choose Y here if you have this FM radio card, and then fill in the
I/O port address and settings below. The following cards either have I/O port address and settings below. The following cards either have
...@@ -285,23 +286,21 @@ config RADIO_GEMTEK ...@@ -285,23 +286,21 @@ config RADIO_GEMTEK
- Typhoon Radio card (some models) - Typhoon Radio card (some models)
- Hama Radio card - Hama Radio card
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-gemtek. module will be called radio-gemtek.
config RADIO_GEMTEK_PORT config RADIO_GEMTEK_PORT
hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)" hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)"
depends on RADIO_GEMTEK=y depends on RADIO_GEMTEK=y
default "34c" default "34c"
help help
Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The
0x34c, if you haven't changed the jumper setting on the card. On card default is 0x34c, if you haven't changed the jumper setting
Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O on the card.
On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
port is 0x20c, 0x248 or 0x28c. port is 0x20c, 0x248 or 0x28c.
If automatic I/O port probing is enabled this port will be used only If automatic I/O port probing is enabled this port will be used only
in case of automatic probing failure, ie. as a fallback. in case of automatic probing failure, ie. as a fallback.
......
/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> /*
* GemTek radio card driver
*
* Copyright 1998 Jonas Munsin <jmunsin@iki.fi>
* *
* GemTek hasn't released any specs on the card, so the protocol had to * GemTek hasn't released any specs on the card, so the protocol had to
* be reverse engineered with dosemu. * be reverse engineered with dosemu.
...@@ -11,9 +14,12 @@ ...@@ -11,9 +14,12 @@
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
* *
* TODO: Allow for more than one of these foolish entities :-) * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
*
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*
* Note: this card seems to swap the left and right audio channels!
*
* Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/ */
#include <linux/module.h> /* Modules */ #include <linux/module.h> /* Modules */
...@@ -25,6 +31,7 @@ ...@@ -25,6 +31,7 @@
#include <linux/io.h> /* outb, outb_p */ #include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include "radio-isa.h"
/* /*
* Module info. * Module info.
...@@ -33,7 +40,7 @@ ...@@ -33,7 +40,7 @@
MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
MODULE_DESCRIPTION("A driver for the GemTek Radio card."); MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.4"); MODULE_VERSION("1.0.0");
/* /*
* Module params. * Module params.
...@@ -46,45 +53,29 @@ MODULE_VERSION("0.0.4"); ...@@ -46,45 +53,29 @@ MODULE_VERSION("0.0.4");
#define CONFIG_RADIO_GEMTEK_PROBE 1 #define CONFIG_RADIO_GEMTEK_PROBE 1
#endif #endif
static int io = CONFIG_RADIO_GEMTEK_PORT; #define GEMTEK_MAX 4
static bool probe = CONFIG_RADIO_GEMTEK_PROBE; static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
static bool hardmute; static bool hardmute;
static bool shutdown = 1; static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT,
static bool keepmuted = 1; [1 ... (GEMTEK_MAX - 1)] = -1 };
static bool initmute = 1; static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 };
static int radio_nr = -1;
module_param(io, int, 0444);
MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
"probing is disabled or fails. The most common I/O ports are: 0x20c "
"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
"work for the combined sound/radiocard).");
module_param(probe, bool, 0444); module_param(probe, bool, 0444);
MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most " MODULE_PARM_DESC(probe, "Enable automatic device probing.");
"common I/O ports used by the card are probed.");
module_param(hardmute, bool, 0644); module_param(hardmute, bool, 0644);
MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may " MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may "
"reduce static noise."); "reduce static noise.");
module_param(shutdown, bool, 0644); module_param_array(io, int, NULL, 0444);
MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when " MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic "
"module is unloaded."); "probing is disabled or fails. The most common I/O ports are: 0x20c "
"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
module_param(keepmuted, bool, 0644); "work for the combined sound/radiocard).");
MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");
module_param(initmute, bool, 0444);
MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");
module_param(radio_nr, int, 0444);
/* module_param_array(radio_nr, int, NULL, 0444);
* Functions for controlling the card. MODULE_PARM_DESC(radio_nr, "Radio device numbers");
*/
#define GEMTEK_LOWFREQ (87*16000)
#define GEMTEK_HIGHFREQ (108*16000)
/* /*
* Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal * Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal
...@@ -108,18 +99,11 @@ module_param(radio_nr, int, 0444); ...@@ -108,18 +99,11 @@ module_param(radio_nr, int, 0444);
#define LONG_DELAY 75 /* usec */ #define LONG_DELAY 75 /* usec */
struct gemtek { struct gemtek {
struct v4l2_device v4l2_dev; struct radio_isa_card isa;
struct video_device vdev; bool muted;
struct mutex lock;
unsigned long lastfreq;
int muted;
int verified;
int io;
u32 bu2614data; u32 bu2614data;
}; };
static struct gemtek gemtek_card;
#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
#define BU2614_VOID_BITS 4 /* unused */ #define BU2614_VOID_BITS 4 /* unused */
...@@ -166,31 +150,24 @@ static struct gemtek gemtek_card; ...@@ -166,31 +150,24 @@ static struct gemtek gemtek_card;
*/ */
static void gemtek_bu2614_transmit(struct gemtek *gt) static void gemtek_bu2614_transmit(struct gemtek *gt)
{ {
struct radio_isa_card *isa = &gt->isa;
int i, bit, q, mute; int i, bit, q, mute;
mutex_lock(&gt->lock);
mute = gt->muted ? GEMTEK_MT : 0x00; mute = gt->muted ? GEMTEK_MT : 0x00;
outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY);
outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
udelay(LONG_DELAY); udelay(LONG_DELAY);
for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
bit = (q & 1) ? GEMTEK_DA : 0; bit = (q & 1) ? GEMTEK_DA : 0;
outb_p(mute | GEMTEK_CE | bit, gt->io); outb_p(mute | GEMTEK_CE | bit, isa->io);
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io); outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
} }
outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
udelay(LONG_DELAY);
mutex_unlock(&gt->lock);
} }
/* /*
...@@ -198,21 +175,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt) ...@@ -198,21 +175,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt)
*/ */
static unsigned long gemtek_convfreq(unsigned long freq) static unsigned long gemtek_convfreq(unsigned long freq)
{ {
return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ; return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ;
}
static struct radio_isa_card *gemtek_alloc(void)
{
struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL);
if (gt)
gt->muted = true;
return gt ? &gt->isa : NULL;
} }
/* /*
* Set FM-frequency. * Set FM-frequency.
*/ */
static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq)
{ {
if (keepmuted && hardmute && gt->muted) struct gemtek *gt = container_of(isa, struct gemtek, isa);
return;
freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
gt->lastfreq = freq; if (hardmute && gt->muted)
gt->muted = 0; return 0;
gemtek_bu2614_set(gt, BU2614_PORT, 0); gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0); gemtek_bu2614_set(gt, BU2614_FMES, 0);
...@@ -220,23 +203,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) ...@@ -220,23 +203,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
gemtek_bu2614_set(gt, BU2614_SWAL, 0); gemtek_bu2614_set(gt, BU2614_SWAL, 0);
gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */
gemtek_bu2614_set(gt, BU2614_TEST, 0); gemtek_bu2614_set(gt, BU2614_TEST, 0);
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
gemtek_bu2614_transmit(gt); gemtek_bu2614_transmit(gt);
return 0;
} }
/* /*
* Set mute flag. * Set mute flag.
*/ */
static void gemtek_mute(struct gemtek *gt) static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{ {
struct gemtek *gt = container_of(isa, struct gemtek, isa);
int i; int i;
gt->muted = 1; gt->muted = mute;
if (hardmute) { if (hardmute) {
if (!mute)
return gemtek_s_frequency(isa, isa->freq);
/* Turn off PLL, disable data output */ /* Turn off PLL, disable data output */
gemtek_bu2614_set(gt, BU2614_PORT, 0); gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */
...@@ -247,367 +232,85 @@ static void gemtek_mute(struct gemtek *gt) ...@@ -247,367 +232,85 @@ static void gemtek_mute(struct gemtek *gt)
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
gemtek_bu2614_set(gt, BU2614_FREQ, 0); gemtek_bu2614_set(gt, BU2614_FREQ, 0);
gemtek_bu2614_transmit(gt); gemtek_bu2614_transmit(gt);
return; return 0;
} }
mutex_lock(&gt->lock);
/* Read bus contents (CE, CK and DA). */ /* Read bus contents (CE, CK and DA). */
i = inb_p(gt->io); i = inb_p(isa->io);
/* Write it back with mute flag set. */ /* Write it back with mute flag set. */
outb_p((i >> 5) | GEMTEK_MT, gt->io); outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io);
udelay(SHORT_DELAY);
mutex_unlock(&gt->lock);
}
/*
* Unset mute flag.
*/
static void gemtek_unmute(struct gemtek *gt)
{
int i;
gt->muted = 0;
if (hardmute) {
/* Turn PLL back on. */
gemtek_setfreq(gt, gt->lastfreq);
return;
}
mutex_lock(&gt->lock);
i = inb_p(gt->io);
outb_p(i >> 5, gt->io);
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
return 0;
mutex_unlock(&gt->lock);
} }
/* static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa)
* Get signal strength (= stereo status).
*/
static inline int gemtek_getsigstr(struct gemtek *gt)
{ {
int sig; if (inb_p(isa->io) & GEMTEK_NS)
return V4L2_TUNER_SUB_MONO;
mutex_lock(&gt->lock); return V4L2_TUNER_SUB_STEREO;
sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
mutex_unlock(&gt->lock);
return sig;
} }
/* /*
* Check if requested card acts like GemTek Radio card. * Check if requested card acts like GemTek Radio card.
*/ */
static int gemtek_verify(struct gemtek *gt, int port) static bool gemtek_probe(struct radio_isa_card *isa, int io)
{ {
int i, q; int i, q;
if (gt->verified == port) q = inb_p(io); /* Read bus contents before probing. */
return 1;
mutex_lock(&gt->lock);
q = inb_p(port); /* Read bus contents before probing. */
/* Try to turn on CE, CK and DA respectively and check if card responds /* Try to turn on CE, CK and DA respectively and check if card responds
properly. */ properly. */
for (i = 0; i < 3; ++i) { for (i = 0; i < 3; ++i) {
outb_p(1 << i, port); outb_p(1 << i, io);
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5))))
mutex_unlock(&gt->lock); return false;
return 0;
}
} }
outb_p(q >> 5, port); /* Write bus contents back. */ outb_p(q >> 5, io); /* Write bus contents back. */
udelay(SHORT_DELAY); udelay(SHORT_DELAY);
return true;
mutex_unlock(&gt->lock);
gt->verified = port;
return 1;
} }
/* static const struct radio_isa_ops gemtek_ops = {
* Automatic probing for card. .alloc = gemtek_alloc,
*/ .probe = gemtek_probe,
static int gemtek_probe(struct gemtek *gt) .s_mute_volume = gemtek_s_mute_volume,
{ .s_frequency = gemtek_s_frequency,
struct v4l2_device *v4l2_dev = &gt->v4l2_dev; .g_rxsubchans = gemtek_g_rxsubchans,
int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
int i;
if (!probe) {
v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");
return -1;
}
v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");
for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);
if (!request_region(ioports[i], 1, "gemtek-probe")) {
v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",
ioports[i]);
continue;
}
if (gemtek_verify(gt, ioports[i])) {
v4l2_info(v4l2_dev, "Card found from I/O port "
"0x%x!\n", ioports[i]);
release_region(ioports[i], 1);
gt->io = ioports[i];
return gt->io;
}
release_region(ioports[i], 1);
}
v4l2_err(v4l2_dev, "Automatic probing failed!\n");
return -1;
}
/*
* Video 4 Linux stuff.
*/
static const struct v4l2_file_operations gemtek_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
}; };
static int vidioc_querycap(struct file *file, void *priv, static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
struct v4l2_capability *v)
{ static struct radio_isa_driver gemtek_driver = {
strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); .driver = {
strlcpy(v->card, "GemTek", sizeof(v->card)); .match = radio_isa_match,
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); .probe = radio_isa_probe,
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; .remove = radio_isa_remove,
return 0; .driver = {
} .name = "radio-gemtek",
},
static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) },
{ .io_params = io,
struct gemtek *gt = video_drvdata(file); .radio_nr_params = radio_nr,
.io_ports = gemtek_ioports,
if (v->index > 0) .num_of_io_ports = ARRAY_SIZE(gemtek_ioports),
return -EINVAL; .region_size = 1,
.card = "GemTek Radio",
strlcpy(v->name, "FM", sizeof(v->name)); .ops = &gemtek_ops,
v->type = V4L2_TUNER_RADIO; .has_stereo = true,
v->rangelow = GEMTEK_LOWFREQ;
v->rangehigh = GEMTEK_HIGHFREQ;
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
v->signal = 0xffff * gemtek_getsigstr(gt);
if (v->signal) {
v->audmode = V4L2_TUNER_MODE_STEREO;
v->rxsubchans = V4L2_TUNER_SUB_STEREO;
} else {
v->audmode = V4L2_TUNER_MODE_MONO;
v->rxsubchans = V4L2_TUNER_SUB_MONO;
}
return 0;
}
static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
{
return (v->index != 0) ? -EINVAL : 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct gemtek *gt = video_drvdata(file);
if (f->tuner != 0)
return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = gt->lastfreq;
return 0;
}
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct gemtek *gt = video_drvdata(file);
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
gemtek_setfreq(gt, f->frequency);
return 0;
}
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
switch (qc->id) {
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
default:
return -EINVAL;
}
}
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct gemtek *gt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = gt->muted;
return 0;
}
return -EINVAL;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct gemtek *gt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value)
gemtek_mute(gt);
else
gemtek_unmute(gt);
return 0;
}
return -EINVAL;
}
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
{
*i = 0;
return 0;
}
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
{
return (i != 0) ? -EINVAL : 0;
}
static int vidioc_g_audio(struct file *file, void *priv, 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 != 0) ? -EINVAL : 0;
}
static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl
}; };
/*
* Initialization / cleanup related stuff.
*/
static int __init gemtek_init(void) static int __init gemtek_init(void)
{ {
struct gemtek *gt = &gemtek_card; gemtek_driver.probe = probe;
struct v4l2_device *v4l2_dev = &gt->v4l2_dev; return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
int res;
strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");
mutex_init(&gt->lock);
gt->verified = -1;
gt->io = io;
gemtek_probe(gt);
if (gt->io) {
if (!request_region(gt->io, 1, "gemtek")) {
v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);
return -EBUSY;
}
if (!gemtek_verify(gt, gt->io))
v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
"respond properly, check your "
"configuration.\n", gt->io);
else
v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);
} else if (probe) {
v4l2_err(v4l2_dev, "Automatic probing failed and no "
"fixed I/O port defined.\n");
return -ENODEV;
} else {
v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
"I/O port defined.");
return -EINVAL;
}
res = v4l2_device_register(NULL, v4l2_dev);
if (res < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
release_region(gt->io, 1);
return res;
}
strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
gt->vdev.v4l2_dev = v4l2_dev;
gt->vdev.fops = &gemtek_fops;
gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
gt->vdev.release = video_device_release_empty;
video_set_drvdata(&gt->vdev, gt);
/* Set defaults */
gt->lastfreq = GEMTEK_LOWFREQ;
gt->bu2614data = 0;
if (initmute)
gemtek_mute(gt);
if (video_register_device(&gt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
v4l2_device_unregister(v4l2_dev);
release_region(gt->io, 1);
return -EBUSY;
}
return 0;
} }
/*
* Module cleanup
*/
static void __exit gemtek_exit(void) static void __exit gemtek_exit(void)
{ {
struct gemtek *gt = &gemtek_card;
struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
if (shutdown) {
hardmute = 1; /* Turn off PLL */ hardmute = 1; /* Turn off PLL */
gemtek_mute(gt); isa_unregister_driver(&gemtek_driver.driver);
} else {
v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");
}
video_unregister_device(&gt->vdev);
v4l2_device_unregister(&gt->v4l2_dev);
release_region(gt->io, 1);
} }
module_init(gemtek_init); module_init(gemtek_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