From 7724325a19fb0a51d2a69bd2915b33f0ff197f5a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 12 Nov 2016 12:46:26 -0200 Subject: dvb-usb: move data_mutex to struct dvb_usb_device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The data_mutex is initialized too late, as it is needed for each device driver's power control, causing an OOPS: dvb-usb: found a 'TerraTec/qanu USB2.0 Highspeed DVB-T Receiver' in warm state. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] __mutex_lock_slowpath+0x6f/0x100 PGD 0 Oops: 0002 [#1] SMP Modules linked in: dvb_usb_cinergyT2(+) dvb_usb CPU: 0 PID: 2029 Comm: modprobe Not tainted 4.9.0-rc4-dvbmod #24 Hardware name: FUJITSU LIFEBOOK A544/FJNBB35 , BIOS Version 1.17 05/09/2014 task: ffff88020e943840 task.stack: ffff8801f36ec000 RIP: 0010:[] [] __mutex_lock_slowpath+0x6f/0x100 RSP: 0018:ffff8801f36efb10 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffff88021509bdc8 RCX: 00000000c0000100 RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffff88021509bdcc RBP: ffff8801f36efb58 R08: ffff88021f216320 R09: 0000000000100000 R10: ffff88021f216320 R11: 00000023fee6c5a1 R12: ffff88020e943840 R13: ffff88021509bdcc R14: 00000000ffffffff R15: ffff88021509bdd0 FS: 00007f21adb86740(0000) GS:ffff88021f200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 0000000215bce000 CR4: 00000000001406f0 Call Trace: mutex_lock+0x16/0x25 cinergyt2_power_ctrl+0x1f/0x60 [dvb_usb_cinergyT2] dvb_usb_device_init+0x21e/0x5d0 [dvb_usb] cinergyt2_usb_probe+0x21/0x50 [dvb_usb_cinergyT2] usb_probe_interface+0xf3/0x2a0 driver_probe_device+0x208/0x2b0 __driver_attach+0x87/0x90 driver_probe_device+0x2b0/0x2b0 bus_for_each_dev+0x52/0x80 bus_add_driver+0x1a3/0x220 driver_register+0x56/0xd0 usb_register_driver+0x77/0x130 do_one_initcall+0x46/0x180 free_vmap_area_noflush+0x38/0x70 kmem_cache_alloc+0x84/0xc0 do_init_module+0x50/0x1be load_module+0x1d8b/0x2100 find_symbol_in_section+0xa0/0xa0 SyS_finit_module+0x89/0x90 entry_SYSCALL_64_fastpath+0x13/0x94 Code: e8 a7 1d 00 00 8b 03 83 f8 01 0f 84 97 00 00 00 48 8b 43 10 4c 8d 7b 08 48 89 63 10 4c 89 3c 24 41 be ff ff ff ff 48 89 44 24 08 <48> 89 20 4c 89 64 24 10 eb 1a 49 c7 44 24 08 02 00 00 00 c6 43 RIP [] __mutex_lock_slowpath+0x6f/0x100 RSP CR2: 0000000000000000 So, move it to the struct dvb_usb_device and initialize it before calling the driver's callbacks. Reported-by: Jörg Otte Tested-by: Jörg Otte Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Linus Torvalds --- drivers/media/usb/dvb-usb/af9005.c | 33 ++++++++---------------- drivers/media/usb/dvb-usb/cinergyT2-core.c | 33 ++++++++---------------- drivers/media/usb/dvb-usb/cxusb.c | 39 ++++++++++++----------------- drivers/media/usb/dvb-usb/cxusb.h | 1 - drivers/media/usb/dvb-usb/dtt200u.c | 40 +++++++++++++----------------- drivers/media/usb/dvb-usb/dvb-usb-init.c | 1 + drivers/media/usb/dvb-usb/dvb-usb.h | 9 +++++-- 7 files changed, 61 insertions(+), 95 deletions(-) diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index b257780fb380..7853261906b1 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -53,7 +53,6 @@ struct af9005_device_state { u8 sequence; int led_state; unsigned char data[256]; - struct mutex data_mutex; }; static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, @@ -72,7 +71,7 @@ static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, return -EINVAL; } - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = 14; /* rest of buffer length low */ st->data[1] = 0; /* rest of buffer length high */ @@ -140,7 +139,7 @@ static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, values[i] = st->data[8 + i]; ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -481,7 +480,7 @@ int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, } packet_len = wlen + 5; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = (u8) (packet_len & 0xff); st->data[1] = (u8) ((packet_len & 0xff00) >> 8); @@ -512,7 +511,7 @@ int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, rbuf[i] = st->data[i + 7]; } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -523,7 +522,7 @@ int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, u8 seq; int ret, i; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); memset(st->data, 0, sizeof(st->data)); @@ -559,7 +558,7 @@ int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, for (i = 0; i < len; i++) values[i] = st->data[6 + i]; } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -847,7 +846,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) return 0; } - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); /* deb_info("rc_query\n"); */ st->data[0] = 3; /* rest of packet length low */ @@ -890,7 +889,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) } ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -1004,20 +1003,8 @@ static struct dvb_usb_device_properties af9005_properties; static int af9005_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; - struct af9005_device_state *st; - int ret; - - ret = dvb_usb_device_init(intf, &af9005_properties, - THIS_MODULE, &d, adapter_nr); - - if (ret < 0) - return ret; - - st = d->priv; - mutex_init(&st->data_mutex); - - return 0; + return dvb_usb_device_init(intf, &af9005_properties, + THIS_MODULE, NULL, adapter_nr); } enum af9005_usb_table_entry { diff --git a/drivers/media/usb/dvb-usb/cinergyT2-core.c b/drivers/media/usb/dvb-usb/cinergyT2-core.c index 8ac825413d5a..290275bc7fde 100644 --- a/drivers/media/usb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/usb/dvb-usb/cinergyT2-core.c @@ -42,7 +42,6 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct cinergyt2_state { u8 rc_counter; unsigned char data[64]; - struct mutex data_mutex; }; /* We are missing a release hook with usb_device data */ @@ -56,12 +55,12 @@ static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable) struct cinergyt2_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_CONTROL_STREAM_TRANSFER; st->data[1] = enable ? 1 : 0; ret = dvb_usb_generic_rw(d, st->data, 2, st->data, 64, 0); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -71,12 +70,12 @@ static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable) struct cinergyt2_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_SLEEP_MODE; st->data[1] = enable ? 0 : 1; ret = dvb_usb_generic_rw(d, st->data, 2, st->data, 3, 0); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -89,7 +88,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_GET_FIRMWARE_VERSION; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 3, 0); @@ -97,7 +96,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep " "state info\n"); } - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); /* Copy this pointer as we are gonna need it in the release phase */ cinergyt2_usb_device = adap->dev; @@ -166,7 +165,7 @@ static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = CINERGYT2_EP1_GET_RC_EVENTS; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0); @@ -202,29 +201,17 @@ static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) } ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int cinergyt2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; - struct cinergyt2_state *st; - int ret; - - ret = dvb_usb_device_init(intf, &cinergyt2_properties, - THIS_MODULE, &d, adapter_nr); - if (ret < 0) - return ret; - - st = d->priv; - mutex_init(&st->data_mutex); - - return 0; + return dvb_usb_device_init(intf, &cinergyt2_properties, + THIS_MODULE, NULL, adapter_nr); } - static struct usb_device_id cinergyt2_usb_table[] = { { USB_DEVICE(USB_VID_TERRATEC, 0x0038) }, { 0 } diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 39772812269d..243403081fa5 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -68,7 +68,7 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d, wo = (rbuf == NULL || rlen == 0); /* write-only */ - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = cmd; memcpy(&st->data[1], wbuf, wlen); if (wo) @@ -77,7 +77,7 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d, ret = dvb_usb_generic_rw(d, st->data, 1 + wlen, rbuf, rlen, 0); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -1461,43 +1461,36 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; - struct cxusb_state *st; - if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_needsfirmware_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_rev2_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, - THIS_MODULE, &d, adapter_nr) || - 0) { - st = d->priv; - mutex_init(&st->data_mutex); - + THIS_MODULE, NULL, adapter_nr) || + 0) return 0; - } return -EINVAL; } diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 9f3ee0e47d5c..18acda19527a 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -37,7 +37,6 @@ struct cxusb_state { struct i2c_client *i2c_client_tuner; unsigned char data[MAX_XFER_SIZE]; - struct mutex data_mutex; }; #endif diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c index f88572c7ae7c..fcbff7fb0c4e 100644 --- a/drivers/media/usb/dvb-usb/dtt200u.c +++ b/drivers/media/usb/dvb-usb/dtt200u.c @@ -22,7 +22,6 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct dtt200u_state { unsigned char data[80]; - struct mutex data_mutex; }; static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) @@ -30,23 +29,24 @@ static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) struct dtt200u_state *st = d->priv; int ret = 0; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_INIT; if (onoff) ret = dvb_usb_generic_write(d, st->data, 2); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { - struct dtt200u_state *st = adap->dev->priv; + struct dvb_usb_device *d = adap->dev; + struct dtt200u_state *st = d->priv; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_STREAMING; st->data[1] = onoff; @@ -61,26 +61,27 @@ static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) ret = dvb_usb_generic_write(adap->dev, st->data, 1); ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) { - struct dtt200u_state *st = adap->dev->priv; + struct dvb_usb_device *d = adap->dev; + struct dtt200u_state *st = d->priv; int ret; pid = onoff ? pid : 0; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = SET_PID_FILTER; st->data[1] = index; st->data[2] = pid & 0xff; st->data[3] = (pid >> 8) & 0x1f; ret = dvb_usb_generic_write(adap->dev, st->data, 4); - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -91,7 +92,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d) u32 scancode; int ret; - mutex_lock(&st->data_mutex); + mutex_lock(&d->data_mutex); st->data[0] = GET_RC_CODE; ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0); @@ -126,7 +127,7 @@ static int dtt200u_rc_query(struct dvb_usb_device *d) deb_info("st->data: %*ph\n", 5, st->data); ret: - mutex_unlock(&st->data_mutex); + mutex_unlock(&d->data_mutex); return ret; } @@ -145,24 +146,17 @@ static struct dvb_usb_device_properties wt220u_miglia_properties; static int dtt200u_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; - struct dtt200u_state *st; - if (0 == dvb_usb_device_init(intf, &dtt200u_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_fc_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties, - THIS_MODULE, &d, adapter_nr) || + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties, - THIS_MODULE, &d, adapter_nr)) { - st = d->priv; - mutex_init(&st->data_mutex); - + THIS_MODULE, NULL, adapter_nr)) return 0; - } return -ENODEV; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 3896ba9a4179..84308569e7dc 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -142,6 +142,7 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) { int ret = 0; + mutex_init(&d->data_mutex); mutex_init(&d->usb_mutex); mutex_init(&d->i2c_mutex); diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h index 639c4678c65b..107255b08b2b 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb.h +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -404,8 +404,12 @@ struct dvb_usb_adapter { * Powered is in/decremented for each call to modify the state. * @udev: pointer to the device's struct usb_device. * - * @usb_mutex: semaphore of USB control messages (reading needs two messages) - * @i2c_mutex: semaphore for i2c-transfers + * @data_mutex: mutex to protect the data structure used to store URB data + * @usb_mutex: mutex of USB control messages (reading needs two messages). + * Please notice that this mutex is used internally at the generic + * URB control functions. So, drivers using dvb_usb_generic_rw() and + * derivated functions should not lock it internally. + * @i2c_mutex: mutex for i2c-transfers * * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB * @@ -433,6 +437,7 @@ struct dvb_usb_device { int powered; /* locking */ + struct mutex data_mutex; struct mutex usb_mutex; /* i2c */ -- cgit v1.2.3 From 1596c387e970cb680d4031fbe4d6eb2c2a4ddb63 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 12 Nov 2016 12:46:27 -0200 Subject: gp8psk: fix gp8psk_usb_in_op() logic Commit bc29131ecb10 ("[media] gp8psk: don't do DMA on stack") fixed the usage of DMA on stack, but the memcpy was wrong for gp8psk_usb_in_op(). Fix it. From Derek's email: "Fix confirmed using 2 different Skywalker models with HD mpeg4, SD mpeg2." Suggested-by: Johannes Stezenbach Fixes: bc29131ecb10 ("[media] gp8psk: don't do DMA on stack") Tested-by: Derek Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Linus Torvalds --- drivers/media/usb/dvb-usb/gp8psk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index adfd76491451..2829e3082d15 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -67,7 +67,6 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 return ret; while (ret >= 0 && ret != blen && try < 3) { - memcpy(st->data, b, blen); ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), req, @@ -81,8 +80,10 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 if (ret < 0 || ret != blen) { warn("usb in %d operation failed.", req); ret = -EIO; - } else + } else { ret = 0; + memcpy(b, st->data, blen); + } deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index); debug_dump(b,blen,deb_xfer); -- cgit v1.2.3 From 7a0786c19d65bd4502b4a53aec9ef75e18192a00 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 12 Nov 2016 12:46:28 -0200 Subject: gp8psk: Fix DVB frontend attach The DVB binding schema at the DVB core assumes that the frontend is a separate driver. Faling to do that causes OOPS when the module is removed, as it tries to do a symbol_put_addr on an internal symbol, causing craches like: WARNING: CPU: 1 PID: 28102 at kernel/module.c:1108 module_put+0x57/0x70 Modules linked in: dvb_usb_gp8psk(-) dvb_usb dvb_core nvidia_drm(PO) nvidia_modeset(PO) snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd soundcore nvidia(PO) [last unloaded: rc_core] CPU: 1 PID: 28102 Comm: rmmod Tainted: P WC O 4.8.4-build.1 #1 Hardware name: MSI MS-7309/MS-7309, BIOS V1.12 02/23/2009 Call Trace: dump_stack+0x44/0x64 __warn+0xfa/0x120 module_put+0x57/0x70 module_put+0x57/0x70 warn_slowpath_null+0x23/0x30 module_put+0x57/0x70 gp8psk_fe_set_frontend+0x460/0x460 [dvb_usb_gp8psk] symbol_put_addr+0x27/0x50 dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb] From Derek's tests: "Attach bug is fixed, tuning works, module unloads without crashing. Everything seems ok!" Reported-by: Derek Tested-by: Derek Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Linus Torvalds --- drivers/media/dvb-frontends/Kconfig | 5 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/gp8psk-fe.c | 397 ++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/gp8psk-fe.h | 82 +++++++ drivers/media/usb/dvb-usb/Makefile | 2 +- drivers/media/usb/dvb-usb/gp8psk-fe.c | 372 ------------------------------ drivers/media/usb/dvb-usb/gp8psk.c | 106 ++++++--- drivers/media/usb/dvb-usb/gp8psk.h | 63 ----- 8 files changed, 561 insertions(+), 467 deletions(-) create mode 100644 drivers/media/dvb-frontends/gp8psk-fe.c create mode 100644 drivers/media/dvb-frontends/gp8psk-fe.h delete mode 100644 drivers/media/usb/dvb-usb/gp8psk-fe.c diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 012225587c25..b71b747ee0ba 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -513,6 +513,11 @@ config DVB_AS102_FE depends on DVB_CORE default DVB_AS102 +config DVB_GP8PSK_FE + tristate + depends on DVB_CORE + default DVB_USB_GP8PSK + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index e90165ad361b..93921a4eaa27 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o diff --git a/drivers/media/dvb-frontends/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c new file mode 100644 index 000000000000..be19afeed7a9 --- /dev/null +++ b/drivers/media/dvb-frontends/gp8psk-fe.c @@ -0,0 +1,397 @@ +/* DVB USB compliant Linux driver for the + * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module + * + * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) + * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) + * + * Thanks to GENPIX for the sample code used to implement this module. + * + * This module is based off the vp7045 and vp702x modules + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gp8psk-fe.h" +#include "dvb_frontend.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + +struct gp8psk_fe_state { + struct dvb_frontend fe; + void *priv; + const struct gp8psk_fe_ops *ops; + bool is_rev1; + u8 lock; + u16 snr; + unsigned long next_status_check; + unsigned long status_check_interval; +}; + +static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 status; + + st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1); + return status & bmDCtuned; +} + +static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0); +} + +static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) +{ + u8 buf[6]; + if (time_after(jiffies,st->next_status_check)) { + st->ops->in(st->priv, GET_SIGNAL_LOCK, 0, 0, &st->lock, 1); + st->ops->in(st->priv, GET_SIGNAL_STRENGTH, 0, 0, buf, 6); + st->snr = (buf[1]) << 8 | buf[0]; + st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; + } + return 0; +} + +static int gp8psk_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + + if (st->lock) + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + + if (*status & FE_HAS_LOCK) + st->status_check_interval = 1000; + else + st->status_check_interval = 100; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + (void) fe; + *ber = 0; + return 0; +} + +/* not supported by this Frontend */ +static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + (void) fe; + *unc = 0; + return 0; +} + +static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + *snr = st->snr; + return 0; +} + +static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + gp8psk_fe_update_status(st); + /* snr is reported in dBu*256 */ + /* snr / 38.4 ~= 100% strength */ + /* snr * 17 returns 100% strength as 65535 */ + if (st->snr > 0xf00) + *strength = 0xffff; + else + *strength = (st->snr << 4) + st->snr; /* snr*17 */ + return 0; +} + +static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 cmd[10]; + u32 freq = c->frequency * 1000; + + dprintk("%s()\n", __func__); + + cmd[4] = freq & 0xff; + cmd[5] = (freq >> 8) & 0xff; + cmd[6] = (freq >> 16) & 0xff; + cmd[7] = (freq >> 24) & 0xff; + + /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ + if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) + c->delivery_system = SYS_TURBO; + + switch (c->delivery_system) { + case SYS_DVBS: + if (c->modulation != QPSK) { + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + c->fec_inner = FEC_AUTO; + break; + case SYS_DVBS2: /* kept for backwards compatibility */ + dprintk("%s: DVB-S2 delivery system selected\n", __func__); + break; + case SYS_TURBO: + dprintk("%s: Turbo-FEC delivery system selected\n", __func__); + break; + + default: + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + cmd[0] = c->symbol_rate & 0xff; + cmd[1] = (c->symbol_rate >> 8) & 0xff; + cmd[2] = (c->symbol_rate >> 16) & 0xff; + cmd[3] = (c->symbol_rate >> 24) & 0xff; + switch (c->modulation) { + case QPSK: + if (st->is_rev1) + if (gp8psk_tuned_to_DCII(fe)) + st->ops->reload(st->priv); + switch (c->fec_inner) { + case FEC_1_2: + cmd[9] = 0; break; + case FEC_2_3: + cmd[9] = 1; break; + case FEC_3_4: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_7_8: + cmd[9] = 4; break; + case FEC_AUTO: + cmd[9] = 5; break; + default: + cmd[9] = 5; break; + } + if (c->delivery_system == SYS_TURBO) + cmd[8] = ADV_MOD_TURBO_QPSK; + else + cmd[8] = ADV_MOD_DVB_QPSK; + break; + case PSK_8: /* PSK_8 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_8PSK; + switch (c->fec_inner) { + case FEC_2_3: + cmd[9] = 0; break; + case FEC_3_4: + cmd[9] = 1; break; + case FEC_3_5: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_8_9: + cmd[9] = 4; break; + default: + cmd[9] = 0; break; + } + break; + case QAM_16: /* QAM_16 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_16QAM; + cmd[9] = 0; + break; + default: /* Unknown modulation */ + dprintk("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + + if (st->is_rev1) + gp8psk_set_tuner_mode(fe, 0); + st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10); + + st->lock = 0; + st->next_status_check = jiffies; + st->status_check_interval = 200; + + return 0; +} + +static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0, + m->msg, m->msg_len)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 cmd; + + dprintk("%s\n", __func__); + + /* These commands are certainly wrong */ + cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; + + if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0, + &cmd, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + if (st->ops->out(st->priv, SET_22KHZ_TONE, + (tone == SEC_TONE_ON), 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, + voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { + return -EINVAL; + } + return 0; +} + +static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0); +} + +static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 cmd = sw_cmd & 0x7f; + + if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0)) + return -EINVAL; + + if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), + 0, NULL, 0)) + return -EINVAL; + + return 0; +} + +static void gp8psk_fe_release(struct dvb_frontend* fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + + kfree(st); +} + +static struct dvb_frontend_ops gp8psk_fe_ops; + +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1) +{ + struct gp8psk_fe_state *st; + + if (!ops || !ops->in || !ops->out || !ops->reload) { + pr_err("Error! gp8psk-fe ops not defined.\n"); + return NULL; + } + + st = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); + if (!st) + return NULL; + + memcpy(&st->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); + st->fe.demodulator_priv = st; + st->ops = ops; + st->priv = priv; + st->is_rev1 = is_rev1; + + pr_info("Frontend %sattached\n", is_rev1 ? "revision 1 " : ""); + + return &st->fe; +} +EXPORT_SYMBOL_GPL(gp8psk_fe_attach); + +static struct dvb_frontend_ops gp8psk_fe_ops = { + .delsys = { SYS_DVBS }, + .info = { + .name = "Genpix DVB-S", + .frequency_min = 800000, + .frequency_max = 2250000, + .frequency_stepsize = 100, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + /* + * FE_CAN_QAM_16 is for compatibility + * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) + */ + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC + }, + + .release = gp8psk_fe_release, + + .init = NULL, + .sleep = NULL, + + .set_frontend = gp8psk_fe_set_frontend, + + .get_tune_settings = gp8psk_fe_get_tune_settings, + + .read_status = gp8psk_fe_read_status, + .read_ber = gp8psk_fe_read_ber, + .read_signal_strength = gp8psk_fe_read_signal_strength, + .read_snr = gp8psk_fe_read_snr, + .read_ucblocks = gp8psk_fe_read_unc_blocks, + + .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, + .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, + .set_tone = gp8psk_fe_set_tone, + .set_voltage = gp8psk_fe_set_voltage, + .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, + .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage +}; diff --git a/drivers/media/dvb-frontends/gp8psk-fe.h b/drivers/media/dvb-frontends/gp8psk-fe.h new file mode 100644 index 000000000000..6c7944b1ecd6 --- /dev/null +++ b/drivers/media/dvb-frontends/gp8psk-fe.h @@ -0,0 +1,82 @@ +/* + * gp8psk_fe driver + * + * 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, 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. + */ + +#ifndef GP8PSK_FE_H +#define GP8PSK_FE_H + +#include + +/* gp8psk commands */ + +#define GET_8PSK_CONFIG 0x80 /* in */ +#define SET_8PSK_CONFIG 0x81 +#define I2C_WRITE 0x83 +#define I2C_READ 0x84 +#define ARM_TRANSFER 0x85 +#define TUNE_8PSK 0x86 +#define GET_SIGNAL_STRENGTH 0x87 /* in */ +#define LOAD_BCM4500 0x88 +#define BOOT_8PSK 0x89 /* in */ +#define START_INTERSIL 0x8A /* in */ +#define SET_LNB_VOLTAGE 0x8B +#define SET_22KHZ_TONE 0x8C +#define SEND_DISEQC_COMMAND 0x8D +#define SET_DVB_MODE 0x8E +#define SET_DN_SWITCH 0x8F +#define GET_SIGNAL_LOCK 0x90 /* in */ +#define GET_FW_VERS 0x92 +#define GET_SERIAL_NUMBER 0x93 /* in */ +#define USE_EXTRA_VOLT 0x94 +#define GET_FPGA_VERS 0x95 +#define CW3K_INIT 0x9d + +/* PSK_configuration bits */ +#define bm8pskStarted 0x01 +#define bm8pskFW_Loaded 0x02 +#define bmIntersilOn 0x04 +#define bmDVBmode 0x08 +#define bm22kHz 0x10 +#define bmSEL18V 0x20 +#define bmDCtuned 0x40 +#define bmArmed 0x80 + +/* Satellite modulation modes */ +#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ +#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ +#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ +#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ + +#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ +#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ +#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ +#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ +#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ +#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ + +/* firmware revision id's */ +#define GP8PSK_FW_REV1 0x020604 +#define GP8PSK_FW_REV2 0x020704 +#define GP8PSK_FW_VERS(_fw_vers) \ + ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) + +struct gp8psk_fe_ops { + int (*in)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*out)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen); + int (*reload)(void *priv); +}; + +struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, + void *priv, bool is_rev1); + +#endif diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 2a7b5a963acf..3b3f32b426d1 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o dvb-usb-vp702x-objs := vp702x.o vp702x-fe.o obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o -dvb-usb-gp8psk-objs := gp8psk.o gp8psk-fe.o +dvb-usb-gp8psk-objs := gp8psk.o obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o dvb-usb-dtt200u-objs := dtt200u.o dtt200u-fe.o diff --git a/drivers/media/usb/dvb-usb/gp8psk-fe.c b/drivers/media/usb/dvb-usb/gp8psk-fe.c deleted file mode 100644 index db6eb79cde07..000000000000 --- a/drivers/media/usb/dvb-usb/gp8psk-fe.c +++ /dev/null @@ -1,372 +0,0 @@ -/* DVB USB compliant Linux driver for the - * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module - * - * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) - * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) - * - * Thanks to GENPIX for the sample code used to implement this module. - * - * This module is based off the vp7045 and vp702x modules - * - * 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. - * - * see Documentation/dvb/README.dvb-usb for more information - */ -#include "gp8psk.h" - -struct gp8psk_fe_state { - struct dvb_frontend fe; - struct dvb_usb_device *d; - u8 lock; - u16 snr; - unsigned long next_status_check; - unsigned long status_check_interval; -}; - -static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - u8 status; - gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); - return status & bmDCtuned; -} - -static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) -{ - struct gp8psk_fe_state *state = fe->demodulator_priv; - return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); -} - -static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) -{ - u8 buf[6]; - if (time_after(jiffies,st->next_status_check)) { - gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); - gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); - st->snr = (buf[1]) << 8 | buf[0]; - st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; - } - return 0; -} - -static int gp8psk_fe_read_status(struct dvb_frontend *fe, - enum fe_status *status) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - gp8psk_fe_update_status(st); - - if (st->lock) - *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; - else - *status = 0; - - if (*status & FE_HAS_LOCK) - st->status_check_interval = 1000; - else - st->status_check_interval = 100; - return 0; -} - -/* not supported by this Frontend */ -static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) -{ - (void) fe; - *ber = 0; - return 0; -} - -/* not supported by this Frontend */ -static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) -{ - (void) fe; - *unc = 0; - return 0; -} - -static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - gp8psk_fe_update_status(st); - /* snr is reported in dBu*256 */ - *snr = st->snr; - return 0; -} - -static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - gp8psk_fe_update_status(st); - /* snr is reported in dBu*256 */ - /* snr / 38.4 ~= 100% strength */ - /* snr * 17 returns 100% strength as 65535 */ - if (st->snr > 0xf00) - *strength = 0xffff; - else - *strength = (st->snr << 4) + st->snr; /* snr*17 */ - return 0; -} - -static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) -{ - tune->min_delay_ms = 800; - return 0; -} - -static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) -{ - struct gp8psk_fe_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u8 cmd[10]; - u32 freq = c->frequency * 1000; - int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); - - deb_fe("%s()\n", __func__); - - cmd[4] = freq & 0xff; - cmd[5] = (freq >> 8) & 0xff; - cmd[6] = (freq >> 16) & 0xff; - cmd[7] = (freq >> 24) & 0xff; - - /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ - if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) - c->delivery_system = SYS_TURBO; - - switch (c->delivery_system) { - case SYS_DVBS: - if (c->modulation != QPSK) { - deb_fe("%s: unsupported modulation selected (%d)\n", - __func__, c->modulation); - return -EOPNOTSUPP; - } - c->fec_inner = FEC_AUTO; - break; - case SYS_DVBS2: /* kept for backwards compatibility */ - deb_fe("%s: DVB-S2 delivery system selected\n", __func__); - break; - case SYS_TURBO: - deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); - break; - - default: - deb_fe("%s: unsupported delivery system selected (%d)\n", - __func__, c->delivery_system); - return -EOPNOTSUPP; - } - - cmd[0] = c->symbol_rate & 0xff; - cmd[1] = (c->symbol_rate >> 8) & 0xff; - cmd[2] = (c->symbol_rate >> 16) & 0xff; - cmd[3] = (c->symbol_rate >> 24) & 0xff; - switch (c->modulation) { - case QPSK: - if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) - if (gp8psk_tuned_to_DCII(fe)) - gp8psk_bcm4500_reload(state->d); - switch (c->fec_inner) { - case FEC_1_2: - cmd[9] = 0; break; - case FEC_2_3: - cmd[9] = 1; break; - case FEC_3_4: - cmd[9] = 2; break; - case FEC_5_6: - cmd[9] = 3; break; - case FEC_7_8: - cmd[9] = 4; break; - case FEC_AUTO: - cmd[9] = 5; break; - default: - cmd[9] = 5; break; - } - if (c->delivery_system == SYS_TURBO) - cmd[8] = ADV_MOD_TURBO_QPSK; - else - cmd[8] = ADV_MOD_DVB_QPSK; - break; - case PSK_8: /* PSK_8 is for compatibility with DN */ - cmd[8] = ADV_MOD_TURBO_8PSK; - switch (c->fec_inner) { - case FEC_2_3: - cmd[9] = 0; break; - case FEC_3_4: - cmd[9] = 1; break; - case FEC_3_5: - cmd[9] = 2; break; - case FEC_5_6: - cmd[9] = 3; break; - case FEC_8_9: - cmd[9] = 4; break; - default: - cmd[9] = 0; break; - } - break; - case QAM_16: /* QAM_16 is for compatibility with DN */ - cmd[8] = ADV_MOD_TURBO_16QAM; - cmd[9] = 0; - break; - default: /* Unknown modulation */ - deb_fe("%s: unsupported modulation selected (%d)\n", - __func__, c->modulation); - return -EOPNOTSUPP; - } - - if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) - gp8psk_set_tuner_mode(fe, 0); - gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); - - state->lock = 0; - state->next_status_check = jiffies; - state->status_check_interval = 200; - - return 0; -} - -static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd *m) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - - deb_fe("%s\n",__func__); - - if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, - m->msg, m->msg_len)) { - return -EINVAL; - } - return 0; -} - -static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd burst) -{ - struct gp8psk_fe_state *st = fe->demodulator_priv; - u8 cmd; - - deb_fe("%s\n",__func__); - - /* These commands are certainly wrong */ - cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; - - if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, - &cmd, 0)) { - return -EINVAL; - } - return 0; -} - -static int gp8psk_fe_set_tone(struct dvb_frontend *fe, - enum fe_sec_tone_mode tone) -{ - struct gp8psk_fe_state* state = fe->demodulator_priv; - - if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, - (tone == SEC_TONE_ON), 0, NULL, 0)) { - return -EINVAL; - } - return 0; -} - -static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, - enum fe_sec_voltage voltage) -{ - struct gp8psk_fe_state* state = fe->demodulator_priv; - - if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, - voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { - return -EINVAL; - } - return 0; -} - -static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) -{ - struct gp8psk_fe_state* state = fe->demodulator_priv; - return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); -} - -static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) -{ - struct gp8psk_fe_state* state = fe->demodulator_priv; - u8 cmd = sw_cmd & 0x7f; - - if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, - NULL, 0)) { - return -EINVAL; - } - if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), - 0, NULL, 0)) { - return -EINVAL; - } - - return 0; -} - -static void gp8psk_fe_release(struct dvb_frontend* fe) -{ - struct gp8psk_fe_state *state = fe->demodulator_priv; - kfree(state); -} - -static struct dvb_frontend_ops gp8psk_fe_ops; - -struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) -{ - struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); - if (s == NULL) - goto error; - - s->d = d; - memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); - s->fe.demodulator_priv = s; - - goto success; -error: - return NULL; -success: - return &s->fe; -} - - -static struct dvb_frontend_ops gp8psk_fe_ops = { - .delsys = { SYS_DVBS }, - .info = { - .name = "Genpix DVB-S", - .frequency_min = 800000, - .frequency_max = 2250000, - .frequency_stepsize = 100, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .symbol_rate_tolerance = 500, /* ppm */ - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - /* - * FE_CAN_QAM_16 is for compatibility - * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) - */ - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC - }, - - .release = gp8psk_fe_release, - - .init = NULL, - .sleep = NULL, - - .set_frontend = gp8psk_fe_set_frontend, - - .get_tune_settings = gp8psk_fe_get_tune_settings, - - .read_status = gp8psk_fe_read_status, - .read_ber = gp8psk_fe_read_ber, - .read_signal_strength = gp8psk_fe_read_signal_strength, - .read_snr = gp8psk_fe_read_snr, - .read_ucblocks = gp8psk_fe_read_unc_blocks, - - .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, - .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, - .set_tone = gp8psk_fe_set_tone, - .set_voltage = gp8psk_fe_set_voltage, - .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, - .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage -}; diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index 2829e3082d15..993bb7a72985 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -15,6 +15,7 @@ * see Documentation/dvb/README.dvb-usb for more information */ #include "gp8psk.h" +#include "gp8psk-fe.h" /* debug */ static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw"; @@ -28,34 +29,8 @@ struct gp8psk_state { unsigned char data[80]; }; -static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers) -{ - return (gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6)); -} - -static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) -{ - return (gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1)); -} - -static void gp8psk_info(struct dvb_usb_device *d) -{ - u8 fpga_vers, fw_vers[6]; - - if (!gp8psk_get_fw_version(d, fw_vers)) - info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", - fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), - 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); - else - info("failed to get FW version"); - - if (!gp8psk_get_fpga_version(d, &fpga_vers)) - info("FPGA Version = %i", fpga_vers); - else - info("failed to get FPGA version"); -} - -int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +static int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, + u16 index, u8 *b, int blen) { struct gp8psk_state *st = d->priv; int ret = 0,try = 0; @@ -93,7 +68,7 @@ int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 return ret; } -int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, +static int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) { struct gp8psk_state *st = d->priv; @@ -124,6 +99,34 @@ int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, return ret; } + +static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers) +{ + return gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6); +} + +static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) +{ + return gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1); +} + +static void gp8psk_info(struct dvb_usb_device *d) +{ + u8 fpga_vers, fw_vers[6]; + + if (!gp8psk_get_fw_version(d, fw_vers)) + info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", + fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), + 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); + else + info("failed to get FW version"); + + if (!gp8psk_get_fpga_version(d, &fpga_vers)) + info("FPGA Version = %i", fpga_vers); + else + info("failed to get FPGA version"); +} + static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) { int ret; @@ -226,10 +229,13 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) return 0; } -int gp8psk_bcm4500_reload(struct dvb_usb_device *d) +static int gp8psk_bcm4500_reload(struct dvb_usb_device *d) { u8 buf; int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); + + deb_xfer("reloading firmware\n"); + /* Turn off 8psk power */ if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) return -EINVAL; @@ -248,9 +254,47 @@ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return gp8psk_usb_out_op(adap->dev, ARM_TRANSFER, onoff, 0 , NULL, 0); } +/* Callbacks for gp8psk-fe.c */ + +static int gp8psk_fe_in(void *priv, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_usb_in_op(d, req, value, index, b, blen); +} + +static int gp8psk_fe_out(void *priv, u8 req, u16 value, + u16 index, u8 *b, int blen) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_usb_out_op(d, req, value, index, b, blen); +} + +static int gp8psk_fe_reload(void *priv) +{ + struct dvb_usb_device *d = priv; + + return gp8psk_bcm4500_reload(d); +} + +const struct gp8psk_fe_ops gp8psk_fe_ops = { + .in = gp8psk_fe_in, + .out = gp8psk_fe_out, + .reload = gp8psk_fe_reload, +}; + static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev); + struct dvb_usb_device *d = adap->dev; + int id = le16_to_cpu(d->udev->descriptor.idProduct); + int is_rev1; + + is_rev1 = (id == USB_PID_GENPIX_8PSK_REV_1_WARM) ? true : false; + + adap->fe_adap[0].fe = dvb_attach(gp8psk_fe_attach, + &gp8psk_fe_ops, d, is_rev1); return 0; } diff --git a/drivers/media/usb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h index ed32b9da4843..d8975b866dee 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.h +++ b/drivers/media/usb/dvb-usb/gp8psk.h @@ -24,58 +24,6 @@ extern int dvb_usb_gp8psk_debug; #define deb_info(args...) dprintk(dvb_usb_gp8psk_debug,0x01,args) #define deb_xfer(args...) dprintk(dvb_usb_gp8psk_debug,0x02,args) #define deb_rc(args...) dprintk(dvb_usb_gp8psk_debug,0x04,args) -#define deb_fe(args...) dprintk(dvb_usb_gp8psk_debug,0x08,args) - -/* Twinhan Vendor requests */ -#define TH_COMMAND_IN 0xC0 -#define TH_COMMAND_OUT 0xC1 - -/* gp8psk commands */ - -#define GET_8PSK_CONFIG 0x80 /* in */ -#define SET_8PSK_CONFIG 0x81 -#define I2C_WRITE 0x83 -#define I2C_READ 0x84 -#define ARM_TRANSFER 0x85 -#define TUNE_8PSK 0x86 -#define GET_SIGNAL_STRENGTH 0x87 /* in */ -#define LOAD_BCM4500 0x88 -#define BOOT_8PSK 0x89 /* in */ -#define START_INTERSIL 0x8A /* in */ -#define SET_LNB_VOLTAGE 0x8B -#define SET_22KHZ_TONE 0x8C -#define SEND_DISEQC_COMMAND 0x8D -#define SET_DVB_MODE 0x8E -#define SET_DN_SWITCH 0x8F -#define GET_SIGNAL_LOCK 0x90 /* in */ -#define GET_FW_VERS 0x92 -#define GET_SERIAL_NUMBER 0x93 /* in */ -#define USE_EXTRA_VOLT 0x94 -#define GET_FPGA_VERS 0x95 -#define CW3K_INIT 0x9d - -/* PSK_configuration bits */ -#define bm8pskStarted 0x01 -#define bm8pskFW_Loaded 0x02 -#define bmIntersilOn 0x04 -#define bmDVBmode 0x08 -#define bm22kHz 0x10 -#define bmSEL18V 0x20 -#define bmDCtuned 0x40 -#define bmArmed 0x80 - -/* Satellite modulation modes */ -#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ -#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ -#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ -#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ - -#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ -#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ -#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ -#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ -#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ -#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ #define GET_USB_SPEED 0x07 @@ -86,15 +34,4 @@ extern int dvb_usb_gp8psk_debug; #define PRODUCT_STRING_READ 0x0D #define FW_BCD_VERSION_READ 0x14 -/* firmware revision id's */ -#define GP8PSK_FW_REV1 0x020604 -#define GP8PSK_FW_REV2 0x020704 -#define GP8PSK_FW_VERS(_fw_vers) ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) - -extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); -extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); -extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, - u16 index, u8 *b, int blen); -extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); - #endif -- cgit v1.2.3