2007-04-19 18:26:47 +04:00
/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card
*
* Copyright ( C ) 2006 Mario Hlawitschka ( dh1pa @ amsat . org )
* Copyright ( C ) 2006 Marco Gittler ( g . marco @ freenet . de )
*
* 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
*/
2007-11-05 20:07:06 +03:00
# define DVB_USB_LOG_PREFIX "opera"
# include "dvb-usb.h"
2007-04-19 18:26:47 +04:00
# include "stv0299.h"
# define OPERA_READ_MSG 0
# define OPERA_WRITE_MSG 1
# define OPERA_I2C_TUNER 0xd1
# define READ_FX2_REG_REQ 0xba
# define READ_MAC_ADDR 0x08
# define OPERA_WRITE_FX2 0xbb
# define OPERA_TUNER_REQ 0xb1
# define REG_1F_SYMBOLRATE_BYTE0 0x1f
# define REG_20_SYMBOLRATE_BYTE1 0x20
# define REG_21_SYMBOLRATE_BYTE2 0x21
2007-04-24 00:52:58 +04:00
# define ADDR_B600_VOLTAGE_13V (0x02)
# define ADDR_B601_VOLTAGE_18V (0x03)
# define ADDR_B1A6_STREAM_CTRL (0x04)
# define ADDR_B880_READ_REMOTE (0x05)
2007-04-19 18:26:47 +04:00
struct opera1_state {
u32 last_key_pressed ;
} ;
struct opera_rc_keys {
u32 keycode ;
u32 event ;
} ;
2007-11-05 20:07:06 +03:00
static int dvb_usb_opera1_debug ;
2007-04-19 18:26:47 +04:00
module_param_named ( debug , dvb_usb_opera1_debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug ,
" set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able)). "
DVB_USB_DEBUG_STATUS ) ;
static int opera1_xilinx_rw ( struct usb_device * dev , u8 request , u16 value ,
u8 * data , u16 len , int flags )
{
int ret ;
u8 r ;
u8 u8buf [ len ] ;
unsigned int pipe = ( flags = = OPERA_READ_MSG ) ?
usb_rcvctrlpipe ( dev , 0 ) : usb_sndctrlpipe ( dev , 0 ) ;
u8 request_type = ( flags = = OPERA_READ_MSG ) ? USB_DIR_IN : USB_DIR_OUT ;
if ( flags = = OPERA_WRITE_MSG )
memcpy ( u8buf , data , len ) ;
ret =
usb_control_msg ( dev , pipe , request , request_type | USB_TYPE_VENDOR ,
value , 0x0 , u8buf , len , 2000 ) ;
if ( request = = OPERA_TUNER_REQ ) {
if ( usb_control_msg ( dev , usb_rcvctrlpipe ( dev , 0 ) ,
OPERA_TUNER_REQ , USB_DIR_IN | USB_TYPE_VENDOR ,
0x01 , 0x0 , & r , 1 , 2000 ) < 1 | | r ! = 0x08 )
return 0 ;
}
if ( flags = = OPERA_READ_MSG )
memcpy ( data , u8buf , len ) ;
return ret ;
}
/* I2C */
static int opera1_usb_i2c_msgxfer ( struct dvb_usb_device * dev , u16 addr ,
2007-04-24 00:52:58 +04:00
u8 * buf , u16 len )
2007-04-19 18:26:47 +04:00
{
int ret = 0 ;
u8 request ;
u16 value ;
if ( ! dev ) {
info ( " no usb_device " ) ;
return - EINVAL ;
}
if ( mutex_lock_interruptible ( & dev - > usb_mutex ) < 0 )
return - EAGAIN ;
2007-04-24 00:52:58 +04:00
switch ( addr > > 1 ) {
case ADDR_B600_VOLTAGE_13V :
request = 0xb6 ;
value = 0x00 ;
break ;
case ADDR_B601_VOLTAGE_18V :
request = 0xb6 ;
value = 0x01 ;
break ;
case ADDR_B1A6_STREAM_CTRL :
request = 0xb1 ;
value = 0xa6 ;
break ;
case ADDR_B880_READ_REMOTE :
request = 0xb8 ;
value = 0x80 ;
break ;
default :
request = 0xb1 ;
value = addr ;
2007-04-19 18:26:47 +04:00
}
2007-04-24 00:52:58 +04:00
ret = opera1_xilinx_rw ( dev - > udev , request ,
value , buf , len ,
addr & 0x01 ? OPERA_READ_MSG : OPERA_WRITE_MSG ) ;
2007-04-19 18:26:47 +04:00
mutex_unlock ( & dev - > usb_mutex ) ;
return ret ;
}
static int opera1_i2c_xfer ( struct i2c_adapter * adap , struct i2c_msg msg [ ] ,
int num )
{
struct dvb_usb_device * d = i2c_get_adapdata ( adap ) ;
int i = 0 , tmp = 0 ;
if ( ! d )
return - ENODEV ;
if ( mutex_lock_interruptible ( & d - > i2c_mutex ) < 0 )
return - EAGAIN ;
for ( i = 0 ; i < num ; i + + ) {
if ( ( tmp = opera1_usb_i2c_msgxfer ( d ,
2007-04-24 00:52:58 +04:00
( msg [ i ] . addr < < 1 ) | ( msg [ i ] . flags & I2C_M_RD ? 0x01 : 0 ) ,
2007-04-19 18:26:47 +04:00
msg [ i ] . buf ,
2007-04-24 00:52:58 +04:00
msg [ i ] . len
) ! = msg [ i ] . len ) ) {
2007-04-19 18:26:47 +04:00
break ;
}
if ( dvb_usb_opera1_debug & 0x10 )
info ( " sending i2c mesage %d %d " , tmp , msg [ i ] . len ) ;
}
mutex_unlock ( & d - > i2c_mutex ) ;
return num ;
}
static u32 opera1_i2c_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C ;
}
static struct i2c_algorithm opera1_i2c_algo = {
. master_xfer = opera1_i2c_xfer ,
. functionality = opera1_i2c_func ,
} ;
static int opera1_set_voltage ( struct dvb_frontend * fe , fe_sec_voltage_t voltage )
{
static u8 command_13v [ 1 ] = { 0x00 } ;
static u8 command_18v [ 1 ] = { 0x01 } ;
struct i2c_msg msg [ ] = {
2007-04-24 00:52:58 +04:00
{ . addr = ADDR_B600_VOLTAGE_13V , . flags = 0 , . buf = command_13v , . len = 1 } ,
2007-04-19 18:26:47 +04:00
} ;
struct dvb_usb_adapter * udev_adap =
( struct dvb_usb_adapter * ) ( fe - > dvb - > priv ) ;
if ( voltage = = SEC_VOLTAGE_18 ) {
2007-04-24 00:52:58 +04:00
msg [ 0 ] . addr = ADDR_B601_VOLTAGE_18V ;
2007-04-19 18:26:47 +04:00
msg [ 0 ] . buf = command_18v ;
}
i2c_transfer ( & udev_adap - > dev - > i2c_adap , msg , 1 ) ;
return 0 ;
}
static int opera1_stv0299_set_symbol_rate ( struct dvb_frontend * fe , u32 srate ,
u32 ratio )
{
stv0299_writereg ( fe , 0x13 , 0x98 ) ;
stv0299_writereg ( fe , 0x14 , 0x95 ) ;
stv0299_writereg ( fe , REG_1F_SYMBOLRATE_BYTE0 , ( ratio > > 16 ) & 0xff ) ;
stv0299_writereg ( fe , REG_20_SYMBOLRATE_BYTE1 , ( ratio > > 8 ) & 0xff ) ;
stv0299_writereg ( fe , REG_21_SYMBOLRATE_BYTE2 , ( ratio ) & 0xf0 ) ;
return 0 ;
}
static u8 opera1_inittab [ ] = {
0x00 , 0xa1 ,
0x01 , 0x15 ,
0x02 , 0x00 ,
0x03 , 0x00 ,
0x04 , 0x7d ,
0x05 , 0x05 ,
0x06 , 0x02 ,
0x07 , 0x00 ,
0x0b , 0x00 ,
0x0c , 0x01 ,
0x0d , 0x81 ,
0x0e , 0x44 ,
0x0f , 0x19 ,
0x10 , 0x3f ,
0x11 , 0x84 ,
0x12 , 0xda ,
0x13 , 0x98 ,
0x14 , 0x95 ,
0x15 , 0xc9 ,
0x16 , 0xeb ,
0x17 , 0x00 ,
0x18 , 0x19 ,
0x19 , 0x8b ,
0x1a , 0x00 ,
0x1b , 0x82 ,
0x1c , 0x7f ,
0x1d , 0x00 ,
0x1e , 0x00 ,
REG_1F_SYMBOLRATE_BYTE0 , 0x06 ,
REG_20_SYMBOLRATE_BYTE1 , 0x50 ,
REG_21_SYMBOLRATE_BYTE2 , 0x10 ,
0x22 , 0x00 ,
0x23 , 0x00 ,
0x24 , 0x37 ,
0x25 , 0xbc ,
0x26 , 0x00 ,
0x27 , 0x00 ,
0x28 , 0x00 ,
0x29 , 0x1e ,
0x2a , 0x14 ,
0x2b , 0x1f ,
0x2c , 0x09 ,
0x2d , 0x0a ,
0x2e , 0x00 ,
0x2f , 0x00 ,
0x30 , 0x00 ,
0x31 , 0x1f ,
0x32 , 0x19 ,
0x33 , 0xfc ,
0x34 , 0x13 ,
0xff , 0xff ,
} ;
static struct stv0299_config opera1_stv0299_config = {
2007-04-24 00:52:58 +04:00
. demod_address = 0xd0 > > 1 ,
2007-04-19 18:26:47 +04:00
. min_delay_ms = 100 ,
. mclk = 88000000UL ,
. invert = 1 ,
. skip_reinit = 0 ,
. lock_output = STV0229_LOCKOUTPUT_0 ,
. volt13_op0_op1 = STV0299_VOLT13_OP0 ,
. inittab = opera1_inittab ,
. set_symbol_rate = opera1_stv0299_set_symbol_rate ,
} ;
static int opera1_frontend_attach ( struct dvb_usb_adapter * d )
{
if ( ( d - > fe =
dvb_attach ( stv0299_attach , & opera1_stv0299_config ,
& d - > dev - > i2c_adap ) ) ! = NULL ) {
d - > fe - > ops . set_voltage = opera1_set_voltage ;
return 0 ;
}
info ( " not attached stv0299 " ) ;
return - EIO ;
}
static int opera1_tuner_attach ( struct dvb_usb_adapter * adap )
{
2007-04-24 00:52:58 +04:00
dvb_attach (
dvb_pll_attach , adap - > fe , 0xc0 > > 1 ,
2007-06-12 23:10:51 +04:00
& adap - > dev - > i2c_adap , DVB_PLL_OPERA1
2007-04-24 00:52:58 +04:00
) ;
2007-04-19 18:26:47 +04:00
return 0 ;
}
static int opera1_power_ctrl ( struct dvb_usb_device * d , int onoff )
{
u8 val = onoff ? 0x01 : 0x00 ;
2007-04-24 00:52:58 +04:00
2007-04-19 18:26:47 +04:00
if ( dvb_usb_opera1_debug )
info ( " power %s " , onoff ? " on " : " off " ) ;
2007-04-24 00:52:58 +04:00
return opera1_xilinx_rw ( d - > udev , 0xb7 , val ,
& val , 1 , OPERA_WRITE_MSG ) ;
2007-04-19 18:26:47 +04:00
}
static int opera1_streaming_ctrl ( struct dvb_usb_adapter * adap , int onoff )
{
static u8 buf_start [ 2 ] = { 0xff , 0x03 } ;
static u8 buf_stop [ 2 ] = { 0xff , 0x00 } ;
struct i2c_msg start_tuner [ ] = {
2007-04-24 00:52:58 +04:00
{ . addr = ADDR_B1A6_STREAM_CTRL , . buf = onoff ? buf_start : buf_stop , . len = 2 } ,
2007-04-19 18:26:47 +04:00
} ;
if ( dvb_usb_opera1_debug )
info ( " streaming %s " , onoff ? " on " : " off " ) ;
i2c_transfer ( & adap - > dev - > i2c_adap , start_tuner , 1 ) ;
return 0 ;
}
static int opera1_pid_filter ( struct dvb_usb_adapter * adap , int index , u16 pid ,
int onoff )
{
u8 b_pid [ 3 ] ;
struct i2c_msg msg [ ] = {
2007-04-24 00:52:58 +04:00
{ . addr = ADDR_B1A6_STREAM_CTRL , . buf = b_pid , . len = 3 } ,
2007-04-19 18:26:47 +04:00
} ;
if ( dvb_usb_opera1_debug )
info ( " pidfilter index: %d pid: %d %s " , index , pid ,
onoff ? " on " : " off " ) ;
b_pid [ 0 ] = ( 2 * index ) + 4 ;
b_pid [ 1 ] = onoff ? ( pid & 0xff ) : ( 0x00 ) ;
b_pid [ 2 ] = onoff ? ( ( pid > > 8 ) & 0xff ) : ( 0x00 ) ;
i2c_transfer ( & adap - > dev - > i2c_adap , msg , 1 ) ;
return 0 ;
}
static int opera1_pid_filter_control ( struct dvb_usb_adapter * adap , int onoff )
{
int u = 0x04 ;
u8 b_pid [ 3 ] ;
struct i2c_msg msg [ ] = {
2007-04-24 00:52:58 +04:00
{ . addr = ADDR_B1A6_STREAM_CTRL , . buf = b_pid , . len = 3 } ,
2007-04-19 18:26:47 +04:00
} ;
if ( dvb_usb_opera1_debug )
info ( " %s hw-pidfilter " , onoff ? " enable " : " disable " ) ;
for ( ; u < 0x7e ; u + = 2 ) {
b_pid [ 0 ] = u ;
b_pid [ 1 ] = 0 ;
b_pid [ 2 ] = 0x80 ;
i2c_transfer ( & adap - > dev - > i2c_adap , msg , 1 ) ;
}
return 0 ;
}
static struct dvb_usb_rc_key opera1_rc_keys [ ] = {
{ 0x5f , 0xa0 , KEY_1 } ,
{ 0x51 , 0xaf , KEY_2 } ,
{ 0x5d , 0xa2 , KEY_3 } ,
{ 0x41 , 0xbe , KEY_4 } ,
{ 0x0b , 0xf5 , KEY_5 } ,
{ 0x43 , 0xbd , KEY_6 } ,
{ 0x47 , 0xb8 , KEY_7 } ,
{ 0x49 , 0xb6 , KEY_8 } ,
{ 0x05 , 0xfa , KEY_9 } ,
{ 0x45 , 0xba , KEY_0 } ,
{ 0x09 , 0xf6 , KEY_UP } , /*chanup */
{ 0x1b , 0xe5 , KEY_DOWN } , /*chandown */
{ 0x5d , 0xa3 , KEY_LEFT } , /*voldown */
{ 0x5f , 0xa1 , KEY_RIGHT } , /*volup */
{ 0x07 , 0xf8 , KEY_SPACE } , /*tab */
{ 0x1f , 0xe1 , KEY_ENTER } , /*play ok */
{ 0x1b , 0xe4 , KEY_Z } , /*zoom */
{ 0x59 , 0xa6 , KEY_M } , /*mute */
{ 0x5b , 0xa5 , KEY_F } , /*tv/f */
{ 0x19 , 0xe7 , KEY_R } , /*rec */
{ 0x01 , 0xfe , KEY_S } , /*Stop */
{ 0x03 , 0xfd , KEY_P } , /*pause */
{ 0x03 , 0xfc , KEY_W } , /*<- -> */
{ 0x07 , 0xf9 , KEY_C } , /*capture */
{ 0x47 , 0xb9 , KEY_Q } , /*exit */
{ 0x43 , 0xbc , KEY_O } , /*power */
} ;
static int opera1_rc_query ( struct dvb_usb_device * dev , u32 * event , int * state )
{
struct opera1_state * opst = dev - > priv ;
u8 rcbuffer [ 32 ] ;
const u16 startmarker1 = 0x10ed ;
const u16 startmarker2 = 0x11ec ;
struct i2c_msg read_remote [ ] = {
2007-04-24 00:52:58 +04:00
{ . addr = ADDR_B880_READ_REMOTE , . buf = rcbuffer , . flags = I2C_M_RD , . len = 32 } ,
2007-04-19 18:26:47 +04:00
} ;
int i = 0 ;
u32 send_key = 0 ;
if ( i2c_transfer ( & dev - > i2c_adap , read_remote , 1 ) = = 1 ) {
for ( i = 0 ; i < 32 ; i + + ) {
if ( rcbuffer [ i ] )
send_key | = 1 ;
if ( i < 31 )
send_key = send_key < < 1 ;
}
if ( send_key & 0x8000 )
send_key = ( send_key < < 1 ) | ( send_key > > 15 & 0x01 ) ;
if ( send_key = = 0xffff & & opst - > last_key_pressed ! = 0 ) {
* state = REMOTE_KEY_REPEAT ;
* event = opst - > last_key_pressed ;
return 0 ;
}
for ( ; send_key ! = 0 ; ) {
if ( send_key > > 16 = = startmarker2 ) {
break ;
} else if ( send_key > > 16 = = startmarker1 ) {
send_key =
( send_key & 0xfffeffff ) | ( startmarker1 < < 16 ) ;
break ;
} else
send_key > > = 1 ;
}
if ( send_key = = 0 )
return 0 ;
send_key = ( send_key & 0xffff ) | 0x0100 ;
for ( i = 0 ; i < ARRAY_SIZE ( opera1_rc_keys ) ; i + + ) {
if ( ( opera1_rc_keys [ i ] . custom * 256 +
opera1_rc_keys [ i ] . data ) = = ( send_key & 0xffff ) ) {
* state = REMOTE_KEY_PRESSED ;
* event = opera1_rc_keys [ i ] . event ;
opst - > last_key_pressed =
opera1_rc_keys [ i ] . event ;
break ;
}
opst - > last_key_pressed = 0 ;
}
} else
* state = REMOTE_NO_KEY_PRESSED ;
return 0 ;
}
static struct usb_device_id opera1_table [ ] = {
{ USB_DEVICE ( USB_VID_CYPRESS , USB_PID_OPERA1_COLD ) } ,
{ USB_DEVICE ( USB_VID_OPERA1 , USB_PID_OPERA1_WARM ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( usb , opera1_table ) ;
static int opera1_read_mac_address ( struct dvb_usb_device * d , u8 mac [ 6 ] )
{
u8 command [ ] = { READ_MAC_ADDR } ;
opera1_xilinx_rw ( d - > udev , 0xb1 , 0xa0 , command , 1 , OPERA_WRITE_MSG ) ;
opera1_xilinx_rw ( d - > udev , 0xb1 , 0xa1 , mac , 6 , OPERA_READ_MSG ) ;
return 0 ;
}
static int opera1_xilinx_load_firmware ( struct usb_device * dev ,
const char * filename )
{
const struct firmware * fw = NULL ;
u8 * b , * p ;
2007-07-05 02:18:34 +04:00
int ret = 0 , i , fpgasize = 40 ;
2007-04-19 18:26:47 +04:00
u8 testval ;
2007-07-05 02:18:34 +04:00
info ( " start downloading fpga firmware %s " , filename ) ;
2007-04-19 18:26:47 +04:00
if ( ( ret = request_firmware ( & fw , filename , & dev - > dev ) ) ! = 0 ) {
err ( " did not find the firmware file. (%s) "
" Please see linux/Documentation/dvb/ for more details on firmware-problems. " ,
filename ) ;
return ret ;
} else {
p = kmalloc ( fw - > size , GFP_KERNEL ) ;
opera1_xilinx_rw ( dev , 0xbc , 0x00 , & testval , 1 , OPERA_READ_MSG ) ;
if ( p ! = NULL & & testval ! = 0x67 ) {
u8 reset = 0 , fpga_command = 0 ;
memcpy ( p , fw - > data , fw - > size ) ;
/* clear fpga ? */
opera1_xilinx_rw ( dev , 0xbc , 0xaa , & fpga_command , 1 ,
OPERA_WRITE_MSG ) ;
2007-07-05 02:18:34 +04:00
for ( i = 0 ; i < fw - > size ; ) {
if ( ( fw - > size - i ) < fpgasize ) {
fpgasize = fw - > size - i ;
}
2007-04-19 18:26:47 +04:00
b = ( u8 * ) p + i ;
if ( opera1_xilinx_rw
2007-07-05 02:18:34 +04:00
( dev , OPERA_WRITE_FX2 , 0x0 , b , fpgasize ,
OPERA_WRITE_MSG ) ! = fpgasize
2007-04-19 18:26:47 +04:00
) {
err ( " error while transferring firmware " ) ;
ret = - EINVAL ;
break ;
}
2007-07-05 02:18:34 +04:00
i = i + fpgasize ;
2007-04-19 18:26:47 +04:00
}
/* restart the CPU */
if ( ret | | opera1_xilinx_rw
( dev , 0xa0 , 0xe600 , & reset , 1 ,
OPERA_WRITE_MSG ) ! = 1 ) {
err ( " could not restart the USB controller CPU. " ) ;
ret = - EINVAL ;
}
kfree ( p ) ;
}
}
if ( fw ) {
release_firmware ( fw ) ;
}
return ret ;
}
static struct dvb_usb_device_properties opera1_properties = {
. caps = DVB_USB_IS_AN_I2C_ADAPTER ,
. usb_ctrl = CYPRESS_FX2 ,
2007-04-24 00:52:58 +04:00
. firmware = " dvb-usb-opera-01.fw " ,
2007-04-19 18:26:47 +04:00
. size_of_priv = sizeof ( struct opera1_state ) ,
. power_ctrl = opera1_power_ctrl ,
. i2c_algo = & opera1_i2c_algo ,
. rc_key_map = opera1_rc_keys ,
. rc_key_map_size = ARRAY_SIZE ( opera1_rc_keys ) ,
. rc_interval = 200 ,
. rc_query = opera1_rc_query ,
. read_mac_address = opera1_read_mac_address ,
. generic_bulk_ctrl_endpoint = 0x00 ,
/* parameter for the MPEG2-data transfer */
. num_adapters = 1 ,
. adapter = {
{
. frontend_attach = opera1_frontend_attach ,
. streaming_ctrl = opera1_streaming_ctrl ,
. tuner_attach = opera1_tuner_attach ,
. caps =
DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF ,
. pid_filter = opera1_pid_filter ,
. pid_filter_ctrl = opera1_pid_filter_control ,
. pid_filter_count = 252 ,
. stream = {
. type = USB_BULK ,
. count = 10 ,
. endpoint = 0x82 ,
. u = {
. bulk = {
. buffersize = 4096 ,
}
}
} ,
}
} ,
. num_device_descs = 1 ,
. devices = {
{ " Opera1 DVB-S USB2.0 " ,
{ & opera1_table [ 0 ] , NULL } ,
{ & opera1_table [ 1 ] , NULL } ,
} ,
}
} ;
static int opera1_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct usb_device * udev = interface_to_usbdev ( intf ) ;
if ( udev - > descriptor . idProduct = = USB_PID_OPERA1_WARM & &
udev - > descriptor . idVendor = = USB_VID_OPERA1 & &
2007-07-05 02:18:34 +04:00
opera1_xilinx_load_firmware ( udev , " dvb-usb-opera1-fpga-01.fw " ) ! = 0
2007-06-28 17:10:00 +04:00
) {
2007-04-19 18:26:47 +04:00
return - EINVAL ;
}
2007-06-28 17:10:00 +04:00
if ( dvb_usb_device_init ( intf , & opera1_properties , THIS_MODULE , NULL ) ! = 0 )
2007-04-19 18:26:47 +04:00
return - EINVAL ;
return 0 ;
}
static struct usb_driver opera1_driver = {
. name = " opera1 " ,
. probe = opera1_probe ,
. disconnect = dvb_usb_device_exit ,
. id_table = opera1_table ,
} ;
static int __init opera1_module_init ( void )
{
int result = 0 ;
if ( ( result = usb_register ( & opera1_driver ) ) ) {
err ( " usb_register failed. Error number %d " , result ) ;
}
return result ;
}
static void __exit opera1_module_exit ( void )
{
usb_deregister ( & opera1_driver ) ;
}
module_init ( opera1_module_init ) ;
module_exit ( opera1_module_exit ) ;
MODULE_AUTHOR ( " Mario Hlawitschka (c) dh1pa@amsat.org " ) ;
MODULE_AUTHOR ( " Marco Gittler (c) g.marco@freenet.de " ) ;
MODULE_DESCRIPTION ( " Driver for Opera1 DVB-S device " ) ;
MODULE_VERSION ( " 0.1 " ) ;
MODULE_LICENSE ( " GPL " ) ;