xref: /linux/drivers/media/usb/b2c2/flexcop-usb.c (revision b178aa6f333b07bda0548d7e45085660a112414d)
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  */
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  */
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 
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 {
182*b178aa6fSHans Verkuil 	int 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 	}
199*b178aa6fSHans Verkuil 	while (len) {
20048016551SRicardo Ribalda 		pagechunk = min(wMax, bytes_left_to_read_on_page(addr, len));
2013785bc17SMauro Carvalho Chehab 		deb_info("%x\n",
2023785bc17SMauro Carvalho Chehab 			(addr & V8_MEMORY_PAGE_MASK) |
2033785bc17SMauro Carvalho Chehab 				(V8_MEMORY_EXTENDED*extended));
2043785bc17SMauro Carvalho Chehab 
2053785bc17SMauro Carvalho Chehab 		ret = flexcop_usb_v8_memory_req(fc_usb, req,
2063785bc17SMauro Carvalho Chehab 			page_start + (addr / V8_MEMORY_PAGE_SIZE),
2073785bc17SMauro Carvalho Chehab 			(addr & V8_MEMORY_PAGE_MASK) |
2083785bc17SMauro Carvalho Chehab 				(V8_MEMORY_EXTENDED*extended),
209*b178aa6fSHans Verkuil 			buf, pagechunk);
2103785bc17SMauro Carvalho Chehab 
2113785bc17SMauro Carvalho Chehab 		if (ret < 0)
2123785bc17SMauro Carvalho Chehab 			return ret;
2133785bc17SMauro Carvalho Chehab 		addr += pagechunk;
214*b178aa6fSHans Verkuil 		buf += pagechunk;
2153785bc17SMauro Carvalho Chehab 		len -= pagechunk;
2163785bc17SMauro Carvalho Chehab 	}
2173785bc17SMauro Carvalho Chehab 	return 0;
2183785bc17SMauro Carvalho Chehab }
2193785bc17SMauro Carvalho Chehab 
2203785bc17SMauro Carvalho Chehab static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
2213785bc17SMauro Carvalho Chehab {
2223785bc17SMauro Carvalho Chehab 	return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
2233785bc17SMauro Carvalho Chehab 		V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
2243785bc17SMauro Carvalho Chehab 		fc->dvb_adapter.proposed_mac, 6);
2253785bc17SMauro Carvalho Chehab }
2263785bc17SMauro Carvalho Chehab 
2273785bc17SMauro Carvalho Chehab /* usb i2c stuff */
2283785bc17SMauro Carvalho Chehab static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
2293785bc17SMauro Carvalho Chehab 		flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
2303785bc17SMauro Carvalho Chehab 		u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
2313785bc17SMauro Carvalho Chehab {
2323785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
2333785bc17SMauro Carvalho Chehab 	u16 wValue, wIndex;
234b430eabaSMauro Carvalho Chehab 	int nWaitTime, pipe, ret;
2353785bc17SMauro Carvalho Chehab 	u8 request_type = USB_TYPE_VENDOR;
2363785bc17SMauro Carvalho Chehab 
237b430eabaSMauro Carvalho Chehab 	if (buflen > sizeof(fc_usb->data)) {
238b430eabaSMauro Carvalho Chehab 		err("Buffer size bigger than max URB control message\n");
239b430eabaSMauro Carvalho Chehab 		return -EIO;
240b430eabaSMauro Carvalho Chehab 	}
241b430eabaSMauro Carvalho Chehab 
2423785bc17SMauro Carvalho Chehab 	switch (func) {
2433785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_WRITE:
2443785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_MULTIWRITE:
2453785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_REPEATWRITE:
2463785bc17SMauro Carvalho Chehab 		/* DKT 020208 - add this to support special case of DiSEqC */
2473785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_CHECKWRITE:
2483785bc17SMauro Carvalho Chehab 		pipe = B2C2_USB_CTRL_PIPE_OUT;
249cd1798a3SJohan Hovold 		nWaitTime = 2000;
2503785bc17SMauro Carvalho Chehab 		request_type |= USB_DIR_OUT;
2513785bc17SMauro Carvalho Chehab 		break;
2523785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_READ:
2533785bc17SMauro Carvalho Chehab 	case USB_FUNC_I2C_REPEATREAD:
2543785bc17SMauro Carvalho Chehab 		pipe = B2C2_USB_CTRL_PIPE_IN;
255cd1798a3SJohan Hovold 		nWaitTime = 2000;
2563785bc17SMauro Carvalho Chehab 		request_type |= USB_DIR_IN;
2573785bc17SMauro Carvalho Chehab 		break;
2583785bc17SMauro Carvalho Chehab 	default:
2593785bc17SMauro Carvalho Chehab 		deb_info("unsupported function for i2c_req %x\n", func);
2603785bc17SMauro Carvalho Chehab 		return -EINVAL;
2613785bc17SMauro Carvalho Chehab 	}
2623785bc17SMauro Carvalho Chehab 	wValue = (func << 8) | (i2c->port << 4);
2633785bc17SMauro Carvalho Chehab 	wIndex = (chipaddr << 8 ) | addr;
2643785bc17SMauro Carvalho Chehab 
2653785bc17SMauro Carvalho Chehab 	deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
2663785bc17SMauro Carvalho Chehab 			func, request_type, req,
2673785bc17SMauro Carvalho Chehab 			wValue & 0xff, wValue >> 8,
2683785bc17SMauro Carvalho Chehab 			wIndex & 0xff, wIndex >> 8);
2693785bc17SMauro Carvalho Chehab 
270b430eabaSMauro Carvalho Chehab 	mutex_lock(&fc_usb->data_mutex);
271b430eabaSMauro Carvalho Chehab 
272b430eabaSMauro Carvalho Chehab 	if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
273b430eabaSMauro Carvalho Chehab 		memcpy(fc_usb->data, buf, buflen);
274b430eabaSMauro Carvalho Chehab 
275b430eabaSMauro Carvalho Chehab 	ret = usb_control_msg(fc_usb->udev, pipe,
2763785bc17SMauro Carvalho Chehab 			req,
2773785bc17SMauro Carvalho Chehab 			request_type,
2783785bc17SMauro Carvalho Chehab 			wValue,
2793785bc17SMauro Carvalho Chehab 			wIndex,
280b430eabaSMauro Carvalho Chehab 			fc_usb->data,
2813785bc17SMauro Carvalho Chehab 			buflen,
282cd1798a3SJohan Hovold 			nWaitTime);
283b430eabaSMauro Carvalho Chehab 
284b430eabaSMauro Carvalho Chehab 	if (ret != buflen)
285b430eabaSMauro Carvalho Chehab 		ret = -EIO;
286b430eabaSMauro Carvalho Chehab 
287b430eabaSMauro Carvalho Chehab 	if (ret >= 0) {
288b430eabaSMauro Carvalho Chehab 		ret = 0;
289b430eabaSMauro Carvalho Chehab 		if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
290b430eabaSMauro Carvalho Chehab 			memcpy(buf, fc_usb->data, buflen);
291b430eabaSMauro Carvalho Chehab 	}
292b430eabaSMauro Carvalho Chehab 
293b430eabaSMauro Carvalho Chehab 	mutex_unlock(&fc_usb->data_mutex);
294b430eabaSMauro Carvalho Chehab 
29574a96b51SColin Ian King 	return ret;
2963785bc17SMauro Carvalho Chehab }
2973785bc17SMauro Carvalho Chehab 
2983785bc17SMauro Carvalho Chehab /* actual bus specific access functions,
2993785bc17SMauro Carvalho Chehab    make sure prototype are/will be equal to pci */
3003785bc17SMauro Carvalho Chehab static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
3013785bc17SMauro Carvalho Chehab 	flexcop_ibi_register reg)
3023785bc17SMauro Carvalho Chehab {
3033785bc17SMauro Carvalho Chehab 	flexcop_ibi_value val;
3043785bc17SMauro Carvalho Chehab 	val.raw = 0;
3053785bc17SMauro Carvalho Chehab 	flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
3063785bc17SMauro Carvalho Chehab 	return val;
3073785bc17SMauro Carvalho Chehab }
3083785bc17SMauro Carvalho Chehab 
3093785bc17SMauro Carvalho Chehab static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
3103785bc17SMauro Carvalho Chehab 		flexcop_ibi_register reg, flexcop_ibi_value val)
3113785bc17SMauro Carvalho Chehab {
3123785bc17SMauro Carvalho Chehab 	return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
3133785bc17SMauro Carvalho Chehab }
3143785bc17SMauro Carvalho Chehab 
3153785bc17SMauro Carvalho Chehab static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
3163785bc17SMauro Carvalho Chehab 		flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
3173785bc17SMauro Carvalho Chehab {
3183785bc17SMauro Carvalho Chehab 	if (op == FC_READ)
3193785bc17SMauro Carvalho Chehab 		return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3203785bc17SMauro Carvalho Chehab 				USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
3213785bc17SMauro Carvalho Chehab 	else
3223785bc17SMauro Carvalho Chehab 		return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3233785bc17SMauro Carvalho Chehab 				USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
3243785bc17SMauro Carvalho Chehab }
3253785bc17SMauro Carvalho Chehab 
3263785bc17SMauro Carvalho Chehab static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
3273785bc17SMauro Carvalho Chehab 	u8 *buffer, int buffer_length)
3283785bc17SMauro Carvalho Chehab {
3293785bc17SMauro Carvalho Chehab 	u8 *b;
3303785bc17SMauro Carvalho Chehab 	int l;
3313785bc17SMauro Carvalho Chehab 
3323785bc17SMauro Carvalho Chehab 	deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
3333785bc17SMauro Carvalho Chehab 		fc_usb->tmp_buffer_length, buffer_length);
3343785bc17SMauro Carvalho Chehab 
3353785bc17SMauro Carvalho Chehab 	if (fc_usb->tmp_buffer_length > 0) {
3363785bc17SMauro Carvalho Chehab 		memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
3373785bc17SMauro Carvalho Chehab 				buffer_length);
3383785bc17SMauro Carvalho Chehab 		fc_usb->tmp_buffer_length += buffer_length;
3393785bc17SMauro Carvalho Chehab 		b = fc_usb->tmp_buffer;
3403785bc17SMauro Carvalho Chehab 		l = fc_usb->tmp_buffer_length;
3413785bc17SMauro Carvalho Chehab 	} else {
3423785bc17SMauro Carvalho Chehab 		b = buffer;
3433785bc17SMauro Carvalho Chehab 		l = buffer_length;
3443785bc17SMauro Carvalho Chehab 	}
3453785bc17SMauro Carvalho Chehab 
3463785bc17SMauro Carvalho Chehab 	while (l >= 190) {
3473785bc17SMauro Carvalho Chehab 		if (*b == 0xff) {
3483785bc17SMauro Carvalho Chehab 			switch (*(b+1) & 0x03) {
3493785bc17SMauro Carvalho Chehab 			case 0x01: /* media packet */
3503785bc17SMauro Carvalho Chehab 				if (*(b+2) == 0x47)
3513785bc17SMauro Carvalho Chehab 					flexcop_pass_dmx_packets(
3523785bc17SMauro Carvalho Chehab 							fc_usb->fc_dev, b+2, 1);
3533785bc17SMauro Carvalho Chehab 				else
3543785bc17SMauro Carvalho Chehab 					deb_ts("not ts packet %*ph\n", 4, b+2);
3553785bc17SMauro Carvalho Chehab 				b += 190;
3563785bc17SMauro Carvalho Chehab 				l -= 190;
3573785bc17SMauro Carvalho Chehab 				break;
3583785bc17SMauro Carvalho Chehab 			default:
3593785bc17SMauro Carvalho Chehab 				deb_ts("wrong packet type\n");
3603785bc17SMauro Carvalho Chehab 				l = 0;
3613785bc17SMauro Carvalho Chehab 				break;
3623785bc17SMauro Carvalho Chehab 			}
3633785bc17SMauro Carvalho Chehab 		} else {
3643785bc17SMauro Carvalho Chehab 			deb_ts("wrong header\n");
3653785bc17SMauro Carvalho Chehab 			l = 0;
3663785bc17SMauro Carvalho Chehab 		}
3673785bc17SMauro Carvalho Chehab 	}
3683785bc17SMauro Carvalho Chehab 
3693785bc17SMauro Carvalho Chehab 	if (l > 0)
3703785bc17SMauro Carvalho Chehab 		memcpy(fc_usb->tmp_buffer, b, l);
3713785bc17SMauro Carvalho Chehab 	fc_usb->tmp_buffer_length = l;
3723785bc17SMauro Carvalho Chehab }
3733785bc17SMauro Carvalho Chehab 
3743785bc17SMauro Carvalho Chehab static void flexcop_usb_urb_complete(struct urb *urb)
3753785bc17SMauro Carvalho Chehab {
3763785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = urb->context;
3773785bc17SMauro Carvalho Chehab 	int i;
3783785bc17SMauro Carvalho Chehab 
3793785bc17SMauro Carvalho Chehab 	if (urb->actual_length > 0)
3803785bc17SMauro Carvalho Chehab 		deb_ts("urb completed, bufsize: %d actlen; %d\n",
3813785bc17SMauro Carvalho Chehab 			urb->transfer_buffer_length, urb->actual_length);
3823785bc17SMauro Carvalho Chehab 
3833785bc17SMauro Carvalho Chehab 	for (i = 0; i < urb->number_of_packets; i++) {
3843785bc17SMauro Carvalho Chehab 		if (urb->iso_frame_desc[i].status < 0) {
3853785bc17SMauro Carvalho Chehab 			err("iso frame descriptor %d has an error: %d\n", i,
3863785bc17SMauro Carvalho Chehab 				urb->iso_frame_desc[i].status);
3873785bc17SMauro Carvalho Chehab 		} else
3883785bc17SMauro Carvalho Chehab 			if (urb->iso_frame_desc[i].actual_length > 0) {
3893785bc17SMauro Carvalho Chehab 				deb_ts("passed %d bytes to the demux\n",
3903785bc17SMauro Carvalho Chehab 					urb->iso_frame_desc[i].actual_length);
3913785bc17SMauro Carvalho Chehab 
3923785bc17SMauro Carvalho Chehab 				flexcop_usb_process_frame(fc_usb,
3933785bc17SMauro Carvalho Chehab 					urb->transfer_buffer +
3943785bc17SMauro Carvalho Chehab 						urb->iso_frame_desc[i].offset,
3953785bc17SMauro Carvalho Chehab 					urb->iso_frame_desc[i].actual_length);
3963785bc17SMauro Carvalho Chehab 			}
3973785bc17SMauro Carvalho Chehab 		urb->iso_frame_desc[i].status = 0;
3983785bc17SMauro Carvalho Chehab 		urb->iso_frame_desc[i].actual_length = 0;
3993785bc17SMauro Carvalho Chehab 	}
4003785bc17SMauro Carvalho Chehab 	usb_submit_urb(urb, GFP_ATOMIC);
4013785bc17SMauro Carvalho Chehab }
4023785bc17SMauro Carvalho Chehab 
4033785bc17SMauro Carvalho Chehab static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
4043785bc17SMauro Carvalho Chehab {
4053785bc17SMauro Carvalho Chehab 	/* submit/kill iso packets */
4063785bc17SMauro Carvalho Chehab 	return 0;
4073785bc17SMauro Carvalho Chehab }
4083785bc17SMauro Carvalho Chehab 
4093785bc17SMauro Carvalho Chehab static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
4103785bc17SMauro Carvalho Chehab {
4113785bc17SMauro Carvalho Chehab 	int i;
4123785bc17SMauro Carvalho Chehab 	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
4133785bc17SMauro Carvalho Chehab 		if (fc_usb->iso_urb[i] != NULL) {
4143785bc17SMauro Carvalho Chehab 			deb_ts("unlinking/killing urb no. %d\n", i);
4153785bc17SMauro Carvalho Chehab 			usb_kill_urb(fc_usb->iso_urb[i]);
4163785bc17SMauro Carvalho Chehab 			usb_free_urb(fc_usb->iso_urb[i]);
4173785bc17SMauro Carvalho Chehab 		}
4183785bc17SMauro Carvalho Chehab 
419cf580e11SQinglang Miao 	usb_free_coherent(fc_usb->udev, fc_usb->buffer_size,
420cf580e11SQinglang Miao 			  fc_usb->iso_buffer, fc_usb->dma_addr);
421cf580e11SQinglang Miao 
4223785bc17SMauro Carvalho Chehab }
4233785bc17SMauro Carvalho Chehab 
4243785bc17SMauro Carvalho Chehab static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
4253785bc17SMauro Carvalho Chehab {
426fd449bb9SJohan Hovold 	struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting;
427fd449bb9SJohan Hovold 	u16 frame_size;
428fd449bb9SJohan Hovold 	int bufsize, i, j, ret;
4293785bc17SMauro Carvalho Chehab 	int buffer_offset = 0;
4303785bc17SMauro Carvalho Chehab 
431a8be6b6eSJohan Hovold 	frame_size = usb_endpoint_maxp(&alt->endpoint[0].desc);
432fd449bb9SJohan Hovold 	bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size;
433fd449bb9SJohan Hovold 
4344a58d390SMauro Carvalho Chehab 	deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
4354a58d390SMauro Carvalho Chehab 	       B2C2_USB_NUM_ISO_URB,
4363785bc17SMauro Carvalho Chehab 			B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
4373785bc17SMauro Carvalho Chehab 
4386c7e3469SChen Gang 	fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
4396c7e3469SChen Gang 			bufsize, GFP_KERNEL, &fc_usb->dma_addr);
4403785bc17SMauro Carvalho Chehab 	if (fc_usb->iso_buffer == NULL)
4413785bc17SMauro Carvalho Chehab 		return -ENOMEM;
4423785bc17SMauro Carvalho Chehab 
4433785bc17SMauro Carvalho Chehab 	memset(fc_usb->iso_buffer, 0, bufsize);
4443785bc17SMauro Carvalho Chehab 	fc_usb->buffer_size = bufsize;
4453785bc17SMauro Carvalho Chehab 
4463785bc17SMauro Carvalho Chehab 	/* creating iso urbs */
4473785bc17SMauro Carvalho Chehab 	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4483785bc17SMauro Carvalho Chehab 		fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
449f835f3eaSOliver Neukum 			GFP_KERNEL);
4503785bc17SMauro Carvalho Chehab 		if (fc_usb->iso_urb[i] == NULL) {
4513785bc17SMauro Carvalho Chehab 			ret = -ENOMEM;
4523785bc17SMauro Carvalho Chehab 			goto urb_error;
4533785bc17SMauro Carvalho Chehab 		}
4543785bc17SMauro Carvalho Chehab 	}
4553785bc17SMauro Carvalho Chehab 
4563785bc17SMauro Carvalho Chehab 	/* initialising and submitting iso urbs */
4573785bc17SMauro Carvalho Chehab 	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4583785bc17SMauro Carvalho Chehab 		int frame_offset = 0;
4593785bc17SMauro Carvalho Chehab 		struct urb *urb = fc_usb->iso_urb[i];
4604a58d390SMauro Carvalho Chehab 		deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",
4614a58d390SMauro Carvalho Chehab 		       i, buffer_offset);
4623785bc17SMauro Carvalho Chehab 
4633785bc17SMauro Carvalho Chehab 		urb->dev = fc_usb->udev;
4643785bc17SMauro Carvalho Chehab 		urb->context = fc_usb;
4653785bc17SMauro Carvalho Chehab 		urb->complete = flexcop_usb_urb_complete;
4663785bc17SMauro Carvalho Chehab 		urb->pipe = B2C2_USB_DATA_PIPE;
4673785bc17SMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP;
4683785bc17SMauro Carvalho Chehab 		urb->interval = 1;
4693785bc17SMauro Carvalho Chehab 		urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
4703785bc17SMauro Carvalho Chehab 		urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
4713785bc17SMauro Carvalho Chehab 		urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
4723785bc17SMauro Carvalho Chehab 
4733785bc17SMauro Carvalho Chehab 		buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
4743785bc17SMauro Carvalho Chehab 		for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
4753785bc17SMauro Carvalho Chehab 			deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
4763785bc17SMauro Carvalho Chehab 					i, j, frame_offset);
4773785bc17SMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = frame_offset;
4783785bc17SMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = frame_size;
4793785bc17SMauro Carvalho Chehab 			frame_offset += frame_size;
4803785bc17SMauro Carvalho Chehab 		}
4813785bc17SMauro Carvalho Chehab 
482f835f3eaSOliver Neukum 		if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_KERNEL))) {
4833785bc17SMauro Carvalho Chehab 			err("submitting urb %d failed with %d.", i, ret);
4843785bc17SMauro Carvalho Chehab 			goto urb_error;
4853785bc17SMauro Carvalho Chehab 		}
4863785bc17SMauro Carvalho Chehab 		deb_ts("submitted urb no. %d.\n", i);
4873785bc17SMauro Carvalho Chehab 	}
4883785bc17SMauro Carvalho Chehab 
4893785bc17SMauro Carvalho Chehab 	/* SRAM */
4903785bc17SMauro Carvalho Chehab 	flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
4913785bc17SMauro Carvalho Chehab 			FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
4923785bc17SMauro Carvalho Chehab 			FC_SRAM_DEST_TARGET_WAN_USB);
4933785bc17SMauro Carvalho Chehab 	flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
4943785bc17SMauro Carvalho Chehab 	flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
4953785bc17SMauro Carvalho Chehab 	return 0;
4963785bc17SMauro Carvalho Chehab 
4973785bc17SMauro Carvalho Chehab urb_error:
4983785bc17SMauro Carvalho Chehab 	flexcop_usb_transfer_exit(fc_usb);
4993785bc17SMauro Carvalho Chehab 	return ret;
5003785bc17SMauro Carvalho Chehab }
5013785bc17SMauro Carvalho Chehab 
5023785bc17SMauro Carvalho Chehab static int flexcop_usb_init(struct flexcop_usb *fc_usb)
5033785bc17SMauro Carvalho Chehab {
5043de50478SJohan Hovold 	struct usb_host_interface *alt;
5053de50478SJohan Hovold 	int ret;
506649cd16cSYang Yingliang 
5073de50478SJohan Hovold 	/* use the alternate setting with the largest buffer */
5083de50478SJohan Hovold 	ret = usb_set_interface(fc_usb->udev, 0, 1);
509649cd16cSYang Yingliang 	if (ret) {
510649cd16cSYang Yingliang 		err("set interface failed.");
511649cd16cSYang Yingliang 		return ret;
512649cd16cSYang Yingliang 	}
513649cd16cSYang Yingliang 
5143de50478SJohan Hovold 	alt = fc_usb->uintf->cur_altsetting;
5153de50478SJohan Hovold 
516f62dc8f6SDongliang Mu 	if (alt->desc.bNumEndpoints < 2)
517bca243b1SJohan Hovold 		return -ENODEV;
5183de50478SJohan Hovold 	if (!usb_endpoint_is_isoc_in(&alt->endpoint[0].desc))
519d725d20eSOliver Neukum 		return -ENODEV;
520bca243b1SJohan Hovold 
5213785bc17SMauro Carvalho Chehab 	switch (fc_usb->udev->speed) {
5223785bc17SMauro Carvalho Chehab 	case USB_SPEED_LOW:
5233785bc17SMauro Carvalho Chehab 		err("cannot handle USB speed because it is too slow.");
5243785bc17SMauro Carvalho Chehab 		return -ENODEV;
5253785bc17SMauro Carvalho Chehab 		break;
5263785bc17SMauro Carvalho Chehab 	case USB_SPEED_FULL:
5273785bc17SMauro Carvalho Chehab 		info("running at FULL speed.");
5283785bc17SMauro Carvalho Chehab 		break;
5293785bc17SMauro Carvalho Chehab 	case USB_SPEED_HIGH:
5303785bc17SMauro Carvalho Chehab 		info("running at HIGH speed.");
5313785bc17SMauro Carvalho Chehab 		break;
53282fe45f3SOliver Neukum 	case USB_SPEED_SUPER:
53382fe45f3SOliver Neukum 		info("running at SUPER speed.");
53482fe45f3SOliver Neukum 		break;
53582fe45f3SOliver Neukum 	case USB_SPEED_SUPER_PLUS:
53682fe45f3SOliver Neukum 		info("running at SUPER+ speed.");
53782fe45f3SOliver Neukum 		break;
538df561f66SGustavo A. R. Silva 	case USB_SPEED_UNKNOWN:
5393785bc17SMauro Carvalho Chehab 	default:
5403785bc17SMauro Carvalho Chehab 		err("cannot handle USB speed because it is unknown.");
5413785bc17SMauro Carvalho Chehab 		return -ENODEV;
5423785bc17SMauro Carvalho Chehab 	}
5433785bc17SMauro Carvalho Chehab 	usb_set_intfdata(fc_usb->uintf, fc_usb);
5443785bc17SMauro Carvalho Chehab 	return 0;
5453785bc17SMauro Carvalho Chehab }
5463785bc17SMauro Carvalho Chehab 
5473785bc17SMauro Carvalho Chehab static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
5483785bc17SMauro Carvalho Chehab {
5493785bc17SMauro Carvalho Chehab 	usb_set_intfdata(fc_usb->uintf, NULL);
5503785bc17SMauro Carvalho Chehab }
5513785bc17SMauro Carvalho Chehab 
5523785bc17SMauro Carvalho Chehab static int flexcop_usb_probe(struct usb_interface *intf,
5533785bc17SMauro Carvalho Chehab 		const struct usb_device_id *id)
5543785bc17SMauro Carvalho Chehab {
5553785bc17SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(intf);
5563785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = NULL;
5573785bc17SMauro Carvalho Chehab 	struct flexcop_device *fc = NULL;
5583785bc17SMauro Carvalho Chehab 	int ret;
5593785bc17SMauro Carvalho Chehab 
5603785bc17SMauro Carvalho Chehab 	if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
5613785bc17SMauro Carvalho Chehab 		err("out of memory\n");
5623785bc17SMauro Carvalho Chehab 		return -ENOMEM;
5633785bc17SMauro Carvalho Chehab 	}
5643785bc17SMauro Carvalho Chehab 
5653785bc17SMauro Carvalho Chehab 	/* general flexcop init */
5663785bc17SMauro Carvalho Chehab 	fc_usb = fc->bus_specific;
5673785bc17SMauro Carvalho Chehab 	fc_usb->fc_dev = fc;
568b430eabaSMauro Carvalho Chehab 	mutex_init(&fc_usb->data_mutex);
5693785bc17SMauro Carvalho Chehab 
5703785bc17SMauro Carvalho Chehab 	fc->read_ibi_reg  = flexcop_usb_read_ibi_reg;
5713785bc17SMauro Carvalho Chehab 	fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
5723785bc17SMauro Carvalho Chehab 	fc->i2c_request = flexcop_usb_i2c_request;
5733785bc17SMauro Carvalho Chehab 	fc->get_mac_addr = flexcop_usb_get_mac_addr;
5743785bc17SMauro Carvalho Chehab 
5753785bc17SMauro Carvalho Chehab 	fc->stream_control = flexcop_usb_stream_control;
5763785bc17SMauro Carvalho Chehab 
5773785bc17SMauro Carvalho Chehab 	fc->pid_filtering = 1;
5783785bc17SMauro Carvalho Chehab 	fc->bus_type = FC_USB;
5793785bc17SMauro Carvalho Chehab 
5803785bc17SMauro Carvalho Chehab 	fc->dev = &udev->dev;
5813785bc17SMauro Carvalho Chehab 	fc->owner = THIS_MODULE;
5823785bc17SMauro Carvalho Chehab 
5833785bc17SMauro Carvalho Chehab 	/* bus specific part */
5843785bc17SMauro Carvalho Chehab 	fc_usb->udev = udev;
5853785bc17SMauro Carvalho Chehab 	fc_usb->uintf = intf;
5863785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_usb_init(fc_usb)) != 0)
5873785bc17SMauro Carvalho Chehab 		goto err_kfree;
5883785bc17SMauro Carvalho Chehab 
5893785bc17SMauro Carvalho Chehab 	/* init flexcop */
5903785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_device_initialize(fc)) != 0)
5913785bc17SMauro Carvalho Chehab 		goto err_usb_exit;
5923785bc17SMauro Carvalho Chehab 
5933785bc17SMauro Carvalho Chehab 	/* xfer init */
5943785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
5953785bc17SMauro Carvalho Chehab 		goto err_fc_exit;
5963785bc17SMauro Carvalho Chehab 
5973785bc17SMauro Carvalho Chehab 	info("%s successfully initialized and connected.", DRIVER_NAME);
5983785bc17SMauro Carvalho Chehab 	return 0;
5993785bc17SMauro Carvalho Chehab 
6003785bc17SMauro Carvalho Chehab err_fc_exit:
6013785bc17SMauro Carvalho Chehab 	flexcop_device_exit(fc);
6023785bc17SMauro Carvalho Chehab err_usb_exit:
6033785bc17SMauro Carvalho Chehab 	flexcop_usb_exit(fc_usb);
6043785bc17SMauro Carvalho Chehab err_kfree:
6053785bc17SMauro Carvalho Chehab 	flexcop_device_kfree(fc);
6063785bc17SMauro Carvalho Chehab 	return ret;
6073785bc17SMauro Carvalho Chehab }
6083785bc17SMauro Carvalho Chehab 
6093785bc17SMauro Carvalho Chehab static void flexcop_usb_disconnect(struct usb_interface *intf)
6103785bc17SMauro Carvalho Chehab {
6113785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
6123785bc17SMauro Carvalho Chehab 	flexcop_usb_transfer_exit(fc_usb);
6133785bc17SMauro Carvalho Chehab 	flexcop_device_exit(fc_usb->fc_dev);
6143785bc17SMauro Carvalho Chehab 	flexcop_usb_exit(fc_usb);
6153785bc17SMauro Carvalho Chehab 	flexcop_device_kfree(fc_usb->fc_dev);
6163785bc17SMauro Carvalho Chehab 	info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
6173785bc17SMauro Carvalho Chehab }
6183785bc17SMauro Carvalho Chehab 
6197fb2e072SArvind Yadav static const struct usb_device_id flexcop_usb_table[] = {
6203785bc17SMauro Carvalho Chehab 	{ USB_DEVICE(0x0af7, 0x0101) },
6213785bc17SMauro Carvalho Chehab 	{ }
6223785bc17SMauro Carvalho Chehab };
6233785bc17SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
6243785bc17SMauro Carvalho Chehab 
6253785bc17SMauro Carvalho Chehab /* usb specific object needed to register this driver with the usb subsystem */
6263785bc17SMauro Carvalho Chehab static struct usb_driver flexcop_usb_driver = {
6273785bc17SMauro Carvalho Chehab 	.name		= "b2c2_flexcop_usb",
6283785bc17SMauro Carvalho Chehab 	.probe		= flexcop_usb_probe,
6293785bc17SMauro Carvalho Chehab 	.disconnect = flexcop_usb_disconnect,
6303785bc17SMauro Carvalho Chehab 	.id_table	= flexcop_usb_table,
6313785bc17SMauro Carvalho Chehab };
6323785bc17SMauro Carvalho Chehab 
6333785bc17SMauro Carvalho Chehab module_usb_driver(flexcop_usb_driver);
6343785bc17SMauro Carvalho Chehab 
6353785bc17SMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
6363785bc17SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_NAME);
6373785bc17SMauro Carvalho Chehab MODULE_LICENSE("GPL");
638