109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23785bc17SMauro Carvalho Chehab /*
33785bc17SMauro Carvalho Chehab * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
43785bc17SMauro Carvalho Chehab * flexcop-usb.c - covers the USB part
53785bc17SMauro Carvalho Chehab * see flexcop.c for copyright information
63785bc17SMauro Carvalho Chehab */
73785bc17SMauro Carvalho Chehab #define FC_LOG_PREFIX "flexcop_usb"
83785bc17SMauro Carvalho Chehab #include "flexcop-usb.h"
93785bc17SMauro Carvalho Chehab #include "flexcop-common.h"
103785bc17SMauro Carvalho Chehab
113785bc17SMauro Carvalho Chehab /* Version information */
123785bc17SMauro Carvalho Chehab #define DRIVER_VERSION "0.1"
133785bc17SMauro Carvalho Chehab #define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
1499e44da7SPatrick Boettcher #define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
153785bc17SMauro Carvalho Chehab
163785bc17SMauro Carvalho Chehab /* debug */
173785bc17SMauro Carvalho Chehab #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
183785bc17SMauro Carvalho Chehab #define dprintk(level, args...) \
19d6bed580SArnd Bergmann do { if ((debug & (level))) printk(args); } while (0)
203785bc17SMauro Carvalho Chehab
213785bc17SMauro Carvalho Chehab #define debug_dump(b, l, method) do {\
223785bc17SMauro Carvalho Chehab int i; \
233785bc17SMauro Carvalho Chehab for (i = 0; i < l; i++) \
243785bc17SMauro Carvalho Chehab method("%02x ", b[i]); \
253785bc17SMauro Carvalho Chehab method("\n"); \
263785bc17SMauro Carvalho Chehab } while (0)
273785bc17SMauro Carvalho Chehab
283785bc17SMauro Carvalho Chehab #define DEBSTATUS ""
293785bc17SMauro Carvalho Chehab #else
30d6bed580SArnd Bergmann #define dprintk(level, args...) no_printk(args)
31d6bed580SArnd Bergmann #define debug_dump(b, l, method) do { } while (0)
323785bc17SMauro Carvalho Chehab #define DEBSTATUS " (debugging is not enabled)"
333785bc17SMauro Carvalho Chehab #endif
343785bc17SMauro Carvalho Chehab
353785bc17SMauro Carvalho Chehab static int debug;
363785bc17SMauro Carvalho Chehab module_param(debug, int, 0644);
374a58d390SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
383785bc17SMauro Carvalho Chehab #undef DEBSTATUS
393785bc17SMauro Carvalho Chehab
403785bc17SMauro Carvalho Chehab #define deb_info(args...) dprintk(0x01, args)
413785bc17SMauro Carvalho Chehab #define deb_ts(args...) dprintk(0x02, args)
423785bc17SMauro Carvalho Chehab #define deb_ctrl(args...) dprintk(0x04, args)
433785bc17SMauro Carvalho Chehab #define deb_i2c(args...) dprintk(0x08, args)
443785bc17SMauro Carvalho Chehab #define deb_v8(args...) dprintk(0x10, args)
453785bc17SMauro Carvalho Chehab
463785bc17SMauro Carvalho Chehab /* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
473785bc17SMauro Carvalho Chehab * in the IBI address, to make the V8 code simpler.
483785bc17SMauro Carvalho Chehab * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
493785bc17SMauro Carvalho Chehab * in general: 0000 0HHH 000L LL00
503785bc17SMauro Carvalho Chehab * IBI ADDRESS FORMAT: RHHH BLLL
513785bc17SMauro Carvalho Chehab *
523785bc17SMauro Carvalho Chehab * where R is the read(1)/write(0) bit, B is the busy bit
533785bc17SMauro Carvalho Chehab * and HHH and LLL are the two sets of three bits from the PCI address.
543785bc17SMauro Carvalho Chehab */
553785bc17SMauro Carvalho Chehab #define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
563785bc17SMauro Carvalho Chehab (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
573785bc17SMauro Carvalho Chehab #define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
583785bc17SMauro Carvalho Chehab (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
593785bc17SMauro Carvalho Chehab
603785bc17SMauro Carvalho Chehab /*
613785bc17SMauro Carvalho Chehab * DKT 020228
623785bc17SMauro Carvalho Chehab * - forget about this VENDOR_BUFFER_SIZE, read and write register
633785bc17SMauro Carvalho Chehab * deal with DWORD or 4 bytes, that should be should from now on
643785bc17SMauro Carvalho Chehab * - from now on, we don't support anything older than firm 1.00
653785bc17SMauro Carvalho Chehab * I eliminated the write register as a 2 trip of writing hi word and lo word
663785bc17SMauro Carvalho Chehab * and force this to write only 4 bytes at a time.
673785bc17SMauro Carvalho Chehab * NOTE: this should work with all the firmware from 1.00 and newer
683785bc17SMauro Carvalho Chehab */
flexcop_usb_readwrite_dw(struct flexcop_device * fc,u16 wRegOffsPCI,u32 * val,u8 read)693785bc17SMauro Carvalho Chehab static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
703785bc17SMauro Carvalho Chehab {
713785bc17SMauro Carvalho Chehab struct flexcop_usb *fc_usb = fc->bus_specific;
723785bc17SMauro Carvalho Chehab u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
733785bc17SMauro Carvalho Chehab u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
743785bc17SMauro Carvalho Chehab u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
753785bc17SMauro Carvalho Chehab (read ? 0x80 : 0);
76b430eabaSMauro Carvalho Chehab int ret;
773785bc17SMauro Carvalho Chehab
78b430eabaSMauro Carvalho Chehab mutex_lock(&fc_usb->data_mutex);
79b430eabaSMauro Carvalho Chehab if (!read)
80b430eabaSMauro Carvalho Chehab memcpy(fc_usb->data, val, sizeof(*val));
81b430eabaSMauro Carvalho Chehab
82b430eabaSMauro Carvalho Chehab ret = usb_control_msg(fc_usb->udev,
833785bc17SMauro Carvalho Chehab read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
843785bc17SMauro Carvalho Chehab request,
853785bc17SMauro Carvalho Chehab request_type, /* 0xc0 read or 0x40 write */
863785bc17SMauro Carvalho Chehab wAddress,
873785bc17SMauro Carvalho Chehab 0,
88b430eabaSMauro Carvalho Chehab fc_usb->data,
893785bc17SMauro Carvalho Chehab sizeof(u32),
90cd1798a3SJohan Hovold B2C2_WAIT_FOR_OPERATION_RDW);
913785bc17SMauro Carvalho Chehab
92b430eabaSMauro Carvalho Chehab if (ret != sizeof(u32)) {
933785bc17SMauro Carvalho Chehab err("error while %s dword from %d (%d).", read ? "reading" :
943785bc17SMauro Carvalho Chehab "writing", wAddress, wRegOffsPCI);
95b430eabaSMauro Carvalho Chehab if (ret >= 0)
96b430eabaSMauro Carvalho Chehab ret = -EIO;
973785bc17SMauro Carvalho Chehab }
98b430eabaSMauro Carvalho Chehab
99b430eabaSMauro Carvalho Chehab if (read && ret >= 0)
100b430eabaSMauro Carvalho Chehab memcpy(val, fc_usb->data, sizeof(*val));
101b430eabaSMauro Carvalho Chehab mutex_unlock(&fc_usb->data_mutex);
102b430eabaSMauro Carvalho Chehab
103b430eabaSMauro Carvalho Chehab return ret;
1043785bc17SMauro Carvalho Chehab }
1053785bc17SMauro Carvalho Chehab /*
1063785bc17SMauro Carvalho Chehab * DKT 010817 - add support for V8 memory read/write and flash update
1073785bc17SMauro Carvalho Chehab */
flexcop_usb_v8_memory_req(struct flexcop_usb * fc_usb,flexcop_usb_request_t req,u8 page,u16 wAddress,u8 * pbBuffer,u32 buflen)1083785bc17SMauro Carvalho Chehab static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
1093785bc17SMauro Carvalho Chehab flexcop_usb_request_t req, u8 page, u16 wAddress,
1103785bc17SMauro Carvalho Chehab u8 *pbBuffer, u32 buflen)
1113785bc17SMauro Carvalho Chehab {
1123785bc17SMauro Carvalho Chehab u8 request_type = USB_TYPE_VENDOR;
1133785bc17SMauro Carvalho Chehab u16 wIndex;
114b430eabaSMauro Carvalho Chehab int nWaitTime, pipe, ret;
1153785bc17SMauro Carvalho Chehab wIndex = page << 8;
1163785bc17SMauro Carvalho Chehab
117b430eabaSMauro Carvalho Chehab if (buflen > sizeof(fc_usb->data)) {
118b430eabaSMauro Carvalho Chehab err("Buffer size bigger than max URB control message\n");
119b430eabaSMauro Carvalho Chehab return -EIO;
120b430eabaSMauro Carvalho Chehab }
121b430eabaSMauro Carvalho Chehab
1223785bc17SMauro Carvalho Chehab switch (req) {
1233785bc17SMauro Carvalho Chehab case B2C2_USB_READ_V8_MEM:
1243785bc17SMauro Carvalho Chehab nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
1253785bc17SMauro Carvalho Chehab request_type |= USB_DIR_IN;
1263785bc17SMauro Carvalho Chehab pipe = B2C2_USB_CTRL_PIPE_IN;
1273785bc17SMauro Carvalho Chehab break;
1283785bc17SMauro Carvalho Chehab case B2C2_USB_WRITE_V8_MEM:
1293785bc17SMauro Carvalho Chehab wIndex |= pbBuffer[0];
1303785bc17SMauro Carvalho Chehab request_type |= USB_DIR_OUT;
1313785bc17SMauro Carvalho Chehab nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
1323785bc17SMauro Carvalho Chehab pipe = B2C2_USB_CTRL_PIPE_OUT;
1333785bc17SMauro Carvalho Chehab break;
1343785bc17SMauro Carvalho Chehab case B2C2_USB_FLASH_BLOCK:
1353785bc17SMauro Carvalho Chehab request_type |= USB_DIR_OUT;
1363785bc17SMauro Carvalho Chehab nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
1373785bc17SMauro Carvalho Chehab pipe = B2C2_USB_CTRL_PIPE_OUT;
1383785bc17SMauro Carvalho Chehab break;
1393785bc17SMauro Carvalho Chehab default:
1403785bc17SMauro Carvalho Chehab deb_info("unsupported request for v8_mem_req %x.\n", req);
1413785bc17SMauro Carvalho Chehab return -EINVAL;
1423785bc17SMauro Carvalho Chehab }
1433785bc17SMauro Carvalho Chehab deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
1443785bc17SMauro Carvalho Chehab wAddress, wIndex, buflen);
1453785bc17SMauro Carvalho Chehab
146b430eabaSMauro Carvalho Chehab mutex_lock(&fc_usb->data_mutex);
147b430eabaSMauro Carvalho Chehab
148b430eabaSMauro Carvalho Chehab if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
149b430eabaSMauro Carvalho Chehab memcpy(fc_usb->data, pbBuffer, buflen);
150b430eabaSMauro Carvalho Chehab
151b430eabaSMauro Carvalho Chehab ret = usb_control_msg(fc_usb->udev, pipe,
1523785bc17SMauro Carvalho Chehab req,
1533785bc17SMauro Carvalho Chehab request_type,
1543785bc17SMauro Carvalho Chehab wAddress,
1553785bc17SMauro Carvalho Chehab wIndex,
156b430eabaSMauro Carvalho Chehab fc_usb->data,
1573785bc17SMauro Carvalho Chehab buflen,
158cd1798a3SJohan Hovold nWaitTime);
159b430eabaSMauro Carvalho Chehab if (ret != buflen)
160b430eabaSMauro Carvalho Chehab ret = -EIO;
1613785bc17SMauro Carvalho Chehab
162b430eabaSMauro Carvalho Chehab if (ret >= 0) {
163b430eabaSMauro Carvalho Chehab ret = 0;
164b430eabaSMauro Carvalho Chehab if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
165b430eabaSMauro Carvalho Chehab memcpy(pbBuffer, fc_usb->data, buflen);
166b430eabaSMauro Carvalho Chehab }
167b430eabaSMauro Carvalho Chehab
168b430eabaSMauro Carvalho Chehab mutex_unlock(&fc_usb->data_mutex);
169b430eabaSMauro Carvalho Chehab
170b430eabaSMauro Carvalho Chehab debug_dump(pbBuffer, ret, deb_v8);
171b430eabaSMauro Carvalho Chehab return ret;
1723785bc17SMauro Carvalho Chehab }
1733785bc17SMauro Carvalho Chehab
1743785bc17SMauro Carvalho Chehab #define bytes_left_to_read_on_page(paddr, buflen) \
1753785bc17SMauro Carvalho Chehab ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
1763785bc17SMauro Carvalho Chehab ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
1773785bc17SMauro Carvalho Chehab
flexcop_usb_memory_req(struct flexcop_usb * fc_usb,flexcop_usb_request_t req,flexcop_usb_mem_page_t page_start,u32 addr,int extended,u8 * buf,u32 len)1783785bc17SMauro Carvalho Chehab static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
1793785bc17SMauro Carvalho Chehab flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
1803785bc17SMauro Carvalho Chehab u32 addr, int extended, u8 *buf, u32 len)
1813785bc17SMauro Carvalho Chehab {
1823785bc17SMauro Carvalho Chehab int i, ret = 0;
1833785bc17SMauro Carvalho Chehab u16 wMax;
1843785bc17SMauro Carvalho Chehab u32 pagechunk = 0;
1853785bc17SMauro Carvalho Chehab
1863785bc17SMauro Carvalho Chehab switch (req) {
1873785bc17SMauro Carvalho Chehab case B2C2_USB_READ_V8_MEM:
1883785bc17SMauro Carvalho Chehab wMax = USB_MEM_READ_MAX;
1893785bc17SMauro Carvalho Chehab break;
1903785bc17SMauro Carvalho Chehab case B2C2_USB_WRITE_V8_MEM:
1913785bc17SMauro Carvalho Chehab wMax = USB_MEM_WRITE_MAX;
1923785bc17SMauro Carvalho Chehab break;
1933785bc17SMauro Carvalho Chehab case B2C2_USB_FLASH_BLOCK:
1943785bc17SMauro Carvalho Chehab wMax = USB_FLASH_MAX;
1953785bc17SMauro Carvalho Chehab break;
1963785bc17SMauro Carvalho Chehab default:
1973785bc17SMauro Carvalho Chehab return -EINVAL;
1983785bc17SMauro Carvalho Chehab }
1993785bc17SMauro Carvalho Chehab for (i = 0; i < len;) {
2003785bc17SMauro Carvalho Chehab pagechunk =
2013785bc17SMauro Carvalho Chehab wMax < bytes_left_to_read_on_page(addr, len) ?
2023785bc17SMauro Carvalho Chehab wMax :
2033785bc17SMauro Carvalho Chehab bytes_left_to_read_on_page(addr, len);
2043785bc17SMauro Carvalho Chehab deb_info("%x\n",
2053785bc17SMauro Carvalho Chehab (addr & V8_MEMORY_PAGE_MASK) |
2063785bc17SMauro Carvalho Chehab (V8_MEMORY_EXTENDED*extended));
2073785bc17SMauro Carvalho Chehab
2083785bc17SMauro Carvalho Chehab ret = flexcop_usb_v8_memory_req(fc_usb, req,
2093785bc17SMauro Carvalho Chehab page_start + (addr / V8_MEMORY_PAGE_SIZE),
2103785bc17SMauro Carvalho Chehab (addr & V8_MEMORY_PAGE_MASK) |
2113785bc17SMauro Carvalho Chehab (V8_MEMORY_EXTENDED*extended),
2123785bc17SMauro Carvalho Chehab &buf[i], pagechunk);
2133785bc17SMauro Carvalho Chehab
2143785bc17SMauro Carvalho Chehab if (ret < 0)
2153785bc17SMauro Carvalho Chehab return ret;
2163785bc17SMauro Carvalho Chehab addr += pagechunk;
2173785bc17SMauro Carvalho Chehab len -= pagechunk;
2183785bc17SMauro Carvalho Chehab }
2193785bc17SMauro Carvalho Chehab return 0;
2203785bc17SMauro Carvalho Chehab }
2213785bc17SMauro Carvalho Chehab
flexcop_usb_get_mac_addr(struct flexcop_device * fc,int extended)2223785bc17SMauro Carvalho Chehab static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
2233785bc17SMauro Carvalho Chehab {
2243785bc17SMauro Carvalho Chehab return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
2253785bc17SMauro Carvalho Chehab V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
2263785bc17SMauro Carvalho Chehab fc->dvb_adapter.proposed_mac, 6);
2273785bc17SMauro Carvalho Chehab }
2283785bc17SMauro Carvalho Chehab
2293785bc17SMauro Carvalho Chehab /* usb i2c stuff */
flexcop_usb_i2c_req(struct flexcop_i2c_adapter * i2c,flexcop_usb_request_t req,flexcop_usb_i2c_function_t func,u8 chipaddr,u8 addr,u8 * buf,u8 buflen)2303785bc17SMauro Carvalho Chehab static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
2313785bc17SMauro Carvalho Chehab flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
2323785bc17SMauro Carvalho Chehab u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
2333785bc17SMauro Carvalho Chehab {
2343785bc17SMauro Carvalho Chehab struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
2353785bc17SMauro Carvalho Chehab u16 wValue, wIndex;
236b430eabaSMauro Carvalho Chehab int nWaitTime, pipe, ret;
2373785bc17SMauro Carvalho Chehab u8 request_type = USB_TYPE_VENDOR;
2383785bc17SMauro Carvalho Chehab
239b430eabaSMauro Carvalho Chehab if (buflen > sizeof(fc_usb->data)) {
240b430eabaSMauro Carvalho Chehab err("Buffer size bigger than max URB control message\n");
241b430eabaSMauro Carvalho Chehab return -EIO;
242b430eabaSMauro Carvalho Chehab }
243b430eabaSMauro Carvalho Chehab
2443785bc17SMauro Carvalho Chehab switch (func) {
2453785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_WRITE:
2463785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_MULTIWRITE:
2473785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_REPEATWRITE:
2483785bc17SMauro Carvalho Chehab /* DKT 020208 - add this to support special case of DiSEqC */
2493785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_CHECKWRITE:
2503785bc17SMauro Carvalho Chehab pipe = B2C2_USB_CTRL_PIPE_OUT;
251cd1798a3SJohan Hovold nWaitTime = 2000;
2523785bc17SMauro Carvalho Chehab request_type |= USB_DIR_OUT;
2533785bc17SMauro Carvalho Chehab break;
2543785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_READ:
2553785bc17SMauro Carvalho Chehab case USB_FUNC_I2C_REPEATREAD:
2563785bc17SMauro Carvalho Chehab pipe = B2C2_USB_CTRL_PIPE_IN;
257cd1798a3SJohan Hovold nWaitTime = 2000;
2583785bc17SMauro Carvalho Chehab request_type |= USB_DIR_IN;
2593785bc17SMauro Carvalho Chehab break;
2603785bc17SMauro Carvalho Chehab default:
2613785bc17SMauro Carvalho Chehab deb_info("unsupported function for i2c_req %x\n", func);
2623785bc17SMauro Carvalho Chehab return -EINVAL;
2633785bc17SMauro Carvalho Chehab }
2643785bc17SMauro Carvalho Chehab wValue = (func << 8) | (i2c->port << 4);
2653785bc17SMauro Carvalho Chehab wIndex = (chipaddr << 8 ) | addr;
2663785bc17SMauro Carvalho Chehab
2673785bc17SMauro Carvalho Chehab deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
2683785bc17SMauro Carvalho Chehab func, request_type, req,
2693785bc17SMauro Carvalho Chehab wValue & 0xff, wValue >> 8,
2703785bc17SMauro Carvalho Chehab wIndex & 0xff, wIndex >> 8);
2713785bc17SMauro Carvalho Chehab
272b430eabaSMauro Carvalho Chehab mutex_lock(&fc_usb->data_mutex);
273b430eabaSMauro Carvalho Chehab
274b430eabaSMauro Carvalho Chehab if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
275b430eabaSMauro Carvalho Chehab memcpy(fc_usb->data, buf, buflen);
276b430eabaSMauro Carvalho Chehab
277b430eabaSMauro Carvalho Chehab ret = usb_control_msg(fc_usb->udev, pipe,
2783785bc17SMauro Carvalho Chehab req,
2793785bc17SMauro Carvalho Chehab request_type,
2803785bc17SMauro Carvalho Chehab wValue,
2813785bc17SMauro Carvalho Chehab wIndex,
282b430eabaSMauro Carvalho Chehab fc_usb->data,
2833785bc17SMauro Carvalho Chehab buflen,
284cd1798a3SJohan Hovold nWaitTime);
285b430eabaSMauro Carvalho Chehab
286b430eabaSMauro Carvalho Chehab if (ret != buflen)
287b430eabaSMauro Carvalho Chehab ret = -EIO;
288b430eabaSMauro Carvalho Chehab
289b430eabaSMauro Carvalho Chehab if (ret >= 0) {
290b430eabaSMauro Carvalho Chehab ret = 0;
291b430eabaSMauro Carvalho Chehab if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
292b430eabaSMauro Carvalho Chehab memcpy(buf, fc_usb->data, buflen);
293b430eabaSMauro Carvalho Chehab }
294b430eabaSMauro Carvalho Chehab
295b430eabaSMauro Carvalho Chehab mutex_unlock(&fc_usb->data_mutex);
296b430eabaSMauro Carvalho Chehab
29774a96b51SColin Ian King return ret;
2983785bc17SMauro Carvalho Chehab }
2993785bc17SMauro Carvalho Chehab
3003785bc17SMauro Carvalho Chehab /* actual bus specific access functions,
3013785bc17SMauro Carvalho Chehab make sure prototype are/will be equal to pci */
flexcop_usb_read_ibi_reg(struct flexcop_device * fc,flexcop_ibi_register reg)3023785bc17SMauro Carvalho Chehab static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
3033785bc17SMauro Carvalho Chehab flexcop_ibi_register reg)
3043785bc17SMauro Carvalho Chehab {
3053785bc17SMauro Carvalho Chehab flexcop_ibi_value val;
3063785bc17SMauro Carvalho Chehab val.raw = 0;
3073785bc17SMauro Carvalho Chehab flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
3083785bc17SMauro Carvalho Chehab return val;
3093785bc17SMauro Carvalho Chehab }
3103785bc17SMauro Carvalho Chehab
flexcop_usb_write_ibi_reg(struct flexcop_device * fc,flexcop_ibi_register reg,flexcop_ibi_value val)3113785bc17SMauro Carvalho Chehab static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
3123785bc17SMauro Carvalho Chehab flexcop_ibi_register reg, flexcop_ibi_value val)
3133785bc17SMauro Carvalho Chehab {
3143785bc17SMauro Carvalho Chehab return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
3153785bc17SMauro Carvalho Chehab }
3163785bc17SMauro Carvalho Chehab
flexcop_usb_i2c_request(struct flexcop_i2c_adapter * i2c,flexcop_access_op_t op,u8 chipaddr,u8 addr,u8 * buf,u16 len)3173785bc17SMauro Carvalho Chehab static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
3183785bc17SMauro Carvalho Chehab flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
3193785bc17SMauro Carvalho Chehab {
3203785bc17SMauro Carvalho Chehab if (op == FC_READ)
3213785bc17SMauro Carvalho Chehab return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3223785bc17SMauro Carvalho Chehab USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
3233785bc17SMauro Carvalho Chehab else
3243785bc17SMauro Carvalho Chehab return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3253785bc17SMauro Carvalho Chehab USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
3263785bc17SMauro Carvalho Chehab }
3273785bc17SMauro Carvalho Chehab
flexcop_usb_process_frame(struct flexcop_usb * fc_usb,u8 * buffer,int buffer_length)3283785bc17SMauro Carvalho Chehab static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
3293785bc17SMauro Carvalho Chehab u8 *buffer, int buffer_length)
3303785bc17SMauro Carvalho Chehab {
3313785bc17SMauro Carvalho Chehab u8 *b;
3323785bc17SMauro Carvalho Chehab int l;
3333785bc17SMauro Carvalho Chehab
3343785bc17SMauro Carvalho Chehab deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
3353785bc17SMauro Carvalho Chehab fc_usb->tmp_buffer_length, buffer_length);
3363785bc17SMauro Carvalho Chehab
3373785bc17SMauro Carvalho Chehab if (fc_usb->tmp_buffer_length > 0) {
3383785bc17SMauro Carvalho Chehab memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
3393785bc17SMauro Carvalho Chehab buffer_length);
3403785bc17SMauro Carvalho Chehab fc_usb->tmp_buffer_length += buffer_length;
3413785bc17SMauro Carvalho Chehab b = fc_usb->tmp_buffer;
3423785bc17SMauro Carvalho Chehab l = fc_usb->tmp_buffer_length;
3433785bc17SMauro Carvalho Chehab } else {
3443785bc17SMauro Carvalho Chehab b = buffer;
3453785bc17SMauro Carvalho Chehab l = buffer_length;
3463785bc17SMauro Carvalho Chehab }
3473785bc17SMauro Carvalho Chehab
3483785bc17SMauro Carvalho Chehab while (l >= 190) {
3493785bc17SMauro Carvalho Chehab if (*b == 0xff) {
3503785bc17SMauro Carvalho Chehab switch (*(b+1) & 0x03) {
3513785bc17SMauro Carvalho Chehab case 0x01: /* media packet */
3523785bc17SMauro Carvalho Chehab if (*(b+2) == 0x47)
3533785bc17SMauro Carvalho Chehab flexcop_pass_dmx_packets(
3543785bc17SMauro Carvalho Chehab fc_usb->fc_dev, b+2, 1);
3553785bc17SMauro Carvalho Chehab else
3563785bc17SMauro Carvalho Chehab deb_ts("not ts packet %*ph\n", 4, b+2);
3573785bc17SMauro Carvalho Chehab b += 190;
3583785bc17SMauro Carvalho Chehab l -= 190;
3593785bc17SMauro Carvalho Chehab break;
3603785bc17SMauro Carvalho Chehab default:
3613785bc17SMauro Carvalho Chehab deb_ts("wrong packet type\n");
3623785bc17SMauro Carvalho Chehab l = 0;
3633785bc17SMauro Carvalho Chehab break;
3643785bc17SMauro Carvalho Chehab }
3653785bc17SMauro Carvalho Chehab } else {
3663785bc17SMauro Carvalho Chehab deb_ts("wrong header\n");
3673785bc17SMauro Carvalho Chehab l = 0;
3683785bc17SMauro Carvalho Chehab }
3693785bc17SMauro Carvalho Chehab }
3703785bc17SMauro Carvalho Chehab
3713785bc17SMauro Carvalho Chehab if (l > 0)
3723785bc17SMauro Carvalho Chehab memcpy(fc_usb->tmp_buffer, b, l);
3733785bc17SMauro Carvalho Chehab fc_usb->tmp_buffer_length = l;
3743785bc17SMauro Carvalho Chehab }
3753785bc17SMauro Carvalho Chehab
flexcop_usb_urb_complete(struct urb * urb)3763785bc17SMauro Carvalho Chehab static void flexcop_usb_urb_complete(struct urb *urb)
3773785bc17SMauro Carvalho Chehab {
3783785bc17SMauro Carvalho Chehab struct flexcop_usb *fc_usb = urb->context;
3793785bc17SMauro Carvalho Chehab int i;
3803785bc17SMauro Carvalho Chehab
3813785bc17SMauro Carvalho Chehab if (urb->actual_length > 0)
3823785bc17SMauro Carvalho Chehab deb_ts("urb completed, bufsize: %d actlen; %d\n",
3833785bc17SMauro Carvalho Chehab urb->transfer_buffer_length, urb->actual_length);
3843785bc17SMauro Carvalho Chehab
3853785bc17SMauro Carvalho Chehab for (i = 0; i < urb->number_of_packets; i++) {
3863785bc17SMauro Carvalho Chehab if (urb->iso_frame_desc[i].status < 0) {
3873785bc17SMauro Carvalho Chehab err("iso frame descriptor %d has an error: %d\n", i,
3883785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].status);
3893785bc17SMauro Carvalho Chehab } else
3903785bc17SMauro Carvalho Chehab if (urb->iso_frame_desc[i].actual_length > 0) {
3913785bc17SMauro Carvalho Chehab deb_ts("passed %d bytes to the demux\n",
3923785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].actual_length);
3933785bc17SMauro Carvalho Chehab
3943785bc17SMauro Carvalho Chehab flexcop_usb_process_frame(fc_usb,
3953785bc17SMauro Carvalho Chehab urb->transfer_buffer +
3963785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].offset,
3973785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].actual_length);
3983785bc17SMauro Carvalho Chehab }
3993785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].status = 0;
4003785bc17SMauro Carvalho Chehab urb->iso_frame_desc[i].actual_length = 0;
4013785bc17SMauro Carvalho Chehab }
4023785bc17SMauro Carvalho Chehab usb_submit_urb(urb, GFP_ATOMIC);
4033785bc17SMauro Carvalho Chehab }
4043785bc17SMauro Carvalho Chehab
flexcop_usb_stream_control(struct flexcop_device * fc,int onoff)4053785bc17SMauro Carvalho Chehab static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
4063785bc17SMauro Carvalho Chehab {
4073785bc17SMauro Carvalho Chehab /* submit/kill iso packets */
4083785bc17SMauro Carvalho Chehab return 0;
4093785bc17SMauro Carvalho Chehab }
4103785bc17SMauro Carvalho Chehab
flexcop_usb_transfer_exit(struct flexcop_usb * fc_usb)4113785bc17SMauro Carvalho Chehab static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
4123785bc17SMauro Carvalho Chehab {
4133785bc17SMauro Carvalho Chehab int i;
4143785bc17SMauro Carvalho Chehab for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
4153785bc17SMauro Carvalho Chehab if (fc_usb->iso_urb[i] != NULL) {
4163785bc17SMauro Carvalho Chehab deb_ts("unlinking/killing urb no. %d\n", i);
4173785bc17SMauro Carvalho Chehab usb_kill_urb(fc_usb->iso_urb[i]);
4183785bc17SMauro Carvalho Chehab usb_free_urb(fc_usb->iso_urb[i]);
4193785bc17SMauro Carvalho Chehab }
4203785bc17SMauro Carvalho Chehab
421cf580e11SQinglang Miao usb_free_coherent(fc_usb->udev, fc_usb->buffer_size,
422cf580e11SQinglang Miao fc_usb->iso_buffer, fc_usb->dma_addr);
423cf580e11SQinglang Miao
4243785bc17SMauro Carvalho Chehab }
4253785bc17SMauro Carvalho Chehab
flexcop_usb_transfer_init(struct flexcop_usb * fc_usb)4263785bc17SMauro Carvalho Chehab static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
4273785bc17SMauro Carvalho Chehab {
428fd449bb9SJohan Hovold struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting;
429fd449bb9SJohan Hovold u16 frame_size;
430fd449bb9SJohan Hovold int bufsize, i, j, ret;
4313785bc17SMauro Carvalho Chehab int buffer_offset = 0;
4323785bc17SMauro Carvalho Chehab
433a8be6b6eSJohan Hovold frame_size = usb_endpoint_maxp(&alt->endpoint[0].desc);
434fd449bb9SJohan Hovold bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size;
435fd449bb9SJohan Hovold
4364a58d390SMauro Carvalho Chehab deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
4374a58d390SMauro Carvalho Chehab B2C2_USB_NUM_ISO_URB,
4383785bc17SMauro Carvalho Chehab B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
4393785bc17SMauro Carvalho Chehab
4406c7e3469SChen Gang fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
4416c7e3469SChen Gang bufsize, GFP_KERNEL, &fc_usb->dma_addr);
4423785bc17SMauro Carvalho Chehab if (fc_usb->iso_buffer == NULL)
4433785bc17SMauro Carvalho Chehab return -ENOMEM;
4443785bc17SMauro Carvalho Chehab
4453785bc17SMauro Carvalho Chehab memset(fc_usb->iso_buffer, 0, bufsize);
4463785bc17SMauro Carvalho Chehab fc_usb->buffer_size = bufsize;
4473785bc17SMauro Carvalho Chehab
4483785bc17SMauro Carvalho Chehab /* creating iso urbs */
4493785bc17SMauro Carvalho Chehab for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4503785bc17SMauro Carvalho Chehab fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
4513785bc17SMauro Carvalho Chehab GFP_ATOMIC);
4523785bc17SMauro Carvalho Chehab if (fc_usb->iso_urb[i] == NULL) {
4533785bc17SMauro Carvalho Chehab ret = -ENOMEM;
4543785bc17SMauro Carvalho Chehab goto urb_error;
4553785bc17SMauro Carvalho Chehab }
4563785bc17SMauro Carvalho Chehab }
4573785bc17SMauro Carvalho Chehab
4583785bc17SMauro Carvalho Chehab /* initialising and submitting iso urbs */
4593785bc17SMauro Carvalho Chehab for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4603785bc17SMauro Carvalho Chehab int frame_offset = 0;
4613785bc17SMauro Carvalho Chehab struct urb *urb = fc_usb->iso_urb[i];
4624a58d390SMauro Carvalho Chehab deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",
4634a58d390SMauro Carvalho Chehab i, buffer_offset);
4643785bc17SMauro Carvalho Chehab
4653785bc17SMauro Carvalho Chehab urb->dev = fc_usb->udev;
4663785bc17SMauro Carvalho Chehab urb->context = fc_usb;
4673785bc17SMauro Carvalho Chehab urb->complete = flexcop_usb_urb_complete;
4683785bc17SMauro Carvalho Chehab urb->pipe = B2C2_USB_DATA_PIPE;
4693785bc17SMauro Carvalho Chehab urb->transfer_flags = URB_ISO_ASAP;
4703785bc17SMauro Carvalho Chehab urb->interval = 1;
4713785bc17SMauro Carvalho Chehab urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
4723785bc17SMauro Carvalho Chehab urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
4733785bc17SMauro Carvalho Chehab urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
4743785bc17SMauro Carvalho Chehab
4753785bc17SMauro Carvalho Chehab buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
4763785bc17SMauro Carvalho Chehab for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
4773785bc17SMauro Carvalho Chehab deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
4783785bc17SMauro Carvalho Chehab i, j, frame_offset);
4793785bc17SMauro Carvalho Chehab urb->iso_frame_desc[j].offset = frame_offset;
4803785bc17SMauro Carvalho Chehab urb->iso_frame_desc[j].length = frame_size;
4813785bc17SMauro Carvalho Chehab frame_offset += frame_size;
4823785bc17SMauro Carvalho Chehab }
4833785bc17SMauro Carvalho Chehab
4843785bc17SMauro Carvalho Chehab if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
4853785bc17SMauro Carvalho Chehab err("submitting urb %d failed with %d.", i, ret);
4863785bc17SMauro Carvalho Chehab goto urb_error;
4873785bc17SMauro Carvalho Chehab }
4883785bc17SMauro Carvalho Chehab deb_ts("submitted urb no. %d.\n", i);
4893785bc17SMauro Carvalho Chehab }
4903785bc17SMauro Carvalho Chehab
4913785bc17SMauro Carvalho Chehab /* SRAM */
4923785bc17SMauro Carvalho Chehab flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
4933785bc17SMauro Carvalho Chehab FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
4943785bc17SMauro Carvalho Chehab FC_SRAM_DEST_TARGET_WAN_USB);
4953785bc17SMauro Carvalho Chehab flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
4963785bc17SMauro Carvalho Chehab flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
4973785bc17SMauro Carvalho Chehab return 0;
4983785bc17SMauro Carvalho Chehab
4993785bc17SMauro Carvalho Chehab urb_error:
5003785bc17SMauro Carvalho Chehab flexcop_usb_transfer_exit(fc_usb);
5013785bc17SMauro Carvalho Chehab return ret;
5023785bc17SMauro Carvalho Chehab }
5033785bc17SMauro Carvalho Chehab
flexcop_usb_init(struct flexcop_usb * fc_usb)5043785bc17SMauro Carvalho Chehab static int flexcop_usb_init(struct flexcop_usb *fc_usb)
5053785bc17SMauro Carvalho Chehab {
5063de50478SJohan Hovold struct usb_host_interface *alt;
5073de50478SJohan Hovold int ret;
508649cd16cSYang Yingliang
5093de50478SJohan Hovold /* use the alternate setting with the largest buffer */
5103de50478SJohan Hovold ret = usb_set_interface(fc_usb->udev, 0, 1);
511649cd16cSYang Yingliang if (ret) {
512649cd16cSYang Yingliang err("set interface failed.");
513649cd16cSYang Yingliang return ret;
514649cd16cSYang Yingliang }
515649cd16cSYang Yingliang
5163de50478SJohan Hovold alt = fc_usb->uintf->cur_altsetting;
5173de50478SJohan Hovold
518*3d50e4ceSDongliang Mu if (alt->desc.bNumEndpoints < 2)
519bca243b1SJohan Hovold return -ENODEV;
5203de50478SJohan Hovold if (!usb_endpoint_is_isoc_in(&alt->endpoint[0].desc))
521d725d20eSOliver Neukum return -ENODEV;
522bca243b1SJohan Hovold
5233785bc17SMauro Carvalho Chehab switch (fc_usb->udev->speed) {
5243785bc17SMauro Carvalho Chehab case USB_SPEED_LOW:
5253785bc17SMauro Carvalho Chehab err("cannot handle USB speed because it is too slow.");
5263785bc17SMauro Carvalho Chehab return -ENODEV;
5273785bc17SMauro Carvalho Chehab break;
5283785bc17SMauro Carvalho Chehab case USB_SPEED_FULL:
5293785bc17SMauro Carvalho Chehab info("running at FULL speed.");
5303785bc17SMauro Carvalho Chehab break;
5313785bc17SMauro Carvalho Chehab case USB_SPEED_HIGH:
5323785bc17SMauro Carvalho Chehab info("running at HIGH speed.");
5333785bc17SMauro Carvalho Chehab break;
534df561f66SGustavo A. R. Silva case USB_SPEED_UNKNOWN:
5353785bc17SMauro Carvalho Chehab default:
5363785bc17SMauro Carvalho Chehab err("cannot handle USB speed because it is unknown.");
5373785bc17SMauro Carvalho Chehab return -ENODEV;
5383785bc17SMauro Carvalho Chehab }
5393785bc17SMauro Carvalho Chehab usb_set_intfdata(fc_usb->uintf, fc_usb);
5403785bc17SMauro Carvalho Chehab return 0;
5413785bc17SMauro Carvalho Chehab }
5423785bc17SMauro Carvalho Chehab
flexcop_usb_exit(struct flexcop_usb * fc_usb)5433785bc17SMauro Carvalho Chehab static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
5443785bc17SMauro Carvalho Chehab {
5453785bc17SMauro Carvalho Chehab usb_set_intfdata(fc_usb->uintf, NULL);
5463785bc17SMauro Carvalho Chehab }
5473785bc17SMauro Carvalho Chehab
flexcop_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)5483785bc17SMauro Carvalho Chehab static int flexcop_usb_probe(struct usb_interface *intf,
5493785bc17SMauro Carvalho Chehab const struct usb_device_id *id)
5503785bc17SMauro Carvalho Chehab {
5513785bc17SMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf);
5523785bc17SMauro Carvalho Chehab struct flexcop_usb *fc_usb = NULL;
5533785bc17SMauro Carvalho Chehab struct flexcop_device *fc = NULL;
5543785bc17SMauro Carvalho Chehab int ret;
5553785bc17SMauro Carvalho Chehab
5563785bc17SMauro Carvalho Chehab if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
5573785bc17SMauro Carvalho Chehab err("out of memory\n");
5583785bc17SMauro Carvalho Chehab return -ENOMEM;
5593785bc17SMauro Carvalho Chehab }
5603785bc17SMauro Carvalho Chehab
5613785bc17SMauro Carvalho Chehab /* general flexcop init */
5623785bc17SMauro Carvalho Chehab fc_usb = fc->bus_specific;
5633785bc17SMauro Carvalho Chehab fc_usb->fc_dev = fc;
564b430eabaSMauro Carvalho Chehab mutex_init(&fc_usb->data_mutex);
5653785bc17SMauro Carvalho Chehab
5663785bc17SMauro Carvalho Chehab fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
5673785bc17SMauro Carvalho Chehab fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
5683785bc17SMauro Carvalho Chehab fc->i2c_request = flexcop_usb_i2c_request;
5693785bc17SMauro Carvalho Chehab fc->get_mac_addr = flexcop_usb_get_mac_addr;
5703785bc17SMauro Carvalho Chehab
5713785bc17SMauro Carvalho Chehab fc->stream_control = flexcop_usb_stream_control;
5723785bc17SMauro Carvalho Chehab
5733785bc17SMauro Carvalho Chehab fc->pid_filtering = 1;
5743785bc17SMauro Carvalho Chehab fc->bus_type = FC_USB;
5753785bc17SMauro Carvalho Chehab
5763785bc17SMauro Carvalho Chehab fc->dev = &udev->dev;
5773785bc17SMauro Carvalho Chehab fc->owner = THIS_MODULE;
5783785bc17SMauro Carvalho Chehab
5793785bc17SMauro Carvalho Chehab /* bus specific part */
5803785bc17SMauro Carvalho Chehab fc_usb->udev = udev;
5813785bc17SMauro Carvalho Chehab fc_usb->uintf = intf;
5823785bc17SMauro Carvalho Chehab if ((ret = flexcop_usb_init(fc_usb)) != 0)
5833785bc17SMauro Carvalho Chehab goto err_kfree;
5843785bc17SMauro Carvalho Chehab
5853785bc17SMauro Carvalho Chehab /* init flexcop */
5863785bc17SMauro Carvalho Chehab if ((ret = flexcop_device_initialize(fc)) != 0)
5873785bc17SMauro Carvalho Chehab goto err_usb_exit;
5883785bc17SMauro Carvalho Chehab
5893785bc17SMauro Carvalho Chehab /* xfer init */
5903785bc17SMauro Carvalho Chehab if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
5913785bc17SMauro Carvalho Chehab goto err_fc_exit;
5923785bc17SMauro Carvalho Chehab
5933785bc17SMauro Carvalho Chehab info("%s successfully initialized and connected.", DRIVER_NAME);
5943785bc17SMauro Carvalho Chehab return 0;
5953785bc17SMauro Carvalho Chehab
5963785bc17SMauro Carvalho Chehab err_fc_exit:
5973785bc17SMauro Carvalho Chehab flexcop_device_exit(fc);
5983785bc17SMauro Carvalho Chehab err_usb_exit:
5993785bc17SMauro Carvalho Chehab flexcop_usb_exit(fc_usb);
6003785bc17SMauro Carvalho Chehab err_kfree:
6013785bc17SMauro Carvalho Chehab flexcop_device_kfree(fc);
6023785bc17SMauro Carvalho Chehab return ret;
6033785bc17SMauro Carvalho Chehab }
6043785bc17SMauro Carvalho Chehab
flexcop_usb_disconnect(struct usb_interface * intf)6053785bc17SMauro Carvalho Chehab static void flexcop_usb_disconnect(struct usb_interface *intf)
6063785bc17SMauro Carvalho Chehab {
6073785bc17SMauro Carvalho Chehab struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
6083785bc17SMauro Carvalho Chehab flexcop_usb_transfer_exit(fc_usb);
6093785bc17SMauro Carvalho Chehab flexcop_device_exit(fc_usb->fc_dev);
6103785bc17SMauro Carvalho Chehab flexcop_usb_exit(fc_usb);
6113785bc17SMauro Carvalho Chehab flexcop_device_kfree(fc_usb->fc_dev);
6123785bc17SMauro Carvalho Chehab info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
6133785bc17SMauro Carvalho Chehab }
6143785bc17SMauro Carvalho Chehab
6157fb2e072SArvind Yadav static const struct usb_device_id flexcop_usb_table[] = {
6163785bc17SMauro Carvalho Chehab { USB_DEVICE(0x0af7, 0x0101) },
6173785bc17SMauro Carvalho Chehab { }
6183785bc17SMauro Carvalho Chehab };
6193785bc17SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
6203785bc17SMauro Carvalho Chehab
6213785bc17SMauro Carvalho Chehab /* usb specific object needed to register this driver with the usb subsystem */
6223785bc17SMauro Carvalho Chehab static struct usb_driver flexcop_usb_driver = {
6233785bc17SMauro Carvalho Chehab .name = "b2c2_flexcop_usb",
6243785bc17SMauro Carvalho Chehab .probe = flexcop_usb_probe,
6253785bc17SMauro Carvalho Chehab .disconnect = flexcop_usb_disconnect,
6263785bc17SMauro Carvalho Chehab .id_table = flexcop_usb_table,
6273785bc17SMauro Carvalho Chehab };
6283785bc17SMauro Carvalho Chehab
6293785bc17SMauro Carvalho Chehab module_usb_driver(flexcop_usb_driver);
6303785bc17SMauro Carvalho Chehab
6313785bc17SMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
6323785bc17SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_NAME);
6333785bc17SMauro Carvalho Chehab MODULE_LICENSE("GPL");
634