* PnP support for the new ISA radio framework?
@ 2012-02-18 16:33 Ondrej Zary
2012-02-22 20:33 ` Ondrej Zary
0 siblings, 1 reply; 4+ messages in thread
From: Ondrej Zary @ 2012-02-18 16:33 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media
Hello,
there are some ISA radio cards with PnP support (e.g. SF16-FMI) but the new
ISA radio framework has no PnP support.
I got AOpen FX-3D/Pro Radio card which is AD1816 with Gemtek radio - and with
PnP. But radio-gemtek fails to load because the radio I/O port is not enabled
(and the driver does not support PnP).
Tried to add PnP support to radio-isa but failed. Splitted non-isa_driver
related parts from radio_isa_probe() to a separate function and tried to
create radio_isa_pnp_probe() only to realize that I'm not able to access
struct radio_isa_driver.
radio_isa_probe() relies on the fact that "driver" (struct isa_driver) is the
first element of struct radio_isa_driver, so these two structs have the same
pointer:
HW radio driver registers the driver by calling:
isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
radio_isa_probe() in radio-isa.c does:
struct radio_isa_driver *drv = pdev->platform_data;
So adding struct pnp_driver to struct radio_isa_driver does not seem to be
possible.
--
Ondrej Zary
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: PnP support for the new ISA radio framework?
2012-02-18 16:33 PnP support for the new ISA radio framework? Ondrej Zary
@ 2012-02-22 20:33 ` Ondrej Zary
2012-02-24 9:35 ` Hans Verkuil
0 siblings, 1 reply; 4+ messages in thread
From: Ondrej Zary @ 2012-02-22 20:33 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media
On Saturday 18 February 2012 17:33:32 Ondrej Zary wrote:
> Hello,
> there are some ISA radio cards with PnP support (e.g. SF16-FMI) but the new
> ISA radio framework has no PnP support.
>
> I got AOpen FX-3D/Pro Radio card which is AD1816 with Gemtek radio - and
> with PnP. But radio-gemtek fails to load because the radio I/O port is not
> enabled (and the driver does not support PnP).
>
> Tried to add PnP support to radio-isa but failed. Splitted non-isa_driver
> related parts from radio_isa_probe() to a separate function and tried to
> create radio_isa_pnp_probe() only to realize that I'm not able to access
> struct radio_isa_driver.
>
> radio_isa_probe() relies on the fact that "driver" (struct isa_driver) is
> the first element of struct radio_isa_driver, so these two structs have the
> same pointer:
> HW radio driver registers the driver by calling:
> isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
> radio_isa_probe() in radio-isa.c does:
> struct radio_isa_driver *drv = pdev->platform_data;
>
> So adding struct pnp_driver to struct radio_isa_driver does not seem to be
> possible.
Adding PnP support to original radio-gemtek (before conversion to ISA radio
framework) is easy. A patch like this (mostly copied from radio-cadet) allows
radio on AOpen FX-3D/Pro Radio card to work.
But how to do this with the new driver?
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -23,6 +23,7 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/pnp.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
@@ -329,6 +330,46 @@ static int gemtek_verify(struct gemtek *gt, int port)
return 1;
}
+#ifdef CONFIG_PNP
+
+static struct pnp_device_id gemtek_pnp_devices[] = {
+ /* AOpen FX-3D/Pro Radio */
+ {.id = "ADS7183", .driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices);
+
+static int gemtek_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+{
+ if (!dev)
+ return -ENODEV;
+ /* only support one device */
+ if (io > 0)
+ return -EBUSY;
+
+ if (!pnp_port_valid(dev, 0))
+ return -ENODEV;
+
+ io = pnp_port_start(dev, 0);
+
+ printk(KERN_INFO "radio-gemtek: PnP reports device at %#x\n", io);
+
+ return io;
+}
+
+static struct pnp_driver gemtek_pnp_driver = {
+ .name = "radio-gemtek",
+ .id_table = gemtek_pnp_devices,
+ .probe = gemtek_pnp_probe,
+ .remove = NULL,
+};
+
+#else
+static struct pnp_driver gemtek_pnp_driver;
+#endif
+
+
/*
* Automatic probing for card.
*/
@@ -536,8 +577,11 @@ static int __init gemtek_init(void)
mutex_init(>->lock);
gt->verified = -1;
+ if (io < 0)
+ pnp_register_driver(&gemtek_pnp_driver);
+ else
+ gemtek_probe(gt);
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);
@@ -608,6 +652,7 @@ static void __exit gemtek_exit(void)
video_unregister_device(>->vdev);
v4l2_device_unregister(>->v4l2_dev);
release_region(gt->io, 1);
+ pnp_unregister_driver(&gemtek_pnp_driver);
}
module_init(gemtek_init);
--
Ondrej Zary
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: PnP support for the new ISA radio framework?
2012-02-22 20:33 ` Ondrej Zary
@ 2012-02-24 9:35 ` Hans Verkuil
2012-02-28 21:08 ` [RFC PATCH] PnP support for the new ISA radio framework Ondrej Zary
0 siblings, 1 reply; 4+ messages in thread
From: Hans Verkuil @ 2012-02-24 9:35 UTC (permalink / raw)
To: Ondrej Zary; +Cc: linux-media
On Wednesday, February 22, 2012 21:33:29 Ondrej Zary wrote:
> On Saturday 18 February 2012 17:33:32 Ondrej Zary wrote:
> > Hello,
> > there are some ISA radio cards with PnP support (e.g. SF16-FMI) but the new
> > ISA radio framework has no PnP support.
> >
> > I got AOpen FX-3D/Pro Radio card which is AD1816 with Gemtek radio - and
> > with PnP. But radio-gemtek fails to load because the radio I/O port is not
> > enabled (and the driver does not support PnP).
> >
> > Tried to add PnP support to radio-isa but failed. Splitted non-isa_driver
> > related parts from radio_isa_probe() to a separate function and tried to
> > create radio_isa_pnp_probe() only to realize that I'm not able to access
> > struct radio_isa_driver.
> >
> > radio_isa_probe() relies on the fact that "driver" (struct isa_driver) is
> > the first element of struct radio_isa_driver, so these two structs have the
> > same pointer:
> > HW radio driver registers the driver by calling:
> > isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
> > radio_isa_probe() in radio-isa.c does:
> > struct radio_isa_driver *drv = pdev->platform_data;
> >
> > So adding struct pnp_driver to struct radio_isa_driver does not seem to be
> > possible.
>
> Adding PnP support to original radio-gemtek (before conversion to ISA radio
> framework) is easy. A patch like this (mostly copied from radio-cadet) allows
> radio on AOpen FX-3D/Pro Radio card to work.
> But how to do this with the new driver?
I've been looking at that and I think the best way is to add radio_isa_pnp_probe
and radio_isa_pnp_remove functions that can be used with the pnp framework.
Both radio_isa_probe and radio_isa_pnp_probe call the same probe() function that
gets the radio_isa_driver pointer.
The struct radio_isa_driver should probably be modified as follows:
struct radio_isa_driver {
struct isa_driver *isa_drv;
struct pnp_driver *pnp_drv;
const struct radio_isa_ops *ops;
...
Or perhaps even better:
struct radio_isa_driver {
union {
struct isa_driver *isa_drv;
struct pnp_driver *pnp_drv;
};
const struct radio_isa_ops *ops;
...
radio_isa_querycap uses driver.name, so I suspect struct radio_isa_driver
should be extended with a const char *name that is set correctly by either
radio_isa_probe or radio_isa_pnp_probe.
Another alternative seems to be what radio-sf16fmi.c does. It uses low-level
pnp functions but not pnp_driver. This approach should fit easily into the
radio-isa framework.
Regards,
Hans
>
>
> --- a/drivers/media/radio/radio-gemtek.c
> +++ b/drivers/media/radio/radio-gemtek.c
> @@ -23,6 +23,7 @@
> #include <linux/videodev2.h> /* kernel radio structs */
> #include <linux/mutex.h>
> #include <linux/io.h> /* outb, outb_p */
> +#include <linux/pnp.h>
> #include <media/v4l2-ioctl.h>
> #include <media/v4l2-device.h>
>
> @@ -329,6 +330,46 @@ static int gemtek_verify(struct gemtek *gt, int port)
> return 1;
> }
>
> +#ifdef CONFIG_PNP
> +
> +static struct pnp_device_id gemtek_pnp_devices[] = {
> + /* AOpen FX-3D/Pro Radio */
> + {.id = "ADS7183", .driver_data = 0},
> + {.id = ""}
> +};
> +
> +MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices);
> +
> +static int gemtek_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
> +{
> + if (!dev)
> + return -ENODEV;
> + /* only support one device */
> + if (io > 0)
> + return -EBUSY;
> +
> + if (!pnp_port_valid(dev, 0))
> + return -ENODEV;
> +
> + io = pnp_port_start(dev, 0);
> +
> + printk(KERN_INFO "radio-gemtek: PnP reports device at %#x\n", io);
> +
> + return io;
> +}
> +
> +static struct pnp_driver gemtek_pnp_driver = {
> + .name = "radio-gemtek",
> + .id_table = gemtek_pnp_devices,
> + .probe = gemtek_pnp_probe,
> + .remove = NULL,
> +};
> +
> +#else
> +static struct pnp_driver gemtek_pnp_driver;
> +#endif
> +
> +
> /*
> * Automatic probing for card.
> */
> @@ -536,8 +577,11 @@ static int __init gemtek_init(void)
> mutex_init(>->lock);
>
> gt->verified = -1;
> + if (io < 0)
> + pnp_register_driver(&gemtek_pnp_driver);
> + else
> + gemtek_probe(gt);
> 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);
> @@ -608,6 +652,7 @@ static void __exit gemtek_exit(void)
> video_unregister_device(>->vdev);
> v4l2_device_unregister(>->v4l2_dev);
> release_region(gt->io, 1);
> + pnp_unregister_driver(&gemtek_pnp_driver);
> }
>
> module_init(gemtek_init);
>
>
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFC PATCH] PnP support for the new ISA radio framework
2012-02-24 9:35 ` Hans Verkuil
@ 2012-02-28 21:08 ` Ondrej Zary
0 siblings, 0 replies; 4+ messages in thread
From: Ondrej Zary @ 2012-02-28 21:08 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media
Hello,
this is the first attempt to add PnP support to the new ISA radio framework.
I don't like the region_size function parameter - it's needed because PnP
reports longer port range than drv->region_size.
There is a small patch to radio-gemtek at the end that uses this PnP support
for AOpen FX-3D/Pro Radio card (it works).
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
index 02bcead..8722728 100644
--- a/drivers/media/radio/radio-isa.c
+++ b/drivers/media/radio/radio-isa.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
@@ -198,56 +199,31 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
return false;
}
-int radio_isa_probe(struct device *pdev, unsigned int dev)
+struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv,
+ struct device *pdev)
{
- struct radio_isa_driver *drv = pdev->platform_data;
- const struct radio_isa_ops *ops = drv->ops;
struct v4l2_device *v4l2_dev;
- struct radio_isa_card *isa;
- int res;
+ struct radio_isa_card *isa = drv->ops->alloc();
+ if (!isa)
+ return NULL;
- isa = drv->ops->alloc();
- if (isa == NULL)
- return -ENOMEM;
dev_set_drvdata(pdev, isa);
isa->drv = drv;
- isa->io = drv->io_params[dev];
v4l2_dev = &isa->v4l2_dev;
strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name));
- if (drv->probe && ops->probe) {
- int i;
-
- for (i = 0; i < drv->num_of_io_ports; ++i) {
- int io = drv->io_ports[i];
-
- if (request_region(io, drv->region_size, v4l2_dev->name)) {
- bool found = ops->probe(isa, io);
-
- release_region(io, drv->region_size);
- if (found) {
- isa->io = io;
- break;
- }
- }
- }
- }
-
- if (!radio_isa_valid_io(drv, isa->io)) {
- int i;
+ return isa;
+}
- if (isa->io < 0)
- return -ENODEV;
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
- drv->io_ports[0]);
- for (i = 1; i < drv->num_of_io_ports; i++)
- printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
- printk(KERN_CONT ".\n");
- kfree(isa);
- return -EINVAL;
- }
+int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev,
+ int radio_nr, unsigned region_size)
+{
+ const struct radio_isa_driver *drv = isa->drv;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
+ int res;
- if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
+ if (!request_region(isa->io, region_size, v4l2_dev->name)) {
v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
kfree(isa);
return -EBUSY;
@@ -300,8 +276,8 @@ int radio_isa_probe(struct device *pdev, unsigned int dev)
v4l2_err(v4l2_dev, "Could not setup card\n");
goto err_node_reg;
}
- res = video_register_device(&isa->vdev, VFL_TYPE_RADIO,
- drv->radio_nr_params[dev]);
+ res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr);
+
if (res < 0) {
v4l2_err(v4l2_dev, "Could not register device node\n");
goto err_node_reg;
@@ -316,24 +292,107 @@ err_node_reg:
err_hdl:
v4l2_device_unregister(&isa->v4l2_dev);
err_dev_reg:
- release_region(isa->io, drv->region_size);
+ release_region(isa->io, region_size);
kfree(isa);
return res;
}
+
+int radio_isa_probe(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev;
+ struct radio_isa_card *isa;
+
+ isa = radio_isa_alloc(drv, pdev);
+ if (!isa)
+ return -ENOMEM;
+ isa->io = drv->io_params[dev];
+ v4l2_dev = &isa->v4l2_dev;
+
+ if (drv->probe && ops->probe) {
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; ++i) {
+ int io = drv->io_ports[i];
+
+ if (request_region(io, drv->region_size, v4l2_dev->name)) {
+ bool found = ops->probe(isa, io);
+
+ release_region(io, drv->region_size);
+ if (found) {
+ isa->io = io;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!radio_isa_valid_io(drv, isa->io)) {
+ int i;
+
+ if (isa->io < 0)
+ return -ENODEV;
+ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
+ drv->io_ports[0]);
+ for (i = 1; i < drv->num_of_io_ports; i++)
+ printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
+ printk(KERN_CONT ".\n");
+ kfree(isa);
+ return -EINVAL;
+ }
+
+ return radio_isa_common_probe(isa, pdev, drv->radio_nr_params[dev],
+ drv->region_size);
+}
EXPORT_SYMBOL_GPL(radio_isa_probe);
-int radio_isa_remove(struct device *pdev, unsigned int dev)
+int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size)
{
- struct radio_isa_card *isa = dev_get_drvdata(pdev);
const struct radio_isa_ops *ops = isa->drv->ops;
ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0);
video_unregister_device(&isa->vdev);
v4l2_ctrl_handler_free(&isa->hdl);
v4l2_device_unregister(&isa->v4l2_dev);
- release_region(isa->io, isa->drv->region_size);
+ release_region(isa->io, region_size);
v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
kfree(isa);
return 0;
}
+
+#ifdef CONFIG_PNP
+int radio_isa_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+{
+ struct pnp_driver *pnp_drv = to_pnp_driver(dev->dev.driver);
+ struct radio_isa_driver *drv = container_of(pnp_drv,
+ struct radio_isa_driver, pnp_driver);
+ struct radio_isa_card *isa;
+
+ if (!pnp_port_valid(dev, 0))
+ return -ENODEV;
+
+ isa = radio_isa_alloc(drv, &dev->dev);
+ if (!isa)
+ return -ENOMEM;
+
+ isa->io = pnp_port_start(dev, 0);
+
+ return radio_isa_common_probe(isa, &dev->dev, 0, pnp_port_len(dev, 0));
+}
+EXPORT_SYMBOL_GPL(radio_isa_pnp_probe);
+
+int radio_isa_pnp_remove(struct pnp_dev *dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(&dev->dev);
+ return radio_isa_common_remove(isa, pnp_port_len(dev, 0));
+}
+EXPORT_SYMBOL_GPL(radio_isa_pnp_remove);
+#endif
+
+int radio_isa_remove(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(pdev);
+ return radio_isa_common_remove(isa, isa->drv->region_size);
+}
EXPORT_SYMBOL_GPL(radio_isa_remove);
diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h
index 8a0ea84..0e7dc25 100644
--- a/drivers/media/radio/radio-isa.h
+++ b/drivers/media/radio/radio-isa.h
@@ -24,6 +24,7 @@
#define _RADIO_ISA_H_
#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
@@ -76,6 +77,9 @@ struct radio_isa_ops {
/* Top level structure needed to instantiate the cards */
struct radio_isa_driver {
struct isa_driver driver;
+#ifdef CONFIG_PNP
+ struct pnp_driver pnp_driver;
+#endif
const struct radio_isa_ops *ops;
/* The module_param_array with the specified I/O ports */
int *io_params;
@@ -101,5 +105,10 @@ struct radio_isa_driver {
int radio_isa_match(struct device *pdev, unsigned int dev);
int radio_isa_probe(struct device *pdev, unsigned int dev);
int radio_isa_remove(struct device *pdev, unsigned int dev);
+#ifdef CONFIG_PNP
+int radio_isa_pnp_probe(struct pnp_dev *dev,
+ const struct pnp_device_id *dev_id);
+int radio_isa_pnp_remove(struct pnp_dev *dev);
+#endif
#endif
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 9d7fdae..6ea0e23 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -29,6 +29,8 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/pnp.h>
+#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include "radio-isa.h"
@@ -282,6 +284,16 @@ static const struct radio_isa_ops gemtek_ops = {
static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
+#ifdef CONFIG_PNP
+static struct pnp_device_id gemtek_pnp_devices[] = {
+ /* AOpen FX-3D/Pro Radio */
+ {.id = "ADS7183", .driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices);
+#endif
+
static struct radio_isa_driver gemtek_driver = {
.driver = {
.match = radio_isa_match,
@@ -291,6 +303,14 @@ static struct radio_isa_driver gemtek_driver = {
.name = "radio-gemtek",
},
},
+#ifdef CONFIG_PNP
+ .pnp_driver = {
+ .name = "radio-gemtek",
+ .id_table = gemtek_pnp_devices,
+ .probe = radio_isa_pnp_probe,
+ .remove = radio_isa_pnp_remove,
+ },
+#endif
.io_params = io,
.radio_nr_params = radio_nr,
.io_ports = gemtek_ioports,
@@ -304,12 +324,14 @@ static struct radio_isa_driver gemtek_driver = {
static int __init gemtek_init(void)
{
gemtek_driver.probe = probe;
+ pnp_register_driver(&gemtek_driver.pnp_driver);
return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
}
static void __exit gemtek_exit(void)
{
hardmute = 1; /* Turn off PLL */
+ pnp_unregister_driver(&gemtek_driver.pnp_driver);
isa_unregister_driver(&gemtek_driver.driver);
}
--
Ondrej Zary
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2012-02-28 21:09 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-18 16:33 PnP support for the new ISA radio framework? Ondrej Zary
2012-02-22 20:33 ` Ondrej Zary
2012-02-24 9:35 ` Hans Verkuil
2012-02-28 21:08 ` [RFC PATCH] PnP support for the new ISA radio framework Ondrej Zary
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.