summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/Documentation/video4linux/CARDLIST.cx881
-rw-r--r--linux/Documentation/video4linux/CARDLIST.em28xx2
-rw-r--r--linux/Documentation/video4linux/CARDLIST.saa71344
-rw-r--r--linux/drivers/media/common/tuners/tuner-xc2028.c4
-rw-r--r--linux/drivers/media/common/tuners/tuner-xc2028.h1
-rw-r--r--linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c1
-rw-r--r--linux/drivers/media/dvb/dvb-usb/cxusb.c16
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dib0700_devices.c8
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c2
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h1
-rw-r--r--linux/drivers/media/dvb/frontends/at76c651.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx22700.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx22702.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx24110.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx24113.c6
-rw-r--r--linux/drivers/media/dvb/frontends/dvb_dummy_fe.c6
-rw-r--r--linux/drivers/media/dvb/frontends/l64781.c2
-rw-r--r--linux/drivers/media/dvb/frontends/lgs8gl5.c2
-rw-r--r--linux/drivers/media/dvb/frontends/mt312.c2
-rw-r--r--linux/drivers/media/dvb/frontends/nxt6000.c2
-rw-r--r--linux/drivers/media/dvb/frontends/or51132.c2
-rw-r--r--linux/drivers/media/dvb/frontends/or51211.c2
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1409.c2
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1411.c2
-rw-r--r--linux/drivers/media/dvb/frontends/si21xx.c2
-rw-r--r--linux/drivers/media/dvb/frontends/sp8870.c2
-rw-r--r--linux/drivers/media/dvb/frontends/sp887x.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0288.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0297.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0299.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda10021.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda10048.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda1004x.c4
-rw-r--r--linux/drivers/media/dvb/frontends/tda10086.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda8083.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda80xx.c2
-rw-r--r--linux/drivers/media/dvb/frontends/ves1820.c2
-rw-r--r--linux/drivers/media/dvb/frontends/ves1x93.c2
-rw-r--r--linux/drivers/media/dvb/siano/Kconfig2
-rw-r--r--linux/drivers/media/dvb/siano/sms-cards.c2
-rw-r--r--linux/drivers/media/dvb/siano/smscoreapi.c2
-rw-r--r--linux/drivers/media/radio/Kconfig26
-rw-r--r--linux/drivers/media/radio/Makefile2
-rw-r--r--linux/drivers/media/radio/radio-si470x.c1870
-rw-r--r--linux/drivers/media/radio/si470x/Kconfig37
-rw-r--r--linux/drivers/media/radio/si470x/Makefile9
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-common.c798
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-i2c.c401
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-usb.c988
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x.h226
-rw-r--r--linux/drivers/media/video/Kconfig2
-rw-r--r--linux/drivers/media/video/bw-qcam.c2
-rw-r--r--linux/drivers/media/video/cx88/cx88-cards.c57
-rw-r--r--linux/drivers/media/video/cx88/cx88-dvb.c2
-rw-r--r--linux/drivers/media/video/cx88/cx88-input.c1
-rw-r--r--linux/drivers/media/video/cx88/cx88.h1
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c165
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-core.c10
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-dvb.c2
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-reg.h3
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c97
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h4
-rw-r--r--linux/drivers/media/video/gspca/gspca.c7
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c4
-rw-r--r--linux/drivers/media/video/gspca/vc032x.c4
-rw-r--r--linux/drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--linux/drivers/media/video/mt9v011.c142
-rw-r--r--linux/drivers/media/video/mx1_camera.c6
-rw-r--r--linux/drivers/media/video/mx3_camera.c19
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-audio.c5
-rw-r--r--linux/drivers/media/video/pwc/pwc-v4l.c2
-rw-r--r--linux/drivers/media/video/pxa_camera.c6
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-cards.c34
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-dvb.c6
-rw-r--r--linux/drivers/media/video/saa7134/saa7134.h4
-rw-r--r--linux/drivers/media/video/sh_mobile_ceu_camera.c5
-rw-r--r--linux/drivers/media/video/stk-webcam.c4
-rw-r--r--linux/drivers/media/video/uvc/uvc_ctrl.c82
-rw-r--r--linux/drivers/media/video/uvc/uvc_driver.c438
-rw-r--r--linux/drivers/media/video/uvc/uvc_isight.c7
-rw-r--r--linux/drivers/media/video/uvc/uvc_status.c4
-rw-r--r--linux/drivers/media/video/uvc/uvc_v4l2.c271
-rw-r--r--linux/drivers/media/video/uvc/uvc_video.c417
-rw-r--r--linux/drivers/media/video/uvc/uvcvideo.h117
-rw-r--r--linux/drivers/media/video/v4l2-ioctl.c15
-rw-r--r--linux/drivers/media/video/zoran/zoran_card.c2
-rw-r--r--v4l2-apps/util/v4l2-ctl.cpp108
-rw-r--r--v4l2-apps/util/v4l2-dbg-micron.h46
-rw-r--r--v4l2-apps/util/v4l2-dbg.cpp19
89 files changed, 3905 insertions, 2686 deletions
diff --git a/linux/Documentation/video4linux/CARDLIST.cx88 b/linux/Documentation/video4linux/CARDLIST.cx88
index 0736518b2..3385f8b09 100644
--- a/linux/Documentation/video4linux/CARDLIST.cx88
+++ b/linux/Documentation/video4linux/CARDLIST.cx88
@@ -80,3 +80,4 @@
79 -> Terratec Cinergy HT PCI MKII [153b:1177]
80 -> Hauppauge WinTV-IR Only [0070:9290]
81 -> Leadtek WinFast DTV1800 Hybrid [107d:6654]
+ 82 -> WinFast DTV2000 H rev. J [107d:6f2b]
diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx
index 084a7b624..66e5f8295 100644
--- a/linux/Documentation/video4linux/CARDLIST.em28xx
+++ b/linux/Documentation/video4linux/CARDLIST.em28xx
@@ -1,5 +1,5 @@
0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800]
- 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
+ 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036]
3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208]
4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201]
diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134
index 15562427e..c913e5614 100644
--- a/linux/Documentation/video4linux/CARDLIST.saa7134
+++ b/linux/Documentation/video4linux/CARDLIST.saa7134
@@ -153,8 +153,8 @@
152 -> Asus Tiger Rev:1.00 [1043:4857]
153 -> Kworld Plus TV Analog Lite PCI [17de:7128]
154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d]
-155 -> Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid [0070:6706,0070:6708]
-156 -> Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a]
+155 -> Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid [0070:6706,0070:6708]
+156 -> Hauppauge WinTV-HVR1120 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a]
157 -> Avermedia AVerTV Studio 507UA [1461:a11b]
158 -> AVerMedia Cardbus TV/Radio (E501R) [1461:b7e9]
159 -> Beholder BeholdTV 505 RDS [0000:505B]
diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c
index 3de5d6685..3cf380e6b 100644
--- a/linux/drivers/media/common/tuners/tuner-xc2028.c
+++ b/linux/drivers/media/common/tuners/tuner-xc2028.c
@@ -1123,8 +1123,8 @@ static int xc2028_sleep(struct dvb_frontend *fe)
struct xc2028_data *priv = fe->tuner_priv;
int rc = 0;
- /* Avoid firmware reload on slow devices */
- if (no_poweroff)
+ /* Avoid firmware reload on slow devices or if PM disabled */
+ if (no_poweroff || priv->ctrl.disable_power_mgmt)
return 0;
tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h
index 19de7928a..a90c35d50 100644
--- a/linux/drivers/media/common/tuners/tuner-xc2028.h
+++ b/linux/drivers/media/common/tuners/tuner-xc2028.h
@@ -38,6 +38,7 @@ struct xc2028_ctrl {
unsigned int input1:1;
unsigned int vhfbw7:1;
unsigned int uhfbw8:1;
+ unsigned int disable_power_mgmt:1;
unsigned int demod;
enum firmware_type type:2;
};
diff --git a/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
index 649f25cca..9cd51ac12 100644
--- a/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
+++ b/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
@@ -275,6 +275,7 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe,
param.tps = cpu_to_le16(compute_tps(fep));
param.freq = cpu_to_le32(fep->frequency / 1000);
param.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+ param.flags = 0;
err = dvb_usb_generic_rw(state->d,
(char *)&param, sizeof(param),
diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c
index f467cad8a..05da84c90 100644
--- a/linux/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c
@@ -38,7 +38,7 @@
#include "mxl5005s.h"
#include "dib7000p.h"
#include "dib0070.h"
-#include "lgs8gl5.h"
+#include "lgs8gxx.h"
/* debug */
static int dvb_usb_cxusb_debug;
@@ -1097,8 +1097,18 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
return -EIO;
}
-static struct lgs8gl5_config lgs8gl5_cfg = {
+static struct lgs8gxx_config d680_lgs8gl5_cfg = {
+ .prod = LGS8GXX_PROD_LGS8GL5,
.demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 0,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 5725, /* 5.725 MHz */
+ .if_neg_center = 0,
+ .ext_adc = 0,
+ .adc_signed = 0,
+ .if_neg_edge = 0,
};
static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1138,7 +1148,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap);
+ adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
if (adap->fe == NULL)
return -EIO;
diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 8ec98f6e0..81cda8061 100644
--- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -1501,6 +1501,7 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_H) },
{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) },
{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) },
+ { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1826,7 +1827,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 8,
+ .num_device_descs = 9,
.devices = {
{ "Terratec Cinergy HT USB XE",
{ &dib0700_usb_id_table[27], NULL },
@@ -1860,7 +1861,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[51], NULL },
{ NULL },
},
-
+ { "YUAN High-Tech STK7700D",
+ { &dib0700_usb_id_table[54], NULL },
+ { NULL },
+ },
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
index 326f76089..cead089bb 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
@@ -19,7 +19,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d)
return -EINVAL;
}
- strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
+ strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
d->i2c_adap.algo = d->props.i2c_algo;
d->i2c_adap.algo_data = NULL;
diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 9593b7289..6eccce1a7 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -252,6 +252,7 @@
#define USB_PID_YUAN_STK7700PH 0x1f08
#define USB_PID_YUAN_PD378S 0x2edc
#define USB_PID_YUAN_MC770 0x0871
+#define USB_PID_YUAN_STK7700D 0x1efc
#define USB_PID_DW2102 0x2102
#define USB_PID_XTENSIONS_XD_380 0x0381
#define USB_PID_TELESTAR_STARSTICK_2 0x8000
diff --git a/linux/drivers/media/dvb/frontends/at76c651.c b/linux/drivers/media/dvb/frontends/at76c651.c
index 33486d54c..510e08f5a 100644
--- a/linux/drivers/media/dvb/frontends/at76c651.c
+++ b/linux/drivers/media/dvb/frontends/at76c651.c
@@ -369,7 +369,7 @@ struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
struct at76c651_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct at76c651_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx22700.c b/linux/drivers/media/dvb/frontends/cx22700.c
index ace5cb171..fbd838eca 100644
--- a/linux/drivers/media/dvb/frontends/cx22700.c
+++ b/linux/drivers/media/dvb/frontends/cx22700.c
@@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct cx22700_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c
index 5d1abe34b..00b5c7e91 100644
--- a/linux/drivers/media/dvb/frontends/cx22702.c
+++ b/linux/drivers/media/dvb/frontends/cx22702.c
@@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
struct cx22702_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c
index 87ae29db0..ffbcfabd8 100644
--- a/linux/drivers/media/dvb/frontends/cx24110.c
+++ b/linux/drivers/media/dvb/frontends/cx24110.c
@@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
int ret;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c
index e4fd533a4..075b2b57c 100644
--- a/linux/drivers/media/dvb/frontends/cx24113.c
+++ b/linux/drivers/media/dvb/frontends/cx24113.c
@@ -303,6 +303,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
{
s32 N;
s64 F;
+ u64 dividend;
u8 R, r;
u8 vcodiv;
u8 factor;
@@ -346,7 +347,10 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
F = freq_hz;
F *= (u64) (R * vcodiv * 262144);
dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
- do_div(F, state->config->xtal_khz*1000 * factor * 2);
+ /* do_div needs an u64 as first argument */
+ dividend = F;
+ do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
+ F = dividend;
dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
F -= (N + 32) * 262144;
diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
index db8a937cc..a7fc7e53a 100644
--- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
diff --git a/linux/drivers/media/dvb/frontends/l64781.c b/linux/drivers/media/dvb/frontends/l64781.c
index e1e70e9e0..3051b64aa 100644
--- a/linux/drivers/media/dvb/frontends/l64781.c
+++ b/linux/drivers/media/dvb/frontends/l64781.c
@@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
{ .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/lgs8gl5.c b/linux/drivers/media/dvb/frontends/lgs8gl5.c
index 380ae1868..8780905bc 100644
--- a/linux/drivers/media/dvb/frontends/lgs8gl5.c
+++ b/linux/drivers/media/dvb/frontends/lgs8gl5.c
@@ -411,7 +411,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
dprintk("%s\n", __func__);
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c
index f348f8bea..4d14f084d 100644
--- a/linux/drivers/media/dvb/frontends/mt312.c
+++ b/linux/drivers/media/dvb/frontends/mt312.c
@@ -784,7 +784,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config,
struct mt312_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/nxt6000.c b/linux/drivers/media/dvb/frontends/nxt6000.c
index 0eef22dbf..a763ec756 100644
--- a/linux/drivers/media/dvb/frontends/nxt6000.c
+++ b/linux/drivers/media/dvb/frontends/nxt6000.c
@@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct nxt6000_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c
index 8133ea3cd..38e67accb 100644
--- a/linux/drivers/media/dvb/frontends/or51132.c
+++ b/linux/drivers/media/dvb/frontends/or51132.c
@@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct or51132_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/or51211.c b/linux/drivers/media/dvb/frontends/or51211.c
index 16cf2fdd5..c709ce677 100644
--- a/linux/drivers/media/dvb/frontends/or51211.c
+++ b/linux/drivers/media/dvb/frontends/or51211.c
@@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
struct or51211_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/s5h1409.c b/linux/drivers/media/dvb/frontends/s5h1409.c
index 3e08d985d..fb3011518 100644
--- a/linux/drivers/media/dvb/frontends/s5h1409.c
+++ b/linux/drivers/media/dvb/frontends/s5h1409.c
@@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c
index 66e2dd6d6..d8adf1e32 100644
--- a/linux/drivers/media/dvb/frontends/s5h1411.c
+++ b/linux/drivers/media/dvb/frontends/s5h1411.c
@@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c
index 216a532ab..ee0a0a2dd 100644
--- a/linux/drivers/media/dvb/frontends/si21xx.c
+++ b/linux/drivers/media/dvb/frontends/si21xx.c
@@ -1003,7 +1003,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
dprintk("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/sp8870.c b/linux/drivers/media/dvb/frontends/sp8870.c
index 1c9a9b405..b85eb60a8 100644
--- a/linux/drivers/media/dvb/frontends/sp8870.c
+++ b/linux/drivers/media/dvb/frontends/sp8870.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
struct sp8870_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c
index 559509ab4..4a7c3d842 100644
--- a/linux/drivers/media/dvb/frontends/sp887x.c
+++ b/linux/drivers/media/dvb/frontends/sp887x.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
struct sp887x_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/stv0288.c b/linux/drivers/media/dvb/frontends/stv0288.c
index ff1194de3..2930a5d67 100644
--- a/linux/drivers/media/dvb/frontends/stv0288.c
+++ b/linux/drivers/media/dvb/frontends/stv0288.c
@@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c
index 1c5a0ae82..2e709a7a5 100644
--- a/linux/drivers/media/dvb/frontends/stv0297.c
+++ b/linux/drivers/media/dvb/frontends/stv0297.c
@@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
struct stv0297_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c
index 6c1cb1973..968874469 100644
--- a/linux/drivers/media/dvb/frontends/stv0299.c
+++ b/linux/drivers/media/dvb/frontends/stv0299.c
@@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c
index c6f90f33a..242636e59 100644
--- a/linux/drivers/media/dvb/frontends/tda10021.c
+++ b/linux/drivers/media/dvb/frontends/tda10021.c
@@ -413,7 +413,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
u8 id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c
index edf5a19a3..fd8c4cf00 100644
--- a/linux/drivers/media/dvb/frontends/tda10048.c
+++ b/linux/drivers/media/dvb/frontends/tda10048.c
@@ -1100,7 +1100,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
dprintk(1, "%s()\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c
index 4981cef8b..f2a8abe0a 100644
--- a/linux/drivers/media/dvb/frontends/tda1004x.c
+++ b/linux/drivers/media/dvb/frontends/tda1004x.c
@@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10045 state\n");
return NULL;
@@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10046 state\n");
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c
index c749f96c2..502ce67cf 100644
--- a/linux/drivers/media/dvb/frontends/tda10086.c
+++ b/linux/drivers/media/dvb/frontends/tda10086.c
@@ -746,7 +746,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
dprintk ("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL);
if (!state)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c
index 5b843b2e6..9369f7442 100644
--- a/linux/drivers/media/dvb/frontends/tda8083.c
+++ b/linux/drivers/media/dvb/frontends/tda8083.c
@@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
struct tda8083_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda80xx.c b/linux/drivers/media/dvb/frontends/tda80xx.c
index 83fc57a22..e04fd984f 100644
--- a/linux/drivers/media/dvb/frontends/tda80xx.c
+++ b/linux/drivers/media/dvb/frontends/tda80xx.c
@@ -646,7 +646,7 @@ struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
int ret;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c
index a184597f1..6e78e4865 100644
--- a/linux/drivers/media/dvb/frontends/ves1820.c
+++ b/linux/drivers/media/dvb/frontends/ves1820.c
@@ -374,7 +374,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
struct ves1820_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/ves1x93.c b/linux/drivers/media/dvb/frontends/ves1x93.c
index bd558960b..8d7854c2f 100644
--- a/linux/drivers/media/dvb/frontends/ves1x93.c
+++ b/linux/drivers/media/dvb/frontends/ves1x93.c
@@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
u8 identity;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/siano/Kconfig b/linux/drivers/media/dvb/siano/Kconfig
index dd863f261..88847d1dc 100644
--- a/linux/drivers/media/dvb/siano/Kconfig
+++ b/linux/drivers/media/dvb/siano/Kconfig
@@ -4,7 +4,7 @@
config DVB_SIANO_SMS1XXX
tristate "Siano SMS1XXX USB dongle support"
- depends on DVB_CORE && USB
+ depends on DVB_CORE && USB && INPUT
---help---
Choose Y here if you have a USB dongle with a SMS1XXX chipset.
diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c
index 76bd3cb82..62c1bf604 100644
--- a/linux/drivers/media/dvb/siano/sms-cards.c
+++ b/linux/drivers/media/dvb/siano/sms-cards.c
@@ -116,8 +116,10 @@ static inline void sms_gpio_assign_11xx_default_led_config(
int sms_board_event(struct smscore_device_t *coredev,
enum SMS_BOARD_EVENTS gevent) {
+#if 0
int board_id = smscore_get_board_id(coredev);
struct sms_board *board = sms_get_board(board_id);
+#endif
struct smscore_gpio_config MyGpioConfig;
sms_gpio_assign_11xx_default_led_config(&MyGpioConfig);
diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c
index 54f59f24e..c0306af09 100644
--- a/linux/drivers/media/dvb/siano/smscoreapi.c
+++ b/linux/drivers/media/dvb/siano/smscoreapi.c
@@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
sms_debug("set device mode to %d", mode);
if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) {
+ if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) {
sms_err("invalid mode specified %d", mode);
return -EINVAL;
}
diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig
index 3315cac87..41bbec6df 100644
--- a/linux/drivers/media/radio/Kconfig
+++ b/linux/drivers/media/radio/Kconfig
@@ -351,29 +351,11 @@ config USB_DSBR
To compile this driver as a module, choose M here: the
module will be called dsbr100.
-config USB_SI470X
- tristate "Silicon Labs Si470x FM Radio Receiver support"
- depends on USB && VIDEO_V4L2
- ---help---
- This is a driver for USB devices with the Silicon Labs SI470x
- chip. Currently these devices are known to work:
- - 10c4:818a: Silicon Labs USB FM Radio Reference Design
- - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
- - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
-
- Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
- if you don't want to use the device solely for RDS receiving,
- it is recommended to also select SND_USB_AUDIO.
-
- Please have a look at the documentation, especially on how
- to redirect the audio stream from the radio to your sound device:
- Documentation/video4linux/si470x.txt
-
- Say Y here if you want to connect this type of radio to your
- computer's USB port.
+config RADIO_SI470X
+ bool "Silicon Labs Si470x FM Radio Receiver support"
+ depends on VIDEO_V4L2
- To compile this driver as a module, choose M here: the
- module will be called radio-si470x.
+source "drivers/media/radio/si470x/Kconfig"
config USB_MR800
tristate "AverMedia MR 800 USB FM radio support"
diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile
index 0f2b35b3e..18111e1fe 100644
--- a/linux/drivers/media/radio/Makefile
+++ b/linux/drivers/media/radio/Makefile
@@ -17,7 +17,7 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
-obj-$(CONFIG_USB_SI470X) += radio-si470x.o
+obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c
deleted file mode 100644
index 5b4cab3d0..000000000
--- a/linux/drivers/media/radio/radio-si470x.c
+++ /dev/null
@@ -1,1870 +0,0 @@
-/*
- * drivers/media/radio/radio-si470x.c
- *
- * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers:
- * - Silicon Labs USB FM Radio Reference Design
- * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
- * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
- * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear)
- *
- * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-/*
- * History:
- * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.0
- * - First working version
- * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.1
- * - Improved error handling, every function now returns errno
- * - Improved multi user access (start/mute/stop)
- * - Channel doesn't get lost anymore after start/mute/stop
- * - RDS support added (polling mode via interrupt EP 1)
- * - marked default module parameters with *value*
- * - switched from bit structs to bit masks
- * - header file cleaned and integrated
- * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.2
- * - hex values are now lower case
- * - commented USB ID for ADS/Tech moved on todo list
- * - blacklisted si470x in hid-quirks.c
- * - rds buffer handling functions integrated into *_work, *_read
- * - rds_command in si470x_poll exchanged against simple retval
- * - check for firmware version 15
- * - code order and prototypes still remain the same
- * - spacing and bottom of band codes remain the same
- * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.3
- * - code reordered to avoid function prototypes
- * - switch/case defaults are now more user-friendly
- * - unified comment style
- * - applied all checkpatch.pl v1.12 suggestions
- * except the warning about the too long lines with bit comments
- * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
- * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.4
- * - avoid poss. locking when doing copy_to_user which may sleep
- * - RDS is automatically activated on read now
- * - code cleaned of unnecessary rds_commands
- * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
- * (thanks to Guillaume RAMOUSSE)
- * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.5
- * - number of seek_retries changed to tune_timeout
- * - fixed problem with incomplete tune operations by own buffers
- * - optimization of variables and printf types
- * - improved error logging
- * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Oliver Neukum <oliver@neukum.org>
- * Version 1.0.6
- * - fixed coverity checker warnings in *_usb_driver_disconnect
- * - probe()/open() race by correct ordering in probe()
- * - DMA coherency rules by separate allocation of all buffers
- * - use of endianness macros
- * - abuse of spinlock, replaced by mutex
- * - racy handling of timer in disconnect,
- * replaced by delayed_work
- * - racy interruptible_sleep_on(),
- * replaced with wait_event_interruptible()
- * - handle signals in read()
- * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Oliver Neukum <oliver@neukum.org>
- * Version 1.0.7
- * - usb autosuspend support
- * - unplugging fixed
- * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.8
- * - hardware frequency seek support
- * - afc indication
- * - more safety checks, let si470x_get_freq return errno
- * - vidioc behavior corrected according to v4l2 spec
- * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>
- * - add support for KWorld USB FM Radio FM700
- * - blacklisted KWorld radio in hid-core.c and hid-ids.h
- * 2008-12-03 Mark Lord <mlord@pobox.com>
- * - add support for DealExtreme USB Radio
- * 2009-01-31 Bob Ross <pigiron@gmx.com>
- * - correction of stereo detection/setting
- * - correction of signal strength indicator scaling
- * 2009-01-31 Rick Bronson <rick@efn.org>
- * Tobias Lorenz <tobias.lorenz@gmx.net>
- * - add LED status output
- * - get HW/SW version from scratchpad
- * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>
- * Version 1.0.10
- * - add support for interrupt mode for RDS endpoint,
- * instead of polling.
- * Improves RDS reception significantly
- *
- * ToDo:
- * - add firmware download/update support
- */
-
-
-/* driver definitions */
-#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
-#define DRIVER_NAME "radio-si470x"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
-#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
-#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.10"
-
-
-/* kernel includes */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include <linux/version.h>
-#include "compat.h"
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/rds.h>
-#include <asm/unaligned.h>
-
-
-/* USB Device ID List */
-static struct usb_device_id si470x_usb_driver_id_table[] = {
- /* Silicon Labs USB FM Radio Reference Design */
- { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
- /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
- /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
- /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
- /* Terminating entry */
- { }
-};
-MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
-
-
-
-/**************************************************************************
- * Module Parameters
- **************************************************************************/
-
-/* Radio Nr */
-static int radio_nr = -1;
-module_param(radio_nr, int, 0444);
-MODULE_PARM_DESC(radio_nr, "Radio Nr");
-
-/* Spacing (kHz) */
-/* 0: 200 kHz (USA, Australia) */
-/* 1: 100 kHz (Europe, Japan) */
-/* 2: 50 kHz */
-static unsigned short space = 2;
-module_param(space, ushort, 0444);
-MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
-
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76 - 108 MHz (Japan wide band) */
-/* 2: 76 - 90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
-/* De-emphasis */
-/* 0: 75 us (USA) */
-/* 1: 50 us (Europe, Australia, Japan) */
-static unsigned short de = 1;
-module_param(de, ushort, 0444);
-MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
-
-/* USB timeout */
-static unsigned int usb_timeout = 500;
-module_param(usb_timeout, uint, 0644);
-MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
-
-/* Tune timeout */
-static unsigned int tune_timeout = 3000;
-module_param(tune_timeout, uint, 0644);
-MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
-
-/* Seek timeout */
-static unsigned int seek_timeout = 5000;
-module_param(seek_timeout, uint, 0644);
-MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
-
-/* RDS buffer blocks */
-static unsigned int rds_buf = 100;
-module_param(rds_buf, uint, 0444);
-MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
-
-/* RDS maximum block errors */
-static unsigned short max_rds_errors = 1;
-/* 0 means 0 errors requiring correction */
-/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
-/* 2 means 3-5 errors requiring correction */
-/* 3 means 6+ errors or errors in checkword, correction not possible */
-module_param(max_rds_errors, ushort, 0644);
-MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
-
-
-/**************************************************************************
- * Register Definitions
- **************************************************************************/
-#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
-#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
-#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
-
-#define DEVICEID 0 /* Device ID */
-#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
-#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
-
-#define CHIPID 1 /* Chip ID */
-#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
-#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
-#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
-
-#define POWERCFG 2 /* Power Configuration */
-#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
-#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
-#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
-#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
-#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
-#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
-#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
-#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
-#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
-
-#define CHANNEL 3 /* Channel */
-#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
-#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
-
-#define SYSCONFIG1 4 /* System Configuration 1 */
-#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
-#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
-#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
-#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
-#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
-#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
-#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
-#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
-#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
-
-#define SYSCONFIG2 5 /* System Configuration 2 */
-#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
-#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
-#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
-
-#define SYSCONFIG3 6 /* System Configuration 3 */
-#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
-#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
-#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
-#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
-
-#define TEST1 7 /* Test 1 */
-#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
-
-#define TEST2 8 /* Test 2 */
-/* TEST2 only contains reserved bits */
-
-#define BOOTCONFIG 9 /* Boot Configuration */
-/* BOOTCONFIG only contains reserved bits */
-
-#define STATUSRSSI 10 /* Status RSSI */
-#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
-#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
-#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
-#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
-#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
-#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
-#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
-#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
-
-#define READCHAN 11 /* Read Channel */
-#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
-#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
-#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
-#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
-
-#define RDSA 12 /* RDSA */
-#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
-
-#define RDSB 13 /* RDSB */
-#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
-
-#define RDSC 14 /* RDSC */
-#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
-
-#define RDSD 15 /* RDSD */
-#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
-
-
-
-/**************************************************************************
- * USB HID Reports
- **************************************************************************/
-
-/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
-/* with the (REPORT_ID - 1) corresponding to the register address across USB */
-/* endpoint 0 using GET_REPORT and SET_REPORT */
-#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
-#define REGISTER_REPORT(reg) ((reg) + 1)
-
-/* Report 17 gives direct read/write access to the entire Si470x register */
-/* map across endpoint 0 using GET_REPORT and SET_REPORT */
-#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define ENTIRE_REPORT 17
-
-/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
-/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
-#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define RDS_REPORT 18
-
-/* Report 19: LED state */
-#define LED_REPORT_SIZE 3
-#define LED_REPORT 19
-
-/* Report 19: stream */
-#define STREAM_REPORT_SIZE 3
-#define STREAM_REPORT 19
-
-/* Report 20: scratch */
-#define SCRATCH_PAGE_SIZE 63
-#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
-#define SCRATCH_REPORT 20
-
-/* Reports 19-22: flash upgrade of the C8051F321 */
-#define WRITE_REPORT_SIZE 4
-#define WRITE_REPORT 19
-#define FLASH_REPORT_SIZE 64
-#define FLASH_REPORT 20
-#define CRC_REPORT_SIZE 3
-#define CRC_REPORT 21
-#define RESPONSE_REPORT_SIZE 2
-#define RESPONSE_REPORT 22
-
-/* Report 23: currently unused, but can accept 60 byte reports on the HID */
-/* interrupt out endpoint 2 every 1 millisecond */
-#define UNUSED_REPORT 23
-
-
-
-/**************************************************************************
- * Software/Hardware Versions
- **************************************************************************/
-#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
-#define RADIO_SW_VERSION 7
-#define RADIO_SW_VERSION_CURRENT 15
-#define RADIO_HW_VERSION 1
-
-#define SCRATCH_PAGE_SW_VERSION 1
-#define SCRATCH_PAGE_HW_VERSION 2
-
-
-
-/**************************************************************************
- * LED State Definitions
- **************************************************************************/
-#define LED_COMMAND 0x35
-
-#define NO_CHANGE_LED 0x00
-#define ALL_COLOR_LED 0x01 /* streaming state */
-#define BLINK_GREEN_LED 0x02 /* connect state */
-#define BLINK_RED_LED 0x04
-#define BLINK_ORANGE_LED 0x10 /* disconnect state */
-#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
-#define SOLID_RED_LED 0x40 /* bootload state */
-#define SOLID_ORANGE_LED 0x80
-
-
-
-/**************************************************************************
- * Stream State Definitions
- **************************************************************************/
-#define STREAM_COMMAND 0x36
-#define STREAM_VIDPID 0x00
-#define STREAM_AUDIO 0xff
-
-
-
-/**************************************************************************
- * Bootloader / Flash Commands
- **************************************************************************/
-
-/* unique id sent to bootloader and required to put into a bootload state */
-#define UNIQUE_BL_ID 0x34
-
-/* mask for the flash data */
-#define FLASH_DATA_MASK 0x55
-
-/* bootloader commands */
-#define GET_SW_VERSION_COMMAND 0x00
-#define SET_PAGE_COMMAND 0x01
-#define ERASE_PAGE_COMMAND 0x02
-#define WRITE_PAGE_COMMAND 0x03
-#define CRC_ON_PAGE_COMMAND 0x04
-#define READ_FLASH_BYTE_COMMAND 0x05
-#define RESET_DEVICE_COMMAND 0x06
-#define GET_HW_VERSION_COMMAND 0x07
-#define BLANK 0xff
-
-/* bootloader command responses */
-#define COMMAND_OK 0x01
-#define COMMAND_FAILED 0x02
-#define COMMAND_PENDING 0x03
-
-
-
-/**************************************************************************
- * General Driver Definitions
- **************************************************************************/
-
-/*
- * si470x_device - private data
- */
-struct si470x_device {
- /* reference to USB and video device */
- struct usb_device *usbdev;
- struct usb_interface *intf;
- struct video_device *videodev;
-
- /* Interrupt endpoint handling */
- char *int_in_buffer;
- struct usb_endpoint_descriptor *int_in_endpoint;
- struct urb *int_in_urb;
- int int_in_running;
-
- /* driver management */
- unsigned int users;
- unsigned char disconnected;
- struct mutex disconnect_lock;
-
- /* Silabs internal registers (0..15) */
- unsigned short registers[RADIO_REGISTER_NUM];
-
- /* RDS receive buffer */
- wait_queue_head_t read_queue;
- struct mutex lock; /* buffer locking */
- unsigned char *buffer; /* size is always multiple of three */
- unsigned int buf_size;
- unsigned int rd_index;
- unsigned int wr_index;
-
- /* scratch page */
- unsigned char software_version;
- unsigned char hardware_version;
-};
-
-
-/*
- * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
- * 62.5 kHz otherwise.
- * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
- * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
- * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
- */
-#define FREQ_MUL (1000000 / 62.5)
-
-
-
-/**************************************************************************
- * General Driver Functions - REGISTER_REPORTs
- **************************************************************************/
-
-/*
- * si470x_get_report - receive a HID report
- */
-static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
-{
- unsigned char *report = (unsigned char *) buf;
- int retval;
-
- retval = usb_control_msg(radio->usbdev,
- usb_rcvctrlpipe(radio->usbdev, 0),
- HID_REQ_GET_REPORT,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
- report[0], 2,
- buf, size, usb_timeout);
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": si470x_get_report: usb_control_msg returned %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * si470x_set_report - send a HID report
- */
-static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
-{
- unsigned char *report = (unsigned char *) buf;
- int retval;
-
- retval = usb_control_msg(radio->usbdev,
- usb_sndctrlpipe(radio->usbdev, 0),
- HID_REQ_SET_REPORT,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- report[0], 2,
- buf, size, usb_timeout);
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": si470x_set_report: usb_control_msg returned %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * si470x_get_register - read register
- */
-static int si470x_get_register(struct si470x_device *radio, int regnr)
-{
- unsigned char buf[REGISTER_REPORT_SIZE];
- int retval;
-
- buf[0] = REGISTER_REPORT(regnr);
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval >= 0)
- radio->registers[regnr] = get_unaligned_be16(&buf[1]);
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_register - write register
- */
-static int si470x_set_register(struct si470x_device *radio, int regnr)
-{
- unsigned char buf[REGISTER_REPORT_SIZE];
- int retval;
-
- buf[0] = REGISTER_REPORT(regnr);
- put_unaligned_be16(radio->registers[regnr], &buf[1]);
-
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_chan - set the channel
- */
-static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
-{
- int retval;
- unsigned long timeout;
- bool timed_out = 0;
-
- /* start tuning */
- radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
- radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
- retval = si470x_set_register(radio, CHANNEL);
- if (retval < 0)
- goto done;
-
- /* wait till tune operation has completed */
- timeout = jiffies + msecs_to_jiffies(tune_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
- (!timed_out));
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");
- if (timed_out)
- printk(KERN_WARNING DRIVER_NAME
- ": tune timed out after %u ms\n", tune_timeout);
-
-stop:
- /* stop tuning */
- radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
- retval = si470x_set_register(radio, CHANNEL);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_get_freq - get the frequency
- */
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
-{
- unsigned int spacing, band_bottom;
- unsigned short chan;
- int retval;
-
- /* Spacing (kHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
- /* 0: 200 kHz (USA, Australia) */
- case 0:
- spacing = 0.200 * FREQ_MUL; break;
- /* 1: 100 kHz (Europe, Japan) */
- case 1:
- spacing = 0.100 * FREQ_MUL; break;
- /* 2: 50 kHz */
- default:
- spacing = 0.050 * FREQ_MUL; break;
- };
-
- /* Bottom of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL; break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL; break;
- };
-
- /* read channel */
- retval = si470x_get_register(radio, READCHAN);
- chan = radio->registers[READCHAN] & READCHAN_READCHAN;
-
- /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
- *freq = chan * spacing + band_bottom;
-
- return retval;
-}
-
-
-/*
- * si470x_set_freq - set the frequency
- */
-static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
-{
- unsigned int spacing, band_bottom;
- unsigned short chan;
-
- /* Spacing (kHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
- /* 0: 200 kHz (USA, Australia) */
- case 0:
- spacing = 0.200 * FREQ_MUL; break;
- /* 1: 100 kHz (Europe, Japan) */
- case 1:
- spacing = 0.100 * FREQ_MUL; break;
- /* 2: 50 kHz */
- default:
- spacing = 0.050 * FREQ_MUL; break;
- };
-
- /* Bottom of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL; break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL; break;
- };
-
- /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
- chan = (freq - band_bottom) / spacing;
-
- return si470x_set_chan(radio, chan);
-}
-
-
-/*
- * si470x_set_seek - set seek
- */
-static int si470x_set_seek(struct si470x_device *radio,
- unsigned int wrap_around, unsigned int seek_upward)
-{
- int retval = 0;
- unsigned long timeout;
- bool timed_out = 0;
-
- /* start seeking */
- radio->registers[POWERCFG] |= POWERCFG_SEEK;
- if (wrap_around == 1)
- radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
- else
- radio->registers[POWERCFG] |= POWERCFG_SKMODE;
- if (seek_upward == 1)
- radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
- else
- radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
- retval = si470x_set_register(radio, POWERCFG);
- if (retval < 0)
- goto done;
-
- /* wait till seek operation has completed */
- timeout = jiffies + msecs_to_jiffies(seek_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
- (!timed_out));
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");
- if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
- printk(KERN_WARNING DRIVER_NAME
- ": seek failed / band limit reached\n");
- if (timed_out)
- printk(KERN_WARNING DRIVER_NAME
- ": seek timed out after %u ms\n", seek_timeout);
-
-stop:
- /* stop seeking */
- radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- /* try again, if timed out */
- if ((retval == 0) && timed_out)
- retval = -EAGAIN;
-
- return retval;
-}
-
-
-/*
- * si470x_start - switch on radio
- */
-static int si470x_start(struct si470x_device *radio)
-{
- int retval;
-
- /* powercfg */
- radio->registers[POWERCFG] =
- POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
- retval = si470x_set_register(radio, POWERCFG);
- if (retval < 0)
- goto done;
-
- /* sysconfig 1 */
- radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- goto done;
-
- /* sysconfig 2 */
- radio->registers[SYSCONFIG2] =
- (0x3f << 8) | /* SEEKTH */
- ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
- ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
- 15; /* VOLUME (max) */
- retval = si470x_set_register(radio, SYSCONFIG2);
- if (retval < 0)
- goto done;
-
- /* reset last channel */
- retval = si470x_set_chan(radio,
- radio->registers[CHANNEL] & CHANNEL_CHAN);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_stop - switch off radio
- */
-static int si470x_stop(struct si470x_device *radio)
-{
- int retval;
-
- /* sysconfig 1 */
- radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- goto done;
-
- /* powercfg */
- radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
- /* POWERCFG_ENABLE has to automatically go low */
- radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_rds_on - switch on rds reception
- */
-static int si470x_rds_on(struct si470x_device *radio)
-{
- int retval;
-
- /* sysconfig 1 */
- mutex_lock(&radio->lock);
- radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
- mutex_unlock(&radio->lock);
-
- return retval;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - ENTIRE_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_all_registers - read entire registers
- */
-static int si470x_get_all_registers(struct si470x_device *radio)
-{
- unsigned char buf[ENTIRE_REPORT_SIZE];
- int retval;
- unsigned char regnr;
-
- buf[0] = ENTIRE_REPORT;
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval >= 0)
- for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
- radio->registers[regnr] = get_unaligned_be16(
- &buf[regnr * RADIO_REGISTER_SIZE + 1]);
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - LED_REPORT
- **************************************************************************/
-
-/*
- * si470x_set_led_state - sets the led state
- */
-static int si470x_set_led_state(struct si470x_device *radio,
- unsigned char led_state)
-{
- unsigned char buf[LED_REPORT_SIZE];
- int retval;
-
- buf[0] = LED_REPORT;
- buf[1] = LED_COMMAND;
- buf[2] = led_state;
-
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - SCRATCH_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_scratch_versions - gets the scratch page and version infos
- */
-static int si470x_get_scratch_page_versions(struct si470x_device *radio)
-{
- unsigned char buf[SCRATCH_REPORT_SIZE];
- int retval;
-
- buf[0] = SCRATCH_REPORT;
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: "
- "si470x_get_report returned %d\n", retval);
- else {
- radio->software_version = buf[1];
- radio->hardware_version = buf[2];
- }
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * RDS Driver Functions
- **************************************************************************/
-
-/*
- * si470x_int_in_callback - rds callback and processing function
- *
- * TODO: do we need to use mutex locks in some sections?
- */
-static void si470x_int_in_callback(struct urb *urb)
-{
- struct si470x_device *radio = urb->context;
- unsigned char buf[RDS_REPORT_SIZE];
- int retval;
- unsigned char regnr;
- unsigned char blocknum;
- unsigned short bler; /* rds block errors */
- unsigned short rds;
- unsigned char tmpbuf[3];
-
- if (urb->status) {
- if (urb->status == -ENOENT ||
- urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN) {
- return;
- } else {
- printk(KERN_WARNING DRIVER_NAME
- ": non-zero urb status (%d)\n", urb->status);
- goto resubmit; /* Maybe we can recover. */
- }
- }
-
- /* safety checks */
- if (radio->disconnected)
- return;
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
- goto resubmit;
-
- if (urb->actual_length > 0) {
- /* Update RDS registers with URB data */
- buf[0] = RDS_REPORT;
- for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
- radio->registers[STATUSRSSI + regnr] =
- get_unaligned_be16(&radio->int_in_buffer[
- regnr * RADIO_REGISTER_SIZE + 1]);
- /* get rds blocks */
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
- /* No RDS group ready, better luck next time */
- goto resubmit;
- }
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
- /* RDS decoder not synchronized */
- goto resubmit;
- }
- for (blocknum = 0; blocknum < 4; blocknum++) {
- switch (blocknum) {
- default:
- bler = (radio->registers[STATUSRSSI] &
- STATUSRSSI_BLERA) >> 9;
- rds = radio->registers[RDSA];
- break;
- case 1:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERB) >> 14;
- rds = radio->registers[RDSB];
- break;
- case 2:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERC) >> 12;
- rds = radio->registers[RDSC];
- break;
- case 3:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERD) >> 10;
- rds = radio->registers[RDSD];
- break;
- };
-
- /* Fill the V4L2 RDS buffer */
- put_unaligned_le16(rds, &tmpbuf);
- tmpbuf[2] = blocknum; /* offset name */
- tmpbuf[2] |= blocknum << 3; /* received offset */
- if (bler > max_rds_errors)
- tmpbuf[2] |= 0x80; /* uncorrectable errors */
- else if (bler > 0)
- tmpbuf[2] |= 0x40; /* corrected error(s) */
-
- /* copy RDS block to internal buffer */
- memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
- radio->wr_index += 3;
-
- /* wrap write pointer */
- if (radio->wr_index >= radio->buf_size)
- radio->wr_index = 0;
-
- /* check for overflow */
- if (radio->wr_index == radio->rd_index) {
- /* increment and wrap read pointer */
- radio->rd_index += 3;
- if (radio->rd_index >= radio->buf_size)
- radio->rd_index = 0;
- }
- }
- if (radio->wr_index != radio->rd_index)
- wake_up_interruptible(&radio->read_queue);
- }
-
-resubmit:
- /* Resubmit if we're still running. */
- if (radio->int_in_running && radio->usbdev) {
- retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);
- if (retval) {
- printk(KERN_WARNING DRIVER_NAME
- ": resubmitting urb failed (%d)", retval);
- radio->int_in_running = 0;
- }
- }
-}
-
-
-
-/**************************************************************************
- * File Operations Interface
- **************************************************************************/
-
-/*
- * si470x_fops_read - read RDS data
- */
-static ssize_t si470x_fops_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
- unsigned int block_count = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
- si470x_rds_on(radio);
- }
-
- /* block if no new data available */
- while (radio->wr_index == radio->rd_index) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EWOULDBLOCK;
- goto done;
- }
- if (wait_event_interruptible(radio->read_queue,
- radio->wr_index != radio->rd_index) < 0) {
- retval = -EINTR;
- goto done;
- }
- }
-
- /* calculate block count from byte count */
- count /= 3;
-
- /* copy RDS block out of internal buffer and to user buffer */
- mutex_lock(&radio->lock);
- while (block_count < count) {
- if (radio->rd_index == radio->wr_index)
- break;
-
- /* always transfer rds complete blocks */
- if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
- /* retval = -EFAULT; */
- break;
-
- /* increment and wrap read pointer */
- radio->rd_index += 3;
- if (radio->rd_index >= radio->buf_size)
- radio->rd_index = 0;
-
- /* increment counters */
- block_count++;
- buf += 3;
- retval += 3;
- }
- mutex_unlock(&radio->lock);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_fops_poll - poll RDS data
- */
-static unsigned int si470x_fops_poll(struct file *file,
- struct poll_table_struct *pts)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
- si470x_rds_on(radio);
- }
-
- poll_wait(file, &radio->read_queue, pts);
-
- if (radio->rd_index != radio->wr_index)
- retval = POLLIN | POLLRDNORM;
-
- return retval;
-}
-
-
-/*
- * si470x_fops_open - file open
- */
-static int si470x_fops_open(struct file *file)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval;
-
- lock_kernel();
- radio->users++;
-
- retval = usb_autopm_get_interface(radio->intf);
- if (retval < 0) {
- radio->users--;
- retval = -EIO;
- goto done;
- }
-
- if (radio->users == 1) {
- /* start radio */
- retval = si470x_start(radio);
- if (retval < 0) {
- usb_autopm_put_interface(radio->intf);
- goto done;
- }
-
- /* initialize interrupt urb */
- usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
- usb_rcvintpipe(radio->usbdev,
- radio->int_in_endpoint->bEndpointAddress),
- radio->int_in_buffer,
- le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
- si470x_int_in_callback,
- radio,
- radio->int_in_endpoint->bInterval);
-
- radio->int_in_running = 1;
- mb();
-
- retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
- if (retval) {
- printk(KERN_INFO DRIVER_NAME
- ": submitting int urb failed (%d)\n", retval);
- radio->int_in_running = 0;
- usb_autopm_put_interface(radio->intf);
- }
- }
-
-done:
- unlock_kernel();
- return retval;
-}
-
-
-/*
- * si470x_fops_release - file release
- */
-static int si470x_fops_release(struct file *file)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety check */
- if (!radio) {
- retval = -ENODEV;
- goto done;
- }
-
- mutex_lock(&radio->disconnect_lock);
- radio->users--;
- if (radio->users == 0) {
- /* shutdown interrupt handler */
- if (radio->int_in_running) {
- radio->int_in_running = 0;
- if (radio->int_in_urb)
- usb_kill_urb(radio->int_in_urb);
- }
-
- if (radio->disconnected) {
- video_unregister_device(radio->videodev);
- kfree(radio->int_in_buffer);
- kfree(radio->buffer);
- kfree(radio);
- goto unlock;
- }
-
- /* cancel read processes */
- wake_up_interruptible(&radio->read_queue);
-
- /* stop radio */
- retval = si470x_stop(radio);
- usb_autopm_put_interface(radio->intf);
- }
-unlock:
- mutex_unlock(&radio->disconnect_lock);
-done:
- return retval;
-}
-
-
-/*
- * si470x_fops - file operations interface
- */
-static const struct v4l2_file_operations si470x_fops = {
- .owner = THIS_MODULE,
- .read = si470x_fops_read,
- .poll = si470x_fops_poll,
- .ioctl = video_ioctl2,
- .open = si470x_fops_open,
- .release = si470x_fops_release,
-};
-
-
-
-/**************************************************************************
- * Video4Linux Interface
- **************************************************************************/
-
-/*
- * si470x_vidioc_querycap - query device capabilities
- */
-static int si470x_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
-{
- struct si470x_device *radio = video_drvdata(file);
-
- strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
- strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
- usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info));
- capability->version = DRIVER_KERNEL_VERSION;
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
- V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
-
- return 0;
-}
-
-
-/*
- * si470x_vidioc_queryctrl - enumerate control items
- */
-static int si470x_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int retval = -EINVAL;
-
- /* abort if qc->id is below V4L2_CID_BASE */
- if (qc->id < V4L2_CID_BASE)
- goto done;
-
- /* search video control */
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
-
- /* disable unsupported base controls */
- /* to satisfy kradio and such apps */
- if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
- qc->flags = V4L2_CTRL_FLAG_DISABLED;
- retval = 0;
- }
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": query controls failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_ctrl - get the value of a control
- */
-static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = radio->registers[SYSCONFIG2] &
- SYSCONFIG2_VOLUME;
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = ((radio->registers[POWERCFG] &
- POWERCFG_DMUTE) == 0) ? 1 : 0;
- break;
- default:
- retval = -EINVAL;
- }
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": get control failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_ctrl - set the value of a control
- */
-static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
- radio->registers[SYSCONFIG2] |= ctrl->value;
- retval = si470x_set_register(radio, SYSCONFIG2);
- break;
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value == 1)
- radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
- else
- radio->registers[POWERCFG] |= POWERCFG_DMUTE;
- retval = si470x_set_register(radio, POWERCFG);
- break;
- default:
- retval = -EINVAL;
- }
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set control failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_audio - get audio attributes
- */
-static int si470x_vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *audio)
-{
- /* driver constants */
- audio->index = 0;
- strcpy(audio->name, "Radio");
- audio->capability = V4L2_AUDCAP_STEREO;
- audio->mode = 0;
-
- return 0;
-}
-
-
-/*
- * si470x_vidioc_g_tuner - get tuner attributes
- */
-static int si470x_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (tuner->index != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto done;
-
- /* driver constants */
- strcpy(tuner->name, "FM");
- tuner->type = V4L2_TUNER_RADIO;
- tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_RDS;
-
- /* range limits */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe, default) */
- default:
- tuner->rangelow = 87.5 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- case 1 :
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2 :
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 90 * FREQ_MUL;
- break;
- };
-
- /* stereo indicator == stereo (instead of mono) */
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- /* If there is a reliable method of detecting an RDS channel,
- then this code should check for that before setting this
- RDS subchannel. */
- tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
-
- /* mono/stereo selector */
- if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
- tuner->audmode = V4L2_TUNER_MODE_STEREO;
- else
- tuner->audmode = V4L2_TUNER_MODE_MONO;
-
- /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
- /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
- tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
- /* the ideal factor is 0xffff/75 = 873,8 */
- tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
-
- /* automatic frequency control: -1: freq to low, 1 freq to high */
- /* AFCRL does only indicate that freq. differs, not if too low/high */
- tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": get tuner failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_tuner - set tuner attributes
- */
-static int si470x_vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = -EINVAL;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (tuner->index != 0)
- goto done;
-
- /* mono/stereo selector */
- switch (tuner->audmode) {
- case V4L2_TUNER_MODE_MONO:
- radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
- break;
- case V4L2_TUNER_MODE_STEREO:
- radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
- break;
- default:
- goto done;
- }
-
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set tuner failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
- */
-static int si470x_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- freq->type = V4L2_TUNER_RADIO;
- retval = si470x_get_freq(radio, &freq->frequency);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": get frequency failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
- */
-static int si470x_vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_set_freq(radio, freq->frequency);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set frequency failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
- */
-static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
- struct v4l2_hw_freq_seek *seek)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (seek->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set hardware frequency seek failed with %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * si470x_ioctl_ops - video device ioctl operations
- */
-static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
- .vidioc_querycap = si470x_vidioc_querycap,
- .vidioc_queryctrl = si470x_vidioc_queryctrl,
- .vidioc_g_ctrl = si470x_vidioc_g_ctrl,
- .vidioc_s_ctrl = si470x_vidioc_s_ctrl,
- .vidioc_g_audio = si470x_vidioc_g_audio,
- .vidioc_g_tuner = si470x_vidioc_g_tuner,
- .vidioc_s_tuner = si470x_vidioc_s_tuner,
- .vidioc_g_frequency = si470x_vidioc_g_frequency,
- .vidioc_s_frequency = si470x_vidioc_s_frequency,
- .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
-};
-
-
-/*
- * si470x_viddev_template - video device interface
- */
-static struct video_device si470x_viddev_template = {
- .fops = &si470x_fops,
- .name = DRIVER_NAME,
- .release = video_device_release,
- .ioctl_ops = &si470x_ioctl_ops,
-};
-
-
-
-/**************************************************************************
- * USB Interface
- **************************************************************************/
-
-/*
- * si470x_usb_driver_probe - probe for the device
- */
-static int si470x_usb_driver_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct si470x_device *radio;
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- int i, int_end_size, retval = 0;
-
- /* private data allocation and initialization */
- radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
- if (!radio) {
- retval = -ENOMEM;
- goto err_initial;
- }
- radio->users = 0;
- radio->disconnected = 0;
- radio->usbdev = interface_to_usbdev(intf);
- radio->intf = intf;
- mutex_init(&radio->disconnect_lock);
- mutex_init(&radio->lock);
-
- iface_desc = intf->cur_altsetting;
-
- /* Set up interrupt endpoint information. */
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
- USB_DIR_IN) && ((endpoint->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
- radio->int_in_endpoint = endpoint;
- }
- if (!radio->int_in_endpoint) {
- printk(KERN_INFO DRIVER_NAME
- ": could not find interrupt in endpoint\n");
- retval = -EIO;
- goto err_radio;
- }
-
- int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
-
- radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);
- if (!radio->int_in_buffer) {
- printk(KERN_INFO DRIVER_NAME
- "could not allocate int_in_buffer");
- retval = -ENOMEM;
- goto err_radio;
- }
-
- radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!radio->int_in_urb) {
- printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb");
- retval = -ENOMEM;
- goto err_intbuffer;
- }
-
- /* video device allocation and initialization */
- radio->videodev = video_device_alloc();
- if (!radio->videodev) {
- retval = -ENOMEM;
- goto err_intbuffer;
- }
- memcpy(radio->videodev, &si470x_viddev_template,
- sizeof(si470x_viddev_template));
- video_set_drvdata(radio->videodev, radio);
-
- /* show some infos about the specific si470x device */
- if (si470x_get_all_registers(radio) < 0) {
- retval = -EIO;
- goto err_video;
- }
- printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
- radio->registers[DEVICEID], radio->registers[CHIPID]);
-
- /* get software and hardware versions */
- if (si470x_get_scratch_page_versions(radio) < 0) {
- retval = -EIO;
- goto err_video;
- }
- printk(KERN_INFO DRIVER_NAME
- ": software version %d, hardware version %d\n",
- radio->software_version, radio->hardware_version);
-
- /* check if device and firmware is current */
- if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
- < RADIO_SW_VERSION_CURRENT) {
- printk(KERN_WARNING DRIVER_NAME
- ": This driver is known to work with "
- "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT);
- printk(KERN_WARNING DRIVER_NAME
- ": but the device has firmware version %hu.\n",
- radio->registers[CHIPID] & CHIPID_FIRMWARE);
- printk(KERN_WARNING DRIVER_NAME
- ": If you have some trouble using this driver,\n");
- printk(KERN_WARNING DRIVER_NAME
- ": please report to V4L ML at "
- "linux-media@vger.kernel.org\n");
- }
-
- /* set initial frequency */
- si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
- /* set led to connect state */
- si470x_set_led_state(radio, BLINK_GREEN_LED);
-
- /* rds buffer allocation */
- radio->buf_size = rds_buf * 3;
- radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
- if (!radio->buffer) {
- retval = -EIO;
- goto err_video;
- }
-
- /* rds buffer configuration */
- radio->wr_index = 0;
- radio->rd_index = 0;
- init_waitqueue_head(&radio->read_queue);
-
- /* register video device */
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
- if (retval) {
- printk(KERN_WARNING DRIVER_NAME
- ": Could not register video device\n");
- goto err_all;
- }
- usb_set_intfdata(intf, radio);
-
- return 0;
-err_all:
- kfree(radio->buffer);
-err_video:
- video_device_release(radio->videodev);
-err_intbuffer:
- kfree(radio->int_in_buffer);
-err_radio:
- kfree(radio);
-err_initial:
- return retval;
-}
-
-
-/*
- * si470x_usb_driver_suspend - suspend the device
- */
-static int si470x_usb_driver_suspend(struct usb_interface *intf,
- pm_message_t message)
-{
- printk(KERN_INFO DRIVER_NAME ": suspending now...\n");
-
- return 0;
-}
-
-
-/*
- * si470x_usb_driver_resume - resume the device
- */
-static int si470x_usb_driver_resume(struct usb_interface *intf)
-{
- printk(KERN_INFO DRIVER_NAME ": resuming now...\n");
-
- return 0;
-}
-
-
-/*
- * si470x_usb_driver_disconnect - disconnect the device
- */
-static void si470x_usb_driver_disconnect(struct usb_interface *intf)
-{
- struct si470x_device *radio = usb_get_intfdata(intf);
-
- mutex_lock(&radio->disconnect_lock);
- radio->disconnected = 1;
- usb_set_intfdata(intf, NULL);
- if (radio->users == 0) {
- /* set led to disconnect state */
- si470x_set_led_state(radio, BLINK_ORANGE_LED);
-
- /* Free data structures. */
- usb_free_urb(radio->int_in_urb);
-
- kfree(radio->int_in_buffer);
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- kfree(radio);
- }
- mutex_unlock(&radio->disconnect_lock);
-}
-
-
-/*
- * si470x_usb_driver - usb driver interface
- */
-static struct usb_driver si470x_usb_driver = {
- .name = DRIVER_NAME,
- .probe = si470x_usb_driver_probe,
- .disconnect = si470x_usb_driver_disconnect,
- .suspend = si470x_usb_driver_suspend,
- .resume = si470x_usb_driver_resume,
- .id_table = si470x_usb_driver_id_table,
- .supports_autosuspend = 1,
-};
-
-
-
-/**************************************************************************
- * Module Interface
- **************************************************************************/
-
-/*
- * si470x_module_init - module init
- */
-static int __init si470x_module_init(void)
-{
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
- return usb_register(&si470x_usb_driver);
-}
-
-
-/*
- * si470x_module_exit - module exit
- */
-static void __exit si470x_module_exit(void)
-{
- usb_deregister(&si470x_usb_driver);
-}
-
-
-module_init(si470x_module_init);
-module_exit(si470x_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/Kconfig b/linux/drivers/media/radio/si470x/Kconfig
new file mode 100644
index 000000000..a466654ee
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/Kconfig
@@ -0,0 +1,37 @@
+config USB_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support with USB"
+ depends on USB && RADIO_SI470X
+ ---help---
+ This is a driver for USB devices with the Silicon Labs SI470x
+ chip. Currently these devices are known to work:
+ - 10c4:818a: Silicon Labs USB FM Radio Reference Design
+ - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
+ - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
+ - 10c5:819a: Sanei Electric FM USB Radio (aka DealExtreme.com PCear)
+
+ Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
+ if you don't want to use the device solely for RDS receiving,
+ it is recommended to also select SND_USB_AUDIO.
+
+ Please have a look at the documentation, especially on how
+ to redirect the audio stream from the radio to your sound device:
+ Documentation/video4linux/si470x.txt
+
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-usb-si470x.
+
+config I2C_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support with I2C"
+ depends on I2C && RADIO_SI470X && !USB_SI470X
+ ---help---
+ This is a driver for I2C devices with the Silicon Labs SI470x
+ chip.
+
+ Say Y here if you want to connect this type of radio to your
+ computer's I2C port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-i2c-si470x.
diff --git a/linux/drivers/media/radio/si470x/Makefile b/linux/drivers/media/radio/si470x/Makefile
new file mode 100644
index 000000000..06964816c
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for radios with Silicon Labs Si470x FM Radio Receivers
+#
+
+radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o
+radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o
+
+obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o
+obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c
new file mode 100644
index 000000000..f33315f2c
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c
@@ -0,0 +1,798 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-common.c
+ *
+ * Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * History:
+ * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.0
+ * - First working version
+ * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.1
+ * - Improved error handling, every function now returns errno
+ * - Improved multi user access (start/mute/stop)
+ * - Channel doesn't get lost anymore after start/mute/stop
+ * - RDS support added (polling mode via interrupt EP 1)
+ * - marked default module parameters with *value*
+ * - switched from bit structs to bit masks
+ * - header file cleaned and integrated
+ * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.2
+ * - hex values are now lower case
+ * - commented USB ID for ADS/Tech moved on todo list
+ * - blacklisted si470x in hid-quirks.c
+ * - rds buffer handling functions integrated into *_work, *_read
+ * - rds_command in si470x_poll exchanged against simple retval
+ * - check for firmware version 15
+ * - code order and prototypes still remain the same
+ * - spacing and bottom of band codes remain the same
+ * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.3
+ * - code reordered to avoid function prototypes
+ * - switch/case defaults are now more user-friendly
+ * - unified comment style
+ * - applied all checkpatch.pl v1.12 suggestions
+ * except the warning about the too long lines with bit comments
+ * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
+ * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.4
+ * - avoid poss. locking when doing copy_to_user which may sleep
+ * - RDS is automatically activated on read now
+ * - code cleaned of unnecessary rds_commands
+ * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
+ * (thanks to Guillaume RAMOUSSE)
+ * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.5
+ * - number of seek_retries changed to tune_timeout
+ * - fixed problem with incomplete tune operations by own buffers
+ * - optimization of variables and printf types
+ * - improved error logging
+ * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Oliver Neukum <oliver@neukum.org>
+ * Version 1.0.6
+ * - fixed coverity checker warnings in *_usb_driver_disconnect
+ * - probe()/open() race by correct ordering in probe()
+ * - DMA coherency rules by separate allocation of all buffers
+ * - use of endianness macros
+ * - abuse of spinlock, replaced by mutex
+ * - racy handling of timer in disconnect,
+ * replaced by delayed_work
+ * - racy interruptible_sleep_on(),
+ * replaced with wait_event_interruptible()
+ * - handle signals in read()
+ * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Oliver Neukum <oliver@neukum.org>
+ * Version 1.0.7
+ * - usb autosuspend support
+ * - unplugging fixed
+ * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.8
+ * - hardware frequency seek support
+ * - afc indication
+ * - more safety checks, let si470x_get_freq return errno
+ * - vidioc behavior corrected according to v4l2 spec
+ * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>
+ * - add support for KWorld USB FM Radio FM700
+ * - blacklisted KWorld radio in hid-core.c and hid-ids.h
+ * 2008-12-03 Mark Lord <mlord@pobox.com>
+ * - add support for DealExtreme USB Radio
+ * 2009-01-31 Bob Ross <pigiron@gmx.com>
+ * - correction of stereo detection/setting
+ * - correction of signal strength indicator scaling
+ * 2009-01-31 Rick Bronson <rick@efn.org>
+ * Tobias Lorenz <tobias.lorenz@gmx.net>
+ * - add LED status output
+ * - get HW/SW version from scratchpad
+ * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>
+ * Version 1.0.10
+ * - add support for interrupt mode for RDS endpoint,
+ * instead of polling.
+ * Improves RDS reception significantly
+ */
+
+
+/* kernel includes */
+#include "radio-si470x.h"
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Spacing (kHz) */
+/* 0: 200 kHz (USA, Australia) */
+/* 1: 100 kHz (Europe, Japan) */
+/* 2: 50 kHz */
+static unsigned short space = 2;
+module_param(space, ushort, 0444);
+MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
+
+/* Bottom of Band (MHz) */
+/* 0: 87.5 - 108 MHz (USA, Europe)*/
+/* 1: 76 - 108 MHz (Japan wide band) */
+/* 2: 76 - 90 MHz (Japan) */
+static unsigned short band = 1;
+module_param(band, ushort, 0444);
+MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+
+/* De-emphasis */
+/* 0: 75 us (USA) */
+/* 1: 50 us (Europe, Australia, Japan) */
+static unsigned short de = 1;
+module_param(de, ushort, 0444);
+MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
+
+/* Tune timeout */
+static unsigned int tune_timeout = 3000;
+module_param(tune_timeout, uint, 0644);
+MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
+
+/* Seek timeout */
+static unsigned int seek_timeout = 5000;
+module_param(seek_timeout, uint, 0644);
+MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
+
+
+
+/**************************************************************************
+ * Generic Functions
+ **************************************************************************/
+
+/*
+ * si470x_set_chan - set the channel
+ */
+static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
+{
+ int retval;
+ unsigned long timeout;
+ bool timed_out = 0;
+
+ /* start tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
+ radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
+ retval = si470x_set_register(radio, CHANNEL);
+ if (retval < 0)
+ goto done;
+
+ /* wait till tune operation has completed */
+ timeout = jiffies + msecs_to_jiffies(tune_timeout);
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto stop;
+ timed_out = time_after(jiffies, timeout);
+ } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+ (!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ dev_warn(&radio->videodev->dev, "tune does not complete\n");
+ if (timed_out)
+ dev_warn(&radio->videodev->dev,
+ "tune timed out after %u ms\n", tune_timeout);
+
+stop:
+ /* stop tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
+ retval = si470x_set_register(radio, CHANNEL);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+ unsigned int spacing, band_bottom;
+ unsigned short chan;
+ int retval;
+
+ /* Spacing (kHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0:
+ spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1:
+ spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default:
+ spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0:
+ band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default:
+ band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* read channel */
+ retval = si470x_get_register(radio, READCHAN);
+ chan = radio->registers[READCHAN] & READCHAN_READCHAN;
+
+ /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
+ *freq = chan * spacing + band_bottom;
+
+ return retval;
+}
+
+
+/*
+ * si470x_set_freq - set the frequency
+ */
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
+{
+ unsigned int spacing, band_bottom;
+ unsigned short chan;
+
+ /* Spacing (kHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0:
+ spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1:
+ spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default:
+ spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0:
+ band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default:
+ band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
+ chan = (freq - band_bottom) / spacing;
+
+ return si470x_set_chan(radio, chan);
+}
+
+
+/*
+ * si470x_set_seek - set seek
+ */
+static int si470x_set_seek(struct si470x_device *radio,
+ unsigned int wrap_around, unsigned int seek_upward)
+{
+ int retval = 0;
+ unsigned long timeout;
+ bool timed_out = 0;
+
+ /* start seeking */
+ radio->registers[POWERCFG] |= POWERCFG_SEEK;
+ if (wrap_around == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_SKMODE;
+ if (seek_upward == 1)
+ radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
+ else
+ radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ goto done;
+
+ /* wait till seek operation has completed */
+ timeout = jiffies + msecs_to_jiffies(seek_timeout);
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto stop;
+ timed_out = time_after(jiffies, timeout);
+ } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+ (!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ dev_warn(&radio->videodev->dev, "seek does not complete\n");
+ if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
+ dev_warn(&radio->videodev->dev,
+ "seek failed / band limit reached\n");
+ if (timed_out)
+ dev_warn(&radio->videodev->dev,
+ "seek timed out after %u ms\n", seek_timeout);
+
+stop:
+ /* stop seeking */
+ radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ /* try again, if timed out */
+ if ((retval == 0) && timed_out)
+ retval = -EAGAIN;
+
+ return retval;
+}
+
+
+/*
+ * si470x_start - switch on radio
+ */
+int si470x_start(struct si470x_device *radio)
+{
+ int retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] =
+ POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ goto done;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ goto done;
+
+ /* sysconfig 2 */
+ radio->registers[SYSCONFIG2] =
+ (0x3f << 8) | /* SEEKTH */
+ ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
+ ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
+ 15; /* VOLUME (max) */
+ retval = si470x_set_register(radio, SYSCONFIG2);
+ if (retval < 0)
+ goto done;
+
+ /* reset last channel */
+ retval = si470x_set_chan(radio,
+ radio->registers[CHANNEL] & CHANNEL_CHAN);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_stop - switch off radio
+ */
+int si470x_stop(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ goto done;
+
+ /* powercfg */
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ /* POWERCFG_ENABLE has to automatically go low */
+ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_rds_on - switch on rds reception
+ */
+int si470x_rds_on(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ mutex_lock(&radio->lock);
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_queryctrl - enumerate control items
+ */
+static int si470x_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = -EINVAL;
+
+ /* abort if qc->id is below V4L2_CID_BASE */
+ if (qc->id < V4L2_CID_BASE)
+ goto done;
+
+ /* search video control */
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+
+ /* disable unsupported base controls */
+ /* to satisfy kradio and such apps */
+ if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
+ qc->flags = V4L2_CTRL_FLAG_DISABLED;
+ retval = 0;
+ }
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "query controls failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_ctrl - get the value of a control
+ */
+static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = radio->registers[SYSCONFIG2] &
+ SYSCONFIG2_VOLUME;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = ((radio->registers[POWERCFG] &
+ POWERCFG_DMUTE) == 0) ? 1 : 0;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "get control failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_ctrl - set the value of a control
+ */
+static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
+ radio->registers[SYSCONFIG2] |= ctrl->value;
+ retval = si470x_set_register(radio, SYSCONFIG2);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_DMUTE;
+ retval = si470x_set_register(radio, POWERCFG);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set control failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_audio - get audio attributes
+ */
+static int si470x_vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ /* driver constants */
+ audio->index = 0;
+ strcpy(audio->name, "Radio");
+ audio->capability = V4L2_AUDCAP_STEREO;
+ audio->mode = 0;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_tuner - get tuner attributes
+ */
+static int si470x_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (tuner->index != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto done;
+
+ /* driver constants */
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS;
+#else
+ tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+#endif
+
+ /* range limits */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe, default) */
+ default:
+ tuner->rangelow = 87.5 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ case 1:
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 90 * FREQ_MUL;
+ break;
+ };
+
+ /* stereo indicator == stereo (instead of mono) */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ else
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ /* If there is a reliable method of detecting an RDS channel,
+ then this code should check for that before setting this
+ RDS subchannel. */
+ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
+#endif
+
+ /* mono/stereo selector */
+ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+ /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
+ /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */
+ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
+ /* the ideal factor is 0xffff/75 = 873,8 */
+ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
+
+ /* automatic frequency control: -1: freq to low, 1 freq to high */
+ /* AFCRL does only indicate that freq. differs, not if too low/high */
+ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "get tuner failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_tuner - set tuner attributes
+ */
+static int si470x_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = -EINVAL;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (tuner->index != 0)
+ goto done;
+
+ /* mono/stereo selector */
+ switch (tuner->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
+ break;
+ default:
+ goto done;
+ }
+
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set tuner failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
+ */
+static int si470x_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (freq->tuner != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ freq->type = V4L2_TUNER_RADIO;
+ retval = si470x_get_freq(radio, &freq->frequency);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "get frequency failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
+ */
+static int si470x_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (freq->tuner != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_set_freq(radio, freq->frequency);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set frequency failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
+ */
+static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (seek->tuner != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set hardware frequency seek failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_ioctl_ops - video device ioctl operations
+ */
+static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
+ .vidioc_querycap = si470x_vidioc_querycap,
+ .vidioc_queryctrl = si470x_vidioc_queryctrl,
+ .vidioc_g_ctrl = si470x_vidioc_g_ctrl,
+ .vidioc_s_ctrl = si470x_vidioc_s_ctrl,
+ .vidioc_g_audio = si470x_vidioc_g_audio,
+ .vidioc_g_tuner = si470x_vidioc_g_tuner,
+ .vidioc_s_tuner = si470x_vidioc_s_tuner,
+ .vidioc_g_frequency = si470x_vidioc_g_frequency,
+ .vidioc_s_frequency = si470x_vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
+};
+
+
+/*
+ * si470x_viddev_template - video device interface
+ */
+struct video_device si470x_viddev_template = {
+ .fops = &si470x_fops,
+ .name = DRIVER_NAME,
+ .release = video_device_release,
+ .ioctl_ops = &si470x_ioctl_ops,
+};
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-i2c.c b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c
new file mode 100644
index 000000000..2d53b6a94
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -0,0 +1,401 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-i2c.c
+ *
+ * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * ToDo:
+ * - RDS support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.0"
+
+/* kernel includes */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "radio-si470x.h"
+
+
+/* I2C Device ID List */
+static const struct i2c_device_id si470x_i2c_id[] = {
+ /* Generic Entry */
+ { "si470x", 0 },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si470x_i2c_id);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+
+
+/**************************************************************************
+ * I2C Definitions
+ **************************************************************************/
+
+/* Write starts with the upper byte of register 0x02 */
+#define WRITE_REG_NUM 8
+#define WRITE_INDEX(i) (i + 0x02)
+
+/* Read starts with the upper byte of register 0x0a */
+#define READ_REG_NUM RADIO_REGISTER_NUM
+#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM)
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTERs
+ **************************************************************************/
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ u16 buf[READ_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+ (void *)buf },
+ };
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]);
+
+ return 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ int i;
+ u16 buf[WRITE_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM,
+ (void *)buf },
+ };
+
+ for (i = 0; i < WRITE_REG_NUM; i++)
+ buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]);
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE REGISTERS
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ int i;
+ u16 buf[READ_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+ (void *)buf },
+ };
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ for (i = 0; i < READ_REG_NUM; i++)
+ radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]);
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - DISCONNECT_CHECK
+ **************************************************************************/
+
+/*
+ * si470x_disconnect_check - check whether radio disconnects
+ */
+int si470x_disconnect_check(struct si470x_device *radio)
+{
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ mutex_lock(&radio->lock);
+ radio->users++;
+
+ if (radio->users == 1)
+ /* start radio */
+ retval = si470x_start(radio);
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety check */
+ if (!radio)
+ return -ENODEV;
+
+ mutex_lock(&radio->lock);
+ radio->users--;
+ if (radio->users == 0)
+ /* stop radio */
+ retval = si470x_stop(radio);
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = video_ioctl2,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ capability->version = DRIVER_KERNEL_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * I2C Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_probe - probe for the device
+ */
+static int __devinit si470x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct si470x_device *radio;
+ int retval = 0;
+ unsigned char version_warning = 0;
+
+ /* private data allocation and initialization */
+ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio) {
+ retval = -ENOMEM;
+ goto err_initial;
+ }
+ radio->users = 0;
+ radio->client = client;
+ mutex_init(&radio->lock);
+
+ /* video device allocation and initialization */
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ retval = -ENOMEM;
+ goto err_radio;
+ }
+ memcpy(radio->videodev, &si470x_viddev_template,
+ sizeof(si470x_viddev_template));
+ video_set_drvdata(radio->videodev, radio);
+
+ /* power up : need 110ms */
+ radio->registers[POWERCFG] = POWERCFG_ENABLE;
+ if (si470x_set_register(radio, POWERCFG) < 0) {
+ retval = -EIO;
+ goto err_all;
+ }
+ msleep(110);
+
+ /* get device and chip versions */
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+ dev_warn(&client->dev,
+ "This driver is known to work with "
+ "firmware version %hu,\n", RADIO_FW_VERSION);
+ dev_warn(&client->dev,
+ "but the device has firmware version %hu.\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE);
+ version_warning = 1;
+ }
+
+ /* give out version warning */
+ if (version_warning == 1) {
+ dev_warn(&client->dev,
+ "If you have some trouble using this driver,\n");
+ dev_warn(&client->dev,
+ "please report to V4L ML at "
+ "linux-media@vger.kernel.org\n");
+ }
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* register video device */
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ dev_warn(&client->dev, "Could not register video device\n");
+ goto err_all;
+ }
+ i2c_set_clientdata(client, radio);
+
+ return 0;
+err_all:
+err_video:
+ video_device_release(radio->videodev);
+err_radio:
+ kfree(radio);
+err_initial:
+ return retval;
+}
+
+
+/*
+ * si470x_i2c_remove - remove the device
+ */
+static __devexit int si470x_i2c_remove(struct i2c_client *client)
+{
+ struct si470x_device *radio = i2c_get_clientdata(client);
+
+ video_unregister_device(radio->videodev);
+ kfree(radio);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+
+/*
+ * si470x_i2c_driver - i2c driver interface
+ */
+static struct i2c_driver si470x_i2c_driver = {
+ .driver = {
+ .name = "si470x",
+ .owner = THIS_MODULE,
+ },
+ .probe = si470x_i2c_probe,
+ .remove = __devexit_p(si470x_i2c_remove),
+ .id_table = si470x_i2c_id,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_init - module init
+ */
+static int __init si470x_i2c_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+ return i2c_add_driver(&si470x_i2c_driver);
+}
+
+
+/*
+ * si470x_i2c_exit - module exit
+ */
+static void __exit si470x_i2c_exit(void)
+{
+ i2c_del_driver(&si470x_i2c_driver);
+}
+
+
+module_init(si470x_i2c_init);
+module_exit(si470x_i2c_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c
new file mode 100644
index 000000000..f2d0e1ddb
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -0,0 +1,988 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-usb.c
+ *
+ * USB driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * ToDo:
+ * - add firmware download/update support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.10"
+
+/* kernel includes */
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "radio-si470x.h"
+
+
+/* USB Device ID List */
+static struct usb_device_id si470x_usb_driver_id_table[] = {
+ /* Silicon Labs USB FM Radio Reference Design */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+ /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+ /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
+ /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* USB timeout */
+static unsigned int usb_timeout = 500;
+module_param(usb_timeout, uint, 0644);
+MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
+
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0444);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static unsigned short max_rds_errors = 1;
+/* 0 means 0 errors requiring correction */
+/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5 errors requiring correction */
+/* 3 means 6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, ushort, 0644);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
+
+
+/**************************************************************************
+ * USB HID Reports
+ **************************************************************************/
+
+/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
+/* with the (REPORT_ID - 1) corresponding to the register address across USB */
+/* endpoint 0 using GET_REPORT and SET_REPORT */
+#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
+#define REGISTER_REPORT(reg) ((reg) + 1)
+
+/* Report 17 gives direct read/write access to the entire Si470x register */
+/* map across endpoint 0 using GET_REPORT and SET_REPORT */
+#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define ENTIRE_REPORT 17
+
+/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
+/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
+#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define RDS_REPORT 18
+
+/* Report 19: LED state */
+#define LED_REPORT_SIZE 3
+#define LED_REPORT 19
+
+/* Report 19: stream */
+#define STREAM_REPORT_SIZE 3
+#define STREAM_REPORT 19
+
+/* Report 20: scratch */
+#define SCRATCH_PAGE_SIZE 63
+#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
+#define SCRATCH_REPORT 20
+
+/* Reports 19-22: flash upgrade of the C8051F321 */
+#define WRITE_REPORT_SIZE 4
+#define WRITE_REPORT 19
+#define FLASH_REPORT_SIZE 64
+#define FLASH_REPORT 20
+#define CRC_REPORT_SIZE 3
+#define CRC_REPORT 21
+#define RESPONSE_REPORT_SIZE 2
+#define RESPONSE_REPORT 22
+
+/* Report 23: currently unused, but can accept 60 byte reports on the HID */
+/* interrupt out endpoint 2 every 1 millisecond */
+#define UNUSED_REPORT 23
+
+
+
+/**************************************************************************
+ * Software/Hardware Versions from Scratch Page
+ **************************************************************************/
+#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
+#define RADIO_SW_VERSION 7
+#define RADIO_HW_VERSION 1
+
+
+
+/**************************************************************************
+ * LED State Definitions
+ **************************************************************************/
+#define LED_COMMAND 0x35
+
+#define NO_CHANGE_LED 0x00
+#define ALL_COLOR_LED 0x01 /* streaming state */
+#define BLINK_GREEN_LED 0x02 /* connect state */
+#define BLINK_RED_LED 0x04
+#define BLINK_ORANGE_LED 0x10 /* disconnect state */
+#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
+#define SOLID_RED_LED 0x40 /* bootload state */
+#define SOLID_ORANGE_LED 0x80
+
+
+
+/**************************************************************************
+ * Stream State Definitions
+ **************************************************************************/
+#define STREAM_COMMAND 0x36
+#define STREAM_VIDPID 0x00
+#define STREAM_AUDIO 0xff
+
+
+
+/**************************************************************************
+ * Bootloader / Flash Commands
+ **************************************************************************/
+
+/* unique id sent to bootloader and required to put into a bootload state */
+#define UNIQUE_BL_ID 0x34
+
+/* mask for the flash data */
+#define FLASH_DATA_MASK 0x55
+
+/* bootloader commands */
+#define GET_SW_VERSION_COMMAND 0x00
+#define SET_PAGE_COMMAND 0x01
+#define ERASE_PAGE_COMMAND 0x02
+#define WRITE_PAGE_COMMAND 0x03
+#define CRC_ON_PAGE_COMMAND 0x04
+#define READ_FLASH_BYTE_COMMAND 0x05
+#define RESET_DEVICE_COMMAND 0x06
+#define GET_HW_VERSION_COMMAND 0x07
+#define BLANK 0xff
+
+/* bootloader command responses */
+#define COMMAND_OK 0x01
+#define COMMAND_FAILED 0x02
+#define COMMAND_PENDING 0x03
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTER_REPORTs
+ **************************************************************************/
+
+/*
+ * si470x_get_report - receive a HID report
+ */
+static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
+{
+ unsigned char *report = (unsigned char *) buf;
+ int retval;
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ report[0], 2,
+ buf, size, usb_timeout);
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev,
+ "si470x_get_report: usb_control_msg returned %d\n",
+ retval);
+ return retval;
+}
+
+
+/*
+ * si470x_set_report - send a HID report
+ */
+static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
+{
+ unsigned char *report = (unsigned char *) buf;
+ int retval;
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_sndctrlpipe(radio->usbdev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ report[0], 2,
+ buf, size, usb_timeout);
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev,
+ "si470x_set_report: usb_control_msg returned %d\n",
+ retval);
+ return retval;
+}
+
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ unsigned char buf[REGISTER_REPORT_SIZE];
+ int retval;
+
+ buf[0] = REGISTER_REPORT(regnr);
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval >= 0)
+ radio->registers[regnr] = get_unaligned_be16(&buf[1]);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ unsigned char buf[REGISTER_REPORT_SIZE];
+ int retval;
+
+ buf[0] = REGISTER_REPORT(regnr);
+ put_unaligned_be16(radio->registers[regnr], &buf[1]);
+
+ retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ unsigned char buf[ENTIRE_REPORT_SIZE];
+ int retval;
+ unsigned char regnr;
+
+ buf[0] = ENTIRE_REPORT;
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+ radio->registers[regnr] = get_unaligned_be16(
+ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - LED_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_set_led_state - sets the led state
+ */
+static int si470x_set_led_state(struct si470x_device *radio,
+ unsigned char led_state)
+{
+ unsigned char buf[LED_REPORT_SIZE];
+ int retval;
+
+ buf[0] = LED_REPORT;
+ buf[1] = LED_COMMAND;
+ buf[2] = led_state;
+
+ retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - SCRATCH_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_scratch_versions - gets the scratch page and version infos
+ */
+static int si470x_get_scratch_page_versions(struct si470x_device *radio)
+{
+ unsigned char buf[SCRATCH_REPORT_SIZE];
+ int retval;
+
+ buf[0] = SCRATCH_REPORT;
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev, "si470x_get_scratch: "
+ "si470x_get_report returned %d\n", retval);
+ else {
+ radio->software_version = buf[1];
+ radio->hardware_version = buf[2];
+ }
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - DISCONNECT_CHECK
+ **************************************************************************/
+
+/*
+ * si470x_disconnect_check - check whether radio disconnects
+ */
+int si470x_disconnect_check(struct si470x_device *radio)
+{
+ if (radio->disconnected)
+ return -EIO;
+ else
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * RDS Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_int_in_callback - rds callback and processing function
+ *
+ * TODO: do we need to use mutex locks in some sections?
+ */
+static void si470x_int_in_callback(struct urb *urb)
+{
+ struct si470x_device *radio = urb->context;
+ unsigned char buf[RDS_REPORT_SIZE];
+ int retval;
+ unsigned char regnr;
+ unsigned char blocknum;
+ unsigned short bler; /* rds block errors */
+ unsigned short rds;
+ unsigned char tmpbuf[3];
+
+ if (urb->status) {
+ if (urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
+ return;
+ } else {
+ dev_warn(&radio->intf->dev,
+ "non-zero urb status (%d)\n", urb->status);
+ goto resubmit; /* Maybe we can recover. */
+ }
+ }
+
+ /* safety checks */
+ if (radio->disconnected)
+ return;
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ goto resubmit;
+
+ if (urb->actual_length > 0) {
+ /* Update RDS registers with URB data */
+ buf[0] = RDS_REPORT;
+ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+ radio->registers[STATUSRSSI + regnr] =
+ get_unaligned_be16(&radio->int_in_buffer[
+ regnr * RADIO_REGISTER_SIZE + 1]);
+ /* get rds blocks */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
+ /* No RDS group ready, better luck next time */
+ goto resubmit;
+ }
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
+ /* RDS decoder not synchronized */
+ goto resubmit;
+ }
+ for (blocknum = 0; blocknum < 4; blocknum++) {
+ switch (blocknum) {
+ default:
+ bler = (radio->registers[STATUSRSSI] &
+ STATUSRSSI_BLERA) >> 9;
+ rds = radio->registers[RDSA];
+ break;
+ case 1:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERB) >> 14;
+ rds = radio->registers[RDSB];
+ break;
+ case 2:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERC) >> 12;
+ rds = radio->registers[RDSC];
+ break;
+ case 3:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERD) >> 10;
+ rds = radio->registers[RDSD];
+ break;
+ };
+
+ /* Fill the V4L2 RDS buffer */
+ put_unaligned_le16(rds, &tmpbuf);
+ tmpbuf[2] = blocknum; /* offset name */
+ tmpbuf[2] |= blocknum << 3; /* received offset */
+ if (bler > max_rds_errors)
+ tmpbuf[2] |= 0x80; /* uncorrectable errors */
+ else if (bler > 0)
+ tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
+ radio->wr_index += 3;
+
+ /* wrap write pointer */
+ if (radio->wr_index >= radio->buf_size)
+ radio->wr_index = 0;
+
+ /* check for overflow */
+ if (radio->wr_index == radio->rd_index) {
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+ }
+ }
+ if (radio->wr_index != radio->rd_index)
+ wake_up_interruptible(&radio->read_queue);
+ }
+
+resubmit:
+ /* Resubmit if we're still running. */
+ if (radio->int_in_running && radio->usbdev) {
+ retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_warn(&radio->intf->dev,
+ "resubmitting urb failed (%d)", retval);
+ radio->int_in_running = 0;
+ }
+ }
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+ unsigned int block_count = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ /* block if no new data available */
+ while (radio->wr_index == radio->rd_index) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ if (wait_event_interruptible(radio->read_queue,
+ radio->wr_index != radio->rd_index) < 0) {
+ retval = -EINTR;
+ goto done;
+ }
+ }
+
+ /* calculate block count from byte count */
+ count /= 3;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ mutex_lock(&radio->lock);
+ while (block_count < count) {
+ if (radio->rd_index == radio->wr_index)
+ break;
+
+ /* always transfer rds complete blocks */
+ if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
+ /* retval = -EFAULT; */
+ break;
+
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+
+ /* increment counters */
+ block_count++;
+ buf += 3;
+ retval += 3;
+ }
+ mutex_unlock(&radio->lock);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ poll_wait(file, &radio->read_queue, pts);
+
+ if (radio->rd_index != radio->wr_index)
+ retval = POLLIN | POLLRDNORM;
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval;
+
+ lock_kernel();
+ radio->users++;
+
+ retval = usb_autopm_get_interface(radio->intf);
+ if (retval < 0) {
+ radio->users--;
+ retval = -EIO;
+ goto done;
+ }
+
+ if (radio->users == 1) {
+ /* start radio */
+ retval = si470x_start(radio);
+ if (retval < 0) {
+ usb_autopm_put_interface(radio->intf);
+ goto done;
+ }
+
+ /* initialize interrupt urb */
+ usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
+ usb_rcvintpipe(radio->usbdev,
+ radio->int_in_endpoint->bEndpointAddress),
+ radio->int_in_buffer,
+ le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
+ si470x_int_in_callback,
+ radio,
+ radio->int_in_endpoint->bInterval);
+
+ radio->int_in_running = 1;
+ mb();
+
+ retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
+ if (retval) {
+ dev_info(&radio->intf->dev,
+ "submitting int urb failed (%d)\n", retval);
+ radio->int_in_running = 0;
+ usb_autopm_put_interface(radio->intf);
+ }
+ }
+
+done:
+ unlock_kernel();
+ return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety check */
+ if (!radio) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ mutex_lock(&radio->disconnect_lock);
+ radio->users--;
+ if (radio->users == 0) {
+ /* shutdown interrupt handler */
+ if (radio->int_in_running) {
+ radio->int_in_running = 0;
+ if (radio->int_in_urb)
+ usb_kill_urb(radio->int_in_urb);
+ }
+
+ if (radio->disconnected) {
+ video_unregister_device(radio->videodev);
+ kfree(radio->int_in_buffer);
+ kfree(radio->buffer);
+ kfree(radio);
+ goto unlock;
+ }
+
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
+ /* stop radio */
+ retval = si470x_stop(radio);
+ usb_autopm_put_interface(radio->intf);
+ }
+unlock:
+ mutex_unlock(&radio->disconnect_lock);
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .read = si470x_fops_read,
+ .poll = si470x_fops_poll,
+ .ioctl = video_ioctl2,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct si470x_device *radio = video_drvdata(file);
+
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ usb_make_path(radio->usbdev, capability->bus_info,
+ sizeof(capability->bus_info));
+ capability->version = DRIVER_KERNEL_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * USB Interface
+ **************************************************************************/
+
+/*
+ * si470x_usb_driver_probe - probe for the device
+ */
+static int si470x_usb_driver_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct si470x_device *radio;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, int_end_size, retval = 0;
+ unsigned char version_warning = 0;
+
+ /* private data allocation and initialization */
+ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio) {
+ retval = -ENOMEM;
+ goto err_initial;
+ }
+ radio->users = 0;
+ radio->disconnected = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ mutex_init(&radio->disconnect_lock);
+ mutex_init(&radio->lock);
+
+ iface_desc = intf->cur_altsetting;
+
+ /* Set up interrupt endpoint information. */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+ USB_DIR_IN) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
+ radio->int_in_endpoint = endpoint;
+ }
+ if (!radio->int_in_endpoint) {
+ dev_info(&intf->dev, "could not find interrupt in endpoint\n");
+ retval = -EIO;
+ goto err_radio;
+ }
+
+ int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
+
+ radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);
+ if (!radio->int_in_buffer) {
+ dev_info(&intf->dev, "could not allocate int_in_buffer");
+ retval = -ENOMEM;
+ goto err_radio;
+ }
+
+ radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!radio->int_in_urb) {
+ dev_info(&intf->dev, "could not allocate int_in_urb");
+ retval = -ENOMEM;
+ goto err_intbuffer;
+ }
+
+ /* video device allocation and initialization */
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ retval = -ENOMEM;
+ goto err_intbuffer;
+ }
+ memcpy(radio->videodev, &si470x_viddev_template,
+ sizeof(si470x_viddev_template));
+ video_set_drvdata(radio->videodev, radio);
+
+ /* get device and chip versions */
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "firmware version %hu,\n", RADIO_FW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has firmware version %hu.\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE);
+ version_warning = 1;
+ }
+
+ /* get software and hardware versions */
+ if (si470x_get_scratch_page_versions(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&intf->dev, "software version %d, hardware version %d\n",
+ radio->software_version, radio->hardware_version);
+ if (radio->software_version < RADIO_SW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "software version %hu,\n", RADIO_SW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has software version %hu.\n",
+ radio->software_version);
+ version_warning = 1;
+ }
+ if (radio->hardware_version < RADIO_HW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "hardware version %hu,\n", RADIO_HW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has hardware version %hu.\n",
+ radio->hardware_version);
+ version_warning = 1;
+ }
+
+ /* give out version warning */
+ if (version_warning == 1) {
+ dev_warn(&intf->dev,
+ "If you have some trouble using this driver,\n");
+ dev_warn(&intf->dev,
+ "please report to V4L ML at "
+ "linux-media@vger.kernel.org\n");
+ }
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* set led to connect state */
+ si470x_set_led_state(radio, BLINK_GREEN_LED);
+
+ /* rds buffer allocation */
+ radio->buf_size = rds_buf * 3;
+ radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ if (!radio->buffer) {
+ retval = -EIO;
+ goto err_video;
+ }
+
+ /* rds buffer configuration */
+ radio->wr_index = 0;
+ radio->rd_index = 0;
+ init_waitqueue_head(&radio->read_queue);
+
+ /* register video device */
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ dev_warn(&intf->dev, "Could not register video device\n");
+ goto err_all;
+ }
+ usb_set_intfdata(intf, radio);
+
+ return 0;
+err_all:
+ kfree(radio->buffer);
+err_video:
+ video_device_release(radio->videodev);
+err_intbuffer:
+ kfree(radio->int_in_buffer);
+err_radio:
+ kfree(radio);
+err_initial:
+ return retval;
+}
+
+
+/*
+ * si470x_usb_driver_suspend - suspend the device
+ */
+static int si470x_usb_driver_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ dev_info(&intf->dev, "suspending now...\n");
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_resume - resume the device
+ */
+static int si470x_usb_driver_resume(struct usb_interface *intf)
+{
+ dev_info(&intf->dev, "resuming now...\n");
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_disconnect - disconnect the device
+ */
+static void si470x_usb_driver_disconnect(struct usb_interface *intf)
+{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
+ mutex_lock(&radio->disconnect_lock);
+ radio->disconnected = 1;
+ usb_set_intfdata(intf, NULL);
+ if (radio->users == 0) {
+ /* set led to disconnect state */
+ si470x_set_led_state(radio, BLINK_ORANGE_LED);
+
+ /* Free data structures. */
+ usb_free_urb(radio->int_in_urb);
+
+ kfree(radio->int_in_buffer);
+ video_unregister_device(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+ mutex_unlock(&radio->disconnect_lock);
+}
+
+
+/*
+ * si470x_usb_driver - usb driver interface
+ */
+static struct usb_driver si470x_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = si470x_usb_driver_probe,
+ .disconnect = si470x_usb_driver_disconnect,
+ .suspend = si470x_usb_driver_suspend,
+ .resume = si470x_usb_driver_resume,
+ .id_table = si470x_usb_driver_id_table,
+ .supports_autosuspend = 1,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_module_init - module init
+ */
+static int __init si470x_module_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+ return usb_register(&si470x_usb_driver);
+}
+
+
+/*
+ * si470x_module_exit - module exit
+ */
+static void __exit si470x_module_exit(void)
+{
+ usb_deregister(&si470x_usb_driver);
+}
+
+
+module_init(si470x_module_init);
+module_exit(si470x_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h
new file mode 100644
index 000000000..f931789c2
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x.h
@@ -0,0 +1,226 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x.h
+ *
+ * Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/* driver definitions */
+#define DRIVER_NAME "radio-si470x"
+
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/version.h>
+#include "compat.h"
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/rds.h>
+#include <asm/unaligned.h>
+
+
+
+/**************************************************************************
+ * Register Definitions
+ **************************************************************************/
+#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
+#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
+#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
+
+#define DEVICEID 0 /* Device ID */
+#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
+#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
+
+#define CHIPID 1 /* Chip ID */
+#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
+#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
+#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
+
+#define POWERCFG 2 /* Power Configuration */
+#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
+#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
+#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
+#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
+#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
+#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
+#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
+#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
+#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
+
+#define CHANNEL 3 /* Channel */
+#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
+#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
+
+#define SYSCONFIG1 4 /* System Configuration 1 */
+#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
+#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
+#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
+#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
+#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
+#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
+#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
+#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
+
+#define SYSCONFIG2 5 /* System Configuration 2 */
+#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
+#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
+#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
+#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
+
+#define SYSCONFIG3 6 /* System Configuration 3 */
+#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
+#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
+#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
+#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
+
+#define TEST1 7 /* Test 1 */
+#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
+
+#define TEST2 8 /* Test 2 */
+/* TEST2 only contains reserved bits */
+
+#define BOOTCONFIG 9 /* Boot Configuration */
+/* BOOTCONFIG only contains reserved bits */
+
+#define STATUSRSSI 10 /* Status RSSI */
+#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
+#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
+#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
+#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
+#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
+#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
+#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
+#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
+
+#define READCHAN 11 /* Read Channel */
+#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
+#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
+#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
+#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
+
+#define RDSA 12 /* RDSA */
+#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
+
+#define RDSB 13 /* RDSB */
+#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
+
+#define RDSC 14 /* RDSC */
+#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
+
+#define RDSD 15 /* RDSD */
+#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
+
+
+
+/**************************************************************************
+ * General Driver Definitions
+ **************************************************************************/
+
+/*
+ * si470x_device - private data
+ */
+struct si470x_device {
+ struct video_device *videodev;
+
+ /* driver management */
+ unsigned int users;
+
+ /* Silabs internal registers (0..15) */
+ unsigned short registers[RADIO_REGISTER_NUM];
+
+ /* RDS receive buffer */
+ wait_queue_head_t read_queue;
+ struct mutex lock; /* buffer locking */
+ unsigned char *buffer; /* size is always multiple of three */
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
+
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+
+ /* Interrupt endpoint handling */
+ char *int_in_buffer;
+ struct usb_endpoint_descriptor *int_in_endpoint;
+ struct urb *int_in_urb;
+ int int_in_running;
+
+ /* scratch page */
+ unsigned char software_version;
+ unsigned char hardware_version;
+
+ /* driver management */
+ unsigned char disconnected;
+ struct mutex disconnect_lock;
+#endif
+
+#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
+ struct i2c_client *client;
+#endif
+};
+
+
+
+/**************************************************************************
+ * Firmware Versions
+ **************************************************************************/
+
+#define RADIO_FW_VERSION 15
+
+
+
+/**************************************************************************
+ * Frequency Multiplicator
+ **************************************************************************/
+
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+
+
+/**************************************************************************
+ * Common Functions
+ **************************************************************************/
+extern const struct v4l2_file_operations si470x_fops;
+extern struct video_device si470x_viddev_template;
+int si470x_get_register(struct si470x_device *radio, int regnr);
+int si470x_set_register(struct si470x_device *radio, int regnr);
+int si470x_disconnect_check(struct si470x_device *radio);
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
+int si470x_start(struct si470x_device *radio);
+int si470x_stop(struct si470x_device *radio);
+int si470x_rds_on(struct si470x_device *radio);
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability);
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig
index 8460013a0..e8a6e4de4 100644
--- a/linux/drivers/media/video/Kconfig
+++ b/linux/drivers/media/video/Kconfig
@@ -991,6 +991,8 @@ source "drivers/media/video/pwc/Kconfig"
config USB_ZR364XX
tristate "USB ZR364XX Camera support"
depends on VIDEO_V4L2
+ select VIDEOBUF_GEN
+ select VIDEOBUF_VMALLOC
---help---
Say Y here if you want to connect this type of camera to your
computer's USB port.
diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c
index 3702baad6..8f6bb845e 100644
--- a/linux/drivers/media/video/bw-qcam.c
+++ b/linux/drivers/media/video/bw-qcam.c
@@ -993,7 +993,7 @@ static int accept_bwqcam(struct parport *port)
if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
/* user gave parport parameters */
- for(n=0; parport[n] && n<MAX_CAMS; n++){
+ for (n = 0; n < MAX_CAMS && parport[n]; n++) {
char *ep;
unsigned long r;
r = simple_strtoul(parport[n], &ep, 0);
diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c
index bf0f26c03..19d57e698 100644
--- a/linux/drivers/media/video/cx88/cx88-cards.c
+++ b/linux/drivers/media/video/cx88/cx88-cards.c
@@ -1316,6 +1316,51 @@ static const struct cx88_board cx88_boards[] = {
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_WINFAST_DTV2000H_J] = {
+ .name = "WinFast DTV2000 H rev. J",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017300,
+ .gpio1 = 0x00008207,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00018300,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_GENIATECH_DVBS] = {
.name = "Geniatech DVB-S",
.tuner_type = TUNER_ABSENT,
@@ -2315,6 +2360,10 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x665e,
.card = CX88_BOARD_WINFAST_DTV2000H,
},{
+ .subvendor = 0x107d,
+ .subdevice = 0x6f2b,
+ .card = CX88_BOARD_WINFAST_DTV2000H_J,
+ },{
.subvendor = 0x18ac,
.subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
.card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
@@ -3036,6 +3085,14 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
ctl->demod = XC3028_FE_OREN538;
break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ /* FIXME: For this board, the xc3028 never recovers after being
+ powered down (the reset GPIO probably is not set properly).
+ We don't have access to the hardware so we cannot determine
+ which GPIO is used for xc3028, so just disable power xc3028
+ power management for now */
+ ctl->disable_power_mgmt = 1;
+ break;
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c
index 36f35f0b5..ab84c085f 100644
--- a/linux/drivers/media/video/cx88/cx88-dvb.c
+++ b/linux/drivers/media/video/cx88/cx88-dvb.c
@@ -502,6 +502,7 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
static struct zl10353_config cx88_geniatech_x8000_mt = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
#if 0
.input_frequency = 0xe609,
.parallel_ts = 1,
@@ -702,6 +703,7 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
case CX88_BOARD_HAUPPAUGE_HVR1300:
diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c
index 7c5e94916..8d0042ab2 100644
--- a/linux/drivers/media/video/cx88/cx88-input.c
+++ b/linux/drivers/media/video/cx88/cx88-input.c
@@ -238,6 +238,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->sampling = 1;
break;
case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_WINFAST_DTV1800H:
ir_codes = ir_codes_winfast;
ir->gpio_addr = MO_GP0_IO;
diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h
index 59fc01a53..014004acd 100644
--- a/linux/drivers/media/video/cx88/cx88.h
+++ b/linux/drivers/media/video/cx88/cx88.h
@@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
#define CX88_BOARD_HAUPPAUGE_IRONLY 80
#define CX88_BOARD_WINFAST_DTV1800H 81
+#define CX88_BOARD_WINFAST_DTV2000H_J 82
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index daa710b36..c936e7660 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -231,9 +231,10 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "EM2710/EM2750/EM2751 webcam grabber",
- .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
+ .no_audio = 1,
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
@@ -261,6 +262,7 @@ struct em28xx_board em28xx_boards[] = {
.name = "Unknown EM2750/28xx video grabber",
.tuner_type = TUNER_ABSENT,
.is_webcam = 1, /* To enable sensor probe */
+ .no_audio = 1,
},
[EM2750_BOARD_DLCW_130] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
@@ -481,6 +483,7 @@ struct em28xx_board em28xx_boards[] = {
.name = "Silvercrest Webcam 1.3mpix",
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
+ .no_audio = 1,
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
@@ -1612,6 +1615,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2750_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2800),
.driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2710),
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2820),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2821),
@@ -1728,7 +1733,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
{0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
- {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+ {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
};
@@ -1835,6 +1840,7 @@ static int em28xx_hint_sensor(struct em28xx *dev)
__be16 version_be;
u16 version;
+ /* Micron sensor detection */
dev->i2c_client.addr = 0xba >> 1;
cmd = 0;
i2c_master_send(&dev->i2c_client, &cmd, 1);
@@ -1843,16 +1849,27 @@ static int em28xx_hint_sensor(struct em28xx *dev)
return -EINVAL;
version = be16_to_cpu(version_be);
-
switch (version) {
case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ em28xx_set_model(dev);
+
sensor_name = "mt9v011";
dev->em28xx_sensor = EM28XX_MT9V011;
dev->sensor_xres = 640;
dev->sensor_yres = 480;
- dev->sensor_xtal = 6300000;
+ /*
+ * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+ * the Silvercrest cam I have here for testing - for higher
+ * resolutions, a high clock cause horizontal artifacts, so we
+ * need to use a lower xclk frequency.
+ * Yet, it would be possible to adjust xclk depending on the
+ * desired resolution, since this affects directly the
+ * frame rate.
+ */
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+ dev->sensor_xtal = 4300000;
/* probably means GRGB 16 bit bayer */
dev->vinmode = 0x0d;
@@ -1865,6 +1882,8 @@ static int em28xx_hint_sensor(struct em28xx *dev)
#endif
case 0x8431:
dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
sensor_name = "mt9m001";
dev->em28xx_sensor = EM28XX_MT9M001;
em28xx_initialize_mt9m001(dev);
@@ -1881,6 +1900,9 @@ static int em28xx_hint_sensor(struct em28xx *dev)
return -EINVAL;
}
+ /* Setup webcam defaults */
+ em28xx_pre_card_setup(dev);
+
em28xx_errdev("Sensor is %s, using model %s entry.\n",
sensor_name, em28xx_boards[dev->model].name);
@@ -1892,60 +1914,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
*/
void em28xx_pre_card_setup(struct em28xx *dev)
{
- int rc;
-
- em28xx_set_model(dev);
-
- em28xx_info("Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
-
- /* Set the default GPO/GPIO for legacy devices */
- dev->reg_gpo_num = EM2880_R04_GPO;
- dev->reg_gpio_num = EM28XX_R08_GPIO;
-
- dev->wait_after_write = 5;
-
- /* Based on the Chip ID, set the device configuration */
- rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
- if (rc > 0) {
- dev->chip_id = rc;
-
- switch (dev->chip_id) {
- case CHIP_ID_EM2750:
- em28xx_info("chip ID is em2750\n");
- break;
- case CHIP_ID_EM2820:
- em28xx_info("chip ID is em2710 or em2820\n");
- break;
- case CHIP_ID_EM2840:
- em28xx_info("chip ID is em2840\n");
- break;
- case CHIP_ID_EM2860:
- em28xx_info("chip ID is em2860\n");
- break;
- case CHIP_ID_EM2870:
- em28xx_info("chip ID is em2870\n");
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2874:
- em28xx_info("chip ID is em2874\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2883:
- em28xx_info("chip ID is em2882/em2883\n");
- dev->wait_after_write = 0;
- break;
- default:
- em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
- }
- }
-
- /* Prepopulate cached GPO register content */
- rc = em28xx_read_reg(dev, dev->reg_gpo_num);
- if (rc >= 0)
- dev->reg_gpo = rc;
-
/* Set the initial XCLK and I2C clock values based on the board
definition */
em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
@@ -2328,7 +2296,20 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
void em28xx_card_setup(struct em28xx *dev)
{
- em28xx_set_model(dev);
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam) {
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
+ else
+ dev->progressive = 1;
+ } else
+ em28xx_set_model(dev);
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
if (em28xx_boards[dev->model].tuner_addr)
@@ -2402,10 +2383,6 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
- case EM2820_BOARD_SILVERCREST_WEBCAM:
- /* FIXME: need to document the registers bellow */
- em28xx_write_reg(dev, 0x0d, 0x42);
- em28xx_write_reg(dev, 0x13, 0x08);
}
if (dev->board.has_snapshot_button)
@@ -2562,7 +2539,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor)
{
struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
+ int retval;
int errCode;
dev->udev = udev;
@@ -2579,6 +2556,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->em28xx_read_reg_req = em28xx_read_reg_req;
dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+ em28xx_set_model(dev);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
+
+ dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
+ retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+ if (retval > 0) {
+ dev->chip_id = retval;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2710:
+ em28xx_info("chip ID is em2710\n");
+ break;
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820 (or em2710)\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2883:
+ em28xx_info("chip ID is em2882/em2883\n");
+ dev->wait_after_write = 0;
+ break;
+ default:
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+ }
+ }
+
+ /* Prepopulate cached GPO register content */
+ retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (retval >= 0)
+ dev->reg_gpo = retval;
+
em28xx_pre_card_setup(dev);
if (!dev->board.is_em2800) {
@@ -2613,14 +2642,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vinmode = 0x10;
dev->vinctl = 0x11;
- /*
- * If the device can be a webcam, seek for a sensor.
- * If sensor is not found, then it isn't a webcam.
- */
- if (dev->board.is_webcam)
- if (em28xx_hint_sensor(dev) < 0)
- dev->board.is_webcam = 0;
-
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c
index f24af40df..46cb13182 100644
--- a/linux/drivers/media/video/em28xx/em28xx-core.c
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c
@@ -494,7 +494,7 @@ int em28xx_audio_setup(struct em28xx *dev)
/* If device doesn't support Usb Audio Class, use vendor class */
if (!dev->has_audio_class)
- dev->has_alsa_audio = 1;
+ dev->has_alsa_audio = ~dev->board.no_audio;
dev->audio_mode.has_audio = 1;
@@ -632,6 +632,9 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
+ if (dev->board.is_webcam)
+ rc = em28xx_write_reg(dev, 0x13, 0x0c);
+
/* enable video capture */
rc = em28xx_write_reg(dev, 0x48, 0x00);
@@ -720,7 +723,10 @@ int em28xx_resolution_set(struct em28xx *dev)
{
int width, height;
width = norm_maxw(dev);
- height = norm_maxh(dev) >> 1;
+ height = norm_maxh(dev);
+
+ if (!dev->progressive)
+ height >>= norm_maxh(dev);
em28xx_set_outfmt(dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c
index e32267383..3d522d547 100644
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c
@@ -479,7 +479,6 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_KWORLD_DVB_310U:
- case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
@@ -489,6 +488,7 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h
index a2676d63c..6bf84bd78 100644
--- a/linux/drivers/media/video/em28xx/em28xx-reg.h
+++ b/linux/drivers/media/video/em28xx/em28xx-reg.h
@@ -176,7 +176,8 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
- CHIP_ID_EM2820 = 18, /* Also used by em2710 */
+ CHIP_ID_EM2710 = 17,
+ CHIP_ID_EM2820 = 18, /* Also used by some em2710 */
CHIP_ID_EM2840 = 20,
CHIP_ID_EM2750 = 33,
CHIP_ID_EM2860 = 34,
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index fcb99fb71..c7d723a03 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -212,15 +212,24 @@ static void em28xx_copy_video(struct em28xx *dev,
startread = p;
remain = len;
- /* Interlaces frame */
- if (buf->top_field)
+ if (dev->progressive)
fieldstart = outp;
- else
- fieldstart = outp + bytesperline;
+ else {
+ /* Interlaces two half frames */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+ }
linesdone = dma_q->pos / bytesperline;
currlinedone = dma_q->pos % bytesperline;
- offset = linesdone * bytesperline * 2 + currlinedone;
+
+ if (dev->progressive)
+ offset = linesdone * bytesperline + currlinedone;
+ else
+ offset = linesdone * bytesperline * 2 + currlinedone;
+
startwrite = fieldstart + offset;
lencopy = bytesperline - currlinedone;
lencopy = lencopy > remain ? remain : lencopy;
@@ -398,7 +407,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
len, (p[2] & 1) ? "odd" : "even");
- if (!(p[2] & 1)) {
+ if (dev->progressive || !(p[2] & 1)) {
if (buf != NULL)
buffer_filled(dev, dma_q, buf);
get_next_buf(dma_q, &buf);
@@ -742,7 +751,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
/* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
- f->fmt.pix.field = dev->interlaced ?
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
mutex_unlock(&dev->lock);
@@ -806,7 +818,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
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;
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
return 0;
}
@@ -899,6 +915,41 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
return 0;
}
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (dev->board.is_webcam)
+ rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+ video, g_parm, p);
+ else
+ v4l2_video_std_frame_period(dev->norm,
+ &p->parm.capture.timeperframe);
+
+ return rc;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->board.is_webcam)
+ return -EINVAL;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+}
+
static const char *iname[] = {
[EM28XX_VMUX_COMPOSITE1] = "Composite1",
[EM28XX_VMUX_COMPOSITE2] = "Composite2",
@@ -976,6 +1027,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
switch (a->index) {
case EM28XX_AMUX_VIDEO:
strcpy(a->name, "Television");
@@ -1022,6 +1076,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
return -EINVAL;
#else
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
if (a->index >= MAX_EM28XX_INPUT)
return -EINVAL;
if (0 == INPUT(a->index)->type)
@@ -1285,8 +1342,9 @@ static int vidioc_g_register(struct file *file, void *priv,
v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
return 0;
case V4L2_CHIP_MATCH_I2C_ADDR:
- /* Not supported yet */
- return -EINVAL;
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
default:
if (!v4l2_chip_match_host(&reg->match))
return -EINVAL;
@@ -1337,8 +1395,9 @@ static int vidioc_s_register(struct file *file, void *priv,
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
return 0;
case V4L2_CHIP_MATCH_I2C_ADDR:
- /* Not supported yet */
- return -EINVAL;
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
default:
if (!v4l2_chip_match_host(&reg->match))
return -EINVAL;
@@ -1444,9 +1503,11 @@ static int vidioc_querycap(struct file *file, void *priv,
#endif
V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ if (dev->audio_mode.has_audio)
+ cap->capabilities |= V4L2_CAP_AUDIO;
+
if (dev->tuner_type != TUNER_ABSENT)
cap->capabilities |= V4L2_CAP_TUNER;
@@ -1722,6 +1783,7 @@ static int em28xx_v4l2_open(struct file *filp)
struct em28xx *dev;
enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
+ enum v4l2_field field;
dev = em28xx_get_device(minor, &fh_type, &radio);
@@ -1778,8 +1840,13 @@ static int em28xx_v4l2_open(struct file *filp)
dev->users++;
+ if (dev->progressive)
+ field = V4L2_FIELD_NONE;
+ else
+ field = V4L2_FIELD_INTERLACED;
+
videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
- NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
+ NULL, &dev->slock, fh->type, field,
sizeof(struct em28xx_buffer), fh);
mutex_unlock(&dev->lock);
@@ -2003,6 +2070,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index df7fe9af7..c76da6985 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -397,6 +397,7 @@ struct em28xx_board {
unsigned int has_dvb:1;
unsigned int has_snapshot_button:1;
unsigned int is_webcam:1;
+ unsigned int no_audio:1;
unsigned int valid:1;
unsigned char xclk, i2c_speed;
@@ -493,6 +494,9 @@ struct em28xx {
int sensor_xres, sensor_yres;
int sensor_xtal;
+ /* Allows progressive (e. g. non-interlaced) mode */
+ int progressive;
+
/* Vinmode/Vinctl used at the driver */
int vinmode, vinctl;
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index 2e5197073..670091e42 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -1504,12 +1504,6 @@ static int vidioc_s_parm(struct file *filp, void *priv,
return 0;
}
-static int vidioc_s_std(struct file *filp, void *priv,
- v4l2_std_id *parm)
-{
- return 0;
-}
-
#ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *priv,
struct video_mbuf *mbuf)
@@ -1980,7 +1974,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
- .vidioc_s_std = vidioc_s_std,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 7127321ac..6b89f33a4 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -178,8 +178,10 @@ sensor_found:
sens_priv->settings =
kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
- if (!sens_priv->settings)
+ if (!sens_priv->settings) {
+ kfree(sens_priv);
return -ENOMEM;
+ }
sd->gspca_dev.cam.cam_mode = s5k83a_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c
index 44dea7949..6f9aab89c 100644
--- a/linux/drivers/media/video/gspca/vc032x.c
+++ b/linux/drivers/media/video/gspca/vc032x.c
@@ -3135,10 +3135,10 @@ static void sethvflip(struct gspca_dev *gspca_dev)
hflip = sd->hflip;
if (sd->flags & FL_HFLIP)
- hflip != hflip;
+ hflip = !hflip;
vflip = sd->vflip;
if (sd->flags & FL_VFLIP)
- vflip != vflip;
+ vflip = !vflip;
switch (sd->sensor) {
case SENSOR_MI1310_SOC:
case SENSOR_MI1320_SOC:
diff --git a/linux/drivers/media/video/hdpvr/hdpvr-video.c b/linux/drivers/media/video/hdpvr/hdpvr-video.c
index 5937de23c..2eb9dc2eb 100644
--- a/linux/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/linux/drivers/media/video/hdpvr/hdpvr-video.c
@@ -1222,6 +1222,8 @@ static const struct video_device hdpvr_video_template = {
V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
V4L2_STD_PAL_60,
+ .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
+ V4L2_STD_PAL_60,
};
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c
index c9706b889..70d359d84 100644
--- a/linux/drivers/media/video/mt9v011.c
+++ b/linux/drivers/media/video/mt9v011.c
@@ -62,13 +62,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = {
.step = 1,
.default_value = 0,
.flags = 0,
- },
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ }
};
struct mt9v011 {
struct v4l2_subdev sd;
unsigned width, height;
unsigned xtal;
+ unsigned hflip:1;
+ unsigned vflip:1;
u16 global_gain, red_bal, blue_bal;
};
@@ -141,7 +162,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = {
{ R0A_MT9V011_CLK_SPEED, 0x0000 },
{ R1E_MT9V011_DIGITAL_ZOOM, 0x0000 },
- { R20_MT9V011_READ_MODE, 0x1000 },
{ R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */
};
@@ -166,7 +186,7 @@ static void set_balance(struct v4l2_subdev *sd)
mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
}
-static void calc_fps(struct v4l2_subdev *sd)
+static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)
{
struct mt9v011 *core = to_mt9v011(sd);
unsigned height, width, hblank, vblank, speed;
@@ -189,6 +209,51 @@ static void calc_fps(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n",
tmp / 1000, tmp % 1000, t_time);
+
+ if (numerator && denominator) {
+ *numerator = 1000;
+ *denominator = (u32)frames_per_ms;
+ }
+}
+
+static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned height, width, hblank, vblank;
+ unsigned row_time, line_time;
+ u64 t_time, speed;
+
+ /* Avoid bogus calculus */
+ if (!numerator || !denominator)
+ return 0;
+
+ height = mt9v011_read(sd, R03_MT9V011_HEIGHT);
+ width = mt9v011_read(sd, R04_MT9V011_WIDTH);
+ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);
+ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);
+
+ row_time = width + 113 + hblank;
+ line_time = height + vblank + 1;
+
+ t_time = core->xtal * ((u64)numerator);
+ /* round to the closest value */
+ t_time += denominator / 2;
+ do_div(t_time, denominator);
+
+ speed = t_time;
+ do_div(speed, row_time * line_time);
+
+ /* Avoid having a negative value for speed */
+ if (speed < 2)
+ speed = 0;
+ else
+ speed -= 2;
+
+ /* Avoid speed overflow */
+ if (speed > 15)
+ return 15;
+
+ return (u16)speed;
}
static void set_res(struct v4l2_subdev *sd)
@@ -217,9 +282,23 @@ static void set_res(struct v4l2_subdev *sd)
mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height);
mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height);
- calc_fps(sd);
+ calc_fps(sd, NULL, NULL);
};
+static void set_read_mode(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned mode = 0x1000;
+
+ if (core->hflip)
+ mode |= 0x4000;
+
+ if (core->vflip)
+ mode |= 0x8000;
+
+ mt9v011_write(sd, R20_MT9V011_READ_MODE, mode);
+}
+
static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
{
int i;
@@ -230,6 +309,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
set_balance(sd);
set_res(sd);
+ set_read_mode(sd);
return 0;
};
@@ -250,6 +330,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_BLUE_BALANCE:
ctrl->value = core->blue_bal;
return 0;
+ case V4L2_CID_HFLIP:
+ ctrl->value = core->hflip ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ ctrl->value = core->vflip ? 1 : 0;
+ return 0;
}
return -EINVAL;
}
@@ -298,6 +384,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_BLUE_BALANCE:
core->blue_bal = ctrl->value;
break;
+ case V4L2_CID_HFLIP:
+ core->hflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
+ case V4L2_CID_VFLIP:
+ core->vflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
default:
return -EINVAL;
}
@@ -332,6 +426,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
return 0;
}
+static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ calc_fps(sd,
+ &cp->timeperframe.numerator,
+ &cp->timeperframe.denominator);
+
+ return 0;
+}
+
+static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ u16 speed;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ speed = calc_speed(sd, tpf->numerator, tpf->denominator);
+
+ mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed);
+ v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed);
+
+ /* Recalculate and update fps info */
+ calc_fps(sd, &tpf->numerator, &tpf->denominator);
+
+ return 0;
+}
+
static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
struct v4l2_pix_format *pix = &fmt->fmt.pix;
@@ -429,6 +561,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = {
.enum_fmt = mt9v011_enum_fmt,
.try_fmt = mt9v011_try_fmt,
.s_fmt = mt9v011_s_fmt,
+ .g_parm = mt9v011_g_parm,
+ .s_parm = mt9v011_s_parm,
};
static const struct v4l2_subdev_ops mt9v011_ops = {
diff --git a/linux/drivers/media/video/mx1_camera.c b/linux/drivers/media/video/mx1_camera.c
index 2d075205b..736c31d23 100644
--- a/linux/drivers/media/video/mx1_camera.c
+++ b/linux/drivers/media/video/mx1_camera.c
@@ -234,6 +234,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -241,13 +242,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
- spin_lock_irqsave(&pcdev->lock, flags);
-
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
@@ -264,8 +262,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
__raw_writel(temp, pcdev->base + CSICR1);
}
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void mx1_videobuf_release(struct videobuf_queue *vq,
diff --git a/linux/drivers/media/video/mx3_camera.c b/linux/drivers/media/video/mx3_camera.c
index e605c076e..9770cb793 100644
--- a/linux/drivers/media/video/mx3_camera.c
+++ b/linux/drivers/media/video/mx3_camera.c
@@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
}
}
-/* Called with .vb_lock held */
+/*
+ * Called with .vb_lock mutex held and
+ * under spinlock_irqsave(&mx3_cam->lock, ...)
+ */
static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -346,7 +349,8 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct idmac_video_param *video = &ichan->params.video;
const struct soc_camera_data_format *data_fmt = icd->current_fmt;
dma_cookie_t cookie;
- unsigned long flags;
+
+ BUG_ON(!irqs_disabled());
/* This is the configuration of one sg-element */
video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
@@ -359,8 +363,6 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
memset((void *)vb->baddr, 0xaa, vb->bsize);
#endif
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_add_tail(&vb->queue, &mx3_cam->capture);
if (!mx3_cam->active) {
@@ -370,24 +372,23 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
vb->state = VIDEOBUF_QUEUED;
}
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ spin_unlock_irq(&mx3_cam->lock);
cookie = txd->tx_submit(txd);
dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg));
+
+ spin_lock_irq(&mx3_cam->lock);
+
if (cookie >= 0)
return;
/* Submit error */
vb->state = VIDEOBUF_PREPARED;
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_del_init(&vb->queue);
if (mx3_cam->active == buf)
mx3_cam->active = NULL;
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
}
/* Called with .vb_lock held */
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
index 42875ec21..d8c9e6632 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -66,9 +66,10 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
u32 input;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo");
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
- if ((sid < ARRAY_SIZE(routing_schemes)) &&
- ((sp = routing_schemes[sid]) != NULL) &&
+ if ((sp != NULL) &&
(hdw->input_val >= 0) &&
(hdw->input_val < sp->cnt)) {
input = sp->def[hdw->input_val];
diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c
index 2876ce084..bdb4ced57 100644
--- a/linux/drivers/media/video/pwc/pwc-v4l.c
+++ b/linux/drivers/media/video/pwc/pwc-v4l.c
@@ -1033,7 +1033,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (std->index != 0)
return -EINVAL;
std->id = V4L2_STD_UNKNOWN;
- strncpy(std->name, "webcam", sizeof(std->name));
+ strlcpy(std->name, "webcam", sizeof(std->name));
return 0;
}
diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c
index 3f1b63e4e..92fb8b191 100644
--- a/linux/drivers/media/video/pxa_camera.c
+++ b/linux/drivers/media/video/pxa_camera.c
@@ -624,6 +624,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
dev_dbg(pcdev->soc_host.dev, "%s\n", __func__);
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -631,13 +632,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__,
vb, vb->baddr, vb->bsize, pcdev->active);
- spin_lock_irqsave(&pcdev->lock, flags);
-
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
@@ -645,8 +643,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
if (!pcdev->active)
pxa_camera_start_capture(pcdev);
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void pxa_videobuf_release(struct videobuf_queue *vq,
diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c
index e2d98ff5c..3ab7732e6 100644
--- a/linux/drivers/media/video/saa7134/saa7134-cards.c
+++ b/linux/drivers/media/video/saa7134/saa7134-cards.c
@@ -1633,7 +1633,7 @@ struct saa7134_board saa7134_boards[] = {
}},
.radio = {
.name = name_radio,
- .amux = LINE1,
+ .amux = TV,
.gpio = 0x00300001,
},
.mute = {
@@ -3370,8 +3370,8 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0200100,
},
},
- [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
- .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid",
+ [SAA7134_BOARD_HAUPPAUGE_HVR1150] = {
+ .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -3402,8 +3402,8 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0800100, /* GPIO 23 HI for FM */
},
},
- [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = {
- .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid",
+ [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
+ .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -4900,7 +4900,7 @@ struct saa7134_board saa7134_boards[] = {
/* Igor Kuznetsov <igk@igk.ru> */
.name = "Beholder BeholdTV H6",
.audio_clock = 0x00187de7,
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -5901,31 +5901,31 @@ struct pci_device_id saa7134_pci_tbl[] = {
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6706,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6707,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6708,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6709,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x670a,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -6458,8 +6458,8 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
switch (command) {
case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */
switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg);
break;
default:
@@ -6479,8 +6479,8 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,
int ret;
switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
/* tda8290 + tda18271 */
ret = saa7134_tda8290_18271_callback(dev, command, arg);
break;
@@ -6526,7 +6526,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
switch (tv.model) {
case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
- case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
@@ -6534,7 +6534,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */
case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
- case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
break;
default:
@@ -6728,8 +6728,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00);
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
/* GPIO 26 high for digital, low for analog */
saa7134_set_gpio(dev, 26, 0);
msleep(1);
@@ -6994,8 +6994,8 @@ int saa7134_board_init2(struct saa7134_dev *dev)
dev->name, saa7134_boards[dev->board].name);
}
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
hauppauge_eeprom(dev, dev->eedata+0x80);
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c
index 360fbdf19..afd9b7e5c 100644
--- a/linux/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1119,7 +1119,7 @@ static int dvb_init(struct saa7134_dev *dev)
&tda827x_cfg_2) < 0)
goto dettach_frontend;
break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
fe0->dvb.frontend = dvb_attach(tda10048_attach,
&hcw_tda10048_config,
&dev->i2c_adap);
@@ -1147,7 +1147,7 @@ static int dvb_init(struct saa7134_dev *dev)
&tda827x_cfg_1) < 0)
goto dettach_frontend;
break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
&dev->i2c_adap);
@@ -1461,7 +1461,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (fe0->dvb.frontend) {
dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
&dev->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3);
+ TUNER_PHILIPS_FMD1216MEX_MK3);
}
break;
case SAA7134_BOARD_AVERMEDIA_A700_PRO:
diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h
index be5abf059..6b0742ad7 100644
--- a/linux/drivers/media/video/saa7134/saa7134.h
+++ b/linux/drivers/media/video/saa7134/saa7134.h
@@ -279,8 +279,8 @@ struct saa7134_format {
#define SAA7134_BOARD_ASUSTeK_TIGER 152
#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
-#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155
-#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156
+#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155
+#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
#define SAA7134_BOARD_BEHOLD_505RDS 159
diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c
index 7ea2a0edf..1bae28a98 100644
--- a/linux/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c
@@ -283,27 +283,24 @@ out:
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
vb, vb->baddr, vb->bsize);
vb->state = VIDEOBUF_QUEUED;
- spin_lock_irqsave(&pcdev->lock, flags);
list_add_tail(&vb->queue, &pcdev->capture);
if (!pcdev->active) {
pcdev->active = vb;
sh_mobile_ceu_capture(pcdev);
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c
index 849867ab2..aaeee8319 100644
--- a/linux/drivers/media/video/stk-webcam.c
+++ b/linux/drivers/media/video/stk-webcam.c
@@ -1051,8 +1051,8 @@ static int stk_setup_format(struct stk_camera *dev)
depth = 1;
else
depth = 2;
- while (stk_sizes[i].m != dev->vsettings.mode
- && i < ARRAY_SIZE(stk_sizes))
+ while (i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode)
i++;
if (i == ARRAY_SIZE(stk_sizes)) {
STK_ERROR("Something is broken in %s\n", __func__);
diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c
index 81928f838..57b6fe937 100644
--- a/linux/drivers/media/video/uvc/uvc_ctrl.c
+++ b/linux/drivers/media/video/uvc/uvc_ctrl.c
@@ -731,7 +731,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
}
}
-struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
__u32 v4l2_id, struct uvc_control_mapping **mapping)
{
struct uvc_control *ctrl = NULL;
@@ -744,17 +744,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
v4l2_id &= V4L2_CTRL_ID_MASK;
/* Find the control. */
- __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next);
+ __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
- list_for_each_entry(entity, &video->iterms, chain) {
+ list_for_each_entry(entity, &chain->iterms, chain) {
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
}
- list_for_each_entry(entity, &video->extensions, chain) {
+ list_for_each_entry(entity, &chain->extensions, chain) {
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
@@ -767,7 +767,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
return ctrl;
}
-int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl)
{
struct uvc_control *ctrl;
@@ -777,7 +777,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
__u8 *data;
int ret;
- ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping);
+ ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL)
return -EINVAL;
@@ -795,9 +795,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
- ret = uvc_query_ctrl(video->dev, UVC_GET_DEF, ctrl->entity->id,
- video->dev->intfnum, ctrl->info->selector, data,
- ctrl->info->size);
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->default_value =
@@ -833,25 +833,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
}
if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
- ret = uvc_query_ctrl(video->dev, UVC_GET_MIN, ctrl->entity->id,
- video->dev->intfnum, ctrl->info->selector, data,
- ctrl->info->size);
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
- ret = uvc_query_ctrl(video->dev, UVC_GET_MAX, ctrl->entity->id,
- video->dev->intfnum, ctrl->info->selector, data,
- ctrl->info->size);
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
- ret = uvc_query_ctrl(video->dev, UVC_GET_RES, ctrl->entity->id,
- video->dev->intfnum, ctrl->info->selector, data,
- ctrl->info->size);
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data);
@@ -888,9 +888,9 @@ out:
* (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
* control lock.
*/
-int uvc_ctrl_begin(struct uvc_video_device *video)
+int uvc_ctrl_begin(struct uvc_video_chain *chain)
{
- return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;
+ return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
}
static int uvc_ctrl_commit_entity(struct uvc_device *dev,
@@ -940,34 +940,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
return 0;
}
-int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback)
+int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
{
struct uvc_entity *entity;
int ret = 0;
/* Find the control. */
- ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
+ ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback);
if (ret < 0)
goto done;
- list_for_each_entry(entity, &video->iterms, chain) {
- ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+ list_for_each_entry(entity, &chain->iterms, chain) {
+ ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
}
- list_for_each_entry(entity, &video->extensions, chain) {
- ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+ list_for_each_entry(entity, &chain->extensions, chain) {
+ ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
}
done:
- mutex_unlock(&video->ctrl_mutex);
+ mutex_unlock(&chain->ctrl_mutex);
return ret;
}
-int uvc_ctrl_get(struct uvc_video_device *video,
+int uvc_ctrl_get(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
@@ -976,13 +976,13 @@ int uvc_ctrl_get(struct uvc_video_device *video,
unsigned int i;
int ret;
- ctrl = uvc_find_control(video, xctrl->id, &mapping);
+ ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
return -EINVAL;
if (!ctrl->loaded) {
- ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ctrl->entity->id,
- video->dev->intfnum, ctrl->info->selector,
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
if (ret < 0)
@@ -1007,7 +1007,7 @@ int uvc_ctrl_get(struct uvc_video_device *video,
return 0;
}
-int uvc_ctrl_set(struct uvc_video_device *video,
+int uvc_ctrl_set(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
@@ -1015,7 +1015,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
s32 value = xctrl->value;
int ret;
- ctrl = uvc_find_control(video, xctrl->id, &mapping);
+ ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
return -EINVAL;
@@ -1030,8 +1030,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
0, ctrl->info->size);
} else {
- ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
- ctrl->entity->id, video->dev->intfnum,
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+ ctrl->entity->id, chain->dev->intfnum,
ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
@@ -1060,7 +1060,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
* Dynamic controls
*/
-int uvc_xu_ctrl_query(struct uvc_video_device *video,
+int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control *xctrl, int set)
{
struct uvc_entity *entity;
@@ -1070,7 +1070,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
int ret;
/* Find the extension unit. */
- list_for_each_entry(entity, &video->extensions, chain) {
+ list_for_each_entry(entity, &chain->extensions, chain) {
if (entity->id == xctrl->unit)
break;
}
@@ -1109,7 +1109,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
(!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
return -EINVAL;
- if (mutex_lock_interruptible(&video->ctrl_mutex))
+ if (mutex_lock_interruptible(&chain->ctrl_mutex))
return -ERESTARTSYS;
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
@@ -1122,8 +1122,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
goto out;
}
- ret = uvc_query_ctrl(video->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
- xctrl->unit, video->dev->intfnum, xctrl->selector,
+ ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
+ xctrl->unit, chain->dev->intfnum, xctrl->selector,
data, xctrl->size);
if (ret < 0)
goto out;
@@ -1139,7 +1139,7 @@ out:
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
xctrl->size);
- mutex_unlock(&video->ctrl_mutex);
+ mutex_unlock(&chain->ctrl_mutex);
return ret;
}
diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c
index 1d09751a3..09b2e4ad5 100644
--- a/linux/drivers/media/video/uvc/uvc_driver.c
+++ b/linux/drivers/media/video/uvc/uvc_driver.c
@@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
return NULL;
}
+static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_streaming *stream;
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->header.bTerminalLink == id)
+ return stream;
+ }
+
+ return NULL;
+}
+
/* ------------------------------------------------------------------------
- * Descriptors handling
+ * Descriptors parsing
*/
static int uvc_parse_format(struct uvc_device *dev,
@@ -551,6 +563,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
mutex_init(&streaming->mutex);
+ streaming->dev = dev;
streaming->intf = usb_get_intf(intf);
streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
@@ -751,7 +764,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
streaming->maxpsize = psize;
}
- list_add_tail(&streaming->list, &dev->streaming);
+ list_add_tail(&streaming->list, &dev->streams);
return 0;
error:
@@ -1159,39 +1172,36 @@ next_descriptor:
}
/* ------------------------------------------------------------------------
- * USB probe and disconnect
+ * UVC device scan
*/
/*
- * Unregister the video devices.
- */
-static void uvc_unregister_video(struct uvc_device *dev)
-{
- if (dev->video.vdev) {
- if (dev->video.vdev->minor == -1)
- video_device_release(dev->video.vdev);
- else
- video_unregister_device(dev->video.vdev);
- dev->video.vdev = NULL;
- }
-}
-
-/*
* Scan the UVC descriptors to locate a chain starting at an Output Terminal
* and containing the following units:
*
- * - one Output Terminal (USB Streaming or Display)
+ * - one or more Output Terminals (USB Streaming or Display)
* - zero or one Processing Unit
- * - zero, one or mode single-input Selector Units
+ * - zero, one or more single-input Selector Units
* - zero or one multiple-input Selector Units, provided all inputs are
* connected to input terminals
* - zero, one or mode single-input Extension Units
* - one or more Input Terminals (Camera, External or USB Streaming)
*
- * A side forward scan is made on each detected entity to check for additional
- * extension units.
+ * The terminal and units must match on of the following structures:
+ *
+ * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
+ * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
+ * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
+ *
+ * +---------+ +---------+ -> OTT_*(0)
+ * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
+ * +---------+ +---------+ -> OTT_*(n)
+ *
+ * The Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported. Single-input Selector Units are ignored.
*/
-static int uvc_scan_chain_entity(struct uvc_video_device *video,
+static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
switch (UVC_ENTITY_TYPE(entity)) {
@@ -1205,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1;
}
- list_add_tail(&entity->chain, &video->extensions);
+ list_add_tail(&entity->chain, &chain->extensions);
break;
case UVC_VC_PROCESSING_UNIT:
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- PU %d", entity->id);
- if (video->processing != NULL) {
+ if (chain->processing != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple "
"Processing Units in chain.\n");
return -1;
}
- video->processing = entity;
+ chain->processing = entity;
break;
case UVC_VC_SELECTOR_UNIT:
@@ -1229,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (entity->selector.bNrInPins == 1)
break;
- if (video->selector != NULL) {
+ if (chain->selector != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
"Units in chain.\n");
return -1;
}
- video->selector = entity;
+ chain->selector = entity;
break;
case UVC_ITT_VENDOR_SPECIFIC:
@@ -1244,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
- list_add_tail(&entity->chain, &video->iterms);
+ list_add_tail(&entity->chain, &chain->iterms);
break;
case UVC_TT_STREAMING:
@@ -1257,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1;
}
- if (video->sterm != NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
- "entities in chain.\n");
- return -1;
- }
-
- list_add_tail(&entity->chain, &video->iterms);
- video->sterm = entity;
+ list_add_tail(&entity->chain, &chain->iterms);
break;
default:
@@ -1276,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return 0;
}
-static int uvc_scan_chain_forward(struct uvc_video_device *video,
+static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
struct uvc_entity *entity, struct uvc_entity *prev)
{
struct uvc_entity *forward;
@@ -1287,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
found = 0;
while (1) {
- forward = uvc_entity_by_reference(video->dev, entity->id,
+ forward = uvc_entity_by_reference(chain->dev, entity->id,
forward);
if (forward == NULL)
break;
-
- if (UVC_ENTITY_TYPE(forward) != UVC_VC_EXTENSION_UNIT ||
- forward == prev)
+ if (forward == prev)
continue;
- if (forward->extension.bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has "
- "more than 1 input pin.\n", entity->id);
- return -1;
- }
+ switch (UVC_ENTITY_TYPE(forward)) {
+ case UVC_VC_EXTENSION_UNIT:
+ if (forward->extension.bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
+ "has more than 1 input pin.\n",
+ entity->id);
+ return -EINVAL;
+ }
- list_add_tail(&forward->chain, &video->extensions);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(" (-> XU");
+ list_add_tail(&forward->chain, &chain->extensions);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
- printk(" %d", forward->id);
- found = 1;
+ printk(" XU %d", forward->id);
+ found = 1;
+ }
+ break;
+
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ if (UVC_ENTITY_IS_ITERM(forward)) {
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+ "terminal %u.\n", forward->id);
+ return -EINVAL;
+ }
+
+ list_add_tail(&forward->chain, &chain->oterms);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
+
+ printk(" OT %d", forward->id);
+ found = 1;
+ }
+ break;
}
}
if (found)
@@ -1317,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
return 0;
}
-static int uvc_scan_chain_backward(struct uvc_video_device *video,
+static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
struct uvc_entity *term;
@@ -1342,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT");
- video->selector = entity;
+ chain->selector = entity;
for (i = 0; i < entity->selector.bNrInPins; ++i) {
id = entity->selector.baSourceID[i];
- term = uvc_entity_by_id(video->dev, id);
+ term = uvc_entity_by_id(chain->dev, id);
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
"input %d isn't connected to an "
@@ -1356,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);
- list_add_tail(&term->chain, &video->iterms);
- uvc_scan_chain_forward(video, term, entity);
+ list_add_tail(&term->chain, &chain->iterms);
+ uvc_scan_chain_forward(chain, term, entity);
}
if (uvc_trace_param & UVC_TRACE_PROBE)
@@ -1370,125 +1396,170 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
return id;
}
-static int uvc_scan_chain(struct uvc_video_device *video)
+static int uvc_scan_chain(struct uvc_video_chain *chain,
+ struct uvc_entity *oterm)
{
struct uvc_entity *entity, *prev;
int id;
- entity = video->oterm;
+ entity = oterm;
+ list_add_tail(&entity->chain, &chain->oterms);
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
- if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
- video->sterm = entity;
-
id = entity->output.bSourceID;
while (id != 0) {
prev = entity;
- entity = uvc_entity_by_id(video->dev, id);
+ entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
"unknown entity %d.\n", id);
- return -1;
+ return -EINVAL;
+ }
+
+ if (entity->chain.next || entity->chain.prev) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "entity %d already in chain.\n", id);
+ return -EINVAL;
}
/* Process entity */
- if (uvc_scan_chain_entity(video, entity) < 0)
- return -1;
+ if (uvc_scan_chain_entity(chain, entity) < 0)
+ return -EINVAL;
/* Forward scan */
- if (uvc_scan_chain_forward(video, entity, prev) < 0)
- return -1;
+ if (uvc_scan_chain_forward(chain, entity, prev) < 0)
+ return -EINVAL;
/* Stop when a terminal is found. */
- if (!UVC_ENTITY_IS_UNIT(entity))
+ if (UVC_ENTITY_IS_TERM(entity))
break;
/* Backward scan */
- id = uvc_scan_chain_backward(video, entity);
+ id = uvc_scan_chain_backward(chain, entity);
if (id < 0)
return id;
}
- if (video->sterm == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
- "chain.\n");
- return -1;
+ return 0;
+}
+
+static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
+{
+ struct uvc_entity *term;
+ unsigned int nterms = 0;
+ char *p = buffer;
+
+ list_for_each_entry(term, terms, chain) {
+ p += sprintf(p, "%u", term->id);
+ if (term->chain.next != terms) {
+ p += sprintf(p, ",");
+ if (++nterms >= 4) {
+ p += sprintf(p, "...");
+ break;
+ }
+ }
}
- return 0;
+ return p - buffer;
+}
+
+static const char *uvc_print_chain(struct uvc_video_chain *chain)
+{
+ static char buffer[43];
+ char *p = buffer;
+
+ p += uvc_print_terms(&chain->iterms, p);
+ p += sprintf(p, " -> ");
+ uvc_print_terms(&chain->oterms, p);
+
+ return buffer;
}
/*
- * Register the video devices.
+ * Scan the device for video chains and register video devices.
*
- * The driver currently supports a single video device per control interface
- * only. The terminal and units must match the following structure:
- *
- * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
- * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
- *
- * The Extension Units, if present, must have a single input pin. The
- * Processing Unit and Extension Units can be in any order. Additional
- * Extension Units connected to the main chain as single-unit branches are
- * also supported.
+ * Chains are scanned starting at their output terminals and walked backwards.
*/
-static int uvc_register_video(struct uvc_device *dev)
+static int uvc_scan_device(struct uvc_device *dev)
{
- struct video_device *vdev;
+ struct uvc_video_chain *chain;
struct uvc_entity *term;
- int found = 0, ret;
- /* Check if the control interface matches the structure we expect. */
list_for_each_entry(term, &dev->entities, list) {
- struct uvc_streaming *streaming;
-
- if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
+ if (!UVC_ENTITY_IS_OTERM(term))
continue;
- memset(&dev->video, 0, sizeof dev->video);
- mutex_init(&dev->video.ctrl_mutex);
- INIT_LIST_HEAD(&dev->video.iterms);
- INIT_LIST_HEAD(&dev->video.extensions);
- dev->video.oterm = term;
- dev->video.dev = dev;
- if (uvc_scan_chain(&dev->video) < 0)
+ /* If the terminal is already included in a chain, skip it.
+ * This can happen for chains that have multiple output
+ * terminals, where all output terminals beside the first one
+ * will be inserted in the chain in forward scans.
+ */
+ if (term->chain.next || term->chain.prev)
continue;
- list_for_each_entry(streaming, &dev->streaming, list) {
- if (streaming->header.bTerminalLink ==
- dev->video.sterm->id) {
- dev->video.streaming = streaming;
- found = 1;
- break;
- }
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&chain->iterms);
+ INIT_LIST_HEAD(&chain->oterms);
+ INIT_LIST_HEAD(&chain->extensions);
+ mutex_init(&chain->ctrl_mutex);
+ chain->dev = dev;
+
+ if (uvc_scan_chain(chain, term) < 0) {
+ kfree(chain);
+ continue;
}
- if (found)
- break;
+ uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
+ uvc_print_chain(chain));
+
+ list_add_tail(&chain->list, &dev->chains);
}
- if (!found) {
+ if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found.\n");
return -1;
}
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- uvc_printk(KERN_INFO, "Found a valid video chain (");
- list_for_each_entry(term, &dev->video.iterms, chain) {
- printk("%d", term->id);
- if (term->chain.next != &dev->video.iterms)
- printk(",");
- }
- printk(" -> %d).\n", dev->video.oterm->id);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device registration and unregistration
+ */
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+ struct uvc_streaming *stream;
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->vdev == NULL)
+ continue;
+
+ if (stream->vdev->minor == -1)
+ video_device_release(stream->vdev);
+ else
+ video_unregister_device(stream->vdev);
+ stream->vdev = NULL;
}
+}
- /* Initialize the video buffers queue. */
- uvc_queue_init(&dev->video.queue, dev->video.streaming->type);
+static int uvc_register_video(struct uvc_device *dev,
+ struct uvc_streaming *stream)
+{
+ struct video_device *vdev;
+ int ret;
/* Initialize the streaming interface with default streaming
* parameters.
*/
- if ((ret = uvc_video_init(&dev->video)) < 0) {
+ ret = uvc_video_init(stream);
+ if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
@@ -1496,8 +1567,11 @@ static int uvc_register_video(struct uvc_device *dev)
/* Register the device with V4L. */
vdev = video_device_alloc();
- if (vdev == NULL)
- return -1;
+ if (vdev == NULL) {
+ uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
+ ret);
+ return -ENOMEM;
+ }
/* We already hold a reference to dev->udev. The video device will be
* unregistered before the reference is released, so we don't need to
@@ -1512,19 +1586,74 @@ static int uvc_register_video(struct uvc_device *dev)
/* Set the driver data before calling video_register_device, otherwise
* uvc_v4l2_open might race us.
*/
- dev->video.vdev = vdev;
- video_set_drvdata(vdev, &dev->video);
-
- if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
- dev->video.vdev = NULL;
+ stream->vdev = vdev;
+ video_set_drvdata(vdev, stream);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
+ ret);
+ stream->vdev = NULL;
video_device_release(vdev);
- return -1;
+ return ret;
}
return 0;
}
/*
+ * Register all video devices in all chains.
+ */
+static int uvc_register_terms(struct uvc_device *dev,
+ struct uvc_video_chain *chain, struct list_head *terms)
+{
+ struct uvc_streaming *stream;
+ struct uvc_entity *term;
+ int ret;
+
+ list_for_each_entry(term, terms, chain) {
+ if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
+ continue;
+
+ stream = uvc_stream_by_id(dev, term->id);
+ if (stream == NULL) {
+ uvc_printk(KERN_INFO, "No streaming interface found "
+ "for terminal %u.", term->id);
+ continue;
+ }
+
+ stream->chain = chain;
+ ret = uvc_register_video(dev, stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int uvc_register_chains(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ int ret;
+
+ list_for_each_entry(chain, &dev->chains, list) {
+ ret = uvc_register_terms(dev, chain, &chain->iterms);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_register_terms(dev, chain, &chain->oterms);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe, disconnect, suspend and resume
+ */
+
+/*
* Delete the UVC device.
*
* Called by the kernel when the last reference to the uvc_device structure
@@ -1545,7 +1674,7 @@ void uvc_delete(struct kref *kref)
struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
struct list_head *p, *n;
- /* Unregister the video device. */
+ /* Unregister the video devices. */
uvc_unregister_video(dev);
usb_put_intf(dev->intf);
usb_put_dev(dev->udev);
@@ -1553,13 +1682,19 @@ void uvc_delete(struct kref *kref)
uvc_status_cleanup(dev);
uvc_ctrl_cleanup_device(dev);
+ list_for_each_safe(p, n, &dev->chains) {
+ struct uvc_video_chain *chain;
+ chain = list_entry(p, struct uvc_video_chain, list);
+ kfree(chain);
+ }
+
list_for_each_safe(p, n, &dev->entities) {
struct uvc_entity *entity;
entity = list_entry(p, struct uvc_entity, list);
kfree(entity);
}
- list_for_each_safe(p, n, &dev->streaming) {
+ list_for_each_safe(p, n, &dev->streams) {
struct uvc_streaming *streaming;
streaming = list_entry(p, struct uvc_streaming, list);
usb_driver_release_interface(&uvc_driver.driver,
@@ -1593,7 +1728,8 @@ static int uvc_probe(struct usb_interface *intf,
return -ENOMEM;
INIT_LIST_HEAD(&dev->entities);
- INIT_LIST_HEAD(&dev->streaming);
+ INIT_LIST_HEAD(&dev->chains);
+ INIT_LIST_HEAD(&dev->streams);
kref_init(&dev->kref);
atomic_set(&dev->users, 0);
@@ -1634,8 +1770,12 @@ static int uvc_probe(struct usb_interface *intf,
if (uvc_ctrl_init_device(dev) < 0)
goto error;
- /* Register the video devices. */
- if (uvc_register_video(dev) < 0)
+ /* Scan the device for video chains. */
+ if (uvc_scan_device(dev) < 0)
+ goto error;
+
+ /* Register video devices. */
+ if (uvc_register_chains(dev) < 0)
goto error;
/* Save our data pointer in the interface data. */
@@ -1689,6 +1829,7 @@ static void uvc_disconnect(struct usb_interface *intf)
static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
{
struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
@@ -1698,18 +1839,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
UVC_SC_VIDEOCONTROL)
return uvc_status_suspend(dev);
- if (dev->video.streaming->intf != intf) {
- uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB "
- "interface mismatch.\n");
- return -EINVAL;
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_suspend(stream);
}
- return uvc_video_suspend(&dev->video);
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
}
static int __uvc_resume(struct usb_interface *intf, int reset)
{
struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
@@ -1726,13 +1869,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
return uvc_status_resume(dev);
}
- if (dev->video.streaming->intf != intf) {
- uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB "
- "interface mismatch.\n");
- return -EINVAL;
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_resume(stream);
}
- return uvc_video_resume(&dev->video);
+ uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
}
static int uvc_resume(struct usb_interface *intf)
@@ -1852,7 +1996,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* ViMicro Vega */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x332d,
@@ -1861,7 +2005,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* ViMicro - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x3410,
@@ -1870,7 +2014,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* ViMicro Venus - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x3420,
diff --git a/linux/drivers/media/video/uvc/uvc_isight.c b/linux/drivers/media/video/uvc/uvc_isight.c
index 436f46268..a9285b570 100644
--- a/linux/drivers/media/video/uvc/uvc_isight.c
+++ b/linux/drivers/media/video/uvc/uvc_isight.c
@@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
return 0;
}
-void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
+void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf)
{
int ret, i;
@@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
* processes the data of the first payload of the new frame.
*/
do {
- ret = isight_decode(&video->queue, buf,
+ ret = isight_decode(&stream->queue, buf,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
@@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
- buf = uvc_queue_next_buffer(&video->queue, buf);
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
} while (ret == -EAGAIN);
}
}
diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c
index b05df63b4..48614d39c 100644
--- a/linux/drivers/media/video/uvc/uvc_status.c
+++ b/linux/drivers/media/video/uvc/uvc_status.c
@@ -157,8 +157,8 @@ static void uvc_status_complete(struct urb *urb)
break;
default:
- uvc_printk(KERN_INFO, "unknown event type %u.\n",
- dev->status[0]);
+ uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
+ "type %u.\n", dev->status[0]);
break;
}
}
diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c
index 31a9ed4ff..b5a16acb6 100644
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c
@@ -40,7 +40,7 @@
* table for the controls that can be mapped directly, and handle the others
* manually.
*/
-static int uvc_v4l2_query_menu(struct uvc_video_device *video,
+static int uvc_v4l2_query_menu(struct uvc_video_chain *chain,
struct v4l2_querymenu *query_menu)
{
struct uvc_menu_info *menu_info;
@@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video,
u32 index = query_menu->index;
u32 id = query_menu->id;
- ctrl = uvc_find_control(video, query_menu->id, &mapping);
+ ctrl = uvc_find_control(chain, query_menu->id, &mapping);
if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
return -EINVAL;
@@ -103,7 +103,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
return interval;
}
-static int uvc_v4l2_try_format(struct uvc_video_device *video,
+static int uvc_v4l2_try_format(struct uvc_streaming *stream,
struct v4l2_format *fmt, struct uvc_streaming_control *probe,
struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
{
@@ -116,7 +116,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
int ret = 0;
__u8 *fcc;
- if (fmt->type != video->streaming->type)
+ if (fmt->type != stream->type)
return -EINVAL;
fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
@@ -126,8 +126,8 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
fmt->fmt.pix.width, fmt->fmt.pix.height);
/* Check if the hardware supports the requested format. */
- for (i = 0; i < video->streaming->nformats; ++i) {
- format = &video->streaming->format[i];
+ for (i = 0; i < stream->nformats; ++i) {
+ format = &stream->format[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
@@ -191,12 +191,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
* developers test their webcams with the Linux driver as well as with
* the Windows driver).
*/
- if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
+ if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
probe->dwMaxVideoFrameSize =
- video->streaming->ctrl.dwMaxVideoFrameSize;
+ stream->ctrl.dwMaxVideoFrameSize;
/* Probe the device. */
- if ((ret = uvc_probe_video(video, probe)) < 0)
+ ret = uvc_probe_video(stream, probe);
+ if (ret < 0)
goto done;
fmt->fmt.pix.width = frame->wWidth;
@@ -216,13 +217,13 @@ done:
return ret;
}
-static int uvc_v4l2_get_format(struct uvc_video_device *video,
+static int uvc_v4l2_get_format(struct uvc_streaming *stream,
struct v4l2_format *fmt)
{
- struct uvc_format *format = video->streaming->cur_format;
- struct uvc_frame *frame = video->streaming->cur_frame;
+ struct uvc_format *format = stream->cur_format;
+ struct uvc_frame *frame = stream->cur_frame;
- if (fmt->type != video->streaming->type)
+ if (fmt->type != stream->type)
return -EINVAL;
if (format == NULL || frame == NULL)
@@ -233,14 +234,14 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video,
fmt->fmt.pix.height = frame->wHeight;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
- fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize;
+ fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
fmt->fmt.pix.colorspace = format->colorspace;
fmt->fmt.pix.priv = 0;
return 0;
}
-static int uvc_v4l2_set_format(struct uvc_video_device *video,
+static int uvc_v4l2_set_format(struct uvc_streaming *stream,
struct v4l2_format *fmt)
{
struct uvc_streaming_control probe;
@@ -248,39 +249,39 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
struct uvc_frame *frame;
int ret;
- if (fmt->type != video->streaming->type)
+ if (fmt->type != stream->type)
return -EINVAL;
- if (uvc_queue_allocated(&video->queue))
+ if (uvc_queue_allocated(&stream->queue))
return -EBUSY;
- ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
+ ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
if (ret < 0)
return ret;
- memcpy(&video->streaming->ctrl, &probe, sizeof probe);
- video->streaming->cur_format = format;
- video->streaming->cur_frame = frame;
+ memcpy(&stream->ctrl, &probe, sizeof probe);
+ stream->cur_format = format;
+ stream->cur_frame = frame;
return 0;
}
-static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
+static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
struct v4l2_streamparm *parm)
{
uint32_t numerator, denominator;
- if (parm->type != video->streaming->type)
+ if (parm->type != stream->type)
return -EINVAL;
- numerator = video->streaming->ctrl.dwFrameInterval;
+ numerator = stream->ctrl.dwFrameInterval;
denominator = 10000000;
uvc_simplify_fraction(&numerator, &denominator, 8, 333);
memset(parm, 0, sizeof *parm);
- parm->type = video->streaming->type;
+ parm->type = stream->type;
- if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
parm->parm.capture.capturemode = 0;
parm->parm.capture.timeperframe.numerator = numerator;
@@ -297,19 +298,19 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
return 0;
}
-static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
+static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
struct v4l2_streamparm *parm)
{
- struct uvc_frame *frame = video->streaming->cur_frame;
+ struct uvc_frame *frame = stream->cur_frame;
struct uvc_streaming_control probe;
struct v4l2_fract timeperframe;
uint32_t interval;
int ret;
- if (parm->type != video->streaming->type)
+ if (parm->type != stream->type)
return -EINVAL;
- if (uvc_queue_streaming(&video->queue))
+ if (uvc_queue_streaming(&stream->queue))
return -EBUSY;
if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -317,7 +318,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
else
timeperframe = parm->parm.output.timeperframe;
- memcpy(&probe, &video->streaming->ctrl, sizeof probe);
+ memcpy(&probe, &stream->ctrl, sizeof probe);
interval = uvc_fraction_to_interval(timeperframe.numerator,
timeperframe.denominator);
@@ -326,10 +327,11 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
/* Probe the device with the new settings. */
- if ((ret = uvc_probe_video(video, &probe)) < 0)
+ ret = uvc_probe_video(stream, &probe);
+ if (ret < 0)
return ret;
- memcpy(&video->streaming->ctrl, &probe, sizeof probe);
+ memcpy(&stream->ctrl, &probe, sizeof probe);
/* Return the actual frame period. */
timeperframe.numerator = probe.dwFrameInterval;
@@ -382,8 +384,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle)
/* Check if the device already has a privileged handle. */
mutex_lock(&uvc_driver.open_mutex);
- if (atomic_inc_return(&handle->device->active) != 1) {
- atomic_dec(&handle->device->active);
+ if (atomic_inc_return(&handle->stream->active) != 1) {
+ atomic_dec(&handle->stream->active);
ret = -EBUSY;
goto done;
}
@@ -398,7 +400,7 @@ done:
static void uvc_dismiss_privileges(struct uvc_fh *handle)
{
if (handle->state == UVC_HANDLE_ACTIVE)
- atomic_dec(&handle->device->active);
+ atomic_dec(&handle->stream->active);
handle->state = UVC_HANDLE_PASSIVE;
}
@@ -414,21 +416,21 @@ static int uvc_has_privileges(struct uvc_fh *handle)
static int uvc_v4l2_open(struct file *file)
{
- struct uvc_video_device *video;
+ struct uvc_streaming *stream;
struct uvc_fh *handle;
int ret = 0;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
mutex_lock(&uvc_driver.open_mutex);
- video = video_drvdata(file);
+ stream = video_drvdata(file);
- if (video->dev->state & UVC_DEV_DISCONNECTED) {
+ if (stream->dev->state & UVC_DEV_DISCONNECTED) {
ret = -ENODEV;
goto done;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
- ret = usb_autopm_get_interface(video->dev->intf);
+ ret = usb_autopm_get_interface(stream->dev->intf);
if (ret < 0)
goto done;
#endif
@@ -437,28 +439,30 @@ static int uvc_v4l2_open(struct file *file)
handle = kzalloc(sizeof *handle, GFP_KERNEL);
if (handle == NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
- usb_autopm_put_interface(video->dev->intf);
+ usb_autopm_put_interface(stream->dev->intf);
#endif
ret = -ENOMEM;
goto done;
}
- if (atomic_inc_return(&video->dev->users) == 1) {
- if ((ret = uvc_status_start(video->dev)) < 0) {
+ if (atomic_inc_return(&stream->dev->users) == 1) {
+ ret = uvc_status_start(stream->dev);
+ if (ret < 0) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
- usb_autopm_put_interface(video->dev->intf);
+ usb_autopm_put_interface(stream->dev->intf);
#endif
- atomic_dec(&video->dev->users);
+ atomic_dec(&stream->dev->users);
kfree(handle);
goto done;
}
}
- handle->device = video;
+ handle->chain = stream->chain;
+ handle->stream = stream;
handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
- kref_get(&video->dev->kref);
+ kref_get(&stream->dev->kref);
done:
mutex_unlock(&uvc_driver.open_mutex);
@@ -467,20 +471,20 @@ done:
static int uvc_v4l2_release(struct file *file)
{
- struct uvc_video_device *video = video_drvdata(file);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+ struct uvc_streaming *stream = handle->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
/* Only free resources if this is a privileged handle. */
if (uvc_has_privileges(handle)) {
- uvc_video_enable(video, 0);
+ uvc_video_enable(stream, 0);
- mutex_lock(&video->queue.mutex);
- if (uvc_free_buffers(&video->queue) < 0)
+ mutex_lock(&stream->queue.mutex);
+ if (uvc_free_buffers(&stream->queue) < 0)
uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "
"free buffers.\n");
- mutex_unlock(&video->queue.mutex);
+ mutex_unlock(&stream->queue.mutex);
}
/* Release the file handle. */
@@ -488,21 +492,22 @@ static int uvc_v4l2_release(struct file *file)
kfree(handle);
file->private_data = NULL;
- if (atomic_dec_return(&video->dev->users) == 0)
- uvc_status_stop(video->dev);
+ if (atomic_dec_return(&stream->dev->users) == 0)
+ uvc_status_stop(stream->dev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
- usb_autopm_put_interface(video->dev->intf);
+ usb_autopm_put_interface(stream->dev->intf);
#endif
- kref_put(&video->dev->kref, uvc_delete);
+ kref_put(&stream->dev->kref, uvc_delete);
return 0;
}
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
- struct uvc_video_device *video = video_get_drvdata(vdev);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+ struct uvc_video_chain *chain = handle->chain;
+ struct uvc_streaming *stream = handle->stream;
long ret = 0;
switch (cmd) {
@@ -514,10 +519,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(cap, 0, sizeof *cap);
strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
strlcpy(cap->card, vdev->name, sizeof cap->card);
- usb_make_path(video->dev->udev,
+ usb_make_path(stream->dev->udev,
cap->bus_info, sizeof(cap->bus_info));
cap->version = DRIVER_VERSION_NUMBER;
- if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING;
else
@@ -528,7 +533,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Get, Set & Query control */
case VIDIOC_QUERYCTRL:
- return uvc_query_v4l2_ctrl(video, arg);
+ return uvc_query_v4l2_ctrl(chain, arg);
case VIDIOC_G_CTRL:
{
@@ -538,12 +543,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(&xctrl, 0, sizeof xctrl);
xctrl.id = ctrl->id;
- ret = uvc_ctrl_begin(video);
- if (ret < 0)
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
return ret;
- ret = uvc_ctrl_get(video, &xctrl);
- uvc_ctrl_rollback(video);
+ ret = uvc_ctrl_get(chain, &xctrl);
+ uvc_ctrl_rollback(chain);
if (ret >= 0)
ctrl->value = xctrl.value;
break;
@@ -558,21 +563,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
xctrl.id = ctrl->id;
xctrl.value = ctrl->value;
- ret = uvc_ctrl_begin(video);
- if (ret < 0)
+ uvc_ctrl_begin(chain);
+ if (ret < 0)
return ret;
- ret = uvc_ctrl_set(video, &xctrl);
+ ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) {
- uvc_ctrl_rollback(video);
+ uvc_ctrl_rollback(chain);
return ret;
}
- ret = uvc_ctrl_commit(video);
+ ret = uvc_ctrl_commit(chain);
break;
}
case VIDIOC_QUERYMENU:
- return uvc_v4l2_query_menu(video, arg);
+ return uvc_v4l2_query_menu(chain, arg);
case VIDIOC_G_EXT_CTRLS:
{
@@ -580,20 +585,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
- ret = uvc_ctrl_begin(video);
- if (ret < 0)
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_get(video, ctrl);
+ ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) {
- uvc_ctrl_rollback(video);
+ uvc_ctrl_rollback(chain);
ctrls->error_idx = i;
return ret;
}
}
ctrls->error_idx = 0;
- ret = uvc_ctrl_rollback(video);
+ ret = uvc_ctrl_rollback(chain);
break;
}
@@ -604,14 +609,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
- ret = uvc_ctrl_begin(video);
+ ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_set(video, ctrl);
+ ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) {
- uvc_ctrl_rollback(video);
+ uvc_ctrl_rollback(chain);
ctrls->error_idx = i;
return ret;
}
@@ -620,31 +625,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ctrls->error_idx = 0;
if (cmd == VIDIOC_S_EXT_CTRLS)
- ret = uvc_ctrl_commit(video);
+ ret = uvc_ctrl_commit(chain);
else
- ret = uvc_ctrl_rollback(video);
+ ret = uvc_ctrl_rollback(chain);
break;
}
/* Get, Set & Enum input */
case VIDIOC_ENUMINPUT:
{
- const struct uvc_entity *selector = video->selector;
+ const struct uvc_entity *selector = chain->selector;
struct v4l2_input *input = arg;
struct uvc_entity *iterm = NULL;
u32 index = input->index;
int pin = 0;
if (selector == NULL ||
- (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0)
return -EINVAL;
- iterm = list_first_entry(&video->iterms,
+ iterm = list_first_entry(&chain->iterms,
struct uvc_entity, chain);
pin = iterm->id;
} else if (pin < selector->selector.bNrInPins) {
pin = selector->selector.baSourceID[index];
- list_for_each_entry(iterm, video->iterms.next, chain) {
+ list_for_each_entry(iterm, chain->iterms.next, chain) {
if (iterm->id == pin)
break;
}
@@ -665,14 +670,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
u8 input;
- if (video->selector == NULL ||
- (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
*(int *)arg = 0;
break;
}
- ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
- video->selector->id, video->dev->intfnum,
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+ chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
if (ret < 0)
return ret;
@@ -688,18 +693,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- if (video->selector == NULL ||
- (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (input != 1)
return -EINVAL;
break;
}
- if (input == 0 || input > video->selector->selector.bNrInPins)
+ if (input == 0 || input > chain->selector->selector.bNrInPins)
return -EINVAL;
- return uvc_query_ctrl(video->dev, UVC_SET_CUR,
- video->selector->id, video->dev->intfnum,
+ return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
+ chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
}
@@ -711,15 +716,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
enum v4l2_buf_type type = fmt->type;
__u32 index = fmt->index;
- if (fmt->type != video->streaming->type ||
- fmt->index >= video->streaming->nformats)
+ if (fmt->type != stream->type ||
+ fmt->index >= stream->nformats)
return -EINVAL;
memset(fmt, 0, sizeof(*fmt));
fmt->index = index;
fmt->type = type;
- format = &video->streaming->format[fmt->index];
+ format = &stream->format[fmt->index];
fmt->flags = 0;
if (format->flags & UVC_FMT_FLAG_COMPRESSED)
fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
@@ -737,17 +742,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);
+ return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
}
case VIDIOC_S_FMT:
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- return uvc_v4l2_set_format(video, arg);
+ return uvc_v4l2_set_format(stream, arg);
case VIDIOC_G_FMT:
- return uvc_v4l2_get_format(video, arg);
+ return uvc_v4l2_get_format(stream, arg);
/* Frame size enumeration */
case VIDIOC_ENUM_FRAMESIZES:
@@ -758,10 +763,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
int i;
/* Look for the given pixel format */
- for (i = 0; i < video->streaming->nformats; i++) {
- if (video->streaming->format[i].fcc ==
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc ==
fsize->pixel_format) {
- format = &video->streaming->format[i];
+ format = &stream->format[i];
break;
}
}
@@ -787,10 +792,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
int i;
/* Look for the given pixel format and frame size */
- for (i = 0; i < video->streaming->nformats; i++) {
- if (video->streaming->format[i].fcc ==
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc ==
fival->pixel_format) {
- format = &video->streaming->format[i];
+ format = &stream->format[i];
break;
}
}
@@ -840,21 +845,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Get & Set streaming parameters */
case VIDIOC_G_PARM:
- return uvc_v4l2_get_streamparm(video, arg);
+ return uvc_v4l2_get_streamparm(stream, arg);
case VIDIOC_S_PARM:
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- return uvc_v4l2_set_streamparm(video, arg);
+ return uvc_v4l2_set_streamparm(stream, arg);
/* Cropping and scaling */
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *ccap = arg;
- struct uvc_frame *frame = video->streaming->cur_frame;
+ struct uvc_frame *frame = stream->cur_frame;
- if (ccap->type != video->streaming->type)
+ if (ccap->type != stream->type)
return -EINVAL;
ccap->bounds.left = 0;
@@ -878,16 +883,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_requestbuffers *rb = arg;
unsigned int bufsize =
- video->streaming->ctrl.dwMaxVideoFrameSize;
+ stream->ctrl.dwMaxVideoFrameSize;
- if (rb->type != video->streaming->type ||
+ if (rb->type != stream->type ||
rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize);
+ ret = uvc_alloc_buffers(&stream->queue, rb->count, bufsize);
if (ret < 0)
return ret;
@@ -900,39 +905,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_buffer *buf = arg;
- if (buf->type != video->streaming->type)
+ if (buf->type != stream->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_query_buffer(&video->queue, buf);
+ return uvc_query_buffer(&stream->queue, buf);
}
case VIDIOC_QBUF:
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_queue_buffer(&video->queue, arg);
+ return uvc_queue_buffer(&stream->queue, arg);
case VIDIOC_DQBUF:
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_dequeue_buffer(&video->queue, arg,
+ return uvc_dequeue_buffer(&stream->queue, arg,
file->f_flags & O_NONBLOCK);
case VIDIOC_STREAMON:
{
int *type = arg;
- if (*type != video->streaming->type)
+ if (*type != stream->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
return -EBUSY;
- if ((ret = uvc_video_enable(video, 1)) < 0)
+ ret = uvc_video_enable(stream, 1);
+ if (ret < 0)
return ret;
break;
}
@@ -941,13 +947,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
int *type = arg;
- if (*type != video->streaming->type)
+ if (*type != stream->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_video_enable(video, 0);
+ return uvc_video_enable(stream, 0);
}
/* Analog video standards make no sense for digital cameras. */
@@ -1021,10 +1027,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
case UVCIOC_CTRL_GET:
- return uvc_xu_ctrl_query(video, arg, 0);
+ return uvc_xu_ctrl_query(chain, arg, 0);
case UVCIOC_CTRL_SET:
- return uvc_xu_ctrl_query(video, arg, 1);
+ return uvc_xu_ctrl_query(chain, arg, 1);
default:
if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
@@ -1078,7 +1084,9 @@ static struct vm_operations_struct uvc_vm_ops = {
static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct uvc_video_device *video = video_drvdata(file);
+ struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_video_queue *queue = &stream->queue;
struct uvc_buffer *uninitialized_var(buffer);
struct page *page;
unsigned long addr, start, size;
@@ -1090,15 +1098,15 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
start = vma->vm_start;
size = vma->vm_end - vma->vm_start;
- mutex_lock(&video->queue.mutex);
+ mutex_lock(&queue->mutex);
- for (i = 0; i < video->queue.count; ++i) {
- buffer = &video->queue.buffer[i];
+ for (i = 0; i < queue->count; ++i) {
+ buffer = &queue->buffer[i];
if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
break;
}
- if (i == video->queue.count || size != video->queue.buf_size) {
+ if (i == queue->count || size != queue->buf_size) {
ret = -EINVAL;
goto done;
}
@@ -1109,7 +1117,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
*/
vma->vm_flags |= VM_IO;
- addr = (unsigned long)video->queue.mem + buffer->buf.m.offset;
+ addr = (unsigned long)queue->mem + buffer->buf.m.offset;
while (size > 0) {
page = vmalloc_to_page((void *)addr);
if ((ret = vm_insert_page(vma, start, page)) < 0)
@@ -1125,17 +1133,18 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
uvc_vm_open(vma);
done:
- mutex_unlock(&video->queue.mutex);
+ mutex_unlock(&queue->mutex);
return ret;
}
static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
{
- struct uvc_video_device *video = video_drvdata(file);
+ struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+ struct uvc_streaming *stream = handle->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
- return uvc_queue_poll(&video->queue, file, wait);
+ return uvc_queue_poll(&stream->queue, file, wait);
}
const struct v4l2_file_operations uvc_fops = {
diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c
index f4f4e1bb3..792a93a1c 100644
--- a/linux/drivers/media/video/uvc/uvc_video.c
+++ b/linux/drivers/media/video/uvc/uvc_video.c
@@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
return 0;
}
-static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
+static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl)
{
struct uvc_format *format;
@@ -69,10 +69,10 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
unsigned int i;
if (ctrl->bFormatIndex <= 0 ||
- ctrl->bFormatIndex > video->streaming->nformats)
+ ctrl->bFormatIndex > stream->nformats)
return;
- format = &video->streaming->format[ctrl->bFormatIndex - 1];
+ format = &stream->format[ctrl->bFormatIndex - 1];
for (i = 0; i < format->nframes; ++i) {
if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) {
@@ -86,12 +86,12 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
(ctrl->dwMaxVideoFrameSize == 0 &&
- video->dev->uvc_version < 0x0110))
+ stream->dev->uvc_version < 0x0110))
ctrl->dwMaxVideoFrameSize =
frame->dwMaxVideoFrameBufferSize;
- if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
- video->streaming->intf->num_altsetting > 1) {
+ if (stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
+ stream->intf->num_altsetting > 1) {
u32 interval;
u32 bandwidth;
@@ -108,7 +108,7 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
- if (video->dev->udev->speed == USB_SPEED_HIGH)
+ if (stream->dev->udev->speed == USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;
@@ -116,22 +116,22 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
}
}
-static int uvc_get_video_ctrl(struct uvc_video_device *video,
+static int uvc_get_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe, __u8 query)
{
__u8 *data;
__u16 size;
int ret;
- size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
data = kmalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
- if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
+ if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
return -EIO;
- ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
+ ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
size, UVC_CTRL_STREAMING_TIMEOUT);
@@ -140,7 +140,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
* answer a GET_MIN or GET_MAX request with the wCompQuality
* field only.
*/
- uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non "
+ uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
"compliance - GET_MIN/MAX(PROBE) incorrectly "
"supported. Enabling workaround.\n");
memset(ctrl, 0, sizeof ctrl);
@@ -152,7 +152,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
* video probe control. Warn once and return, the caller will
* fall back to GET_CUR.
*/
- uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non "
+ uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non "
"compliance - GET_DEF(PROBE) not supported. "
"Enabling workaround.\n");
ret = -EIO;
@@ -184,7 +184,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
ctrl->bMinVersion = data[32];
ctrl->bMaxVersion = data[33];
} else {
- ctrl->dwClockFrequency = video->dev->clock_frequency;
+ ctrl->dwClockFrequency = stream->dev->clock_frequency;
ctrl->bmFramingInfo = 0;
ctrl->bPreferedVersion = 0;
ctrl->bMinVersion = 0;
@@ -195,7 +195,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
* dwMaxPayloadTransferSize fields. Try to get the value from the
* format and frame descriptors.
*/
- uvc_fixup_video_ctrl(video, ctrl);
+ uvc_fixup_video_ctrl(stream, ctrl);
ret = 0;
out:
@@ -203,14 +203,14 @@ out:
return ret;
}
-static int uvc_set_video_ctrl(struct uvc_video_device *video,
+static int uvc_set_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe)
{
__u8 *data;
__u16 size;
int ret;
- size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -235,8 +235,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video,
data[33] = ctrl->bMaxVersion;
}
- ret = __uvc_query_ctrl(video->dev, UVC_SET_CUR, 0,
- video->streaming->intfnum,
+ ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
size, UVC_CTRL_STREAMING_TIMEOUT);
if (ret != size) {
@@ -250,7 +249,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video,
return ret;
}
-int uvc_probe_video(struct uvc_video_device *video,
+int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe)
{
struct uvc_streaming_control probe_min, probe_max;
@@ -258,7 +257,7 @@ int uvc_probe_video(struct uvc_video_device *video,
unsigned int i;
int ret;
- mutex_lock(&video->streaming->mutex);
+ mutex_lock(&stream->mutex);
/* Perform probing. The device should adjust the requested values
* according to its capabilities. However, some devices, namely the
@@ -267,15 +266,16 @@ int uvc_probe_video(struct uvc_video_device *video,
* that reason, if the needed bandwidth exceeds the maximum available
* bandwidth, try to lower the quality.
*/
- if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0)
+ ret = uvc_set_video_ctrl(stream, probe, 1);
+ if (ret < 0)
goto done;
/* Get the minimum and maximum values for compression settings. */
- if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
- ret = uvc_get_video_ctrl(video, &probe_min, 1, UVC_GET_MIN);
+ if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+ ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN);
if (ret < 0)
goto done;
- ret = uvc_get_video_ctrl(video, &probe_max, 1, UVC_GET_MAX);
+ ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX);
if (ret < 0)
goto done;
@@ -283,21 +283,21 @@ int uvc_probe_video(struct uvc_video_device *video,
}
for (i = 0; i < 2; ++i) {
- ret = uvc_set_video_ctrl(video, probe, 1);
+ ret = uvc_set_video_ctrl(stream, probe, 1);
if (ret < 0)
goto done;
- ret = uvc_get_video_ctrl(video, probe, 1, UVC_GET_CUR);
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
if (ret < 0)
goto done;
- if (video->streaming->intf->num_altsetting == 1)
+ if (stream->intf->num_altsetting == 1)
break;
bandwidth = probe->dwMaxPayloadTransferSize;
- if (bandwidth <= video->streaming->maxpsize)
+ if (bandwidth <= stream->maxpsize)
break;
- if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
+ if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
ret = -ENOSPC;
goto done;
}
@@ -310,14 +310,14 @@ int uvc_probe_video(struct uvc_video_device *video,
}
done:
- mutex_unlock(&video->streaming->mutex);
+ mutex_unlock(&stream->mutex);
return ret;
}
-int uvc_commit_video(struct uvc_video_device *video,
+int uvc_commit_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe)
{
- return uvc_set_video_ctrl(video, probe, 0);
+ return uvc_set_video_ctrl(stream, probe, 0);
}
/* ------------------------------------------------------------------------
@@ -369,7 +369,7 @@ int uvc_commit_video(struct uvc_video_device *video,
* to be called with a NULL buf parameter. uvc_video_decode_data and
* uvc_video_decode_end will never be called with a NULL buffer.
*/
-static int uvc_video_decode_start(struct uvc_video_device *video,
+static int uvc_video_decode_start(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
__u8 fid;
@@ -395,25 +395,25 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
* NULL.
*/
if (buf == NULL) {
- video->last_fid = fid;
+ stream->last_fid = fid;
return -ENODATA;
}
/* Synchronize to the input stream by waiting for the FID bit to be
* toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
- * video->last_fid is initialized to -1, so the first isochronous
+ * stream->last_fid is initialized to -1, so the first isochronous
* frame will always be in sync.
*
- * If the device doesn't toggle the FID bit, invert video->last_fid
+ * If the device doesn't toggle the FID bit, invert stream->last_fid
* when the EOF bit is set to force synchronisation on the next packet.
*/
if (buf->state != UVC_BUF_STATE_ACTIVE) {
- if (fid == video->last_fid) {
+ if (fid == stream->last_fid) {
uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
"sync).\n");
- if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
+ if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
(data[1] & UVC_STREAM_EOF))
- video->last_fid ^= UVC_STREAM_FID;
+ stream->last_fid ^= UVC_STREAM_FID;
return -ENODATA;
}
@@ -428,7 +428,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
* last payload can be lost anyway). We thus must check if the FID has
* been toggled.
*
- * video->last_fid is initialized to -1, so the first isochronous
+ * stream->last_fid is initialized to -1, so the first isochronous
* frame will never trigger an end of frame detection.
*
* Empty buffers (bytesused == 0) don't trigger end of frame detection
@@ -436,22 +436,22 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
* avoids detecting end of frame conditions at FID toggling if the
* previous payload had the EOF bit set.
*/
- if (fid != video->last_fid && buf->buf.bytesused != 0) {
+ if (fid != stream->last_fid && buf->buf.bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
"toggled).\n");
buf->state = UVC_BUF_STATE_DONE;
return -EAGAIN;
}
- video->last_fid = fid;
+ stream->last_fid = fid;
return data[0];
}
-static void uvc_video_decode_data(struct uvc_video_device *video,
+static void uvc_video_decode_data(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
- struct uvc_video_queue *queue = &video->queue;
+ struct uvc_video_queue *queue = &stream->queue;
unsigned int maxlen, nbytes;
void *mem;
@@ -472,7 +472,7 @@ static void uvc_video_decode_data(struct uvc_video_device *video,
}
}
-static void uvc_video_decode_end(struct uvc_video_device *video,
+static void uvc_video_decode_end(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
/* Mark the buffer as done if the EOF marker is set. */
@@ -481,8 +481,8 @@ static void uvc_video_decode_end(struct uvc_video_device *video,
if (data[0] == len)
uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
buf->state = UVC_BUF_STATE_DONE;
- if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
- video->last_fid ^= UVC_STREAM_FID;
+ if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
+ stream->last_fid ^= UVC_STREAM_FID;
}
}
@@ -497,26 +497,26 @@ static void uvc_video_decode_end(struct uvc_video_device *video,
* uvc_video_encode_data is called for every URB and copies the data from the
* video buffer to the transfer buffer.
*/
-static int uvc_video_encode_header(struct uvc_video_device *video,
+static int uvc_video_encode_header(struct uvc_streaming *stream,
struct uvc_buffer *buf, __u8 *data, int len)
{
data[0] = 2; /* Header length */
data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
- | (video->last_fid & UVC_STREAM_FID);
+ | (stream->last_fid & UVC_STREAM_FID);
return 2;
}
-static int uvc_video_encode_data(struct uvc_video_device *video,
+static int uvc_video_encode_data(struct uvc_streaming *stream,
struct uvc_buffer *buf, __u8 *data, int len)
{
- struct uvc_video_queue *queue = &video->queue;
+ struct uvc_video_queue *queue = &stream->queue;
unsigned int nbytes;
void *mem;
/* Copy video data to the URB buffer. */
mem = queue->mem + buf->buf.m.offset + queue->buf_used;
nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
- nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size,
+ nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size,
nbytes);
memcpy(data, mem, nbytes);
@@ -532,8 +532,8 @@ static int uvc_video_encode_data(struct uvc_video_device *video,
/*
* Completion handler for video URBs.
*/
-static void uvc_video_decode_isoc(struct urb *urb,
- struct uvc_video_device *video, struct uvc_buffer *buf)
+static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
{
u8 *mem;
int ret, i;
@@ -548,31 +548,32 @@ static void uvc_video_decode_isoc(struct urb *urb,
/* Decode the payload header. */
mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
do {
- ret = uvc_video_decode_start(video, buf, mem,
+ ret = uvc_video_decode_start(stream, buf, mem,
urb->iso_frame_desc[i].actual_length);
if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&video->queue, buf);
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
} while (ret == -EAGAIN);
if (ret < 0)
continue;
/* Decode the payload data. */
- uvc_video_decode_data(video, buf, mem + ret,
+ uvc_video_decode_data(stream, buf, mem + ret,
urb->iso_frame_desc[i].actual_length - ret);
/* Process the header again. */
- uvc_video_decode_end(video, buf, mem,
+ uvc_video_decode_end(stream, buf, mem,
urb->iso_frame_desc[i].actual_length);
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
- buf = uvc_queue_next_buffer(&video->queue, buf);
+ buf = uvc_queue_next_buffer(&stream->queue, buf);
}
}
-static void uvc_video_decode_bulk(struct urb *urb,
- struct uvc_video_device *video, struct uvc_buffer *buf)
+static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
{
u8 *mem;
int len, ret;
@@ -582,24 +583,25 @@ static void uvc_video_decode_bulk(struct urb *urb,
mem = urb->transfer_buffer;
len = urb->actual_length;
- video->bulk.payload_size += len;
+ stream->bulk.payload_size += len;
/* If the URB is the first of its payload, decode and save the
* header.
*/
- if (video->bulk.header_size == 0 && !video->bulk.skip_payload) {
+ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
do {
- ret = uvc_video_decode_start(video, buf, mem, len);
+ ret = uvc_video_decode_start(stream, buf, mem, len);
if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&video->queue, buf);
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
} while (ret == -EAGAIN);
/* If an error occured skip the rest of the payload. */
if (ret < 0 || buf == NULL) {
- video->bulk.skip_payload = 1;
+ stream->bulk.skip_payload = 1;
} else {
- memcpy(video->bulk.header, mem, ret);
- video->bulk.header_size = ret;
+ memcpy(stream->bulk.header, mem, ret);
+ stream->bulk.header_size = ret;
mem += ret;
len -= ret;
@@ -612,33 +614,34 @@ static void uvc_video_decode_bulk(struct urb *urb,
*/
/* Process video data. */
- if (!video->bulk.skip_payload && buf != NULL)
- uvc_video_decode_data(video, buf, mem, len);
+ if (!stream->bulk.skip_payload && buf != NULL)
+ uvc_video_decode_data(stream, buf, mem, len);
/* Detect the payload end by a URB smaller than the maximum size (or
* a payload size equal to the maximum) and process the header again.
*/
if (urb->actual_length < urb->transfer_buffer_length ||
- video->bulk.payload_size >= video->bulk.max_payload_size) {
- if (!video->bulk.skip_payload && buf != NULL) {
- uvc_video_decode_end(video, buf, video->bulk.header,
- video->bulk.payload_size);
+ stream->bulk.payload_size >= stream->bulk.max_payload_size) {
+ if (!stream->bulk.skip_payload && buf != NULL) {
+ uvc_video_decode_end(stream, buf, stream->bulk.header,
+ stream->bulk.payload_size);
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
- buf = uvc_queue_next_buffer(&video->queue, buf);
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
}
- video->bulk.header_size = 0;
- video->bulk.skip_payload = 0;
- video->bulk.payload_size = 0;
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
}
}
-static void uvc_video_encode_bulk(struct urb *urb,
- struct uvc_video_device *video, struct uvc_buffer *buf)
+static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
{
u8 *mem = urb->transfer_buffer;
- int len = video->urb_size, ret;
+ int len = stream->urb_size, ret;
if (buf == NULL) {
urb->transfer_buffer_length = 0;
@@ -646,34 +649,34 @@ static void uvc_video_encode_bulk(struct urb *urb,
}
/* If the URB is the first of its payload, add the header. */
- if (video->bulk.header_size == 0) {
- ret = uvc_video_encode_header(video, buf, mem, len);
- video->bulk.header_size = ret;
- video->bulk.payload_size += ret;
+ if (stream->bulk.header_size == 0) {
+ ret = uvc_video_encode_header(stream, buf, mem, len);
+ stream->bulk.header_size = ret;
+ stream->bulk.payload_size += ret;
mem += ret;
len -= ret;
}
/* Process video data. */
- ret = uvc_video_encode_data(video, buf, mem, len);
+ ret = uvc_video_encode_data(stream, buf, mem, len);
- video->bulk.payload_size += ret;
+ stream->bulk.payload_size += ret;
len -= ret;
- if (buf->buf.bytesused == video->queue.buf_used ||
- video->bulk.payload_size == video->bulk.max_payload_size) {
- if (buf->buf.bytesused == video->queue.buf_used) {
- video->queue.buf_used = 0;
+ if (buf->buf.bytesused == stream->queue.buf_used ||
+ stream->bulk.payload_size == stream->bulk.max_payload_size) {
+ if (buf->buf.bytesused == stream->queue.buf_used) {
+ stream->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
- uvc_queue_next_buffer(&video->queue, buf);
- video->last_fid ^= UVC_STREAM_FID;
+ uvc_queue_next_buffer(&stream->queue, buf);
+ stream->last_fid ^= UVC_STREAM_FID;
}
- video->bulk.header_size = 0;
- video->bulk.payload_size = 0;
+ stream->bulk.header_size = 0;
+ stream->bulk.payload_size = 0;
}
- urb->transfer_buffer_length = video->urb_size - len;
+ urb->transfer_buffer_length = stream->urb_size - len;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
@@ -682,8 +685,8 @@ static void uvc_video_complete(struct urb *urb, struct pt_regs *regs)
static void uvc_video_complete(struct urb *urb)
#endif
{
- struct uvc_video_device *video = urb->context;
- struct uvc_video_queue *queue = &video->queue;
+ struct uvc_streaming *stream = urb->context;
+ struct uvc_video_queue *queue = &stream->queue;
struct uvc_buffer *buf = NULL;
unsigned long flags;
int ret;
@@ -697,7 +700,7 @@ static void uvc_video_complete(struct urb *urb)
"completion handler.\n", urb->status);
case -ENOENT: /* usb_kill_urb() called. */
- if (video->frozen)
+ if (stream->frozen)
return;
case -ECONNRESET: /* usb_unlink_urb() called. */
@@ -712,7 +715,7 @@ static void uvc_video_complete(struct urb *urb)
queue);
spin_unlock_irqrestore(&queue->irqlock, flags);
- video->decode(urb, video, buf);
+ stream->decode(urb, stream, buf);
if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
@@ -723,19 +726,19 @@ static void uvc_video_complete(struct urb *urb)
/*
* Free transfer buffers.
*/
-static void uvc_free_urb_buffers(struct uvc_video_device *video)
+static void uvc_free_urb_buffers(struct uvc_streaming *stream)
{
unsigned int i;
for (i = 0; i < UVC_URBS; ++i) {
- if (video->urb_buffer[i]) {
- usb_buffer_free(video->dev->udev, video->urb_size,
- video->urb_buffer[i], video->urb_dma[i]);
- video->urb_buffer[i] = NULL;
+ if (stream->urb_buffer[i]) {
+ usb_buffer_free(stream->dev->udev, stream->urb_size,
+ stream->urb_buffer[i], stream->urb_dma[i]);
+ stream->urb_buffer[i] = NULL;
}
}
- video->urb_size = 0;
+ stream->urb_size = 0;
}
/*
@@ -749,15 +752,15 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video)
*
* Return the number of allocated packets on success or 0 when out of memory.
*/
-static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
+static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
unsigned int size, unsigned int psize, gfp_t gfp_flags)
{
unsigned int npackets;
unsigned int i;
/* Buffers are already allocated, bail out. */
- if (video->urb_size)
- return video->urb_size / psize;
+ if (stream->urb_size)
+ return stream->urb_size / psize;
/* Compute the number of packets. Bulk endpoints might transfer UVC
* payloads accross multiple URBs.
@@ -769,17 +772,17 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
/* Retry allocations until one succeed. */
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
- video->urb_buffer[i] = usb_buffer_alloc(
- video->dev->udev, psize * npackets,
- gfp_flags | __GFP_NOWARN, &video->urb_dma[i]);
- if (!video->urb_buffer[i]) {
- uvc_free_urb_buffers(video);
+ stream->urb_buffer[i] = usb_buffer_alloc(
+ stream->dev->udev, psize * npackets,
+ gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+ if (!stream->urb_buffer[i]) {
+ uvc_free_urb_buffers(stream);
break;
}
}
if (i == UVC_URBS) {
- video->urb_size = psize * npackets;
+ stream->urb_size = psize * npackets;
return npackets;
}
}
@@ -790,29 +793,30 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
/*
* Uninitialize isochronous/bulk URBs and free transfer buffers.
*/
-static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers)
+static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
{
struct urb *urb;
unsigned int i;
for (i = 0; i < UVC_URBS; ++i) {
- if ((urb = video->urb[i]) == NULL)
+ urb = stream->urb[i];
+ if (urb == NULL)
continue;
usb_kill_urb(urb);
usb_free_urb(urb);
- video->urb[i] = NULL;
+ stream->urb[i] = NULL;
}
if (free_buffers)
- uvc_free_urb_buffers(video);
+ uvc_free_urb_buffers(stream);
}
/*
* Initialize isochronous URBs and allocate transfer buffers. The packet size
* is given by the endpoint.
*/
-static int uvc_init_video_isoc(struct uvc_video_device *video,
+static int uvc_init_video_isoc(struct uvc_streaming *stream,
struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
struct urb *urb;
@@ -822,9 +826,9 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- size = video->streaming->ctrl.dwMaxVideoFrameSize;
+ size = stream->ctrl.dwMaxVideoFrameSize;
- npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;
@@ -833,18 +837,18 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
- uvc_uninit_video(video, 1);
+ uvc_uninit_video(stream, 1);
return -ENOMEM;
}
- urb->dev = video->dev->udev;
- urb->context = video;
- urb->pipe = usb_rcvisocpipe(video->dev->udev,
+ urb->dev = stream->dev->udev;
+ urb->context = stream;
+ urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->interval = ep->desc.bInterval;
- urb->transfer_buffer = video->urb_buffer[i];
- urb->transfer_dma = video->urb_dma[i];
+ urb->transfer_buffer = stream->urb_buffer[i];
+ urb->transfer_dma = stream->urb_dma[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
@@ -854,7 +858,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
urb->iso_frame_desc[j].length = psize;
}
- video->urb[i] = urb;
+ stream->urb[i] = urb;
}
return 0;
@@ -864,7 +868,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
* Initialize bulk URBs and allocate transfer buffers. The packet size is
* given by the endpoint.
*/
-static int uvc_init_video_bulk(struct uvc_video_device *video,
+static int uvc_init_video_bulk(struct uvc_streaming *stream,
struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
struct urb *urb;
@@ -873,39 +877,39 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
u32 size;
psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
- size = video->streaming->ctrl.dwMaxPayloadTransferSize;
- video->bulk.max_payload_size = size;
+ size = stream->ctrl.dwMaxPayloadTransferSize;
+ stream->bulk.max_payload_size = size;
- npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;
size = npackets * psize;
if (usb_endpoint_dir_in(&ep->desc))
- pipe = usb_rcvbulkpipe(video->dev->udev,
+ pipe = usb_rcvbulkpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
else
- pipe = usb_sndbulkpipe(video->dev->udev,
+ pipe = usb_sndbulkpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
- if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
size = 0;
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(0, gfp_flags);
if (urb == NULL) {
- uvc_uninit_video(video, 1);
+ uvc_uninit_video(stream, 1);
return -ENOMEM;
}
- usb_fill_bulk_urb(urb, video->dev->udev, pipe,
- video->urb_buffer[i], size, uvc_video_complete,
- video);
+ usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
+ stream->urb_buffer[i], size, uvc_video_complete,
+ stream);
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
- urb->transfer_dma = video->urb_dma[i];
+ urb->transfer_dma = stream->urb_dma[i];
- video->urb[i] = urb;
+ stream->urb[i] = urb;
}
return 0;
@@ -914,35 +918,35 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
/*
* Initialize isochronous/bulk URBs and allocate transfer buffers.
*/
-static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
+static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
{
- struct usb_interface *intf = video->streaming->intf;
+ struct usb_interface *intf = stream->intf;
struct usb_host_interface *alts;
struct usb_host_endpoint *ep = NULL;
- int intfnum = video->streaming->intfnum;
+ int intfnum = stream->intfnum;
unsigned int bandwidth, psize, i;
int ret;
- video->last_fid = -1;
- video->bulk.header_size = 0;
- video->bulk.skip_payload = 0;
- video->bulk.payload_size = 0;
+ stream->last_fid = -1;
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
if (intf->num_altsetting > 1) {
/* Isochronous endpoint, select the alternate setting. */
- bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize;
+ bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
if (bandwidth == 0) {
uvc_printk(KERN_WARNING, "device %s requested null "
"bandwidth, defaulting to lowest.\n",
- video->vdev->name);
+ stream->dev->name);
bandwidth = 1;
}
for (i = 0; i < intf->num_altsetting; ++i) {
alts = &intf->altsetting[i];
ep = uvc_find_endpoint(alts,
- video->streaming->header.bEndpointAddress);
+ stream->header.bEndpointAddress);
if (ep == NULL)
continue;
@@ -956,18 +960,19 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
if (i >= intf->num_altsetting)
return -EIO;
- if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0)
+ ret = usb_set_interface(stream->dev->udev, intfnum, i);
+ if (ret < 0)
return ret;
- ret = uvc_init_video_isoc(video, ep, gfp_flags);
+ ret = uvc_init_video_isoc(stream, ep, gfp_flags);
} else {
/* Bulk endpoint, proceed to URB initialization. */
ep = uvc_find_endpoint(&intf->altsetting[0],
- video->streaming->header.bEndpointAddress);
+ stream->header.bEndpointAddress);
if (ep == NULL)
return -EIO;
- ret = uvc_init_video_bulk(video, ep, gfp_flags);
+ ret = uvc_init_video_bulk(stream, ep, gfp_flags);
}
if (ret < 0)
@@ -975,10 +980,11 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
/* Submit the URBs. */
for (i = 0; i < UVC_URBS; ++i) {
- if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) {
+ ret = usb_submit_urb(stream->urb[i], gfp_flags);
+ if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to submit URB %u "
"(%d).\n", i, ret);
- uvc_uninit_video(video, 1);
+ uvc_uninit_video(stream, 1);
return ret;
}
}
@@ -997,14 +1003,14 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
* video buffers in any way. We mark the device as frozen to make sure the URB
* completion handler won't try to cancel the queue when we kill the URBs.
*/
-int uvc_video_suspend(struct uvc_video_device *video)
+int uvc_video_suspend(struct uvc_streaming *stream)
{
- if (!uvc_queue_streaming(&video->queue))
+ if (!uvc_queue_streaming(&stream->queue))
return 0;
- video->frozen = 1;
- uvc_uninit_video(video, 0);
- usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+ stream->frozen = 1;
+ uvc_uninit_video(stream, 0);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
return 0;
}
@@ -1016,22 +1022,24 @@ int uvc_video_suspend(struct uvc_video_device *video)
* buffers, making sure userspace applications are notified of the problem
* instead of waiting forever.
*/
-int uvc_video_resume(struct uvc_video_device *video)
+int uvc_video_resume(struct uvc_streaming *stream)
{
int ret;
- video->frozen = 0;
+ stream->frozen = 0;
- if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) {
- uvc_queue_enable(&video->queue, 0);
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0) {
+ uvc_queue_enable(&stream->queue, 0);
return ret;
}
- if (!uvc_queue_streaming(&video->queue))
+ if (!uvc_queue_streaming(&stream->queue))
return 0;
- if ((ret = uvc_init_video(video, GFP_NOIO)) < 0)
- uvc_queue_enable(&video->queue, 0);
+ ret = uvc_init_video(stream, GFP_NOIO);
+ if (ret < 0)
+ uvc_queue_enable(&stream->queue, 0);
return ret;
}
@@ -1050,48 +1058,53 @@ int uvc_video_resume(struct uvc_video_device *video)
*
* This function is called before registering the device with V4L.
*/
-int uvc_video_init(struct uvc_video_device *video)
+int uvc_video_init(struct uvc_streaming *stream)
{
- struct uvc_streaming_control *probe = &video->streaming->ctrl;
+ struct uvc_streaming_control *probe = &stream->ctrl;
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
unsigned int i;
int ret;
- if (video->streaming->nformats == 0) {
+ if (stream->nformats == 0) {
uvc_printk(KERN_INFO, "No supported video formats found.\n");
return -EINVAL;
}
+ atomic_set(&stream->active, 0);
+
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&stream->queue, stream->type);
+
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
* they don't receive a SET_INTERFACE request before any other video
* control request.
*/
- usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
/* Set the streaming probe control with default streaming parameters
* retrieved from the device. Webcams that don't suport GET_DEF
* requests on the probe control will just keep their current streaming
* parameters.
*/
- if (uvc_get_video_ctrl(video, probe, 1, UVC_GET_DEF) == 0)
- uvc_set_video_ctrl(video, probe, 1);
+ if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
+ uvc_set_video_ctrl(stream, probe, 1);
/* Initialize the streaming parameters with the probe control current
* value. This makes sure SET_CUR requests on the streaming commit
* control will always use values retrieved from a successful GET_CUR
* request on the probe control, as required by the UVC specification.
*/
- ret = uvc_get_video_ctrl(video, probe, 1, UVC_GET_CUR);
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
if (ret < 0)
return ret;
/* Check if the default format descriptor exists. Use the first
* available format otherwise.
*/
- for (i = video->streaming->nformats; i > 0; --i) {
- format = &video->streaming->format[i-1];
+ for (i = stream->nformats; i > 0; --i) {
+ format = &stream->format[i-1];
if (format->index == probe->bFormatIndex)
break;
}
@@ -1116,21 +1129,20 @@ int uvc_video_init(struct uvc_video_device *video)
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
- video->streaming->cur_format = format;
- video->streaming->cur_frame = frame;
- atomic_set(&video->active, 0);
+ stream->cur_format = format;
+ stream->cur_frame = frame;
/* Select the video decoding function */
- if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
- video->decode = uvc_video_decode_isight;
- else if (video->streaming->intf->num_altsetting > 1)
- video->decode = uvc_video_decode_isoc;
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ stream->decode = uvc_video_decode_isight;
+ else if (stream->intf->num_altsetting > 1)
+ stream->decode = uvc_video_decode_isoc;
else
- video->decode = uvc_video_decode_bulk;
+ stream->decode = uvc_video_decode_bulk;
} else {
- if (video->streaming->intf->num_altsetting == 1)
- video->decode = uvc_video_encode_bulk;
+ if (stream->intf->num_altsetting == 1)
+ stream->decode = uvc_video_encode_bulk;
else {
uvc_printk(KERN_INFO, "Isochronous endpoints are not "
"supported for video output devices.\n");
@@ -1144,31 +1156,32 @@ int uvc_video_init(struct uvc_video_device *video)
/*
* Enable or disable the video stream.
*/
-int uvc_video_enable(struct uvc_video_device *video, int enable)
+int uvc_video_enable(struct uvc_streaming *stream, int enable)
{
int ret;
if (!enable) {
- uvc_uninit_video(video, 1);
- usb_set_interface(video->dev->udev,
- video->streaming->intfnum, 0);
- uvc_queue_enable(&video->queue, 0);
+ uvc_uninit_video(stream, 1);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ uvc_queue_enable(&stream->queue, 0);
return 0;
}
- if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
uvc_no_drop_param)
- video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
+ stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
else
- video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
+ stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
- if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+ ret = uvc_queue_enable(&stream->queue, 1);
+ if (ret < 0)
return ret;
/* Commit the streaming parameters. */
- if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0)
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0)
return ret;
- return uvc_init_video(video, GFP_KERNEL);
+ return uvc_init_video(stream, GFP_KERNEL);
}
diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h
index e700bdc20..30a17f24e 100644
--- a/linux/drivers/media/video/uvc/uvcvideo.h
+++ b/linux/drivers/media/video/uvc/uvcvideo.h
@@ -81,9 +81,11 @@ struct uvc_xu_control {
#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
#define UVC_ENTITY_IS_ITERM(entity) \
- (((entity)->type & 0x8000) == UVC_TERM_INPUT)
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_INPUT)
#define UVC_ENTITY_IS_OTERM(entity) \
- (((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
/* ------------------------------------------------------------------------
@@ -362,26 +364,6 @@ struct uvc_streaming_header {
__u8 bTriggerUsage;
};
-struct uvc_streaming {
- struct list_head list;
-
- struct usb_interface *intf;
- int intfnum;
- __u16 maxpsize;
-
- struct uvc_streaming_header header;
- enum v4l2_buf_type type;
-
- unsigned int nformats;
- struct uvc_format *format;
-
- struct uvc_streaming_control ctrl;
- struct uvc_format *cur_format;
- struct uvc_frame *cur_frame;
-
- struct mutex mutex;
-};
-
enum uvc_buffer_state {
UVC_BUF_STATE_IDLE = 0,
UVC_BUF_STATE_QUEUED = 1,
@@ -423,26 +405,45 @@ struct uvc_video_queue {
struct list_head irqqueue;
};
-struct uvc_video_device {
+struct uvc_video_chain {
struct uvc_device *dev;
- struct video_device *vdev;
- atomic_t active;
- unsigned int frozen : 1;
+ struct list_head list;
struct list_head iterms; /* Input terminals */
- struct uvc_entity *oterm; /* Output terminal */
- struct uvc_entity *sterm; /* USB streaming terminal */
- struct uvc_entity *processing;
- struct uvc_entity *selector;
- struct list_head extensions;
+ struct list_head oterms; /* Output terminals */
+ struct uvc_entity *processing; /* Processing unit */
+ struct uvc_entity *selector; /* Selector unit */
+ struct list_head extensions; /* Extension units */
+
struct mutex ctrl_mutex;
+};
- struct uvc_video_queue queue;
+struct uvc_streaming {
+ struct list_head list;
+ struct uvc_device *dev;
+ struct video_device *vdev;
+ struct uvc_video_chain *chain;
+ atomic_t active;
+
+ struct usb_interface *intf;
+ int intfnum;
+ __u16 maxpsize;
+
+ struct uvc_streaming_header header;
+ enum v4l2_buf_type type;
- /* Video streaming object, must always be non-NULL. */
- struct uvc_streaming *streaming;
+ unsigned int nformats;
+ struct uvc_format *format;
- void (*decode) (struct urb *urb, struct uvc_video_device *video,
+ struct uvc_streaming_control ctrl;
+ struct uvc_format *cur_format;
+ struct uvc_frame *cur_frame;
+
+ struct mutex mutex;
+
+ unsigned int frozen : 1;
+ struct uvc_video_queue queue;
+ void (*decode) (struct urb *urb, struct uvc_streaming *video,
struct uvc_buffer *buf);
/* Context data used by the bulk completion handler. */
@@ -484,8 +485,10 @@ struct uvc_device {
__u32 clock_frequency;
struct list_head entities;
+ struct list_head chains;
- struct uvc_video_device video;
+ /* Video Streaming interfaces */
+ struct list_head streams;
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
@@ -493,9 +496,6 @@ struct uvc_device {
__u8 *status;
struct input_dev *input;
char input_phys[64];
-
- /* Video Streaming interfaces */
- struct list_head streaming;
};
enum uvc_handle_state {
@@ -504,7 +504,8 @@ enum uvc_handle_state {
};
struct uvc_fh {
- struct uvc_video_device *device;
+ struct uvc_video_chain *chain;
+ struct uvc_streaming *stream;
enum uvc_handle_state state;
};
@@ -601,13 +602,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
extern const struct v4l2_file_operations uvc_fops;
/* Video */
-extern int uvc_video_init(struct uvc_video_device *video);
-extern int uvc_video_suspend(struct uvc_video_device *video);
-extern int uvc_video_resume(struct uvc_video_device *video);
-extern int uvc_video_enable(struct uvc_video_device *video, int enable);
-extern int uvc_probe_video(struct uvc_video_device *video,
+extern int uvc_video_init(struct uvc_streaming *stream);
+extern int uvc_video_suspend(struct uvc_streaming *stream);
+extern int uvc_video_resume(struct uvc_streaming *stream);
+extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
+extern int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe);
-extern int uvc_commit_video(struct uvc_video_device *video,
+extern int uvc_commit_video(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size);
@@ -621,9 +622,9 @@ extern int uvc_status_suspend(struct uvc_device *dev);
extern int uvc_status_resume(struct uvc_device *dev);
/* Controls */
-extern struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
__u32 v4l2_id, struct uvc_control_mapping **mapping);
-extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl);
extern int uvc_ctrl_add_info(struct uvc_control_info *info);
@@ -633,23 +634,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_device(struct uvc_device *dev);
extern void uvc_ctrl_init(void);
-extern int uvc_ctrl_begin(struct uvc_video_device *video);
-extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback);
-static inline int uvc_ctrl_commit(struct uvc_video_device *video)
+extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
+extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
+static inline int uvc_ctrl_commit(struct uvc_video_chain *chain)
{
- return __uvc_ctrl_commit(video, 0);
+ return __uvc_ctrl_commit(chain, 0);
}
-static inline int uvc_ctrl_rollback(struct uvc_video_device *video)
+static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain)
{
- return __uvc_ctrl_commit(video, 1);
+ return __uvc_ctrl_commit(chain, 1);
}
-extern int uvc_ctrl_get(struct uvc_video_device *video,
+extern int uvc_ctrl_get(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl);
-extern int uvc_ctrl_set(struct uvc_video_device *video,
+extern int uvc_ctrl_set(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl);
-extern int uvc_xu_ctrl_query(struct uvc_video_device *video,
+extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control *ctrl, int set);
/* Utility functions */
@@ -661,7 +662,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
struct usb_host_interface *alts, __u8 epaddr);
/* Quirks support */
-void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
+void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
struct uvc_buffer *buf);
#endif /* __KERNEL__ */
diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c
index 80c94dc5a..d746d9555 100644
--- a/linux/drivers/media/video/v4l2-ioctl.c
+++ b/linux/drivers/media/video/v4l2-ioctl.c
@@ -1088,8 +1088,10 @@ static long __video_do_ioctl(struct file *file,
/* Calls the specific handler */
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, id);
- else
+ else if (vfd->current_norm)
*id = vfd->current_norm;
+ else
+ ret = -EINVAL;
if (!ret)
dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
@@ -1560,12 +1562,19 @@ static long __video_do_ioctl(struct file *file,
break;
ret = ops->vidioc_g_parm(file, fh, p);
} else {
+ v4l2_std_id std = vfd->current_norm;
+
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- v4l2_video_std_frame_period(vfd->current_norm,
- &p->parm.capture.timeperframe);
ret = 0;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ else if (std == 0)
+ ret = -EINVAL;
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
}
dbgarg(cmd, "type=%d\n", p->type);
diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c
index 217fec9e4..375f158fd 100644
--- a/linux/drivers/media/video/zoran/zoran_card.c
+++ b/linux/drivers/media/video/zoran/zoran_card.c
@@ -1169,7 +1169,7 @@ zoran_setup_videocodec (struct zoran *zr,
m->type = 0;
m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
- strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+ strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
m->data = zr;
switch (type)
diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp
index b2246c557..60ebc5474 100644
--- a/v4l2-apps/util/v4l2-ctl.cpp
+++ b/v4l2-apps/util/v4l2-ctl.cpp
@@ -77,6 +77,8 @@ enum Option {
OptSetTuner = 't',
OptGetVideoFormat = 'V',
OptSetVideoFormat = 'v',
+ OptGetParm = 'P',
+ OptSetParm = 'p',
OptGetSlicedVbiOutFormat = 128,
OptGetOverlayFormat,
@@ -134,6 +136,8 @@ enum Option {
OptGetModulator,
OptSetModulator,
OptListDevices,
+ OptGetOutputParm,
+ OptSetOutputParm,
OptLast = 256
};
@@ -222,6 +226,10 @@ static struct option long_options[] = {
{"list-formats", no_argument, 0, OptListFormats},
{"get-standard", no_argument, 0, OptGetStandard},
{"set-standard", required_argument, 0, OptSetStandard},
+ {"get-parm", no_argument, 0, OptGetParm},
+ {"set-parm", required_argument, 0, OptSetParm},
+ {"get-output-parm", no_argument, 0, OptGetOutputParm},
+ {"set-output-parm", required_argument, 0, OptSetOutputParm},
{"info", no_argument, 0, OptGetDriverInfo},
{"list-ctrls", no_argument, 0, OptListCtrls},
{"list-ctrls-menus", no_argument, 0, OptListCtrlsMenus},
@@ -306,6 +314,9 @@ static void usage(void)
" ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n"
" secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n"
" --list-standards display supported video standards [VIDIOC_ENUMSTD]\n"
+ " -P, --get-parm display video parameters [VIDIOC_G_PARM]\n"
+ " -p, --set-parm=<fps>\n"
+ " set video framerate in <fps> [VIDIOC_S_PARM]\n"
" -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n"
" -t, --set-tuner=<mode>\n"
" set the audio mode of the tuner [VIDIOC_S_TUNER]\n"
@@ -418,6 +429,9 @@ static void usage(void)
" bilingual: Modulate as bilingual\n"
" mono-sap: Modulate as mono with Second Audio Program\n"
" stereo-sap: Modulate as stereo with Second Audio Program\n"
+ " --get-output-parm display output video parameters [VIDIOC_G_PARM]\n"
+ " --set-output-parm=<fps>\n"
+ " set output video framerate in <fps> [VIDIOC_S_PARM]\n"
"\n");
printf("Expert options:\n"
" --streamoff turn the stream off [VIDIOC_STREAMOFF]\n"
@@ -824,7 +838,7 @@ static void printcropcap(const struct v4l2_cropcap &cropcap)
printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator);
}
-static void printfmt(struct v4l2_format vfmt)
+static void printfmt(const struct v4l2_format &vfmt)
{
const flag_def vbi_def[] = {
{ V4L2_VBI_UNSYNC, "unsynchronized" },
@@ -1419,11 +1433,14 @@ int main(int argc, char **argv)
struct v4l2_rect vcrop_out_overlay; /* crop rect */
struct v4l2_framebuffer fbuf; /* fbuf */
struct v4l2_jpegcompression jpegcomp; /* jpeg compression */
+ struct v4l2_streamparm parm; /* get/set parm */
int input; /* set_input/get_input */
int output; /* set_output/get_output */
int txsubchans; /* set_modulator */
v4l2_std_id std; /* get_std/set_std */
double freq = 0; /* get/set frequency */
+ double fps = 0; /* set framerate speed, in fps */
+ double output_fps = 0; /* set framerate speed, in fps */
struct v4l2_frequency vf; /* get_freq/set_freq */
struct v4l2_standard vs; /* list_std */
int overlay; /* overlay */
@@ -1690,6 +1707,12 @@ int main(int argc, char **argv)
std = strtol(optarg, 0L, 0) | (1ULL << 63);
}
break;
+ case OptSetParm:
+ fps = strtod(optarg, NULL);
+ break;
+ case OptSetOutputParm:
+ output_fps = strtod(optarg, NULL);
+ break;
case OptGetCtrl:
subs = optarg;
while (*subs != '\0') {
@@ -1915,6 +1938,8 @@ int main(int argc, char **argv)
options[OptGetAudioInput] = 1;
options[OptGetAudioOutput] = 1;
options[OptGetStandard] = 1;
+ options[OptGetParm] = 1;
+ options[OptGetOutputParm] = 1;
options[OptGetFreq] = 1;
options[OptGetTuner] = 1;
options[OptGetModulator] = 1;
@@ -1976,6 +2001,43 @@ int main(int argc, char **argv)
printf("Standard set to %08llx\n", (unsigned long long)std);
}
+
+ if (options[OptSetParm]) {
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parm.parm.capture.timeperframe.numerator = 1000;
+ parm.parm.capture.timeperframe.denominator =
+ fps * parm.parm.capture.timeperframe.numerator;
+
+ if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) {
+ struct v4l2_fract *tf = &parm.parm.capture.timeperframe;
+
+ if (!tf->denominator || !tf->numerator)
+ printf("Invalid frame rate\n");
+ else
+ printf("Frame rate set to %.3f fps\n",
+ 1.0 * tf->denominator / tf->numerator);
+ }
+ }
+
+ if (options[OptSetOutputParm]) {
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ parm.parm.output.timeperframe.numerator = 1000;
+ parm.parm.output.timeperframe.denominator =
+ fps * parm.parm.output.timeperframe.numerator;
+
+ if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) {
+ struct v4l2_fract *tf = &parm.parm.output.timeperframe;
+
+ if (!tf->denominator || !tf->numerator)
+ printf("Invalid frame rate\n");
+ else
+ printf("Frame rate set to %.3f fps\n",
+ 1.0 * tf->denominator / tf->numerator);
+ }
+ }
+
if (options[OptSetInput]) {
if (doioctl(fd, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT") == 0) {
printf("Video input set to %d", input);
@@ -2451,6 +2513,50 @@ set_vid_fmt_error:
}
}
+ if (options[OptGetParm]) {
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) {
+ const struct v4l2_fract &tf = parm.parm.capture.timeperframe;
+
+ printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str());
+ if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)
+ printf("\tCapabilities : timeperframe\n");
+ if (parm.parm.capture.capturemode & V4L2_MODE_HIGHQUALITY)
+ printf("\tCapture mode : high quality\n");
+ if (!tf.denominator || !tf.numerator)
+ printf("\tFrames per second: invalid (%d/%d)\n",
+ tf.denominator, tf.numerator);
+ else
+ printf("\tFrames per second: %.3f (%d/%d)\n",
+ (1.0 * tf.denominator) / tf.numerator,
+ tf.denominator, tf.numerator);
+ printf("\tRead buffers : %d\n", parm.parm.output.writebuffers);
+ }
+ }
+
+ if (options[OptGetOutputParm]) {
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) {
+ const struct v4l2_fract &tf = parm.parm.output.timeperframe;
+
+ printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str());
+ if (parm.parm.output.capability & V4L2_CAP_TIMEPERFRAME)
+ printf("\tCapabilities : timeperframe\n");
+ if (parm.parm.output.outputmode & V4L2_MODE_HIGHQUALITY)
+ printf("\tOutput mode : high quality\n");
+ if (!tf.denominator || !tf.numerator)
+ printf("\tFrames per second: invalid (%d/%d)\n",
+ tf.denominator, tf.numerator);
+ else
+ printf("\tFrames per second: %.3f (%d/%d)\n",
+ (1.0 * tf.denominator) / tf.numerator,
+ tf.denominator, tf.numerator);
+ printf("\tWrite buffers : %d\n", parm.parm.output.writebuffers);
+ }
+ }
+
if (options[OptGetCtrl] && !get_ctrls.empty()) {
struct v4l2_ext_controls ctrls = { 0 };
diff --git a/v4l2-apps/util/v4l2-dbg-micron.h b/v4l2-apps/util/v4l2-dbg-micron.h
new file mode 100644
index 000000000..8466322bf
--- /dev/null
+++ b/v4l2-apps/util/v4l2-dbg-micron.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org>
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "v4l2-dbg.h"
+
+#define MT9V011_IDENT "mt9v011"
+
+/* Register name prefix */
+#define MT9V011_PREFIX "MT9V011_"
+
+static struct board_regs mt9v011_regs[] = {
+ {0x00, MT9V011_PREFIX "CHIP_VERSION"},
+ {0x01, MT9V011_PREFIX "ROWSTART"},
+ {0x02, MT9V011_PREFIX "COLSTART"},
+ {0x03, MT9V011_PREFIX "HEIGHT"},
+ {0x04, MT9V011_PREFIX "WIDTH"},
+ {0x05, MT9V011_PREFIX "HBLANK"},
+ {0x06, MT9V011_PREFIX "VBLANK"},
+ {0x07, MT9V011_PREFIX "OUT_CTRL"},
+ {0x09, MT9V011_PREFIX "SHUTTER_WIDTH"},
+ {0x0a, MT9V011_PREFIX "CLK_SPEED"},
+ {0x0b, MT9V011_PREFIX "RESTART"},
+ {0x0c, MT9V011_PREFIX "SHUTTER_DELAY"},
+ {0x0d, MT9V011_PREFIX "RESET"},
+ {0x1e, MT9V011_PREFIX "DIGITAL_ZOOM"},
+ {0x20, MT9V011_PREFIX "READ_MODE"},
+ {0x2b, MT9V011_PREFIX "GREEN_1_GAIN"},
+ {0x2c, MT9V011_PREFIX "BLUE_GAIN"},
+ {0x2d, MT9V011_PREFIX "RED_GAIN"},
+ {0x2e, MT9V011_PREFIX "GREEN_2_GAIN"},
+ {0x35, MT9V011_PREFIX "GLOBAL_GAIN"},
+ {0xf1, MT9V011_PREFIX "CHIP_ENABLE"},
+};
diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp
index 424b0fffe..1a481c04c 100644
--- a/v4l2-apps/util/v4l2-dbg.cpp
+++ b/v4l2-apps/util/v4l2-dbg.cpp
@@ -47,6 +47,7 @@
#include "v4l2-dbg-em28xx.h"
#include "v4l2-dbg-ac97.h"
#include "v4l2-dbg-tvp5150.h"
+#include "v4l2-dbg-micron.h"
#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0])))
@@ -61,7 +62,7 @@ struct board_list {
static const struct board_list boards[] = {
#define AC97_BOARD 0
- { /* From ac97-dbg.h */
+ { /* From v4l2-dbg-ac97.h */
AC97_IDENT,
sizeof(AC97_PREFIX) - 1,
ac97_regs,
@@ -69,7 +70,7 @@ static const struct board_list boards[] = {
NULL,
0,
},
- { /* From bttv-dbg.h */
+ { /* From v4l2-dbg-bttv.h */
BTTV_IDENT,
sizeof(BTTV_PREFIX) - 1,
bt8xx_regs,
@@ -77,7 +78,7 @@ static const struct board_list boards[] = {
bt8xx_regs_other,
ARRAY_SIZE(bt8xx_regs_other),
},
- { /* From saa7134-dbg.h */
+ { /* From v4l2-dbg-saa7134.h */
SAA7134_IDENT,
sizeof(SAA7134_PREFIX) - 1,
saa7134_regs,
@@ -85,7 +86,7 @@ static const struct board_list boards[] = {
NULL,
0,
},
- { /* From em28xx-dbg.h */
+ { /* From v4l2-dbg-em28xx.h */
EM28XX_IDENT,
sizeof(EM28XX_PREFIX) - 1,
em28xx_regs,
@@ -93,7 +94,7 @@ static const struct board_list boards[] = {
em28xx_alt_regs,
ARRAY_SIZE(em28xx_alt_regs),
},
- { /* From tvp5150-dbg.h */
+ { /* From v4l2-dbg-tvp5150.h */
TVP5150_IDENT,
sizeof(TVP5150_PREFIX) - 1,
tvp5150_regs,
@@ -101,6 +102,14 @@ static const struct board_list boards[] = {
NULL,
0,
},
+ { /* From v4l2-dbg-micron.h */
+ MT9V011_IDENT,
+ sizeof(MT9V011_PREFIX) - 1,
+ mt9v011_regs,
+ ARRAY_SIZE(mt9v011_regs),
+ NULL,
+ 0,
+ },
};
struct chipid {