aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video/em28xx/em28xx-video.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-video.c')
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c881
1 files changed, 278 insertions, 603 deletions
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 4ea1f1e0489..416b691c33c 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -39,6 +39,7 @@
#include "em28xx.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -47,9 +48,8 @@
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
"Sascha Sommer <saschasommer@freenet.de>"
-#define DRIVER_NAME "em28xx"
#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 {\
if (video_debug) \
@@ -72,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
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 vbi_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(vbi_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(vbi_nr, "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");
@@ -93,8 +87,15 @@ static unsigned int video_debug;
module_param(video_debug, int, 0644);
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 */
+static struct em28xx_fmt format[] = {
+ {
+ .name = "16bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ },
+};
/* supported controls */
/* Common to all boards */
@@ -120,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
}
};
-static struct usb_driver em28xx_usb_driver;
-
/* ------------------------------------------------------------------
DMA and thread functions
------------------------------------------------------------------*/
@@ -386,16 +385,18 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
struct em28xx *dev = fh->dev;
struct v4l2_frequency f;
- *size = 16 * fh->dev->width * fh->dev->height >> 3;
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+
if (0 == *count)
*count = EM28XX_DEF_BUF;
if (*count < EM28XX_MIN_BUF)
*count = EM28XX_MIN_BUF;
- /* Ask tuner to go to analog mode */
+ /* Ask tuner to go to analog or radio mode */
memset(&f, 0, sizeof(f));
f.frequency = dev->ctl_freq;
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
@@ -438,9 +439,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx *dev = fh->dev;
int rc = 0, urb_init = 0;
- /* FIXME: It assumes depth = 16 */
- /* The only currently supported format is 16 bits/pixel */
- buf->vb.size = 16 * dev->width * dev->height >> 3;
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
@@ -508,56 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
/********************* 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->is_em2800) {
- retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
- /* enable vbi capturing */
-
-/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */
-/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */
- em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1);
-
- dev->mute = 1; /* maybe not the right place... */
- dev->volume = 0x1f;
-
- em28xx_outfmt_set_yuv422(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)
{
struct v4l2_routing route;
@@ -566,10 +515,14 @@ static void video_mux(struct em28xx *dev, int index)
route.output = 0;
dev->ctl_input = index;
dev->ctl_ainput = INPUT(index)->amux;
+ dev->ctl_aoutput = INPUT(index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- if (dev->has_msp34xx) {
+ if (dev->board.has_msp34xx) {
if (dev->i2s_speed) {
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ,
&dev->i2s_speed);
@@ -595,12 +548,10 @@ static int res_get(struct em28xx_fh *fh)
return rc;
if (dev->stream_on)
- return -EINVAL;
+ return -EBUSY;
- mutex_lock(&dev->lock);
dev->stream_on = 1;
fh->stream_on = 1;
- mutex_unlock(&dev->lock);
return rc;
}
@@ -613,10 +564,8 @@ static void res_free(struct em28xx_fh *fh)
{
struct em28xx *dev = fh->dev;
- mutex_lock(&dev->lock);
fh->stream_on = 0;
dev->stream_on = 0;
- mutex_unlock(&dev->lock);
}
/*
@@ -703,8 +652,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
@@ -716,6 +665,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -726,24 +686,30 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
unsigned int maxw = norm_maxw(dev);
unsigned int maxh = norm_maxh(dev);
unsigned int hscale, vscale;
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
/* width must even because of the YUYV format
height must be even because of interlacing */
height &= 0xfffe;
- width &= 0xfffe;
+ width &= 0xfffe;
- if (height < 32)
+ if (unlikely(height < 32))
height = 32;
- if (height > maxh)
+ if (unlikely(height > maxh))
height = maxh;
- if (width < 48)
+ if (unlikely(width < 48))
width = 48;
- if (width > maxw)
+ if (unlikely(width > maxw))
width = maxw;
- mutex_lock(&dev->lock);
-
- if (dev->is_em2800) {
+ if (dev->board.is_em2800) {
/* the em2800 can only scale down to 50% */
if (height % (maxh / 2))
height = maxh;
@@ -766,13 +732,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = width;
f->fmt.pix.height = height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = width * 2;
- f->fmt.pix.sizeimage = width * 2 * height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- mutex_unlock(&dev->lock);
return 0;
}
@@ -782,14 +747,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
+ struct em28xx_fmt *fmt;
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+
vidioc_try_fmt_vid_cap(file, priv, f);
- mutex_lock(&dev->lock);
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ rc = -EINVAL;
+ goto out;
+ }
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
em28xx_errdev("%s queue busy\n", __func__);
@@ -806,6 +778,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
/* set new image size */
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
+ dev->format = fmt;
get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
em28xx_set_alternate(dev);
@@ -831,15 +804,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
mutex_lock(&dev->lock);
dev->norm = *norm;
- mutex_unlock(&dev->lock);
/* Adjusts width/height, if needed */
f.fmt.pix.width = dev->width;
f.fmt.pix.height = dev->height;
vidioc_try_fmt_vid_cap(file, priv, &f);
- mutex_lock(&dev->lock);
-
/* set new image size */
dev->width = f.fmt.pix.width;
dev->height = f.fmt.pix.height;
@@ -928,20 +898,38 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- unsigned int index = a->index;
-
- if (a->index > 1)
- return -EINVAL;
-
- index = dev->ctl_ainput;
- if (index == 0)
+ switch (a->index) {
+ case EM28XX_AMUX_VIDEO:
strcpy(a->name, "Television");
- else
+ break;
+ case EM28XX_AMUX_LINE_IN:
strcpy(a->name, "Line In");
+ break;
+ case EM28XX_AMUX_VIDEO2:
+ strcpy(a->name, "Television alt");
+ break;
+ case EM28XX_AMUX_PHONE:
+ strcpy(a->name, "Phone");
+ break;
+ case EM28XX_AMUX_MIC:
+ strcpy(a->name, "Mic");
+ break;
+ case EM28XX_AMUX_CD:
+ strcpy(a->name, "CD");
+ break;
+ case EM28XX_AMUX_AUX:
+ strcpy(a->name, "Aux");
+ break;
+ case EM28XX_AMUX_PCM_OUT:
+ strcpy(a->name, "PCM");
+ break;
+ default:
+ return -EINVAL;
+ }
+ a->index = dev->ctl_ainput;
a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
return 0;
}
@@ -951,9 +939,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
+ mutex_lock(&dev->lock);
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ dev->ctl_aoutput = INPUT(a->index)->aout;
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -974,7 +968,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
qc->id = id;
- if (!dev->has_msp34xx) {
+ if (!dev->board.has_msp34xx) {
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
@@ -1002,17 +996,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
rc = check_dev(dev);
if (rc < 0)
return rc;
- mutex_lock(&dev->lock);
+ rc = 0;
- if (!dev->has_msp34xx)
- rc = em28xx_get_ctrl(dev, ctrl);
- else
- rc = -EINVAL;
+ mutex_lock(&dev->lock);
- if (rc == -EINVAL) {
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
- rc = 0;
- }
+ else
+ rc = em28xx_get_ctrl(dev, ctrl);
mutex_unlock(&dev->lock);
return rc;
@@ -1032,7 +1023,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
else {
rc = 1;
@@ -1112,8 +1103,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
+ mutex_lock(&dev->lock);
f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1143,6 +1136,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1159,46 +1153,111 @@ static int em28xx_reg_len(int reg)
}
}
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_CHIP_IDENT, chip);
+
+ return 0;
+}
+
+
static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_register *reg)
+ struct v4l2_dbg_register *reg)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int ret;
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ ret = em28xx_read_ac97(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ reg->size = 1;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
- if (em28xx_reg_len(reg->reg) == 1) {
+ /* Match host */
+ reg->size = em28xx_reg_len(reg->reg);
+ if (reg->size == 1) {
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+
if (ret < 0)
return ret;
reg->val = ret;
} else {
- __le64 val = 0;
+ __le16 val = 0;
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
reg->reg, (char *)&val, 2);
+ mutex_unlock(&dev->lock);
if (ret < 0)
return ret;
- reg->val = le64_to_cpu(val);
+ reg->val = le16_to_cpu(val);
}
return 0;
}
static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_register *reg)
+ struct v4l2_dbg_register *reg)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- __le64 buf;
+ __le16 buf;
+ int rc;
- buf = cpu_to_le64(reg->val);
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_ac97(dev, reg->reg, reg->val);
+ mutex_unlock(&dev->lock);
+
+ return rc;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
+ return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ buf = cpu_to_le16(reg->val);
- return em28xx_write_regs(dev, reg->reg, (char *)&buf,
- em28xx_reg_len(reg->reg));
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
#endif
@@ -1235,10 +1294,15 @@ static int vidioc_streamon(struct file *file, void *priv,
return rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+
+ if (likely(rc >= 0))
+ rc = videobuf_streamon(&fh->vb_vidq);
- return (videobuf_streamon(&fh->vb_vidq));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
static int vidioc_streamoff(struct file *file, void *priv,
@@ -1257,9 +1321,13 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (type != fh->type)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
videobuf_streamoff(&fh->vb_vidq);
res_free(fh);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1271,7 +1339,7 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
@@ -1288,15 +1356,13 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmtd)
+ struct v4l2_fmtdesc *f)
{
- if (fmtd->index != 0)
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
return -EINVAL;
- fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd->description, "Packed YUY2");
- fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
- memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
return 0;
}
@@ -1424,7 +1490,7 @@ static int radio_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
@@ -1442,7 +1508,10 @@ static int radio_g_tuner(struct file *file, void *priv,
strcpy(t->name, "Radio");
t->type = V4L2_TUNER_RADIO;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1474,7 +1543,9 @@ static int radio_s_tuner(struct file *file, void *priv,
if (0 != t->index)
return -EINVAL;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1513,31 +1584,16 @@ static int radio_queryctrl(struct file *file, void *priv,
* em28xx_v4l2_open()
* inits the device and starts isoc transfer
*/
-static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
+static int em28xx_v4l2_open(struct file *filp)
{
- int minor = iminor(inode);
- int errCode = 0, radio = 0;
- struct em28xx *h, *dev = NULL;
+ int minor = video_devdata(filp)->minor;
+ int errCode = 0, radio;
+ struct em28xx *dev;
+ enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
- enum v4l2_buf_type fh_type = 0;
- mutex_lock(&em28xx_devlist_mutex);
- 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);
+ dev = em28xx_get_device(minor, &fh_type, &radio);
+
if (NULL == dev)
return -ENODEV;
@@ -1571,7 +1627,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
/* Needed, since GPIO might have disabled power of
some i2c device
*/
- em28xx_config_i2c(dev);
+ em28xx_wake_i2c(dev);
}
if (fh->radio) {
@@ -1595,16 +1651,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
* unregisters the v4l2,i2c and usb devices
* 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 */
- em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->num, dev->vbi_dev->num);
- list_del(&dev->devlist);
- if (dev->sbutton_input_dev)
- em28xx_deregister_snapshot_button(dev);
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
@@ -1613,6 +1664,8 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
+ em28xx_info("V4L2 device /dev/vbi%d deregistered\n",
+ dev->vbi_dev->num);
if (-1 != dev->vbi_dev->minor)
video_unregister_device(dev->vbi_dev);
else
@@ -1620,17 +1673,14 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vbi_dev = NULL;
}
if (dev->vdev) {
+ em28xx_info("V4L2 device /dev/video%d deregistered\n",
+ dev->vdev->num);
if (-1 != dev->vdev->minor)
video_unregister_device(dev->vdev);
else
video_device_release(dev->vdev);
dev->vdev = NULL;
}
- em28xx_i2c_unregister(dev);
- usb_put_dev(dev->udev);
-
- /* Mark device as unused */
- em28xx_devused &= ~(1<<dev->devno);
}
/*
@@ -1638,7 +1688,7 @@ static void em28xx_release_resources(struct em28xx *dev)
* stops streaming and deallocates all resources allocated by the v4l2
* calls and ioctls
*/
-static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
+static int em28xx_v4l2_close(struct file *filp)
{
struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
@@ -1647,11 +1697,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
+ mutex_lock(&dev->lock);
if (res_check(fh))
res_free(fh);
- mutex_lock(&dev->lock);
-
if (dev->users == 1) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
@@ -1665,9 +1714,12 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
return 0;
}
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
/* do this before setting alternate! */
em28xx_uninit_isoc(dev);
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
/* set alternate 0 */
dev->alt = 0;
@@ -1706,8 +1758,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (unlikely(res_get(fh)))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
@@ -1729,7 +1785,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
if (rc < 0)
return rc;
- if (unlikely(res_get(fh) < 0))
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
return POLLERR;
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
@@ -1747,13 +1807,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
struct em28xx *dev = fh->dev;
int rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
-
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
+
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
@@ -1764,7 +1828,7 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
return rc;
}
-static const struct file_operations em28xx_v4l_fops = {
+static const struct v4l2_file_operations em28xx_v4l_fops = {
.owner = THIS_MODULE,
.open = em28xx_v4l2_open,
.release = em28xx_v4l2_close,
@@ -1772,8 +1836,6 @@ static const struct file_operations em28xx_v4l_fops = {
.poll = em28xx_v4l2_poll,
.mmap = em28xx_v4l2_mmap,
.ioctl = video_ioctl2,
- .llseek = no_llseek,
- .compat_ioctl = v4l_compat_ioctl32,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -1810,6 +1872,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#endif
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
@@ -1827,13 +1890,11 @@ static const struct video_device em28xx_video_template = {
.current_norm = V4L2_STD_PAL,
};
-static const struct file_operations radio_fops = {
+static const struct v4l2_file_operations radio_fops = {
.owner = THIS_MODULE,
.open = em28xx_v4l2_open,
.release = em28xx_v4l2_close,
.ioctl = video_ioctl2,
- .compat_ioctl = v4l_compat_ioctl32,
- .llseek = no_llseek,
};
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
@@ -1865,44 +1926,6 @@ static struct video_device em28xx_radio_template = {
/******************************** 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,
const struct video_device *template,
@@ -1925,11 +1948,43 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return vfd;
}
-
-static int register_analog_devices(struct em28xx *dev)
+int em28xx_register_analog_devices(struct em28xx *dev)
{
int ret;
+ 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;
+
/* allocate and fill video video_device struct */
dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
if (!dev->vdev) {
@@ -1978,383 +2033,3 @@ static int register_analog_devices(struct em28xx *dev)
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->lock);
- 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->is_em2800 = em28xx_boards[dev->model].is_em2800;
-
- 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_analog_set(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n",
- __func__, errCode);
- return 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->has_msp34xx) {
- /* Send a reset to other chips via gpio */
- errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
- 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_regs_req(dev, 0x00, 0x08, "\xff", 1);
- 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);
-
- return 0;
-
-fail_reg_devices:
- mutex_unlock(&dev->lock);
- 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
- request_module("em28xx-alsa");
-
- if (dev->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;
-
- 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",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- endpoint = &interface->cur_altsetting->endpoint[1].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) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- 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;
- }
- }
-
- printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
- dev->has_audio_class ? "Has" : "Doesn't have");
-
- /* compute alternate max packet sizes */
- uif = udev->actconfig->interface[0];
-
- dev->num_alt = uif->num_altsetting;
- em28xx_info("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[1].desc.
- wMaxPacketSize);
- dev->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- em28xx_info("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 */
- retval = em28xx_init_dev(&dev, udev, nr);
- if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
- kfree(dev);
-
- return retval;
- }
-
- em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
-
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
- request_modules(dev);
-
- 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);