162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III 462306a36Sopenharmony_ci * flexcop-usb.c - covers the USB part 562306a36Sopenharmony_ci * see flexcop.c for copyright information 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define FC_LOG_PREFIX "flexcop_usb" 862306a36Sopenharmony_ci#include "flexcop-usb.h" 962306a36Sopenharmony_ci#include "flexcop-common.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* Version information */ 1262306a36Sopenharmony_ci#define DRIVER_VERSION "0.1" 1362306a36Sopenharmony_ci#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" 1462306a36Sopenharmony_ci#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* debug */ 1762306a36Sopenharmony_ci#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG 1862306a36Sopenharmony_ci#define dprintk(level, args...) \ 1962306a36Sopenharmony_ci do { if ((debug & (level))) printk(args); } while (0) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define debug_dump(b, l, method) do {\ 2262306a36Sopenharmony_ci int i; \ 2362306a36Sopenharmony_ci for (i = 0; i < l; i++) \ 2462306a36Sopenharmony_ci method("%02x ", b[i]); \ 2562306a36Sopenharmony_ci method("\n"); \ 2662306a36Sopenharmony_ci} while (0) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DEBSTATUS "" 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ci#define dprintk(level, args...) no_printk(args) 3162306a36Sopenharmony_ci#define debug_dump(b, l, method) do { } while (0) 3262306a36Sopenharmony_ci#define DEBSTATUS " (debugging is not enabled)" 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int debug; 3662306a36Sopenharmony_cimodule_param(debug, int, 0644); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); 3862306a36Sopenharmony_ci#undef DEBSTATUS 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define deb_info(args...) dprintk(0x01, args) 4162306a36Sopenharmony_ci#define deb_ts(args...) dprintk(0x02, args) 4262306a36Sopenharmony_ci#define deb_ctrl(args...) dprintk(0x04, args) 4362306a36Sopenharmony_ci#define deb_i2c(args...) dprintk(0x08, args) 4462306a36Sopenharmony_ci#define deb_v8(args...) dprintk(0x10, args) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits 4762306a36Sopenharmony_ci * in the IBI address, to make the V8 code simpler. 4862306a36Sopenharmony_ci * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) 4962306a36Sopenharmony_ci * in general: 0000 0HHH 000L LL00 5062306a36Sopenharmony_ci * IBI ADDRESS FORMAT: RHHH BLLL 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * where R is the read(1)/write(0) bit, B is the busy bit 5362306a36Sopenharmony_ci * and HHH and LLL are the two sets of three bits from the PCI address. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ 5662306a36Sopenharmony_ci (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) 5762306a36Sopenharmony_ci#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ 5862306a36Sopenharmony_ci (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * DKT 020228 6262306a36Sopenharmony_ci * - forget about this VENDOR_BUFFER_SIZE, read and write register 6362306a36Sopenharmony_ci * deal with DWORD or 4 bytes, that should be should from now on 6462306a36Sopenharmony_ci * - from now on, we don't support anything older than firm 1.00 6562306a36Sopenharmony_ci * I eliminated the write register as a 2 trip of writing hi word and lo word 6662306a36Sopenharmony_ci * and force this to write only 4 bytes at a time. 6762306a36Sopenharmony_ci * NOTE: this should work with all the firmware from 1.00 and newer 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct flexcop_usb *fc_usb = fc->bus_specific; 7262306a36Sopenharmony_ci u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; 7362306a36Sopenharmony_ci u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; 7462306a36Sopenharmony_ci u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | 7562306a36Sopenharmony_ci (read ? 0x80 : 0); 7662306a36Sopenharmony_ci int ret; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci mutex_lock(&fc_usb->data_mutex); 7962306a36Sopenharmony_ci if (!read) 8062306a36Sopenharmony_ci memcpy(fc_usb->data, val, sizeof(*val)); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = usb_control_msg(fc_usb->udev, 8362306a36Sopenharmony_ci read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, 8462306a36Sopenharmony_ci request, 8562306a36Sopenharmony_ci request_type, /* 0xc0 read or 0x40 write */ 8662306a36Sopenharmony_ci wAddress, 8762306a36Sopenharmony_ci 0, 8862306a36Sopenharmony_ci fc_usb->data, 8962306a36Sopenharmony_ci sizeof(u32), 9062306a36Sopenharmony_ci B2C2_WAIT_FOR_OPERATION_RDW); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (ret != sizeof(u32)) { 9362306a36Sopenharmony_ci err("error while %s dword from %d (%d).", read ? "reading" : 9462306a36Sopenharmony_ci "writing", wAddress, wRegOffsPCI); 9562306a36Sopenharmony_ci if (ret >= 0) 9662306a36Sopenharmony_ci ret = -EIO; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (read && ret >= 0) 10062306a36Sopenharmony_ci memcpy(val, fc_usb->data, sizeof(*val)); 10162306a36Sopenharmony_ci mutex_unlock(&fc_usb->data_mutex); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * DKT 010817 - add support for V8 memory read/write and flash update 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, 10962306a36Sopenharmony_ci flexcop_usb_request_t req, u8 page, u16 wAddress, 11062306a36Sopenharmony_ci u8 *pbBuffer, u32 buflen) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u8 request_type = USB_TYPE_VENDOR; 11362306a36Sopenharmony_ci u16 wIndex; 11462306a36Sopenharmony_ci int nWaitTime, pipe, ret; 11562306a36Sopenharmony_ci wIndex = page << 8; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (buflen > sizeof(fc_usb->data)) { 11862306a36Sopenharmony_ci err("Buffer size bigger than max URB control message\n"); 11962306a36Sopenharmony_ci return -EIO; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci switch (req) { 12362306a36Sopenharmony_ci case B2C2_USB_READ_V8_MEM: 12462306a36Sopenharmony_ci nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; 12562306a36Sopenharmony_ci request_type |= USB_DIR_IN; 12662306a36Sopenharmony_ci pipe = B2C2_USB_CTRL_PIPE_IN; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case B2C2_USB_WRITE_V8_MEM: 12962306a36Sopenharmony_ci wIndex |= pbBuffer[0]; 13062306a36Sopenharmony_ci request_type |= USB_DIR_OUT; 13162306a36Sopenharmony_ci nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; 13262306a36Sopenharmony_ci pipe = B2C2_USB_CTRL_PIPE_OUT; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case B2C2_USB_FLASH_BLOCK: 13562306a36Sopenharmony_ci request_type |= USB_DIR_OUT; 13662306a36Sopenharmony_ci nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; 13762306a36Sopenharmony_ci pipe = B2C2_USB_CTRL_PIPE_OUT; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci default: 14062306a36Sopenharmony_ci deb_info("unsupported request for v8_mem_req %x.\n", req); 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, 14462306a36Sopenharmony_ci wAddress, wIndex, buflen); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci mutex_lock(&fc_usb->data_mutex); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) 14962306a36Sopenharmony_ci memcpy(fc_usb->data, pbBuffer, buflen); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = usb_control_msg(fc_usb->udev, pipe, 15262306a36Sopenharmony_ci req, 15362306a36Sopenharmony_ci request_type, 15462306a36Sopenharmony_ci wAddress, 15562306a36Sopenharmony_ci wIndex, 15662306a36Sopenharmony_ci fc_usb->data, 15762306a36Sopenharmony_ci buflen, 15862306a36Sopenharmony_ci nWaitTime); 15962306a36Sopenharmony_ci if (ret != buflen) 16062306a36Sopenharmony_ci ret = -EIO; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (ret >= 0) { 16362306a36Sopenharmony_ci ret = 0; 16462306a36Sopenharmony_ci if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) 16562306a36Sopenharmony_ci memcpy(pbBuffer, fc_usb->data, buflen); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci mutex_unlock(&fc_usb->data_mutex); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci debug_dump(pbBuffer, ret, deb_v8); 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define bytes_left_to_read_on_page(paddr, buflen) \ 17562306a36Sopenharmony_ci ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ 17662306a36Sopenharmony_ci ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, 17962306a36Sopenharmony_ci flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, 18062306a36Sopenharmony_ci u32 addr, int extended, u8 *buf, u32 len) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int i, ret = 0; 18362306a36Sopenharmony_ci u16 wMax; 18462306a36Sopenharmony_ci u32 pagechunk = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci switch (req) { 18762306a36Sopenharmony_ci case B2C2_USB_READ_V8_MEM: 18862306a36Sopenharmony_ci wMax = USB_MEM_READ_MAX; 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case B2C2_USB_WRITE_V8_MEM: 19162306a36Sopenharmony_ci wMax = USB_MEM_WRITE_MAX; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case B2C2_USB_FLASH_BLOCK: 19462306a36Sopenharmony_ci wMax = USB_FLASH_MAX; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci for (i = 0; i < len;) { 20062306a36Sopenharmony_ci pagechunk = 20162306a36Sopenharmony_ci wMax < bytes_left_to_read_on_page(addr, len) ? 20262306a36Sopenharmony_ci wMax : 20362306a36Sopenharmony_ci bytes_left_to_read_on_page(addr, len); 20462306a36Sopenharmony_ci deb_info("%x\n", 20562306a36Sopenharmony_ci (addr & V8_MEMORY_PAGE_MASK) | 20662306a36Sopenharmony_ci (V8_MEMORY_EXTENDED*extended)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = flexcop_usb_v8_memory_req(fc_usb, req, 20962306a36Sopenharmony_ci page_start + (addr / V8_MEMORY_PAGE_SIZE), 21062306a36Sopenharmony_ci (addr & V8_MEMORY_PAGE_MASK) | 21162306a36Sopenharmony_ci (V8_MEMORY_EXTENDED*extended), 21262306a36Sopenharmony_ci &buf[i], pagechunk); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (ret < 0) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci addr += pagechunk; 21762306a36Sopenharmony_ci len -= pagechunk; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, 22562306a36Sopenharmony_ci V8_MEMORY_PAGE_FLASH, 0x1f010, 1, 22662306a36Sopenharmony_ci fc->dvb_adapter.proposed_mac, 6); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* usb i2c stuff */ 23062306a36Sopenharmony_cistatic int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, 23162306a36Sopenharmony_ci flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, 23262306a36Sopenharmony_ci u8 chipaddr, u8 addr, u8 *buf, u8 buflen) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct flexcop_usb *fc_usb = i2c->fc->bus_specific; 23562306a36Sopenharmony_ci u16 wValue, wIndex; 23662306a36Sopenharmony_ci int nWaitTime, pipe, ret; 23762306a36Sopenharmony_ci u8 request_type = USB_TYPE_VENDOR; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (buflen > sizeof(fc_usb->data)) { 24062306a36Sopenharmony_ci err("Buffer size bigger than max URB control message\n"); 24162306a36Sopenharmony_ci return -EIO; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci switch (func) { 24562306a36Sopenharmony_ci case USB_FUNC_I2C_WRITE: 24662306a36Sopenharmony_ci case USB_FUNC_I2C_MULTIWRITE: 24762306a36Sopenharmony_ci case USB_FUNC_I2C_REPEATWRITE: 24862306a36Sopenharmony_ci /* DKT 020208 - add this to support special case of DiSEqC */ 24962306a36Sopenharmony_ci case USB_FUNC_I2C_CHECKWRITE: 25062306a36Sopenharmony_ci pipe = B2C2_USB_CTRL_PIPE_OUT; 25162306a36Sopenharmony_ci nWaitTime = 2000; 25262306a36Sopenharmony_ci request_type |= USB_DIR_OUT; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case USB_FUNC_I2C_READ: 25562306a36Sopenharmony_ci case USB_FUNC_I2C_REPEATREAD: 25662306a36Sopenharmony_ci pipe = B2C2_USB_CTRL_PIPE_IN; 25762306a36Sopenharmony_ci nWaitTime = 2000; 25862306a36Sopenharmony_ci request_type |= USB_DIR_IN; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci default: 26162306a36Sopenharmony_ci deb_info("unsupported function for i2c_req %x\n", func); 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci wValue = (func << 8) | (i2c->port << 4); 26562306a36Sopenharmony_ci wIndex = (chipaddr << 8 ) | addr; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", 26862306a36Sopenharmony_ci func, request_type, req, 26962306a36Sopenharmony_ci wValue & 0xff, wValue >> 8, 27062306a36Sopenharmony_ci wIndex & 0xff, wIndex >> 8); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci mutex_lock(&fc_usb->data_mutex); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) 27562306a36Sopenharmony_ci memcpy(fc_usb->data, buf, buflen); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = usb_control_msg(fc_usb->udev, pipe, 27862306a36Sopenharmony_ci req, 27962306a36Sopenharmony_ci request_type, 28062306a36Sopenharmony_ci wValue, 28162306a36Sopenharmony_ci wIndex, 28262306a36Sopenharmony_ci fc_usb->data, 28362306a36Sopenharmony_ci buflen, 28462306a36Sopenharmony_ci nWaitTime); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (ret != buflen) 28762306a36Sopenharmony_ci ret = -EIO; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (ret >= 0) { 29062306a36Sopenharmony_ci ret = 0; 29162306a36Sopenharmony_ci if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) 29262306a36Sopenharmony_ci memcpy(buf, fc_usb->data, buflen); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci mutex_unlock(&fc_usb->data_mutex); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* actual bus specific access functions, 30162306a36Sopenharmony_ci make sure prototype are/will be equal to pci */ 30262306a36Sopenharmony_cistatic flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, 30362306a36Sopenharmony_ci flexcop_ibi_register reg) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci flexcop_ibi_value val; 30662306a36Sopenharmony_ci val.raw = 0; 30762306a36Sopenharmony_ci flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); 30862306a36Sopenharmony_ci return val; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, 31262306a36Sopenharmony_ci flexcop_ibi_register reg, flexcop_ibi_value val) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, 31862306a36Sopenharmony_ci flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci if (op == FC_READ) 32162306a36Sopenharmony_ci return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, 32262306a36Sopenharmony_ci USB_FUNC_I2C_READ, chipaddr, addr, buf, len); 32362306a36Sopenharmony_ci else 32462306a36Sopenharmony_ci return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, 32562306a36Sopenharmony_ci USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, 32962306a36Sopenharmony_ci u8 *buffer, int buffer_length) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci u8 *b; 33262306a36Sopenharmony_ci int l; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", 33562306a36Sopenharmony_ci fc_usb->tmp_buffer_length, buffer_length); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (fc_usb->tmp_buffer_length > 0) { 33862306a36Sopenharmony_ci memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, 33962306a36Sopenharmony_ci buffer_length); 34062306a36Sopenharmony_ci fc_usb->tmp_buffer_length += buffer_length; 34162306a36Sopenharmony_ci b = fc_usb->tmp_buffer; 34262306a36Sopenharmony_ci l = fc_usb->tmp_buffer_length; 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci b = buffer; 34562306a36Sopenharmony_ci l = buffer_length; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci while (l >= 190) { 34962306a36Sopenharmony_ci if (*b == 0xff) { 35062306a36Sopenharmony_ci switch (*(b+1) & 0x03) { 35162306a36Sopenharmony_ci case 0x01: /* media packet */ 35262306a36Sopenharmony_ci if (*(b+2) == 0x47) 35362306a36Sopenharmony_ci flexcop_pass_dmx_packets( 35462306a36Sopenharmony_ci fc_usb->fc_dev, b+2, 1); 35562306a36Sopenharmony_ci else 35662306a36Sopenharmony_ci deb_ts("not ts packet %*ph\n", 4, b+2); 35762306a36Sopenharmony_ci b += 190; 35862306a36Sopenharmony_ci l -= 190; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci deb_ts("wrong packet type\n"); 36262306a36Sopenharmony_ci l = 0; 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci deb_ts("wrong header\n"); 36762306a36Sopenharmony_ci l = 0; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (l > 0) 37262306a36Sopenharmony_ci memcpy(fc_usb->tmp_buffer, b, l); 37362306a36Sopenharmony_ci fc_usb->tmp_buffer_length = l; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void flexcop_usb_urb_complete(struct urb *urb) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct flexcop_usb *fc_usb = urb->context; 37962306a36Sopenharmony_ci int i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (urb->actual_length > 0) 38262306a36Sopenharmony_ci deb_ts("urb completed, bufsize: %d actlen; %d\n", 38362306a36Sopenharmony_ci urb->transfer_buffer_length, urb->actual_length); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 38662306a36Sopenharmony_ci if (urb->iso_frame_desc[i].status < 0) { 38762306a36Sopenharmony_ci err("iso frame descriptor %d has an error: %d\n", i, 38862306a36Sopenharmony_ci urb->iso_frame_desc[i].status); 38962306a36Sopenharmony_ci } else 39062306a36Sopenharmony_ci if (urb->iso_frame_desc[i].actual_length > 0) { 39162306a36Sopenharmony_ci deb_ts("passed %d bytes to the demux\n", 39262306a36Sopenharmony_ci urb->iso_frame_desc[i].actual_length); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci flexcop_usb_process_frame(fc_usb, 39562306a36Sopenharmony_ci urb->transfer_buffer + 39662306a36Sopenharmony_ci urb->iso_frame_desc[i].offset, 39762306a36Sopenharmony_ci urb->iso_frame_desc[i].actual_length); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci urb->iso_frame_desc[i].status = 0; 40062306a36Sopenharmony_ci urb->iso_frame_desc[i].actual_length = 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci usb_submit_urb(urb, GFP_ATOMIC); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci /* submit/kill iso packets */ 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci int i; 41462306a36Sopenharmony_ci for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) 41562306a36Sopenharmony_ci if (fc_usb->iso_urb[i] != NULL) { 41662306a36Sopenharmony_ci deb_ts("unlinking/killing urb no. %d\n", i); 41762306a36Sopenharmony_ci usb_kill_urb(fc_usb->iso_urb[i]); 41862306a36Sopenharmony_ci usb_free_urb(fc_usb->iso_urb[i]); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci usb_free_coherent(fc_usb->udev, fc_usb->buffer_size, 42262306a36Sopenharmony_ci fc_usb->iso_buffer, fc_usb->dma_addr); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting; 42962306a36Sopenharmony_ci u16 frame_size; 43062306a36Sopenharmony_ci int bufsize, i, j, ret; 43162306a36Sopenharmony_ci int buffer_offset = 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci frame_size = usb_endpoint_maxp(&alt->endpoint[0].desc); 43462306a36Sopenharmony_ci bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n", 43762306a36Sopenharmony_ci B2C2_USB_NUM_ISO_URB, 43862306a36Sopenharmony_ci B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev, 44162306a36Sopenharmony_ci bufsize, GFP_KERNEL, &fc_usb->dma_addr); 44262306a36Sopenharmony_ci if (fc_usb->iso_buffer == NULL) 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci memset(fc_usb->iso_buffer, 0, bufsize); 44662306a36Sopenharmony_ci fc_usb->buffer_size = bufsize; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* creating iso urbs */ 44962306a36Sopenharmony_ci for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { 45062306a36Sopenharmony_ci fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, 45162306a36Sopenharmony_ci GFP_ATOMIC); 45262306a36Sopenharmony_ci if (fc_usb->iso_urb[i] == NULL) { 45362306a36Sopenharmony_ci ret = -ENOMEM; 45462306a36Sopenharmony_ci goto urb_error; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* initialising and submitting iso urbs */ 45962306a36Sopenharmony_ci for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { 46062306a36Sopenharmony_ci int frame_offset = 0; 46162306a36Sopenharmony_ci struct urb *urb = fc_usb->iso_urb[i]; 46262306a36Sopenharmony_ci deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n", 46362306a36Sopenharmony_ci i, buffer_offset); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci urb->dev = fc_usb->udev; 46662306a36Sopenharmony_ci urb->context = fc_usb; 46762306a36Sopenharmony_ci urb->complete = flexcop_usb_urb_complete; 46862306a36Sopenharmony_ci urb->pipe = B2C2_USB_DATA_PIPE; 46962306a36Sopenharmony_ci urb->transfer_flags = URB_ISO_ASAP; 47062306a36Sopenharmony_ci urb->interval = 1; 47162306a36Sopenharmony_ci urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; 47262306a36Sopenharmony_ci urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; 47362306a36Sopenharmony_ci urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; 47662306a36Sopenharmony_ci for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { 47762306a36Sopenharmony_ci deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", 47862306a36Sopenharmony_ci i, j, frame_offset); 47962306a36Sopenharmony_ci urb->iso_frame_desc[j].offset = frame_offset; 48062306a36Sopenharmony_ci urb->iso_frame_desc[j].length = frame_size; 48162306a36Sopenharmony_ci frame_offset += frame_size; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { 48562306a36Sopenharmony_ci err("submitting urb %d failed with %d.", i, ret); 48662306a36Sopenharmony_ci goto urb_error; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci deb_ts("submitted urb no. %d.\n", i); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* SRAM */ 49262306a36Sopenharmony_ci flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | 49362306a36Sopenharmony_ci FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, 49462306a36Sopenharmony_ci FC_SRAM_DEST_TARGET_WAN_USB); 49562306a36Sopenharmony_ci flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); 49662306a36Sopenharmony_ci flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciurb_error: 50062306a36Sopenharmony_ci flexcop_usb_transfer_exit(fc_usb); 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int flexcop_usb_init(struct flexcop_usb *fc_usb) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct usb_host_interface *alt; 50762306a36Sopenharmony_ci int ret; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* use the alternate setting with the largest buffer */ 51062306a36Sopenharmony_ci ret = usb_set_interface(fc_usb->udev, 0, 1); 51162306a36Sopenharmony_ci if (ret) { 51262306a36Sopenharmony_ci err("set interface failed."); 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci alt = fc_usb->uintf->cur_altsetting; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (alt->desc.bNumEndpoints < 1) 51962306a36Sopenharmony_ci return -ENODEV; 52062306a36Sopenharmony_ci if (!usb_endpoint_is_isoc_in(&alt->endpoint[0].desc)) 52162306a36Sopenharmony_ci return -ENODEV; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci switch (fc_usb->udev->speed) { 52462306a36Sopenharmony_ci case USB_SPEED_LOW: 52562306a36Sopenharmony_ci err("cannot handle USB speed because it is too slow."); 52662306a36Sopenharmony_ci return -ENODEV; 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case USB_SPEED_FULL: 52962306a36Sopenharmony_ci info("running at FULL speed."); 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case USB_SPEED_HIGH: 53262306a36Sopenharmony_ci info("running at HIGH speed."); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci case USB_SPEED_UNKNOWN: 53562306a36Sopenharmony_ci default: 53662306a36Sopenharmony_ci err("cannot handle USB speed because it is unknown."); 53762306a36Sopenharmony_ci return -ENODEV; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci usb_set_intfdata(fc_usb->uintf, fc_usb); 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic void flexcop_usb_exit(struct flexcop_usb *fc_usb) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci usb_set_intfdata(fc_usb->uintf, NULL); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int flexcop_usb_probe(struct usb_interface *intf, 54962306a36Sopenharmony_ci const struct usb_device_id *id) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 55262306a36Sopenharmony_ci struct flexcop_usb *fc_usb = NULL; 55362306a36Sopenharmony_ci struct flexcop_device *fc = NULL; 55462306a36Sopenharmony_ci int ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { 55762306a36Sopenharmony_ci err("out of memory\n"); 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* general flexcop init */ 56262306a36Sopenharmony_ci fc_usb = fc->bus_specific; 56362306a36Sopenharmony_ci fc_usb->fc_dev = fc; 56462306a36Sopenharmony_ci mutex_init(&fc_usb->data_mutex); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci fc->read_ibi_reg = flexcop_usb_read_ibi_reg; 56762306a36Sopenharmony_ci fc->write_ibi_reg = flexcop_usb_write_ibi_reg; 56862306a36Sopenharmony_ci fc->i2c_request = flexcop_usb_i2c_request; 56962306a36Sopenharmony_ci fc->get_mac_addr = flexcop_usb_get_mac_addr; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci fc->stream_control = flexcop_usb_stream_control; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci fc->pid_filtering = 1; 57462306a36Sopenharmony_ci fc->bus_type = FC_USB; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci fc->dev = &udev->dev; 57762306a36Sopenharmony_ci fc->owner = THIS_MODULE; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* bus specific part */ 58062306a36Sopenharmony_ci fc_usb->udev = udev; 58162306a36Sopenharmony_ci fc_usb->uintf = intf; 58262306a36Sopenharmony_ci if ((ret = flexcop_usb_init(fc_usb)) != 0) 58362306a36Sopenharmony_ci goto err_kfree; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* init flexcop */ 58662306a36Sopenharmony_ci if ((ret = flexcop_device_initialize(fc)) != 0) 58762306a36Sopenharmony_ci goto err_usb_exit; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* xfer init */ 59062306a36Sopenharmony_ci if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) 59162306a36Sopenharmony_ci goto err_fc_exit; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci info("%s successfully initialized and connected.", DRIVER_NAME); 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cierr_fc_exit: 59762306a36Sopenharmony_ci flexcop_device_exit(fc); 59862306a36Sopenharmony_cierr_usb_exit: 59962306a36Sopenharmony_ci flexcop_usb_exit(fc_usb); 60062306a36Sopenharmony_cierr_kfree: 60162306a36Sopenharmony_ci flexcop_device_kfree(fc); 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void flexcop_usb_disconnect(struct usb_interface *intf) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct flexcop_usb *fc_usb = usb_get_intfdata(intf); 60862306a36Sopenharmony_ci flexcop_usb_transfer_exit(fc_usb); 60962306a36Sopenharmony_ci flexcop_device_exit(fc_usb->fc_dev); 61062306a36Sopenharmony_ci flexcop_usb_exit(fc_usb); 61162306a36Sopenharmony_ci flexcop_device_kfree(fc_usb->fc_dev); 61262306a36Sopenharmony_ci info("%s successfully deinitialized and disconnected.", DRIVER_NAME); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic const struct usb_device_id flexcop_usb_table[] = { 61662306a36Sopenharmony_ci { USB_DEVICE(0x0af7, 0x0101) }, 61762306a36Sopenharmony_ci { } 61862306a36Sopenharmony_ci}; 61962306a36Sopenharmony_ciMODULE_DEVICE_TABLE (usb, flexcop_usb_table); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 62262306a36Sopenharmony_cistatic struct usb_driver flexcop_usb_driver = { 62362306a36Sopenharmony_ci .name = "b2c2_flexcop_usb", 62462306a36Sopenharmony_ci .probe = flexcop_usb_probe, 62562306a36Sopenharmony_ci .disconnect = flexcop_usb_disconnect, 62662306a36Sopenharmony_ci .id_table = flexcop_usb_table, 62762306a36Sopenharmony_ci}; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cimodule_usb_driver(flexcop_usb_driver); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 63262306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_NAME); 63362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 634