2019-05-31 11:09:32 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-05-14 20:23:56 +04:00
/* DVB USB compliant Linux driver for the
2007-08-19 00:52:35 +04:00
* - GENPIX 8 pks / qpsk / DCII USB2 .0 DVB - S module
2006-05-14 20:23:56 +04:00
*
2007-08-19 00:52:35 +04:00
* Copyright ( C ) 2006 , 2007 Alan Nisota ( alannisota @ gmail . com )
* Copyright ( C ) 2006 , 2007 Genpix Electronics ( genpix @ genpix - electronics . com )
2006-05-14 20:23:56 +04:00
*
* Thanks to GENPIX for the sample code used to implement this module .
*
* This module is based off the vp7045 and vp702x modules
*
2020-03-04 17:54:10 +03:00
* see Documentation / driver - api / media / drivers / dvb - usb . rst for more information
2006-05-14 20:23:56 +04:00
*/
# include "gp8psk.h"
2016-11-12 17:46:28 +03:00
# include "gp8psk-fe.h"
2006-05-14 20:23:56 +04:00
/* debug */
static char bcm4500_firmware [ ] = " dvb-usb-gp8psk-02.fw " ;
int dvb_usb_gp8psk_debug ;
module_param_named ( debug , dvb_usb_gp8psk_debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " set debugging level (1=info,xfer=2,rc=4 (or-able)). " DVB_USB_DEBUG_STATUS ) ;
2008-04-10 02:13:13 +04:00
DVB_DEFINE_MOD_OPT_ADAPTER_NR ( adapter_nr ) ;
2016-10-07 17:24:21 +03:00
struct gp8psk_state {
unsigned char data [ 80 ] ;
} ;
2016-11-12 17:46:28 +03:00
static int gp8psk_usb_in_op ( struct dvb_usb_device * d , u8 req , u16 value ,
u16 index , u8 * b , int blen )
2006-05-14 20:23:56 +04:00
{
2016-10-07 17:24:21 +03:00
struct gp8psk_state * st = d - > priv ;
2006-05-14 20:23:56 +04:00
int ret = 0 , try = 0 ;
2016-10-07 20:12:48 +03:00
if ( blen > sizeof ( st - > data ) )
return - EIO ;
2006-05-14 20:23:56 +04:00
if ( ( ret = mutex_lock_interruptible ( & d - > usb_mutex ) ) )
return ret ;
while ( ret > = 0 & & ret ! = blen & & try < 3 ) {
ret = usb_control_msg ( d - > udev ,
usb_rcvctrlpipe ( d - > udev , 0 ) ,
req ,
USB_TYPE_VENDOR | USB_DIR_IN ,
2016-10-07 17:24:21 +03:00
value , index , st - > data , blen ,
2006-05-14 20:23:56 +04:00
2000 ) ;
deb_info ( " reading number %d (ret: %d) \n " , try , ret ) ;
try + + ;
}
if ( ret < 0 | | ret ! = blen ) {
2007-08-19 00:52:35 +04:00
warn ( " usb in %d operation failed. " , req ) ;
2006-05-14 20:23:56 +04:00
ret = - EIO ;
2016-11-12 17:46:27 +03:00
} else {
2006-05-14 20:23:56 +04:00
ret = 0 ;
2016-11-12 17:46:27 +03:00
memcpy ( b , st - > data , blen ) ;
}
2006-05-14 20:23:56 +04:00
deb_xfer ( " in: req. %x, val: %x, ind: %x, buffer: " , req , value , index ) ;
debug_dump ( b , blen , deb_xfer ) ;
mutex_unlock ( & d - > usb_mutex ) ;
return ret ;
}
2016-11-12 17:46:28 +03:00
static int gp8psk_usb_out_op ( struct dvb_usb_device * d , u8 req , u16 value ,
2006-05-14 20:23:56 +04:00
u16 index , u8 * b , int blen )
{
2016-10-07 17:24:21 +03:00
struct gp8psk_state * st = d - > priv ;
2006-05-14 20:23:56 +04:00
int ret ;
deb_xfer ( " out: req. %x, val: %x, ind: %x, buffer: " , req , value , index ) ;
debug_dump ( b , blen , deb_xfer ) ;
2016-10-07 20:12:48 +03:00
if ( blen > sizeof ( st - > data ) )
return - EIO ;
2006-05-14 20:23:56 +04:00
if ( ( ret = mutex_lock_interruptible ( & d - > usb_mutex ) ) )
return ret ;
2016-10-07 17:24:21 +03:00
memcpy ( st - > data , b , blen ) ;
2006-05-14 20:23:56 +04:00
if ( usb_control_msg ( d - > udev ,
usb_sndctrlpipe ( d - > udev , 0 ) ,
req ,
USB_TYPE_VENDOR | USB_DIR_OUT ,
2016-10-07 17:24:21 +03:00
value , index , st - > data , blen ,
2006-05-14 20:23:56 +04:00
2000 ) ! = blen ) {
warn ( " usb out operation failed. " ) ;
ret = - EIO ;
} else
ret = 0 ;
mutex_unlock ( & d - > usb_mutex ) ;
return ret ;
}
2016-11-12 17:46:28 +03:00
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 " ) ;
}
2006-05-14 20:23:56 +04:00
static int gp8psk_load_bcm4500fw ( struct dvb_usb_device * d )
{
int ret ;
const struct firmware * fw = NULL ;
2008-05-24 03:13:08 +04:00
const u8 * ptr ;
u8 * buf ;
2006-05-14 20:23:56 +04:00
if ( ( ret = request_firmware ( & fw , bcm4500_firmware ,
& d - > udev - > dev ) ) ! = 0 ) {
2018-05-09 00:10:05 +03:00
err ( " did not find the bcm4500 firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware " ,
2006-05-14 20:23:56 +04:00
bcm4500_firmware , ret ) ;
return ret ;
}
2006-05-14 20:29:48 +04:00
ret = - EINVAL ;
if ( gp8psk_usb_out_op ( d , LOAD_BCM4500 , 1 , 0 , NULL , 0 ) )
goto out_rel_fw ;
2006-05-14 20:23:56 +04:00
2007-08-19 00:52:35 +04:00
info ( " downloading bcm4500 firmware from file '%s' " , bcm4500_firmware ) ;
2006-05-14 20:23:56 +04:00
ptr = fw - > data ;
2018-05-05 19:09:46 +03:00
buf = kmalloc ( 64 , GFP_KERNEL ) ;
2010-04-28 01:11:20 +04:00
if ( ! buf ) {
ret = - ENOMEM ;
goto out_rel_fw ;
}
2006-05-14 20:23:56 +04:00
while ( ptr [ 0 ] ! = 0xff ) {
u16 buflen = ptr [ 0 ] + 4 ;
if ( ptr + buflen > = fw - > data + fw - > size ) {
err ( " failed to load bcm4500 firmware. " ) ;
2006-05-14 20:29:48 +04:00
goto out_free ;
2006-05-14 20:23:56 +04:00
}
2016-10-07 20:12:48 +03:00
if ( buflen > 64 ) {
2016-12-30 17:46:19 +03:00
err ( " firmware chunk size bigger than 64 bytes. " ) ;
2016-10-07 20:12:48 +03:00
goto out_free ;
}
2006-05-14 20:23:56 +04:00
memcpy ( buf , ptr , buflen ) ;
if ( dvb_usb_generic_write ( d , buf , buflen ) ) {
err ( " failed to load bcm4500 firmware. " ) ;
2006-05-14 20:29:48 +04:00
goto out_free ;
2006-05-14 20:23:56 +04:00
}
ptr + = buflen ;
}
2006-05-14 20:29:48 +04:00
ret = 0 ;
out_free :
2006-05-14 20:23:56 +04:00
kfree ( buf ) ;
2006-05-14 20:29:48 +04:00
out_rel_fw :
release_firmware ( fw ) ;
return ret ;
2006-05-14 20:23:56 +04:00
}
static int gp8psk_power_ctrl ( struct dvb_usb_device * d , int onoff )
{
2020-11-27 09:40:21 +03:00
u8 status = 0 , buf ;
2007-08-19 00:52:35 +04:00
int gp_product_id = le16_to_cpu ( d - > udev - > descriptor . idProduct ) ;
2006-05-14 20:23:56 +04:00
if ( onoff ) {
gp8psk_usb_in_op ( d , GET_8PSK_CONFIG , 0 , 0 , & status , 1 ) ;
2007-08-19 00:52:35 +04:00
if ( ! ( status & bm8pskStarted ) ) { /* started */
if ( gp_product_id = = USB_PID_GENPIX_SKYWALKER_CW3K )
gp8psk_usb_out_op ( d , CW3K_INIT , 1 , 0 , NULL , 0 ) ;
2006-05-14 20:23:56 +04:00
if ( gp8psk_usb_in_op ( d , BOOT_8PSK , 1 , 0 , & buf , 1 ) )
return - EINVAL ;
2010-10-30 22:49:49 +04:00
gp8psk_info ( d ) ;
2007-08-19 00:52:35 +04:00
}
2006-05-14 20:23:56 +04:00
2007-08-19 00:52:35 +04:00
if ( gp_product_id = = USB_PID_GENPIX_8PSK_REV_1_WARM )
if ( ! ( status & bm8pskFW_Loaded ) ) /* BCM4500 firmware loaded */
if ( gp8psk_load_bcm4500fw ( d ) )
2008-05-12 02:53:39 +04:00
return - EINVAL ;
2006-05-14 20:23:56 +04:00
2007-08-19 00:52:35 +04:00
if ( ! ( status & bmIntersilOn ) ) /* LNB Power */
2006-05-14 20:23:56 +04:00
if ( gp8psk_usb_in_op ( d , START_INTERSIL , 1 , 0 ,
& buf , 1 ) )
2008-05-12 02:53:39 +04:00
return - EINVAL ;
2006-05-14 20:23:56 +04:00
2007-08-19 00:52:35 +04:00
/* Set DVB mode to 1 */
if ( gp_product_id = = USB_PID_GENPIX_8PSK_REV_1_WARM )
if ( gp8psk_usb_out_op ( d , SET_DVB_MODE , 1 , 0 , NULL , 0 ) )
2008-05-12 02:53:39 +04:00
return - EINVAL ;
2007-08-19 00:52:35 +04:00
/* Abort possible TS (if previous tune crashed) */
if ( gp8psk_usb_out_op ( d , ARM_TRANSFER , 0 , 0 , NULL , 0 ) )
2008-05-12 02:53:39 +04:00
return - EINVAL ;
2006-05-14 20:23:56 +04:00
} else {
/* Turn off LNB power */
if ( gp8psk_usb_in_op ( d , START_INTERSIL , 0 , 0 , & buf , 1 ) )
2008-05-12 02:53:39 +04:00
return - EINVAL ;
2006-05-14 20:23:56 +04:00
/* Turn off 8psk power */
if ( gp8psk_usb_in_op ( d , BOOT_8PSK , 0 , 0 , & buf , 1 ) )
return - EINVAL ;
2007-08-19 00:52:35 +04:00
if ( gp_product_id = = USB_PID_GENPIX_SKYWALKER_CW3K )
gp8psk_usb_out_op ( d , CW3K_INIT , 0 , 0 , NULL , 0 ) ;
2006-05-14 20:23:56 +04:00
}
return 0 ;
}
2016-11-12 17:46:28 +03:00
static int gp8psk_bcm4500_reload ( struct dvb_usb_device * d )
2008-12-20 18:03:06 +03:00
{
u8 buf ;
int gp_product_id = le16_to_cpu ( d - > udev - > descriptor . idProduct ) ;
2016-11-12 17:46:28 +03:00
deb_xfer ( " reloading firmware \n " ) ;
2008-12-20 18:03:06 +03:00
/* Turn off 8psk power */
if ( gp8psk_usb_in_op ( d , BOOT_8PSK , 0 , 0 , & buf , 1 ) )
return - EINVAL ;
/* Turn On 8psk power */
if ( gp8psk_usb_in_op ( d , BOOT_8PSK , 1 , 0 , & buf , 1 ) )
return - EINVAL ;
/* load BCM4500 firmware */
if ( gp_product_id = = USB_PID_GENPIX_8PSK_REV_1_WARM )
if ( gp8psk_load_bcm4500fw ( d ) )
2008-12-30 13:07:53 +03:00
return - EINVAL ;
2008-12-20 18:03:06 +03:00
return 0 ;
}
2006-05-14 20:23:56 +04:00
2006-09-30 13:53:48 +04:00
static int gp8psk_streaming_ctrl ( struct dvb_usb_adapter * adap , int onoff )
2006-05-14 20:23:56 +04:00
{
2006-09-30 13:53:48 +04:00
return gp8psk_usb_out_op ( adap - > dev , ARM_TRANSFER , onoff , 0 , NULL , 0 ) ;
2006-05-14 20:23:56 +04:00
}
2016-11-12 17:46:28 +03:00
/* 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 ) ;
}
2017-01-12 18:32:29 +03:00
static const struct gp8psk_fe_ops gp8psk_fe_ops = {
2016-11-12 17:46:28 +03:00
. in = gp8psk_fe_in ,
. out = gp8psk_fe_out ,
. reload = gp8psk_fe_reload ,
} ;
2006-09-30 13:53:48 +04:00
static int gp8psk_frontend_attach ( struct dvb_usb_adapter * adap )
2006-05-14 20:23:56 +04:00
{
2016-11-12 17:46:28 +03:00
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 ) ;
2006-05-14 20:23:56 +04:00
return 0 ;
}
2006-09-30 13:53:48 +04:00
static struct dvb_usb_device_properties gp8psk_properties ;
2006-05-14 20:23:56 +04:00
static int gp8psk_usb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
2007-08-19 00:52:35 +04:00
int ret ;
struct usb_device * udev = interface_to_usbdev ( intf ) ;
2008-04-10 02:13:13 +04:00
ret = dvb_usb_device_init ( intf , & gp8psk_properties ,
THIS_MODULE , NULL , adapter_nr ) ;
2007-08-19 00:52:35 +04:00
if ( ret = = 0 ) {
info ( " found Genpix USB device pID = %x (hex) " ,
le16_to_cpu ( udev - > descriptor . idProduct ) ) ;
}
return ret ;
2006-05-14 20:23:56 +04:00
}
static struct usb_device_id gp8psk_usb_table [ ] = {
2007-08-19 00:52:35 +04:00
{ USB_DEVICE ( USB_VID_GENPIX , USB_PID_GENPIX_8PSK_REV_1_COLD ) } ,
{ USB_DEVICE ( USB_VID_GENPIX , USB_PID_GENPIX_8PSK_REV_1_WARM ) } ,
{ USB_DEVICE ( USB_VID_GENPIX , USB_PID_GENPIX_8PSK_REV_2 ) } ,
{ USB_DEVICE ( USB_VID_GENPIX , USB_PID_GENPIX_SKYWALKER_1 ) } ,
2010-10-16 21:23:51 +04:00
{ USB_DEVICE ( USB_VID_GENPIX , USB_PID_GENPIX_SKYWALKER_2 ) } ,
2009-05-20 12:52:13 +04:00
/* { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_CW3K) }, */
2006-05-14 20:23:56 +04:00
{ 0 } ,
} ;
MODULE_DEVICE_TABLE ( usb , gp8psk_usb_table ) ;
2006-09-30 13:53:48 +04:00
static struct dvb_usb_device_properties gp8psk_properties = {
2006-05-14 20:23:56 +04:00
. usb_ctrl = CYPRESS_FX2 ,
. firmware = " dvb-usb-gp8psk-01.fw " ,
2016-10-07 17:24:21 +03:00
. size_of_priv = sizeof ( struct gp8psk_state ) ,
2006-09-30 13:53:48 +04:00
. num_adapters = 1 ,
. adapter = {
{
2011-09-06 16:31:57 +04:00
. num_frontends = 1 ,
. fe = { {
2006-10-13 18:34:46 +04:00
. streaming_ctrl = gp8psk_streaming_ctrl ,
. frontend_attach = gp8psk_frontend_attach ,
/* parameter for the MPEG2-data transfer */
2006-09-30 13:53:48 +04:00
. stream = {
. type = USB_BULK ,
2006-10-13 18:34:46 +04:00
. count = 7 ,
. endpoint = 0x82 ,
. u = {
. bulk = {
. buffersize = 8192 ,
}
}
} ,
2011-09-06 16:31:57 +04:00
} } ,
2006-09-30 13:53:48 +04:00
}
} ,
. power_ctrl = gp8psk_power_ctrl ,
. generic_bulk_ctrl_endpoint = 0x01 ,
2006-05-14 20:23:56 +04:00
2010-10-16 21:23:51 +04:00
. num_device_descs = 4 ,
2006-05-14 20:23:56 +04:00
. devices = {
2007-08-19 00:52:35 +04:00
{ . name = " Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver " ,
2006-05-14 20:23:56 +04:00
. cold_ids = { & gp8psk_usb_table [ 0 ] , NULL } ,
. warm_ids = { & gp8psk_usb_table [ 1 ] , NULL } ,
} ,
2007-08-19 00:52:35 +04:00
{ . name = " Genpix 8PSK-to-USB2 Rev.2 DVB-S receiver " ,
. cold_ids = { NULL } ,
. warm_ids = { & gp8psk_usb_table [ 2 ] , NULL } ,
} ,
{ . name = " Genpix SkyWalker-1 DVB-S receiver " ,
. cold_ids = { NULL } ,
. warm_ids = { & gp8psk_usb_table [ 3 ] , NULL } ,
} ,
2010-10-16 21:23:51 +04:00
{ . name = " Genpix SkyWalker-2 DVB-S receiver " ,
. cold_ids = { NULL } ,
. warm_ids = { & gp8psk_usb_table [ 4 ] , NULL } ,
} ,
2006-09-28 21:03:26 +04:00
{ NULL } ,
2006-05-14 20:23:56 +04:00
}
} ;
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gp8psk_usb_driver = {
. name = " dvb_usb_gp8psk " ,
. probe = gp8psk_usb_probe ,
. disconnect = dvb_usb_device_exit ,
. id_table = gp8psk_usb_table ,
} ;
2011-11-18 21:46:12 +04:00
module_usb_driver ( gp8psk_usb_driver ) ;
2006-05-14 20:23:56 +04:00
MODULE_AUTHOR ( " Alan Nisota <alannisota@gamil.com> " ) ;
2010-10-16 23:18:15 +04:00
MODULE_DESCRIPTION ( " Driver for Genpix DVB-S " ) ;
2007-08-19 00:52:35 +04:00
MODULE_VERSION ( " 1.1 " ) ;
2006-05-14 20:23:56 +04:00
MODULE_LICENSE ( " GPL " ) ;