V4L/DVB (9979): em28xx: move usb probe code to a proper place
em28xx-video were holding several code that are not specific to V4L2 interface. This patch moves the core code for em28xx-core, and usb probing code into em28xx-cards. This opens the possibility of breaking em28xx into a core module and a V4L2 module, loaded only on devices that have analog interfaces. Some cleanup may be done at em28xx-cards to optimize the config code. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Родитель
126be90f72
Коммит
1a23f81b7d
|
@ -37,6 +37,8 @@
|
||||||
|
|
||||||
#include "em28xx.h"
|
#include "em28xx.h"
|
||||||
|
|
||||||
|
#define DRIVER_NAME "em28xx"
|
||||||
|
|
||||||
static int tuner = -1;
|
static int tuner = -1;
|
||||||
module_param(tuner, int, 0444);
|
module_param(tuner, int, 0444);
|
||||||
MODULE_PARM_DESC(tuner, "tuner type");
|
MODULE_PARM_DESC(tuner, "tuner type");
|
||||||
|
@ -45,6 +47,13 @@ static unsigned int disable_ir;
|
||||||
module_param(disable_ir, int, 0444);
|
module_param(disable_ir, int, 0444);
|
||||||
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
|
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
|
||||||
|
|
||||||
|
static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
||||||
|
module_param_array(card, int, NULL, 0444);
|
||||||
|
MODULE_PARM_DESC(card, "card type");
|
||||||
|
|
||||||
|
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
|
||||||
|
static unsigned long em28xx_devused;
|
||||||
|
|
||||||
struct em28xx_hash_table {
|
struct em28xx_hash_table {
|
||||||
unsigned long hash;
|
unsigned long hash;
|
||||||
unsigned int model;
|
unsigned int model;
|
||||||
|
@ -1801,3 +1810,424 @@ void em28xx_card_setup(struct em28xx *dev)
|
||||||
em28xx_ir_init(dev);
|
em28xx_ir_init(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(CONFIG_MODULES) && defined(MODULE)
|
||||||
|
static void request_module_async(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct em28xx *dev = container_of(work,
|
||||||
|
struct em28xx, request_module_wk);
|
||||||
|
|
||||||
|
if (dev->has_audio_class)
|
||||||
|
request_module("snd-usb-audio");
|
||||||
|
else if (dev->has_alsa_audio)
|
||||||
|
request_module("em28xx-alsa");
|
||||||
|
|
||||||
|
if (dev->board.has_dvb)
|
||||||
|
request_module("em28xx-dvb");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void request_modules(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
INIT_WORK(&dev->request_module_wk, request_module_async);
|
||||||
|
schedule_work(&dev->request_module_wk);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define request_modules(dev)
|
||||||
|
#endif /* CONFIG_MODULES */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_realease_resources()
|
||||||
|
* unregisters the v4l2,i2c and usb devices
|
||||||
|
* called when the device gets disconected or at module unload
|
||||||
|
*/
|
||||||
|
void em28xx_release_resources(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
if (dev->sbutton_input_dev)
|
||||||
|
em28xx_deregister_snapshot_button(dev);
|
||||||
|
|
||||||
|
if (dev->ir)
|
||||||
|
em28xx_ir_fini(dev);
|
||||||
|
|
||||||
|
/*FIXME: I2C IR should be disconnected */
|
||||||
|
|
||||||
|
em28xx_release_analog_resources(dev);
|
||||||
|
|
||||||
|
em28xx_remove_from_devlist(dev);
|
||||||
|
|
||||||
|
em28xx_i2c_unregister(dev);
|
||||||
|
usb_put_dev(dev->udev);
|
||||||
|
|
||||||
|
/* Mark device as unused */
|
||||||
|
em28xx_devused &= ~(1 << dev->devno);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_init_dev()
|
||||||
|
* allocates and inits the device structs, registers i2c bus and v4l device
|
||||||
|
*/
|
||||||
|
int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
|
||||||
|
int minor)
|
||||||
|
{
|
||||||
|
struct em28xx *dev = *devhandle;
|
||||||
|
int retval = -ENOMEM;
|
||||||
|
int errCode;
|
||||||
|
|
||||||
|
dev->udev = udev;
|
||||||
|
mutex_init(&dev->ctrl_urb_lock);
|
||||||
|
spin_lock_init(&dev->slock);
|
||||||
|
init_waitqueue_head(&dev->open);
|
||||||
|
init_waitqueue_head(&dev->wait_frame);
|
||||||
|
init_waitqueue_head(&dev->wait_stream);
|
||||||
|
|
||||||
|
dev->em28xx_write_regs = em28xx_write_regs;
|
||||||
|
dev->em28xx_read_reg = em28xx_read_reg;
|
||||||
|
dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
|
||||||
|
dev->em28xx_write_regs_req = em28xx_write_regs_req;
|
||||||
|
dev->em28xx_read_reg_req = em28xx_read_reg_req;
|
||||||
|
dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
|
||||||
|
|
||||||
|
em28xx_pre_card_setup(dev);
|
||||||
|
|
||||||
|
if (!dev->board.is_em2800) {
|
||||||
|
/* Sets I2C speed to 100 KHz */
|
||||||
|
retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
|
||||||
|
if (retval < 0) {
|
||||||
|
em28xx_errdev("%s: em28xx_write_regs_req failed!"
|
||||||
|
" retval [%d]\n",
|
||||||
|
__func__, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* register i2c bus */
|
||||||
|
errCode = em28xx_i2c_register(dev);
|
||||||
|
if (errCode < 0) {
|
||||||
|
em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
|
||||||
|
__func__, errCode);
|
||||||
|
return errCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do board specific init and eeprom reading */
|
||||||
|
em28xx_card_setup(dev);
|
||||||
|
|
||||||
|
/* Configure audio */
|
||||||
|
errCode = em28xx_audio_setup(dev);
|
||||||
|
if (errCode < 0) {
|
||||||
|
em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
|
||||||
|
__func__, errCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wake i2c devices */
|
||||||
|
em28xx_wake_i2c(dev);
|
||||||
|
|
||||||
|
/* init video dma queues */
|
||||||
|
INIT_LIST_HEAD(&dev->vidq.active);
|
||||||
|
INIT_LIST_HEAD(&dev->vidq.queued);
|
||||||
|
|
||||||
|
|
||||||
|
if (dev->board.has_msp34xx) {
|
||||||
|
/* Send a reset to other chips via gpio */
|
||||||
|
errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
|
||||||
|
if (errCode < 0) {
|
||||||
|
em28xx_errdev("%s: em28xx_write_regs_req - "
|
||||||
|
"msp34xx(1) failed! errCode [%d]\n",
|
||||||
|
__func__, errCode);
|
||||||
|
return errCode;
|
||||||
|
}
|
||||||
|
msleep(3);
|
||||||
|
|
||||||
|
errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
|
||||||
|
if (errCode < 0) {
|
||||||
|
em28xx_errdev("%s: em28xx_write_regs_req - "
|
||||||
|
"msp34xx(2) failed! errCode [%d]\n",
|
||||||
|
__func__, errCode);
|
||||||
|
return errCode;
|
||||||
|
}
|
||||||
|
msleep(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
em28xx_add_into_devlist(dev);
|
||||||
|
|
||||||
|
errCode = em28xx_analog_config(dev);
|
||||||
|
if (errCode) {
|
||||||
|
em28xx_errdev("error configuring device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = em28xx_register_analog_devices(dev);
|
||||||
|
if (retval < 0) {
|
||||||
|
em28xx_release_resources(dev);
|
||||||
|
goto fail_reg_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
em28xx_init_extension(dev);
|
||||||
|
|
||||||
|
/* Save some power by putting tuner to sleep */
|
||||||
|
em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_reg_devices:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_usb_probe()
|
||||||
|
* checks for supported devices
|
||||||
|
*/
|
||||||
|
static int em28xx_usb_probe(struct usb_interface *interface,
|
||||||
|
const struct usb_device_id *id)
|
||||||
|
{
|
||||||
|
const struct usb_endpoint_descriptor *endpoint;
|
||||||
|
struct usb_device *udev;
|
||||||
|
struct usb_interface *uif;
|
||||||
|
struct em28xx *dev = NULL;
|
||||||
|
int retval = -ENODEV;
|
||||||
|
int i, nr, ifnum, isoc_pipe;
|
||||||
|
char *speed;
|
||||||
|
char descr[255] = "";
|
||||||
|
|
||||||
|
udev = usb_get_dev(interface_to_usbdev(interface));
|
||||||
|
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
||||||
|
|
||||||
|
/* Check to see next free device and mark as used */
|
||||||
|
nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
|
||||||
|
em28xx_devused |= 1<<nr;
|
||||||
|
|
||||||
|
/* Don't register audio interfaces */
|
||||||
|
if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
|
||||||
|
em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
|
||||||
|
"interface %i, class %i\n",
|
||||||
|
le16_to_cpu(udev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(udev->descriptor.idProduct),
|
||||||
|
ifnum,
|
||||||
|
interface->altsetting[0].desc.bInterfaceClass);
|
||||||
|
|
||||||
|
em28xx_devused &= ~(1<<nr);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint = &interface->cur_altsetting->endpoint[0].desc;
|
||||||
|
|
||||||
|
/* check if the device has the iso in endpoint at the correct place */
|
||||||
|
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
|
||||||
|
USB_ENDPOINT_XFER_ISOC &&
|
||||||
|
(interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) {
|
||||||
|
/* It's a newer em2874/em2875 device */
|
||||||
|
isoc_pipe = 0;
|
||||||
|
} else {
|
||||||
|
int check_interface = 1;
|
||||||
|
isoc_pipe = 1;
|
||||||
|
endpoint = &interface->cur_altsetting->endpoint[1].desc;
|
||||||
|
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
|
||||||
|
USB_ENDPOINT_XFER_ISOC)
|
||||||
|
check_interface = 0;
|
||||||
|
|
||||||
|
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
||||||
|
check_interface = 0;
|
||||||
|
|
||||||
|
if (!check_interface) {
|
||||||
|
em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
|
||||||
|
"interface %i, class %i found.\n",
|
||||||
|
le16_to_cpu(udev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(udev->descriptor.idProduct),
|
||||||
|
ifnum,
|
||||||
|
interface->altsetting[0].desc.bInterfaceClass);
|
||||||
|
|
||||||
|
em28xx_err(DRIVER_NAME " This is an anciliary "
|
||||||
|
"interface not used by the driver\n");
|
||||||
|
|
||||||
|
em28xx_devused &= ~(1<<nr);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (udev->speed) {
|
||||||
|
case USB_SPEED_LOW:
|
||||||
|
speed = "1.5";
|
||||||
|
break;
|
||||||
|
case USB_SPEED_UNKNOWN:
|
||||||
|
case USB_SPEED_FULL:
|
||||||
|
speed = "12";
|
||||||
|
break;
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
|
speed = "480";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
speed = "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udev->manufacturer)
|
||||||
|
strlcpy(descr, udev->manufacturer, sizeof(descr));
|
||||||
|
|
||||||
|
if (udev->product) {
|
||||||
|
if (*descr)
|
||||||
|
strlcat(descr, " ", sizeof(descr));
|
||||||
|
strlcat(descr, udev->product, sizeof(descr));
|
||||||
|
}
|
||||||
|
if (*descr)
|
||||||
|
strlcat(descr, " ", sizeof(descr));
|
||||||
|
|
||||||
|
printk(DRIVER_NAME ": New device %s@ %s Mbps "
|
||||||
|
"(%04x:%04x, interface %d, class %d)\n",
|
||||||
|
descr,
|
||||||
|
speed,
|
||||||
|
le16_to_cpu(udev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(udev->descriptor.idProduct),
|
||||||
|
ifnum,
|
||||||
|
interface->altsetting->desc.bInterfaceNumber);
|
||||||
|
|
||||||
|
if (nr >= EM28XX_MAXBOARDS) {
|
||||||
|
printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
|
||||||
|
EM28XX_MAXBOARDS);
|
||||||
|
em28xx_devused &= ~(1<<nr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate memory for our device state and initialize it */
|
||||||
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
|
if (dev == NULL) {
|
||||||
|
em28xx_err(DRIVER_NAME ": out of memory!\n");
|
||||||
|
em28xx_devused &= ~(1<<nr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(dev->name, 29, "em28xx #%d", nr);
|
||||||
|
dev->devno = nr;
|
||||||
|
dev->model = id->driver_info;
|
||||||
|
dev->alt = -1;
|
||||||
|
|
||||||
|
/* Checks if audio is provided by some interface */
|
||||||
|
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
|
||||||
|
uif = udev->config->interface[i];
|
||||||
|
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
|
||||||
|
dev->has_audio_class = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute alternate max packet sizes */
|
||||||
|
uif = udev->actconfig->interface[0];
|
||||||
|
|
||||||
|
dev->num_alt = uif->num_altsetting;
|
||||||
|
dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
|
||||||
|
|
||||||
|
if (dev->alt_max_pkt_size == NULL) {
|
||||||
|
em28xx_errdev("out of memory!\n");
|
||||||
|
em28xx_devused &= ~(1<<nr);
|
||||||
|
kfree(dev);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < dev->num_alt ; i++) {
|
||||||
|
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
|
||||||
|
dev->alt_max_pkt_size[i] =
|
||||||
|
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
|
||||||
|
dev->model = card[nr];
|
||||||
|
|
||||||
|
/* allocate device struct */
|
||||||
|
mutex_init(&dev->lock);
|
||||||
|
mutex_lock(&dev->lock);
|
||||||
|
retval = em28xx_init_dev(&dev, udev, nr);
|
||||||
|
if (retval) {
|
||||||
|
em28xx_devused &= ~(1<<dev->devno);
|
||||||
|
kfree(dev);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save our data pointer in this interface device */
|
||||||
|
usb_set_intfdata(interface, dev);
|
||||||
|
|
||||||
|
request_modules(dev);
|
||||||
|
|
||||||
|
/* Should be the last thing to do, to avoid newer udev's to
|
||||||
|
open the device before fully initializing it
|
||||||
|
*/
|
||||||
|
mutex_unlock(&dev->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_usb_disconnect()
|
||||||
|
* called when the device gets diconencted
|
||||||
|
* video device will be unregistered on v4l2_close in case it is still open
|
||||||
|
*/
|
||||||
|
static void em28xx_usb_disconnect(struct usb_interface *interface)
|
||||||
|
{
|
||||||
|
struct em28xx *dev;
|
||||||
|
|
||||||
|
dev = usb_get_intfdata(interface);
|
||||||
|
usb_set_intfdata(interface, NULL);
|
||||||
|
|
||||||
|
if (!dev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
em28xx_info("disconnecting %s\n", dev->vdev->name);
|
||||||
|
|
||||||
|
/* wait until all current v4l2 io is finished then deallocate
|
||||||
|
resources */
|
||||||
|
mutex_lock(&dev->lock);
|
||||||
|
|
||||||
|
wake_up_interruptible_all(&dev->open);
|
||||||
|
|
||||||
|
if (dev->users) {
|
||||||
|
em28xx_warn
|
||||||
|
("device /dev/video%d is open! Deregistration and memory "
|
||||||
|
"deallocation are deferred on close.\n",
|
||||||
|
dev->vdev->num);
|
||||||
|
|
||||||
|
dev->state |= DEV_MISCONFIGURED;
|
||||||
|
em28xx_uninit_isoc(dev);
|
||||||
|
dev->state |= DEV_DISCONNECTED;
|
||||||
|
wake_up_interruptible(&dev->wait_frame);
|
||||||
|
wake_up_interruptible(&dev->wait_stream);
|
||||||
|
} else {
|
||||||
|
dev->state |= DEV_DISCONNECTED;
|
||||||
|
em28xx_release_resources(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
em28xx_close_extension(dev);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->lock);
|
||||||
|
|
||||||
|
if (!dev->users) {
|
||||||
|
kfree(dev->alt_max_pkt_size);
|
||||||
|
kfree(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct usb_driver em28xx_usb_driver = {
|
||||||
|
.name = "em28xx",
|
||||||
|
.probe = em28xx_usb_probe,
|
||||||
|
.disconnect = em28xx_usb_disconnect,
|
||||||
|
.id_table = em28xx_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init em28xx_module_init(void)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* register this driver with the USB subsystem */
|
||||||
|
result = usb_register(&em28xx_usb_driver);
|
||||||
|
if (result)
|
||||||
|
em28xx_err(DRIVER_NAME
|
||||||
|
" usb_register failed. Error number %d.\n", result);
|
||||||
|
|
||||||
|
printk(KERN_INFO DRIVER_NAME " driver loaded\n");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit em28xx_module_exit(void)
|
||||||
|
{
|
||||||
|
/* deregister this driver with the USB subsystem */
|
||||||
|
usb_deregister(&em28xx_usb_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(em28xx_module_init);
|
||||||
|
module_exit(em28xx_module_exit);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
#include <media/v4l2-common.h>
|
||||||
|
|
||||||
#include "em28xx.h"
|
#include "em28xx.h"
|
||||||
|
|
||||||
|
@ -974,3 +975,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
|
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_wake_i2c()
|
||||||
|
* configure i2c attached devices
|
||||||
|
*/
|
||||||
|
void em28xx_wake_i2c(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
struct v4l2_routing route;
|
||||||
|
int zero = 0;
|
||||||
|
|
||||||
|
route.input = INPUT(dev->ctl_input)->vmux;
|
||||||
|
route.output = 0;
|
||||||
|
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
|
||||||
|
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
|
||||||
|
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Device control list
|
||||||
|
*/
|
||||||
|
|
||||||
|
static LIST_HEAD(em28xx_devlist);
|
||||||
|
static DEFINE_MUTEX(em28xx_devlist_mutex);
|
||||||
|
|
||||||
|
struct em28xx *em28xx_get_device(struct inode *inode,
|
||||||
|
enum v4l2_buf_type *fh_type,
|
||||||
|
int *has_radio)
|
||||||
|
{
|
||||||
|
struct em28xx *h, *dev = NULL;
|
||||||
|
int minor = iminor(inode);
|
||||||
|
|
||||||
|
*fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
*has_radio = 0;
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_devlist_mutex);
|
||||||
|
list_for_each_entry(h, &em28xx_devlist, devlist) {
|
||||||
|
if (h->vdev->minor == minor)
|
||||||
|
dev = h;
|
||||||
|
if (h->vbi_dev->minor == minor) {
|
||||||
|
dev = h;
|
||||||
|
*fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
|
||||||
|
}
|
||||||
|
if (h->radio_dev &&
|
||||||
|
h->radio_dev->minor == minor) {
|
||||||
|
dev = h;
|
||||||
|
*has_radio = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&em28xx_devlist_mutex);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* em28xx_realease_resources()
|
||||||
|
* unregisters the v4l2,i2c and usb devices
|
||||||
|
* called when the device gets disconected or at module unload
|
||||||
|
*/
|
||||||
|
void em28xx_remove_from_devlist(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
mutex_lock(&em28xx_devlist_mutex);
|
||||||
|
list_del(&dev->devlist);
|
||||||
|
mutex_unlock(&em28xx_devlist_mutex);
|
||||||
|
};
|
||||||
|
|
||||||
|
void em28xx_add_into_devlist(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
mutex_lock(&em28xx_devlist_mutex);
|
||||||
|
list_add_tail(&dev->devlist, &em28xx_devlist);
|
||||||
|
mutex_unlock(&em28xx_devlist_mutex);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extension interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
static LIST_HEAD(em28xx_extension_devlist);
|
||||||
|
static DEFINE_MUTEX(em28xx_extension_devlist_lock);
|
||||||
|
|
||||||
|
int em28xx_register_extension(struct em28xx_ops *ops)
|
||||||
|
{
|
||||||
|
struct em28xx *dev = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_devlist_mutex);
|
||||||
|
mutex_lock(&em28xx_extension_devlist_lock);
|
||||||
|
list_add_tail(&ops->next, &em28xx_extension_devlist);
|
||||||
|
list_for_each_entry(dev, &em28xx_devlist, devlist) {
|
||||||
|
if (dev)
|
||||||
|
ops->init(dev);
|
||||||
|
}
|
||||||
|
printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
|
||||||
|
mutex_unlock(&em28xx_extension_devlist_lock);
|
||||||
|
mutex_unlock(&em28xx_devlist_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(em28xx_register_extension);
|
||||||
|
|
||||||
|
void em28xx_unregister_extension(struct em28xx_ops *ops)
|
||||||
|
{
|
||||||
|
struct em28xx *dev = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_devlist_mutex);
|
||||||
|
list_for_each_entry(dev, &em28xx_devlist, devlist) {
|
||||||
|
if (dev)
|
||||||
|
ops->fini(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_extension_devlist_lock);
|
||||||
|
printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
|
||||||
|
list_del(&ops->next);
|
||||||
|
mutex_unlock(&em28xx_extension_devlist_lock);
|
||||||
|
mutex_unlock(&em28xx_devlist_mutex);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(em28xx_unregister_extension);
|
||||||
|
|
||||||
|
void em28xx_init_extension(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
struct em28xx_ops *ops = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_extension_devlist_lock);
|
||||||
|
if (!list_empty(&em28xx_extension_devlist)) {
|
||||||
|
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
|
||||||
|
if (ops->init)
|
||||||
|
ops->init(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&em28xx_extension_devlist_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void em28xx_close_extension(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
struct em28xx_ops *ops = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&em28xx_extension_devlist_lock);
|
||||||
|
if (!list_empty(&em28xx_extension_devlist)) {
|
||||||
|
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
|
||||||
|
if (ops->fini)
|
||||||
|
ops->fini(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&em28xx_extension_devlist_lock);
|
||||||
|
}
|
||||||
|
|
|
@ -48,9 +48,8 @@
|
||||||
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
|
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
|
||||||
"Sascha Sommer <saschasommer@freenet.de>"
|
"Sascha Sommer <saschasommer@freenet.de>"
|
||||||
|
|
||||||
#define DRIVER_NAME "em28xx"
|
|
||||||
#define DRIVER_DESC "Empia em28xx based USB video device driver"
|
#define DRIVER_DESC "Empia em28xx based USB video device driver"
|
||||||
#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
|
#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1)
|
||||||
|
|
||||||
#define em28xx_videodbg(fmt, arg...) do {\
|
#define em28xx_videodbg(fmt, arg...) do {\
|
||||||
if (video_debug) \
|
if (video_debug) \
|
||||||
|
@ -73,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
static LIST_HEAD(em28xx_devlist);
|
|
||||||
static DEFINE_MUTEX(em28xx_devlist_mutex);
|
|
||||||
|
|
||||||
static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
|
||||||
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
||||||
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
||||||
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
||||||
|
|
||||||
module_param_array(card, int, NULL, 0444);
|
|
||||||
module_param_array(video_nr, int, NULL, 0444);
|
module_param_array(video_nr, int, NULL, 0444);
|
||||||
module_param_array(vbi_nr, int, NULL, 0444);
|
module_param_array(vbi_nr, int, NULL, 0444);
|
||||||
module_param_array(radio_nr, int, NULL, 0444);
|
module_param_array(radio_nr, int, NULL, 0444);
|
||||||
MODULE_PARM_DESC(card, "card type");
|
|
||||||
MODULE_PARM_DESC(video_nr, "video device numbers");
|
MODULE_PARM_DESC(video_nr, "video device numbers");
|
||||||
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
|
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
|
||||||
MODULE_PARM_DESC(radio_nr, "radio device numbers");
|
MODULE_PARM_DESC(radio_nr, "radio device numbers");
|
||||||
|
@ -94,9 +87,6 @@ static unsigned int video_debug;
|
||||||
module_param(video_debug, int, 0644);
|
module_param(video_debug, int, 0644);
|
||||||
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
|
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
|
||||||
|
|
||||||
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
|
|
||||||
static unsigned long em28xx_devused;
|
|
||||||
|
|
||||||
/* supported video standards */
|
/* supported video standards */
|
||||||
static struct em28xx_fmt format[] = {
|
static struct em28xx_fmt format[] = {
|
||||||
{
|
{
|
||||||
|
@ -131,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_driver em28xx_usb_driver;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
DMA and thread functions
|
DMA and thread functions
|
||||||
------------------------------------------------------------------*/
|
------------------------------------------------------------------*/
|
||||||
|
@ -519,56 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
|
||||||
|
|
||||||
/********************* v4l2 interface **************************************/
|
/********************* v4l2 interface **************************************/
|
||||||
|
|
||||||
/*
|
|
||||||
* em28xx_config()
|
|
||||||
* inits registers with sane defaults
|
|
||||||
*/
|
|
||||||
static int em28xx_config(struct em28xx *dev)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
/* Sets I2C speed to 100 KHz */
|
|
||||||
if (!dev->board.is_em2800) {
|
|
||||||
retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
|
|
||||||
if (retval < 0) {
|
|
||||||
em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
|
|
||||||
__func__, retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable vbi capturing */
|
|
||||||
|
|
||||||
/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
|
|
||||||
/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
|
|
||||||
em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
|
|
||||||
|
|
||||||
dev->mute = 1; /* maybe not the right place... */
|
|
||||||
dev->volume = 0x1f;
|
|
||||||
|
|
||||||
em28xx_set_outfmt(dev);
|
|
||||||
em28xx_colorlevels_set_default(dev);
|
|
||||||
em28xx_compression_disable(dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* em28xx_config_i2c()
|
|
||||||
* configure i2c attached devices
|
|
||||||
*/
|
|
||||||
static void em28xx_config_i2c(struct em28xx *dev)
|
|
||||||
{
|
|
||||||
struct v4l2_routing route;
|
|
||||||
int zero = 0;
|
|
||||||
|
|
||||||
route.input = INPUT(dev->ctl_input)->vmux;
|
|
||||||
route.output = 0;
|
|
||||||
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
|
|
||||||
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
|
|
||||||
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void video_mux(struct em28xx *dev, int index)
|
static void video_mux(struct em28xx *dev, int index)
|
||||||
{
|
{
|
||||||
struct v4l2_routing route;
|
struct v4l2_routing route;
|
||||||
|
@ -1647,28 +1585,12 @@ static int radio_queryctrl(struct file *file, void *priv,
|
||||||
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
int minor = iminor(inode);
|
int minor = iminor(inode);
|
||||||
int errCode = 0, radio = 0;
|
int errCode = 0, radio;
|
||||||
struct em28xx *h, *dev = NULL;
|
struct em28xx *dev;
|
||||||
|
enum v4l2_buf_type fh_type;
|
||||||
struct em28xx_fh *fh;
|
struct em28xx_fh *fh;
|
||||||
enum v4l2_buf_type fh_type = 0;
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_devlist_mutex);
|
dev = em28xx_get_device(inode, &fh_type, &radio);
|
||||||
list_for_each_entry(h, &em28xx_devlist, devlist) {
|
|
||||||
if (h->vdev->minor == minor) {
|
|
||||||
dev = h;
|
|
||||||
fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
}
|
|
||||||
if (h->vbi_dev->minor == minor) {
|
|
||||||
dev = h;
|
|
||||||
fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
|
|
||||||
}
|
|
||||||
if (h->radio_dev &&
|
|
||||||
h->radio_dev->minor == minor) {
|
|
||||||
radio = 1;
|
|
||||||
dev = h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&em28xx_devlist_mutex);
|
|
||||||
|
|
||||||
if (NULL == dev)
|
if (NULL == dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1703,7 +1625,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
||||||
/* Needed, since GPIO might have disabled power of
|
/* Needed, since GPIO might have disabled power of
|
||||||
some i2c device
|
some i2c device
|
||||||
*/
|
*/
|
||||||
em28xx_config_i2c(dev);
|
em28xx_wake_i2c(dev);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (fh->radio) {
|
if (fh->radio) {
|
||||||
|
@ -1727,18 +1649,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
||||||
* unregisters the v4l2,i2c and usb devices
|
* unregisters the v4l2,i2c and usb devices
|
||||||
* called when the device gets disconected or at module unload
|
* called when the device gets disconected or at module unload
|
||||||
*/
|
*/
|
||||||
static void em28xx_release_resources(struct em28xx *dev)
|
void em28xx_release_analog_resources(struct em28xx *dev)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*FIXME: I2C IR should be disconnected */
|
/*FIXME: I2C IR should be disconnected */
|
||||||
|
|
||||||
list_del(&dev->devlist);
|
|
||||||
if (dev->sbutton_input_dev)
|
|
||||||
em28xx_deregister_snapshot_button(dev);
|
|
||||||
|
|
||||||
if (dev->ir)
|
|
||||||
em28xx_ir_fini(dev);
|
|
||||||
|
|
||||||
if (dev->radio_dev) {
|
if (dev->radio_dev) {
|
||||||
if (-1 != dev->radio_dev->minor)
|
if (-1 != dev->radio_dev->minor)
|
||||||
video_unregister_device(dev->radio_dev);
|
video_unregister_device(dev->radio_dev);
|
||||||
|
@ -1764,11 +1679,6 @@ static void em28xx_release_resources(struct em28xx *dev)
|
||||||
video_device_release(dev->vdev);
|
video_device_release(dev->vdev);
|
||||||
dev->vdev = NULL;
|
dev->vdev = NULL;
|
||||||
}
|
}
|
||||||
em28xx_i2c_unregister(dev);
|
|
||||||
usb_put_dev(dev->udev);
|
|
||||||
|
|
||||||
/* Mark device as unused */
|
|
||||||
em28xx_devused &= ~(1<<dev->devno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2018,44 +1928,6 @@ static struct video_device em28xx_radio_template = {
|
||||||
/******************************** usb interface ******************************/
|
/******************************** usb interface ******************************/
|
||||||
|
|
||||||
|
|
||||||
static LIST_HEAD(em28xx_extension_devlist);
|
|
||||||
static DEFINE_MUTEX(em28xx_extension_devlist_lock);
|
|
||||||
|
|
||||||
int em28xx_register_extension(struct em28xx_ops *ops)
|
|
||||||
{
|
|
||||||
struct em28xx *dev = NULL;
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_devlist_mutex);
|
|
||||||
mutex_lock(&em28xx_extension_devlist_lock);
|
|
||||||
list_add_tail(&ops->next, &em28xx_extension_devlist);
|
|
||||||
list_for_each_entry(dev, &em28xx_devlist, devlist) {
|
|
||||||
if (dev)
|
|
||||||
ops->init(dev);
|
|
||||||
}
|
|
||||||
printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
|
|
||||||
mutex_unlock(&em28xx_extension_devlist_lock);
|
|
||||||
mutex_unlock(&em28xx_devlist_mutex);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(em28xx_register_extension);
|
|
||||||
|
|
||||||
void em28xx_unregister_extension(struct em28xx_ops *ops)
|
|
||||||
{
|
|
||||||
struct em28xx *dev = NULL;
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_devlist_mutex);
|
|
||||||
list_for_each_entry(dev, &em28xx_devlist, devlist) {
|
|
||||||
if (dev)
|
|
||||||
ops->fini(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_extension_devlist_lock);
|
|
||||||
printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
|
|
||||||
list_del(&ops->next);
|
|
||||||
mutex_unlock(&em28xx_extension_devlist_lock);
|
|
||||||
mutex_unlock(&em28xx_devlist_mutex);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(em28xx_unregister_extension);
|
|
||||||
|
|
||||||
static struct video_device *em28xx_vdev_init(struct em28xx *dev,
|
static struct video_device *em28xx_vdev_init(struct em28xx *dev,
|
||||||
const struct video_device *template,
|
const struct video_device *template,
|
||||||
|
@ -2078,7 +1950,45 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
|
||||||
return vfd;
|
return vfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int register_analog_devices(struct em28xx *dev)
|
int em28xx_analog_config(struct em28xx *dev)
|
||||||
|
{
|
||||||
|
printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
|
||||||
|
dev->name,
|
||||||
|
(EM28XX_VERSION_CODE >> 16) & 0xff,
|
||||||
|
(EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
|
||||||
|
|
||||||
|
/* Analog specific initialization */
|
||||||
|
dev->format = &format[0];
|
||||||
|
video_mux(dev, 0);
|
||||||
|
|
||||||
|
/* enable vbi capturing */
|
||||||
|
|
||||||
|
/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
|
||||||
|
/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
|
||||||
|
em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
|
||||||
|
|
||||||
|
dev->mute = 1; /* maybe not the right place... */
|
||||||
|
dev->volume = 0x1f;
|
||||||
|
|
||||||
|
em28xx_set_outfmt(dev);
|
||||||
|
em28xx_colorlevels_set_default(dev);
|
||||||
|
em28xx_compression_disable(dev);
|
||||||
|
|
||||||
|
/* set default norm */
|
||||||
|
dev->norm = em28xx_video_template.current_norm;
|
||||||
|
dev->width = norm_maxw(dev);
|
||||||
|
dev->height = norm_maxh(dev);
|
||||||
|
dev->interlaced = EM28XX_INTERLACED_DEFAULT;
|
||||||
|
dev->hscale = 0;
|
||||||
|
dev->vscale = 0;
|
||||||
|
|
||||||
|
/* FIXME: This is a very bad hack! Not all devices have TV on input 2 */
|
||||||
|
dev->ctl_input = 2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int em28xx_register_analog_devices(struct em28xx *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -2130,440 +2040,3 @@ static int register_analog_devices(struct em28xx *dev)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* em28xx_init_dev()
|
|
||||||
* allocates and inits the device structs, registers i2c bus and v4l device
|
|
||||||
*/
|
|
||||||
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
|
|
||||||
int minor)
|
|
||||||
{
|
|
||||||
struct em28xx_ops *ops = NULL;
|
|
||||||
struct em28xx *dev = *devhandle;
|
|
||||||
int retval = -ENOMEM;
|
|
||||||
int errCode;
|
|
||||||
unsigned int maxh, maxw;
|
|
||||||
|
|
||||||
dev->udev = udev;
|
|
||||||
mutex_init(&dev->ctrl_urb_lock);
|
|
||||||
spin_lock_init(&dev->slock);
|
|
||||||
init_waitqueue_head(&dev->open);
|
|
||||||
init_waitqueue_head(&dev->wait_frame);
|
|
||||||
init_waitqueue_head(&dev->wait_stream);
|
|
||||||
|
|
||||||
dev->em28xx_write_regs = em28xx_write_regs;
|
|
||||||
dev->em28xx_read_reg = em28xx_read_reg;
|
|
||||||
dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
|
|
||||||
dev->em28xx_write_regs_req = em28xx_write_regs_req;
|
|
||||||
dev->em28xx_read_reg_req = em28xx_read_reg_req;
|
|
||||||
dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
|
|
||||||
dev->format = &format[0];
|
|
||||||
|
|
||||||
em28xx_pre_card_setup(dev);
|
|
||||||
|
|
||||||
errCode = em28xx_config(dev);
|
|
||||||
if (errCode) {
|
|
||||||
em28xx_errdev("error configuring device\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* register i2c bus */
|
|
||||||
errCode = em28xx_i2c_register(dev);
|
|
||||||
if (errCode < 0) {
|
|
||||||
em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
|
|
||||||
__func__, errCode);
|
|
||||||
return errCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do board specific init and eeprom reading */
|
|
||||||
em28xx_card_setup(dev);
|
|
||||||
|
|
||||||
/* Configure audio */
|
|
||||||
errCode = em28xx_audio_setup(dev);
|
|
||||||
if (errCode < 0) {
|
|
||||||
em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
|
|
||||||
__func__, errCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* configure the device */
|
|
||||||
em28xx_config_i2c(dev);
|
|
||||||
|
|
||||||
/* set default norm */
|
|
||||||
dev->norm = em28xx_video_template.current_norm;
|
|
||||||
|
|
||||||
maxw = norm_maxw(dev);
|
|
||||||
maxh = norm_maxh(dev);
|
|
||||||
|
|
||||||
/* set default image size */
|
|
||||||
dev->width = maxw;
|
|
||||||
dev->height = maxh;
|
|
||||||
dev->interlaced = EM28XX_INTERLACED_DEFAULT;
|
|
||||||
dev->hscale = 0;
|
|
||||||
dev->vscale = 0;
|
|
||||||
dev->ctl_input = 2;
|
|
||||||
|
|
||||||
errCode = em28xx_config(dev);
|
|
||||||
if (errCode < 0) {
|
|
||||||
em28xx_errdev("%s: em28xx_config - errCode [%d]!\n",
|
|
||||||
__func__, errCode);
|
|
||||||
return errCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* init video dma queues */
|
|
||||||
INIT_LIST_HEAD(&dev->vidq.active);
|
|
||||||
INIT_LIST_HEAD(&dev->vidq.queued);
|
|
||||||
|
|
||||||
|
|
||||||
if (dev->board.has_msp34xx) {
|
|
||||||
/* Send a reset to other chips via gpio */
|
|
||||||
errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
|
|
||||||
if (errCode < 0) {
|
|
||||||
em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n",
|
|
||||||
__func__, errCode);
|
|
||||||
return errCode;
|
|
||||||
}
|
|
||||||
msleep(3);
|
|
||||||
|
|
||||||
errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
|
|
||||||
if (errCode < 0) {
|
|
||||||
em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n",
|
|
||||||
__func__, errCode);
|
|
||||||
return errCode;
|
|
||||||
}
|
|
||||||
msleep(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
video_mux(dev, 0);
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_devlist_mutex);
|
|
||||||
list_add_tail(&dev->devlist, &em28xx_devlist);
|
|
||||||
retval = register_analog_devices(dev);
|
|
||||||
if (retval < 0) {
|
|
||||||
em28xx_release_resources(dev);
|
|
||||||
mutex_unlock(&em28xx_devlist_mutex);
|
|
||||||
goto fail_reg_devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_extension_devlist_lock);
|
|
||||||
if (!list_empty(&em28xx_extension_devlist)) {
|
|
||||||
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
|
|
||||||
if (ops->id)
|
|
||||||
ops->init(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&em28xx_extension_devlist_lock);
|
|
||||||
mutex_unlock(&em28xx_devlist_mutex);
|
|
||||||
|
|
||||||
/* Save some power by putting tuner to sleep */
|
|
||||||
em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail_reg_devices:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_MODULES) && defined(MODULE)
|
|
||||||
static void request_module_async(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct em28xx *dev = container_of(work,
|
|
||||||
struct em28xx, request_module_wk);
|
|
||||||
|
|
||||||
if (dev->has_audio_class)
|
|
||||||
request_module("snd-usb-audio");
|
|
||||||
else if (dev->has_alsa_audio)
|
|
||||||
request_module("em28xx-alsa");
|
|
||||||
|
|
||||||
if (dev->board.has_dvb)
|
|
||||||
request_module("em28xx-dvb");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void request_modules(struct em28xx *dev)
|
|
||||||
{
|
|
||||||
INIT_WORK(&dev->request_module_wk, request_module_async);
|
|
||||||
schedule_work(&dev->request_module_wk);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define request_modules(dev)
|
|
||||||
#endif /* CONFIG_MODULES */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* em28xx_usb_probe()
|
|
||||||
* checks for supported devices
|
|
||||||
*/
|
|
||||||
static int em28xx_usb_probe(struct usb_interface *interface,
|
|
||||||
const struct usb_device_id *id)
|
|
||||||
{
|
|
||||||
const struct usb_endpoint_descriptor *endpoint;
|
|
||||||
struct usb_device *udev;
|
|
||||||
struct usb_interface *uif;
|
|
||||||
struct em28xx *dev = NULL;
|
|
||||||
int retval = -ENODEV;
|
|
||||||
int i, nr, ifnum, isoc_pipe;
|
|
||||||
char *speed;
|
|
||||||
char descr[255] = "";
|
|
||||||
|
|
||||||
udev = usb_get_dev(interface_to_usbdev(interface));
|
|
||||||
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
|
||||||
|
|
||||||
/* Check to see next free device and mark as used */
|
|
||||||
nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
|
|
||||||
em28xx_devused |= 1<<nr;
|
|
||||||
|
|
||||||
/* Don't register audio interfaces */
|
|
||||||
if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
|
|
||||||
em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
|
|
||||||
"interface %i, class %i\n",
|
|
||||||
le16_to_cpu(udev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(udev->descriptor.idProduct),
|
|
||||||
ifnum,
|
|
||||||
interface->altsetting[0].desc.bInterfaceClass);
|
|
||||||
|
|
||||||
em28xx_devused &= ~(1<<nr);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoint = &interface->cur_altsetting->endpoint[0].desc;
|
|
||||||
|
|
||||||
/* check if the device has the iso in endpoint at the correct place */
|
|
||||||
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
|
|
||||||
USB_ENDPOINT_XFER_ISOC &&
|
|
||||||
(interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940))
|
|
||||||
{
|
|
||||||
/* It's a newer em2874/em2875 device */
|
|
||||||
isoc_pipe = 0;
|
|
||||||
} else {
|
|
||||||
int check_interface = 1;
|
|
||||||
isoc_pipe = 1;
|
|
||||||
endpoint = &interface->cur_altsetting->endpoint[1].desc;
|
|
||||||
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
|
|
||||||
USB_ENDPOINT_XFER_ISOC)
|
|
||||||
check_interface = 0;
|
|
||||||
|
|
||||||
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
|
||||||
check_interface = 0;
|
|
||||||
|
|
||||||
if (!check_interface) {
|
|
||||||
em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
|
|
||||||
"interface %i, class %i found.\n",
|
|
||||||
le16_to_cpu(udev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(udev->descriptor.idProduct),
|
|
||||||
ifnum,
|
|
||||||
interface->altsetting[0].desc.bInterfaceClass);
|
|
||||||
|
|
||||||
em28xx_err(DRIVER_NAME " This is an anciliary "
|
|
||||||
"interface not used by the driver\n");
|
|
||||||
|
|
||||||
em28xx_devused &= ~(1<<nr);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (udev->speed) {
|
|
||||||
case USB_SPEED_LOW:
|
|
||||||
speed = "1.5";
|
|
||||||
break;
|
|
||||||
case USB_SPEED_UNKNOWN:
|
|
||||||
case USB_SPEED_FULL:
|
|
||||||
speed = "12";
|
|
||||||
break;
|
|
||||||
case USB_SPEED_HIGH:
|
|
||||||
speed = "480";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
speed = "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (udev->manufacturer)
|
|
||||||
strlcpy(descr, udev->manufacturer, sizeof(descr));
|
|
||||||
|
|
||||||
if (udev->product) {
|
|
||||||
if (*descr)
|
|
||||||
strlcat(descr, " ", sizeof(descr));
|
|
||||||
strlcat(descr, udev->product, sizeof(descr));
|
|
||||||
}
|
|
||||||
if (*descr)
|
|
||||||
strlcat(descr, " ", sizeof(descr));
|
|
||||||
|
|
||||||
printk(DRIVER_NAME ": New device %s@ %s Mbps "
|
|
||||||
"(%04x:%04x, interface %d, class %d)\n",
|
|
||||||
descr,
|
|
||||||
speed,
|
|
||||||
le16_to_cpu(udev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(udev->descriptor.idProduct),
|
|
||||||
ifnum,
|
|
||||||
interface->altsetting->desc.bInterfaceNumber);
|
|
||||||
|
|
||||||
if (nr >= EM28XX_MAXBOARDS) {
|
|
||||||
printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
|
|
||||||
EM28XX_MAXBOARDS);
|
|
||||||
em28xx_devused &= ~(1<<nr);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate memory for our device state and initialize it */
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
if (dev == NULL) {
|
|
||||||
em28xx_err(DRIVER_NAME ": out of memory!\n");
|
|
||||||
em28xx_devused &= ~(1<<nr);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(dev->name, 29, "em28xx #%d", nr);
|
|
||||||
dev->devno = nr;
|
|
||||||
dev->model = id->driver_info;
|
|
||||||
dev->alt = -1;
|
|
||||||
|
|
||||||
/* Checks if audio is provided by some interface */
|
|
||||||
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
|
|
||||||
uif = udev->config->interface[i];
|
|
||||||
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
|
|
||||||
dev->has_audio_class = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* compute alternate max packet sizes */
|
|
||||||
uif = udev->actconfig->interface[0];
|
|
||||||
|
|
||||||
dev->num_alt = uif->num_altsetting;
|
|
||||||
em28xx_videodbg("Alternate settings: %i\n", dev->num_alt);
|
|
||||||
/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */
|
|
||||||
dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
|
|
||||||
|
|
||||||
if (dev->alt_max_pkt_size == NULL) {
|
|
||||||
em28xx_errdev("out of memory!\n");
|
|
||||||
em28xx_devused &= ~(1<<nr);
|
|
||||||
kfree(dev);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < dev->num_alt ; i++) {
|
|
||||||
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
|
|
||||||
wMaxPacketSize);
|
|
||||||
dev->alt_max_pkt_size[i] =
|
|
||||||
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
||||||
em28xx_videodbg("Alternate setting %i, max size= %i\n", i,
|
|
||||||
dev->alt_max_pkt_size[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
|
|
||||||
dev->model = card[nr];
|
|
||||||
|
|
||||||
/* allocate device struct */
|
|
||||||
mutex_init(&dev->lock);
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
retval = em28xx_init_dev(&dev, udev, nr);
|
|
||||||
if (retval) {
|
|
||||||
em28xx_devused &= ~(1<<dev->devno);
|
|
||||||
kfree(dev);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* save our data pointer in this interface device */
|
|
||||||
usb_set_intfdata(interface, dev);
|
|
||||||
|
|
||||||
request_modules(dev);
|
|
||||||
|
|
||||||
/* Should be the last thing to do, to avoid newer udev's to
|
|
||||||
open the device before fully initializing it
|
|
||||||
*/
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* em28xx_usb_disconnect()
|
|
||||||
* called when the device gets diconencted
|
|
||||||
* video device will be unregistered on v4l2_close in case it is still open
|
|
||||||
*/
|
|
||||||
static void em28xx_usb_disconnect(struct usb_interface *interface)
|
|
||||||
{
|
|
||||||
struct em28xx *dev;
|
|
||||||
struct em28xx_ops *ops = NULL;
|
|
||||||
|
|
||||||
dev = usb_get_intfdata(interface);
|
|
||||||
usb_set_intfdata(interface, NULL);
|
|
||||||
|
|
||||||
if (!dev)
|
|
||||||
return;
|
|
||||||
|
|
||||||
em28xx_info("disconnecting %s\n", dev->vdev->name);
|
|
||||||
|
|
||||||
/* wait until all current v4l2 io is finished then deallocate
|
|
||||||
resources */
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
|
|
||||||
wake_up_interruptible_all(&dev->open);
|
|
||||||
|
|
||||||
if (dev->users) {
|
|
||||||
em28xx_warn
|
|
||||||
("device /dev/video%d is open! Deregistration and memory "
|
|
||||||
"deallocation are deferred on close.\n",
|
|
||||||
dev->vdev->num);
|
|
||||||
|
|
||||||
dev->state |= DEV_MISCONFIGURED;
|
|
||||||
em28xx_uninit_isoc(dev);
|
|
||||||
dev->state |= DEV_DISCONNECTED;
|
|
||||||
wake_up_interruptible(&dev->wait_frame);
|
|
||||||
wake_up_interruptible(&dev->wait_stream);
|
|
||||||
} else {
|
|
||||||
dev->state |= DEV_DISCONNECTED;
|
|
||||||
em28xx_release_resources(dev);
|
|
||||||
}
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
|
|
||||||
mutex_lock(&em28xx_extension_devlist_lock);
|
|
||||||
if (!list_empty(&em28xx_extension_devlist)) {
|
|
||||||
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
|
|
||||||
ops->fini(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&em28xx_extension_devlist_lock);
|
|
||||||
|
|
||||||
if (!dev->users) {
|
|
||||||
kfree(dev->alt_max_pkt_size);
|
|
||||||
kfree(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct usb_driver em28xx_usb_driver = {
|
|
||||||
.name = "em28xx",
|
|
||||||
.probe = em28xx_usb_probe,
|
|
||||||
.disconnect = em28xx_usb_disconnect,
|
|
||||||
.id_table = em28xx_id_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init em28xx_module_init(void)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
|
|
||||||
printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
|
|
||||||
(EM28XX_VERSION_CODE >> 16) & 0xff,
|
|
||||||
(EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
|
|
||||||
#ifdef SNAPSHOT
|
|
||||||
printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
|
|
||||||
SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* register this driver with the USB subsystem */
|
|
||||||
result = usb_register(&em28xx_usb_driver);
|
|
||||||
if (result)
|
|
||||||
em28xx_err(DRIVER_NAME
|
|
||||||
" usb_register failed. Error number %d.\n", result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit em28xx_module_exit(void)
|
|
||||||
{
|
|
||||||
/* deregister this driver with the USB subsystem */
|
|
||||||
usb_deregister(&em28xx_usb_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(em28xx_module_init);
|
|
||||||
module_exit(em28xx_module_exit);
|
|
||||||
|
|
||||||
|
|
|
@ -581,10 +581,21 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
||||||
void em28xx_uninit_isoc(struct em28xx *dev);
|
void em28xx_uninit_isoc(struct em28xx *dev);
|
||||||
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
|
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
|
||||||
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
|
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
|
||||||
|
void em28xx_wake_i2c(struct em28xx *dev);
|
||||||
/* Provided by em28xx-video.c */
|
void em28xx_remove_from_devlist(struct em28xx *dev);
|
||||||
|
void em28xx_add_into_devlist(struct em28xx *dev);
|
||||||
|
struct em28xx *em28xx_get_device(struct inode *inode,
|
||||||
|
enum v4l2_buf_type *fh_type,
|
||||||
|
int *has_radio);
|
||||||
int em28xx_register_extension(struct em28xx_ops *dev);
|
int em28xx_register_extension(struct em28xx_ops *dev);
|
||||||
void em28xx_unregister_extension(struct em28xx_ops *dev);
|
void em28xx_unregister_extension(struct em28xx_ops *dev);
|
||||||
|
void em28xx_init_extension(struct em28xx *dev);
|
||||||
|
void em28xx_close_extension(struct em28xx *dev);
|
||||||
|
|
||||||
|
/* Provided by em28xx-video.c */
|
||||||
|
int em28xx_analog_config(struct em28xx *dev);
|
||||||
|
int em28xx_register_analog_devices(struct em28xx *dev);
|
||||||
|
void em28xx_release_analog_resources(struct em28xx *dev);
|
||||||
|
|
||||||
/* Provided by em28xx-cards.c */
|
/* Provided by em28xx-cards.c */
|
||||||
extern int em2800_variant_detect(struct usb_device *udev, int model);
|
extern int em2800_variant_detect(struct usb_device *udev, int model);
|
||||||
|
@ -595,6 +606,7 @@ extern struct usb_device_id em28xx_id_table[];
|
||||||
extern const unsigned int em28xx_bcount;
|
extern const unsigned int em28xx_bcount;
|
||||||
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
|
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
|
||||||
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
|
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
|
||||||
|
void em28xx_release_resources(struct em28xx *dev);
|
||||||
|
|
||||||
/* Provided by em28xx-input.c */
|
/* Provided by em28xx-input.c */
|
||||||
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
|
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче