xref: /linux/drivers/media/usb/b2c2/flexcop-usb.c (revision fd449bb9ac44fdc334907db7bcc20ade9a4037cd)
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 {
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 
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 */
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 */
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 
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 
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 
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 
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 
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 
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 
4263785bc17SMauro Carvalho Chehab static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
4273785bc17SMauro Carvalho Chehab {
428*fd449bb9SJohan Hovold 	struct usb_host_interface *alt = fc_usb->uintf->cur_altsetting;
429*fd449bb9SJohan Hovold 	u16 frame_size;
430*fd449bb9SJohan Hovold 	int bufsize, i, j, ret;
4313785bc17SMauro Carvalho Chehab 	int buffer_offset = 0;
4323785bc17SMauro Carvalho Chehab 
433*fd449bb9SJohan Hovold 	frame_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
434*fd449bb9SJohan Hovold 	bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size;
435*fd449bb9SJohan 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 
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 
5183de50478SJohan Hovold 	if (alt->desc.bNumEndpoints < 1)
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 
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 
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 
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