Path: blob/master/drivers/media/dvb/b2c2/flexcop-usb.c
15111 views
/*1* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III2* flexcop-usb.c - covers the USB part3* see flexcop.c for copyright information4*/5#define FC_LOG_PREFIX "flexcop_usb"6#include "flexcop-usb.h"7#include "flexcop-common.h"89/* Version information */10#define DRIVER_VERSION "0.1"11#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"12#define DRIVER_AUTHOR "Patrick Boettcher <[email protected]>"1314/* debug */15#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG16#define dprintk(level,args...) \17do { if ((debug & level)) printk(args); } while (0)1819#define debug_dump(b, l, method) do {\20int i; \21for (i = 0; i < l; i++) \22method("%02x ", b[i]); \23method("\n"); \24} while (0)2526#define DEBSTATUS ""27#else28#define dprintk(level, args...)29#define debug_dump(b, l, method)30#define DEBSTATUS " (debugging is not enabled)"31#endif3233static int debug;34module_param(debug, int, 0644);35MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,"36"ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);37#undef DEBSTATUS3839#define deb_info(args...) dprintk(0x01, args)40#define deb_ts(args...) dprintk(0x02, args)41#define deb_ctrl(args...) dprintk(0x04, args)42#define deb_i2c(args...) dprintk(0x08, args)43#define deb_v8(args...) dprintk(0x10, args)4445/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits46* in the IBI address, to make the V8 code simpler.47* PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)48* in general: 0000 0HHH 000L LL0049* IBI ADDRESS FORMAT: RHHH BLLL50*51* where R is the read(1)/write(0) bit, B is the busy bit52* and HHH and LLL are the two sets of three bits from the PCI address.53*/54#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \55(((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))56#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \57(((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))5859/*60* DKT 02022861* - forget about this VENDOR_BUFFER_SIZE, read and write register62* deal with DWORD or 4 bytes, that should be should from now on63* - from now on, we don't support anything older than firm 1.0064* I eliminated the write register as a 2 trip of writing hi word and lo word65* and force this to write only 4 bytes at a time.66* NOTE: this should work with all the firmware from 1.00 and newer67*/68static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)69{70struct flexcop_usb *fc_usb = fc->bus_specific;71u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;72u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;73u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |74(read ? 0x80 : 0);7576int len = usb_control_msg(fc_usb->udev,77read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,78request,79request_type, /* 0xc0 read or 0x40 write */80wAddress,810,82val,83sizeof(u32),84B2C2_WAIT_FOR_OPERATION_RDW * HZ);8586if (len != sizeof(u32)) {87err("error while %s dword from %d (%d).", read ? "reading" :88"writing", wAddress, wRegOffsPCI);89return -EIO;90}91return 0;92}93/*94* DKT 010817 - add support for V8 memory read/write and flash update95*/96static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,97flexcop_usb_request_t req, u8 page, u16 wAddress,98u8 *pbBuffer, u32 buflen)99{100u8 request_type = USB_TYPE_VENDOR;101u16 wIndex;102int nWaitTime, pipe, len;103wIndex = page << 8;104105switch (req) {106case B2C2_USB_READ_V8_MEM:107nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;108request_type |= USB_DIR_IN;109pipe = B2C2_USB_CTRL_PIPE_IN;110break;111case B2C2_USB_WRITE_V8_MEM:112wIndex |= pbBuffer[0];113request_type |= USB_DIR_OUT;114nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;115pipe = B2C2_USB_CTRL_PIPE_OUT;116break;117case B2C2_USB_FLASH_BLOCK:118request_type |= USB_DIR_OUT;119nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;120pipe = B2C2_USB_CTRL_PIPE_OUT;121break;122default:123deb_info("unsupported request for v8_mem_req %x.\n", req);124return -EINVAL;125}126deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,127wAddress, wIndex, buflen);128129len = usb_control_msg(fc_usb->udev, pipe,130req,131request_type,132wAddress,133wIndex,134pbBuffer,135buflen,136nWaitTime * HZ);137138debug_dump(pbBuffer, len, deb_v8);139return len == buflen ? 0 : -EIO;140}141142#define bytes_left_to_read_on_page(paddr,buflen) \143((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \144? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))145146static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,147flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,148u32 addr, int extended, u8 *buf, u32 len)149{150int i,ret = 0;151u16 wMax;152u32 pagechunk = 0;153154switch(req) {155case B2C2_USB_READ_V8_MEM:156wMax = USB_MEM_READ_MAX;157break;158case B2C2_USB_WRITE_V8_MEM:159wMax = USB_MEM_WRITE_MAX;160break;161case B2C2_USB_FLASH_BLOCK:162wMax = USB_FLASH_MAX;163break;164default:165return -EINVAL;166break;167}168for (i = 0; i < len;) {169pagechunk =170wMax < bytes_left_to_read_on_page(addr, len) ?171wMax :172bytes_left_to_read_on_page(addr, len);173deb_info("%x\n",174(addr & V8_MEMORY_PAGE_MASK) |175(V8_MEMORY_EXTENDED*extended));176177ret = flexcop_usb_v8_memory_req(fc_usb, req,178page_start + (addr / V8_MEMORY_PAGE_SIZE),179(addr & V8_MEMORY_PAGE_MASK) |180(V8_MEMORY_EXTENDED*extended),181&buf[i], pagechunk);182183if (ret < 0)184return ret;185addr += pagechunk;186len -= pagechunk;187}188return 0;189}190191static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)192{193return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,194V8_MEMORY_PAGE_FLASH, 0x1f010, 1,195fc->dvb_adapter.proposed_mac, 6);196}197198#if 0199static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,200flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,201u16 buflen, u8 *pvBuffer)202{203u16 wValue;204u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;205int nWaitTime = 2,206pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;207wValue = (func << 8) | extra;208209len = usb_control_msg(fc_usb->udev,pipe,210B2C2_USB_UTILITY,211request_type,212wValue,213wIndex,214pvBuffer,215buflen,216nWaitTime * HZ);217return len == buflen ? 0 : -EIO;218}219#endif220221/* usb i2c stuff */222static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,223flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,224u8 chipaddr, u8 addr, u8 *buf, u8 buflen)225{226struct flexcop_usb *fc_usb = i2c->fc->bus_specific;227u16 wValue, wIndex;228int nWaitTime,pipe,len;229u8 request_type = USB_TYPE_VENDOR;230231switch (func) {232case USB_FUNC_I2C_WRITE:233case USB_FUNC_I2C_MULTIWRITE:234case USB_FUNC_I2C_REPEATWRITE:235/* DKT 020208 - add this to support special case of DiSEqC */236case USB_FUNC_I2C_CHECKWRITE:237pipe = B2C2_USB_CTRL_PIPE_OUT;238nWaitTime = 2;239request_type |= USB_DIR_OUT;240break;241case USB_FUNC_I2C_READ:242case USB_FUNC_I2C_REPEATREAD:243pipe = B2C2_USB_CTRL_PIPE_IN;244nWaitTime = 2;245request_type |= USB_DIR_IN;246break;247default:248deb_info("unsupported function for i2c_req %x\n", func);249return -EINVAL;250}251wValue = (func << 8) | (i2c->port << 4);252wIndex = (chipaddr << 8 ) | addr;253254deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",255func, request_type, req,256wValue & 0xff, wValue >> 8,257wIndex & 0xff, wIndex >> 8);258259len = usb_control_msg(fc_usb->udev,pipe,260req,261request_type,262wValue,263wIndex,264buf,265buflen,266nWaitTime * HZ);267return len == buflen ? 0 : -EREMOTEIO;268}269270/* actual bus specific access functions,271make sure prototype are/will be equal to pci */272static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,273flexcop_ibi_register reg)274{275flexcop_ibi_value val;276val.raw = 0;277flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);278return val;279}280281static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,282flexcop_ibi_register reg, flexcop_ibi_value val)283{284return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);285}286287static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,288flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)289{290if (op == FC_READ)291return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,292USB_FUNC_I2C_READ, chipaddr, addr, buf, len);293else294return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,295USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);296}297298static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,299u8 *buffer, int buffer_length)300{301u8 *b;302int l;303304deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",305fc_usb->tmp_buffer_length, buffer_length);306307if (fc_usb->tmp_buffer_length > 0) {308memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,309buffer_length);310fc_usb->tmp_buffer_length += buffer_length;311b = fc_usb->tmp_buffer;312l = fc_usb->tmp_buffer_length;313} else {314b=buffer;315l=buffer_length;316}317318while (l >= 190) {319if (*b == 0xff) {320switch (*(b+1) & 0x03) {321case 0x01: /* media packet */322if (*(b+2) == 0x47)323flexcop_pass_dmx_packets(324fc_usb->fc_dev, b+2, 1);325else326deb_ts(327"not ts packet %02x %02x %02x %02x \n",328*(b+2), *(b+3),329*(b+4), *(b+5));330b += 190;331l -= 190;332break;333default:334deb_ts("wrong packet type\n");335l = 0;336break;337}338} else {339deb_ts("wrong header\n");340l = 0;341}342}343344if (l>0)345memcpy(fc_usb->tmp_buffer, b, l);346fc_usb->tmp_buffer_length = l;347}348349static void flexcop_usb_urb_complete(struct urb *urb)350{351struct flexcop_usb *fc_usb = urb->context;352int i;353354if (urb->actual_length > 0)355deb_ts("urb completed, bufsize: %d actlen; %d\n",356urb->transfer_buffer_length, urb->actual_length);357358for (i = 0; i < urb->number_of_packets; i++) {359if (urb->iso_frame_desc[i].status < 0) {360err("iso frame descriptor %d has an error: %d\n", i,361urb->iso_frame_desc[i].status);362} else363if (urb->iso_frame_desc[i].actual_length > 0) {364deb_ts("passed %d bytes to the demux\n",365urb->iso_frame_desc[i].actual_length);366367flexcop_usb_process_frame(fc_usb,368urb->transfer_buffer +369urb->iso_frame_desc[i].offset,370urb->iso_frame_desc[i].actual_length);371}372urb->iso_frame_desc[i].status = 0;373urb->iso_frame_desc[i].actual_length = 0;374}375usb_submit_urb(urb,GFP_ATOMIC);376}377378static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)379{380/* submit/kill iso packets */381return 0;382}383384static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)385{386int i;387for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)388if (fc_usb->iso_urb[i] != NULL) {389deb_ts("unlinking/killing urb no. %d\n",i);390usb_kill_urb(fc_usb->iso_urb[i]);391usb_free_urb(fc_usb->iso_urb[i]);392}393394if (fc_usb->iso_buffer != NULL)395pci_free_consistent(NULL,396fc_usb->buffer_size, fc_usb->iso_buffer,397fc_usb->dma_addr);398}399400static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)401{402u16 frame_size = le16_to_cpu(403fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);404int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *405frame_size, i, j, ret;406int buffer_offset = 0;407408deb_ts("creating %d iso-urbs with %d frames "409"each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,410B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);411412fc_usb->iso_buffer = pci_alloc_consistent(NULL,413bufsize, &fc_usb->dma_addr);414if (fc_usb->iso_buffer == NULL)415return -ENOMEM;416417memset(fc_usb->iso_buffer, 0, bufsize);418fc_usb->buffer_size = bufsize;419420/* creating iso urbs */421for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {422fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,423GFP_ATOMIC);424if (fc_usb->iso_urb[i] == NULL) {425ret = -ENOMEM;426goto urb_error;427}428}429430/* initialising and submitting iso urbs */431for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {432int frame_offset = 0;433struct urb *urb = fc_usb->iso_urb[i];434deb_ts("initializing and submitting urb no. %d "435"(buf_offset: %d).\n", i, buffer_offset);436437urb->dev = fc_usb->udev;438urb->context = fc_usb;439urb->complete = flexcop_usb_urb_complete;440urb->pipe = B2C2_USB_DATA_PIPE;441urb->transfer_flags = URB_ISO_ASAP;442urb->interval = 1;443urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;444urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;445urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;446447buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;448for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {449deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",450i, j, frame_offset);451urb->iso_frame_desc[j].offset = frame_offset;452urb->iso_frame_desc[j].length = frame_size;453frame_offset += frame_size;454}455456if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {457err("submitting urb %d failed with %d.", i, ret);458goto urb_error;459}460deb_ts("submitted urb no. %d.\n",i);461}462463/* SRAM */464flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |465FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,466FC_SRAM_DEST_TARGET_WAN_USB);467flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);468flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);469return 0;470471urb_error:472flexcop_usb_transfer_exit(fc_usb);473return ret;474}475476static int flexcop_usb_init(struct flexcop_usb *fc_usb)477{478/* use the alternate setting with the larges buffer */479usb_set_interface(fc_usb->udev,0,1);480switch (fc_usb->udev->speed) {481case USB_SPEED_LOW:482err("cannot handle USB speed because it is too slow.");483return -ENODEV;484break;485case USB_SPEED_FULL:486info("running at FULL speed.");487break;488case USB_SPEED_HIGH:489info("running at HIGH speed.");490break;491case USB_SPEED_UNKNOWN: /* fall through */492default:493err("cannot handle USB speed because it is unknown.");494return -ENODEV;495}496usb_set_intfdata(fc_usb->uintf, fc_usb);497return 0;498}499500static void flexcop_usb_exit(struct flexcop_usb *fc_usb)501{502usb_set_intfdata(fc_usb->uintf, NULL);503}504505static int flexcop_usb_probe(struct usb_interface *intf,506const struct usb_device_id *id)507{508struct usb_device *udev = interface_to_usbdev(intf);509struct flexcop_usb *fc_usb = NULL;510struct flexcop_device *fc = NULL;511int ret;512513if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {514err("out of memory\n");515return -ENOMEM;516}517518/* general flexcop init */519fc_usb = fc->bus_specific;520fc_usb->fc_dev = fc;521522fc->read_ibi_reg = flexcop_usb_read_ibi_reg;523fc->write_ibi_reg = flexcop_usb_write_ibi_reg;524fc->i2c_request = flexcop_usb_i2c_request;525fc->get_mac_addr = flexcop_usb_get_mac_addr;526527fc->stream_control = flexcop_usb_stream_control;528529fc->pid_filtering = 1;530fc->bus_type = FC_USB;531532fc->dev = &udev->dev;533fc->owner = THIS_MODULE;534535/* bus specific part */536fc_usb->udev = udev;537fc_usb->uintf = intf;538if ((ret = flexcop_usb_init(fc_usb)) != 0)539goto err_kfree;540541/* init flexcop */542if ((ret = flexcop_device_initialize(fc)) != 0)543goto err_usb_exit;544545/* xfer init */546if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)547goto err_fc_exit;548549info("%s successfully initialized and connected.", DRIVER_NAME);550return 0;551552err_fc_exit:553flexcop_device_exit(fc);554err_usb_exit:555flexcop_usb_exit(fc_usb);556err_kfree:557flexcop_device_kfree(fc);558return ret;559}560561static void flexcop_usb_disconnect(struct usb_interface *intf)562{563struct flexcop_usb *fc_usb = usb_get_intfdata(intf);564flexcop_usb_transfer_exit(fc_usb);565flexcop_device_exit(fc_usb->fc_dev);566flexcop_usb_exit(fc_usb);567flexcop_device_kfree(fc_usb->fc_dev);568info("%s successfully deinitialized and disconnected.", DRIVER_NAME);569}570571static struct usb_device_id flexcop_usb_table [] = {572{ USB_DEVICE(0x0af7, 0x0101) },573{ }574};575MODULE_DEVICE_TABLE (usb, flexcop_usb_table);576577/* usb specific object needed to register this driver with the usb subsystem */578static struct usb_driver flexcop_usb_driver = {579.name = "b2c2_flexcop_usb",580.probe = flexcop_usb_probe,581.disconnect = flexcop_usb_disconnect,582.id_table = flexcop_usb_table,583};584585/* module stuff */586static int __init flexcop_usb_module_init(void)587{588int result;589if ((result = usb_register(&flexcop_usb_driver))) {590err("usb_register failed. (%d)", result);591return result;592}593return 0;594}595596static void __exit flexcop_usb_module_exit(void)597{598/* deregister this driver from the USB subsystem */599usb_deregister(&flexcop_usb_driver);600}601602module_init(flexcop_usb_module_init);603module_exit(flexcop_usb_module_exit);604605MODULE_AUTHOR(DRIVER_AUTHOR);606MODULE_DESCRIPTION(DRIVER_NAME);607MODULE_LICENSE("GPL");608609610