xref: /linux/drivers/media/usb/b2c2/flexcop-usb.c (revision 4a58d39075e1e2bc5ca8b379278659d95f072363)
13785bc17SMauro Carvalho Chehab /*
23785bc17SMauro Carvalho Chehab  * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
33785bc17SMauro Carvalho Chehab  * flexcop-usb.c - covers the USB part
43785bc17SMauro Carvalho Chehab  * see flexcop.c for copyright information
53785bc17SMauro Carvalho Chehab  */
63785bc17SMauro Carvalho Chehab #define FC_LOG_PREFIX "flexcop_usb"
73785bc17SMauro Carvalho Chehab #include "flexcop-usb.h"
83785bc17SMauro Carvalho Chehab #include "flexcop-common.h"
93785bc17SMauro Carvalho Chehab 
103785bc17SMauro Carvalho Chehab /* Version information */
113785bc17SMauro Carvalho Chehab #define DRIVER_VERSION "0.1"
123785bc17SMauro Carvalho Chehab #define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
1399e44da7SPatrick Boettcher #define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
143785bc17SMauro Carvalho Chehab 
153785bc17SMauro Carvalho Chehab /* debug */
163785bc17SMauro Carvalho Chehab #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
173785bc17SMauro Carvalho Chehab #define dprintk(level,args...) \
183785bc17SMauro Carvalho Chehab 	do { if ((debug & level)) printk(args); } while (0)
193785bc17SMauro Carvalho Chehab 
203785bc17SMauro Carvalho Chehab #define debug_dump(b, l, method) do {\
213785bc17SMauro Carvalho Chehab 	int i; \
223785bc17SMauro Carvalho Chehab 	for (i = 0; i < l; i++) \
233785bc17SMauro Carvalho Chehab 		method("%02x ", b[i]); \
243785bc17SMauro Carvalho Chehab 	method("\n"); \
253785bc17SMauro Carvalho Chehab } while (0)
263785bc17SMauro Carvalho Chehab 
273785bc17SMauro Carvalho Chehab #define DEBSTATUS ""
283785bc17SMauro Carvalho Chehab #else
293785bc17SMauro Carvalho Chehab #define dprintk(level, args...)
303785bc17SMauro Carvalho Chehab #define debug_dump(b, l, method)
313785bc17SMauro Carvalho Chehab #define DEBSTATUS " (debugging is not enabled)"
323785bc17SMauro Carvalho Chehab #endif
333785bc17SMauro Carvalho Chehab 
343785bc17SMauro Carvalho Chehab static int debug;
353785bc17SMauro Carvalho Chehab module_param(debug, int, 0644);
36*4a58d390SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
373785bc17SMauro Carvalho Chehab #undef DEBSTATUS
383785bc17SMauro Carvalho Chehab 
393785bc17SMauro Carvalho Chehab #define deb_info(args...) dprintk(0x01, args)
403785bc17SMauro Carvalho Chehab #define deb_ts(args...) dprintk(0x02, args)
413785bc17SMauro Carvalho Chehab #define deb_ctrl(args...) dprintk(0x04, args)
423785bc17SMauro Carvalho Chehab #define deb_i2c(args...) dprintk(0x08, args)
433785bc17SMauro Carvalho Chehab #define deb_v8(args...) dprintk(0x10, args)
443785bc17SMauro Carvalho Chehab 
453785bc17SMauro Carvalho Chehab /* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
463785bc17SMauro Carvalho Chehab  * in the IBI address, to make the V8 code simpler.
473785bc17SMauro Carvalho Chehab  * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
483785bc17SMauro Carvalho Chehab  *                  in general: 0000 0HHH 000L LL00
493785bc17SMauro Carvalho Chehab  * IBI ADDRESS FORMAT:                    RHHH BLLL
503785bc17SMauro Carvalho Chehab  *
513785bc17SMauro Carvalho Chehab  * where R is the read(1)/write(0) bit, B is the busy bit
523785bc17SMauro Carvalho Chehab  * and HHH and LLL are the two sets of three bits from the PCI address.
533785bc17SMauro Carvalho Chehab  */
543785bc17SMauro Carvalho Chehab #define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
553785bc17SMauro Carvalho Chehab 	(((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
563785bc17SMauro Carvalho Chehab #define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
573785bc17SMauro Carvalho Chehab 	(((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
583785bc17SMauro Carvalho Chehab 
593785bc17SMauro Carvalho Chehab /*
603785bc17SMauro Carvalho Chehab  * DKT 020228
613785bc17SMauro Carvalho Chehab  * - forget about this VENDOR_BUFFER_SIZE, read and write register
623785bc17SMauro Carvalho Chehab  *   deal with DWORD or 4 bytes, that should be should from now on
633785bc17SMauro Carvalho Chehab  * - from now on, we don't support anything older than firm 1.00
643785bc17SMauro Carvalho Chehab  *   I eliminated the write register as a 2 trip of writing hi word and lo word
653785bc17SMauro Carvalho Chehab  *   and force this to write only 4 bytes at a time.
663785bc17SMauro Carvalho Chehab  *   NOTE: this should work with all the firmware from 1.00 and newer
673785bc17SMauro Carvalho Chehab  */
683785bc17SMauro Carvalho Chehab static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
693785bc17SMauro Carvalho Chehab {
703785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = fc->bus_specific;
713785bc17SMauro Carvalho Chehab 	u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
723785bc17SMauro Carvalho Chehab 	u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
733785bc17SMauro Carvalho Chehab 	u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
743785bc17SMauro Carvalho Chehab 		(read ? 0x80 : 0);
75b430eabaSMauro Carvalho Chehab 	int ret;
763785bc17SMauro Carvalho Chehab 
77b430eabaSMauro Carvalho Chehab 	mutex_lock(&fc_usb->data_mutex);
78b430eabaSMauro Carvalho Chehab 	if (!read)
79b430eabaSMauro Carvalho Chehab 		memcpy(fc_usb->data, val, sizeof(*val));
80b430eabaSMauro Carvalho Chehab 
81b430eabaSMauro Carvalho Chehab 	ret = usb_control_msg(fc_usb->udev,
823785bc17SMauro Carvalho Chehab 			read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
833785bc17SMauro Carvalho Chehab 			request,
843785bc17SMauro Carvalho Chehab 			request_type, /* 0xc0 read or 0x40 write */
853785bc17SMauro Carvalho Chehab 			wAddress,
863785bc17SMauro Carvalho Chehab 			0,
87b430eabaSMauro Carvalho Chehab 			fc_usb->data,
883785bc17SMauro Carvalho Chehab 			sizeof(u32),
893785bc17SMauro Carvalho Chehab 			B2C2_WAIT_FOR_OPERATION_RDW * HZ);
903785bc17SMauro Carvalho Chehab 
91b430eabaSMauro Carvalho Chehab 	if (ret != sizeof(u32)) {
923785bc17SMauro Carvalho Chehab 		err("error while %s dword from %d (%d).", read ? "reading" :
933785bc17SMauro Carvalho Chehab 				"writing", wAddress, wRegOffsPCI);
94b430eabaSMauro Carvalho Chehab 		if (ret >= 0)
95b430eabaSMauro Carvalho Chehab 			ret = -EIO;
963785bc17SMauro Carvalho Chehab 	}
97b430eabaSMauro Carvalho Chehab 
98b430eabaSMauro Carvalho Chehab 	if (read && ret >= 0)
99b430eabaSMauro Carvalho Chehab 		memcpy(val, fc_usb->data, sizeof(*val));
100b430eabaSMauro Carvalho Chehab 	mutex_unlock(&fc_usb->data_mutex);
101b430eabaSMauro Carvalho Chehab 
102b430eabaSMauro Carvalho Chehab 	return ret;
1033785bc17SMauro Carvalho Chehab }
1043785bc17SMauro Carvalho Chehab /*
1053785bc17SMauro Carvalho Chehab  * DKT 010817 - add support for V8 memory read/write and flash update
1063785bc17SMauro Carvalho Chehab  */
1073785bc17SMauro Carvalho Chehab static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
1083785bc17SMauro Carvalho Chehab 		flexcop_usb_request_t req, u8 page, u16 wAddress,
1093785bc17SMauro Carvalho Chehab 		u8 *pbBuffer, u32 buflen)
1103785bc17SMauro Carvalho Chehab {
1113785bc17SMauro Carvalho Chehab 	u8 request_type = USB_TYPE_VENDOR;
1123785bc17SMauro Carvalho Chehab 	u16 wIndex;
113b430eabaSMauro Carvalho Chehab 	int nWaitTime, pipe, ret;
1143785bc17SMauro Carvalho Chehab 	wIndex = page << 8;
1153785bc17SMauro Carvalho Chehab 
116b430eabaSMauro Carvalho Chehab 	if (buflen > sizeof(fc_usb->data)) {
117b430eabaSMauro Carvalho Chehab 		err("Buffer size bigger than max URB control message\n");
118b430eabaSMauro Carvalho Chehab 		return -EIO;
119b430eabaSMauro Carvalho Chehab 	}
120b430eabaSMauro Carvalho Chehab 
1213785bc17SMauro Carvalho Chehab 	switch (req) {
1223785bc17SMauro Carvalho Chehab 	case B2C2_USB_READ_V8_MEM:
1233785bc17SMauro Carvalho Chehab 		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
1243785bc17SMauro Carvalho Chehab 		request_type |= USB_DIR_IN;
1253785bc17SMauro Carvalho Chehab 		pipe = B2C2_USB_CTRL_PIPE_IN;
1263785bc17SMauro Carvalho Chehab 		break;
1273785bc17SMauro Carvalho Chehab 	case B2C2_USB_WRITE_V8_MEM:
1283785bc17SMauro Carvalho Chehab 		wIndex |= pbBuffer[0];
1293785bc17SMauro Carvalho Chehab 		request_type |= USB_DIR_OUT;
1303785bc17SMauro Carvalho Chehab 		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
1313785bc17SMauro Carvalho Chehab 		pipe = B2C2_USB_CTRL_PIPE_OUT;
1323785bc17SMauro Carvalho Chehab 		break;
1333785bc17SMauro Carvalho Chehab 	case B2C2_USB_FLASH_BLOCK:
1343785bc17SMauro Carvalho Chehab 		request_type |= USB_DIR_OUT;
1353785bc17SMauro Carvalho Chehab 		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
1363785bc17SMauro Carvalho Chehab 		pipe = B2C2_USB_CTRL_PIPE_OUT;
1373785bc17SMauro Carvalho Chehab 		break;
1383785bc17SMauro Carvalho Chehab 	default:
1393785bc17SMauro Carvalho Chehab 		deb_info("unsupported request for v8_mem_req %x.\n", req);
1403785bc17SMauro Carvalho Chehab 		return -EINVAL;
1413785bc17SMauro Carvalho Chehab 	}
1423785bc17SMauro Carvalho Chehab 	deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
1433785bc17SMauro Carvalho Chehab 			wAddress, wIndex, buflen);
1443785bc17SMauro Carvalho Chehab 
145b430eabaSMauro Carvalho Chehab 	mutex_lock(&fc_usb->data_mutex);
146b430eabaSMauro Carvalho Chehab 
147b430eabaSMauro Carvalho Chehab 	if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
148b430eabaSMauro Carvalho Chehab 		memcpy(fc_usb->data, pbBuffer, buflen);
149b430eabaSMauro Carvalho Chehab 
150b430eabaSMauro Carvalho Chehab 	ret = usb_control_msg(fc_usb->udev, pipe,
1513785bc17SMauro Carvalho Chehab 			req,
1523785bc17SMauro Carvalho Chehab 			request_type,
1533785bc17SMauro Carvalho Chehab 			wAddress,
1543785bc17SMauro Carvalho Chehab 			wIndex,
155b430eabaSMauro Carvalho Chehab 			fc_usb->data,
1563785bc17SMauro Carvalho Chehab 			buflen,
1573785bc17SMauro Carvalho Chehab 			nWaitTime * HZ);
158b430eabaSMauro Carvalho Chehab 	if (ret != buflen)
159b430eabaSMauro Carvalho Chehab 		ret = -EIO;
1603785bc17SMauro Carvalho Chehab 
161b430eabaSMauro Carvalho Chehab 	if (ret >= 0) {
162b430eabaSMauro Carvalho Chehab 		ret = 0;
163b430eabaSMauro Carvalho Chehab 		if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
164b430eabaSMauro Carvalho Chehab 			memcpy(pbBuffer, fc_usb->data, buflen);
165b430eabaSMauro Carvalho Chehab 	}
166b430eabaSMauro Carvalho Chehab 
167b430eabaSMauro Carvalho Chehab 	mutex_unlock(&fc_usb->data_mutex);
168b430eabaSMauro Carvalho Chehab 
169b430eabaSMauro Carvalho Chehab 	debug_dump(pbBuffer, ret, deb_v8);
170b430eabaSMauro Carvalho Chehab 	return ret;
1713785bc17SMauro Carvalho Chehab }
1723785bc17SMauro Carvalho Chehab 
1733785bc17SMauro Carvalho Chehab #define bytes_left_to_read_on_page(paddr,buflen) \
1743785bc17SMauro Carvalho Chehab 	((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
1753785bc17SMauro Carvalho Chehab 	 ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
1763785bc17SMauro Carvalho Chehab 
1773785bc17SMauro Carvalho Chehab static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
1783785bc17SMauro Carvalho Chehab 		flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
1793785bc17SMauro Carvalho Chehab 		u32 addr, int extended, u8 *buf, u32 len)
1803785bc17SMauro Carvalho Chehab {
1813785bc17SMauro Carvalho Chehab 	int i,ret = 0;
1823785bc17SMauro Carvalho Chehab 	u16 wMax;
1833785bc17SMauro Carvalho Chehab 	u32 pagechunk = 0;
1843785bc17SMauro Carvalho Chehab 
1853785bc17SMauro Carvalho Chehab 	switch(req) {
1863785bc17SMauro Carvalho Chehab 	case B2C2_USB_READ_V8_MEM:
1873785bc17SMauro Carvalho Chehab 		wMax = USB_MEM_READ_MAX;
1883785bc17SMauro Carvalho Chehab 		break;
1893785bc17SMauro Carvalho Chehab 	case B2C2_USB_WRITE_V8_MEM:
1903785bc17SMauro Carvalho Chehab 		wMax = USB_MEM_WRITE_MAX;
1913785bc17SMauro Carvalho Chehab 		break;
1923785bc17SMauro Carvalho Chehab 	case B2C2_USB_FLASH_BLOCK:
1933785bc17SMauro Carvalho Chehab 		wMax = USB_FLASH_MAX;
1943785bc17SMauro Carvalho Chehab 		break;
1953785bc17SMauro Carvalho Chehab 	default:
1963785bc17SMauro Carvalho Chehab 		return -EINVAL;
1973785bc17SMauro Carvalho Chehab 		break;
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;
2513785bc17SMauro Carvalho Chehab 		nWaitTime = 2;
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;
2573785bc17SMauro Carvalho Chehab 		nWaitTime = 2;
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,
2843785bc17SMauro Carvalho Chehab 			nWaitTime * HZ);
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 
297b430eabaSMauro Carvalho Chehab 	return 0;
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 
4213785bc17SMauro Carvalho Chehab 	if (fc_usb->iso_buffer != NULL)
4226c7e3469SChen Gang 		usb_free_coherent(fc_usb->udev,
4233785bc17SMauro Carvalho Chehab 			fc_usb->buffer_size, fc_usb->iso_buffer,
4243785bc17SMauro Carvalho Chehab 			fc_usb->dma_addr);
4253785bc17SMauro Carvalho Chehab }
4263785bc17SMauro Carvalho Chehab 
4273785bc17SMauro Carvalho Chehab static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
4283785bc17SMauro Carvalho Chehab {
4293785bc17SMauro Carvalho Chehab 	u16 frame_size = le16_to_cpu(
4303785bc17SMauro Carvalho Chehab 		fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
4313785bc17SMauro Carvalho Chehab 	int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
4323785bc17SMauro Carvalho Chehab 		frame_size, i, j, ret;
4333785bc17SMauro Carvalho Chehab 	int buffer_offset = 0;
4343785bc17SMauro Carvalho Chehab 
435*4a58d390SMauro Carvalho Chehab 	deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
436*4a58d390SMauro Carvalho Chehab 	       B2C2_USB_NUM_ISO_URB,
4373785bc17SMauro Carvalho Chehab 			B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
4383785bc17SMauro Carvalho Chehab 
4396c7e3469SChen Gang 	fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
4406c7e3469SChen Gang 			bufsize, GFP_KERNEL, &fc_usb->dma_addr);
4413785bc17SMauro Carvalho Chehab 	if (fc_usb->iso_buffer == NULL)
4423785bc17SMauro Carvalho Chehab 		return -ENOMEM;
4433785bc17SMauro Carvalho Chehab 
4443785bc17SMauro Carvalho Chehab 	memset(fc_usb->iso_buffer, 0, bufsize);
4453785bc17SMauro Carvalho Chehab 	fc_usb->buffer_size = bufsize;
4463785bc17SMauro Carvalho Chehab 
4473785bc17SMauro Carvalho Chehab 	/* creating iso urbs */
4483785bc17SMauro Carvalho Chehab 	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4493785bc17SMauro Carvalho Chehab 		fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
4503785bc17SMauro Carvalho Chehab 			GFP_ATOMIC);
4513785bc17SMauro Carvalho Chehab 		if (fc_usb->iso_urb[i] == NULL) {
4523785bc17SMauro Carvalho Chehab 			ret = -ENOMEM;
4533785bc17SMauro Carvalho Chehab 			goto urb_error;
4543785bc17SMauro Carvalho Chehab 		}
4553785bc17SMauro Carvalho Chehab 	}
4563785bc17SMauro Carvalho Chehab 
4573785bc17SMauro Carvalho Chehab 	/* initialising and submitting iso urbs */
4583785bc17SMauro Carvalho Chehab 	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4593785bc17SMauro Carvalho Chehab 		int frame_offset = 0;
4603785bc17SMauro Carvalho Chehab 		struct urb *urb = fc_usb->iso_urb[i];
461*4a58d390SMauro Carvalho Chehab 		deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",
462*4a58d390SMauro Carvalho Chehab 		       i, buffer_offset);
4633785bc17SMauro Carvalho Chehab 
4643785bc17SMauro Carvalho Chehab 		urb->dev = fc_usb->udev;
4653785bc17SMauro Carvalho Chehab 		urb->context = fc_usb;
4663785bc17SMauro Carvalho Chehab 		urb->complete = flexcop_usb_urb_complete;
4673785bc17SMauro Carvalho Chehab 		urb->pipe = B2C2_USB_DATA_PIPE;
4683785bc17SMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP;
4693785bc17SMauro Carvalho Chehab 		urb->interval = 1;
4703785bc17SMauro Carvalho Chehab 		urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
4713785bc17SMauro Carvalho Chehab 		urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
4723785bc17SMauro Carvalho Chehab 		urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
4733785bc17SMauro Carvalho Chehab 
4743785bc17SMauro Carvalho Chehab 		buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
4753785bc17SMauro Carvalho Chehab 		for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
4763785bc17SMauro Carvalho Chehab 			deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
4773785bc17SMauro Carvalho Chehab 					i, j, frame_offset);
4783785bc17SMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = frame_offset;
4793785bc17SMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = frame_size;
4803785bc17SMauro Carvalho Chehab 			frame_offset += frame_size;
4813785bc17SMauro Carvalho Chehab 		}
4823785bc17SMauro Carvalho Chehab 
4833785bc17SMauro Carvalho Chehab 		if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
4843785bc17SMauro Carvalho Chehab 			err("submitting urb %d failed with %d.", i, ret);
4853785bc17SMauro Carvalho Chehab 			goto urb_error;
4863785bc17SMauro Carvalho Chehab 		}
4873785bc17SMauro Carvalho Chehab 		deb_ts("submitted urb no. %d.\n",i);
4883785bc17SMauro Carvalho Chehab 	}
4893785bc17SMauro Carvalho Chehab 
4903785bc17SMauro Carvalho Chehab 	/* SRAM */
4913785bc17SMauro Carvalho Chehab 	flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
4923785bc17SMauro Carvalho Chehab 			FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
4933785bc17SMauro Carvalho Chehab 			FC_SRAM_DEST_TARGET_WAN_USB);
4943785bc17SMauro Carvalho Chehab 	flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
4953785bc17SMauro Carvalho Chehab 	flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
4963785bc17SMauro Carvalho Chehab 	return 0;
4973785bc17SMauro Carvalho Chehab 
4983785bc17SMauro Carvalho Chehab urb_error:
4993785bc17SMauro Carvalho Chehab 	flexcop_usb_transfer_exit(fc_usb);
5003785bc17SMauro Carvalho Chehab 	return ret;
5013785bc17SMauro Carvalho Chehab }
5023785bc17SMauro Carvalho Chehab 
5033785bc17SMauro Carvalho Chehab static int flexcop_usb_init(struct flexcop_usb *fc_usb)
5043785bc17SMauro Carvalho Chehab {
5053785bc17SMauro Carvalho Chehab 	/* use the alternate setting with the larges buffer */
5063785bc17SMauro Carvalho Chehab 	usb_set_interface(fc_usb->udev,0,1);
5073785bc17SMauro Carvalho Chehab 	switch (fc_usb->udev->speed) {
5083785bc17SMauro Carvalho Chehab 	case USB_SPEED_LOW:
5093785bc17SMauro Carvalho Chehab 		err("cannot handle USB speed because it is too slow.");
5103785bc17SMauro Carvalho Chehab 		return -ENODEV;
5113785bc17SMauro Carvalho Chehab 		break;
5123785bc17SMauro Carvalho Chehab 	case USB_SPEED_FULL:
5133785bc17SMauro Carvalho Chehab 		info("running at FULL speed.");
5143785bc17SMauro Carvalho Chehab 		break;
5153785bc17SMauro Carvalho Chehab 	case USB_SPEED_HIGH:
5163785bc17SMauro Carvalho Chehab 		info("running at HIGH speed.");
5173785bc17SMauro Carvalho Chehab 		break;
5183785bc17SMauro Carvalho Chehab 	case USB_SPEED_UNKNOWN: /* fall through */
5193785bc17SMauro Carvalho Chehab 	default:
5203785bc17SMauro Carvalho Chehab 		err("cannot handle USB speed because it is unknown.");
5213785bc17SMauro Carvalho Chehab 		return -ENODEV;
5223785bc17SMauro Carvalho Chehab 	}
5233785bc17SMauro Carvalho Chehab 	usb_set_intfdata(fc_usb->uintf, fc_usb);
5243785bc17SMauro Carvalho Chehab 	return 0;
5253785bc17SMauro Carvalho Chehab }
5263785bc17SMauro Carvalho Chehab 
5273785bc17SMauro Carvalho Chehab static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
5283785bc17SMauro Carvalho Chehab {
5293785bc17SMauro Carvalho Chehab 	usb_set_intfdata(fc_usb->uintf, NULL);
5303785bc17SMauro Carvalho Chehab }
5313785bc17SMauro Carvalho Chehab 
5323785bc17SMauro Carvalho Chehab static int flexcop_usb_probe(struct usb_interface *intf,
5333785bc17SMauro Carvalho Chehab 		const struct usb_device_id *id)
5343785bc17SMauro Carvalho Chehab {
5353785bc17SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(intf);
5363785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = NULL;
5373785bc17SMauro Carvalho Chehab 	struct flexcop_device *fc = NULL;
5383785bc17SMauro Carvalho Chehab 	int ret;
5393785bc17SMauro Carvalho Chehab 
5403785bc17SMauro Carvalho Chehab 	if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
5413785bc17SMauro Carvalho Chehab 		err("out of memory\n");
5423785bc17SMauro Carvalho Chehab 		return -ENOMEM;
5433785bc17SMauro Carvalho Chehab 	}
5443785bc17SMauro Carvalho Chehab 
5453785bc17SMauro Carvalho Chehab 	/* general flexcop init */
5463785bc17SMauro Carvalho Chehab 	fc_usb = fc->bus_specific;
5473785bc17SMauro Carvalho Chehab 	fc_usb->fc_dev = fc;
548b430eabaSMauro Carvalho Chehab 	mutex_init(&fc_usb->data_mutex);
5493785bc17SMauro Carvalho Chehab 
5503785bc17SMauro Carvalho Chehab 	fc->read_ibi_reg  = flexcop_usb_read_ibi_reg;
5513785bc17SMauro Carvalho Chehab 	fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
5523785bc17SMauro Carvalho Chehab 	fc->i2c_request = flexcop_usb_i2c_request;
5533785bc17SMauro Carvalho Chehab 	fc->get_mac_addr = flexcop_usb_get_mac_addr;
5543785bc17SMauro Carvalho Chehab 
5553785bc17SMauro Carvalho Chehab 	fc->stream_control = flexcop_usb_stream_control;
5563785bc17SMauro Carvalho Chehab 
5573785bc17SMauro Carvalho Chehab 	fc->pid_filtering = 1;
5583785bc17SMauro Carvalho Chehab 	fc->bus_type = FC_USB;
5593785bc17SMauro Carvalho Chehab 
5603785bc17SMauro Carvalho Chehab 	fc->dev = &udev->dev;
5613785bc17SMauro Carvalho Chehab 	fc->owner = THIS_MODULE;
5623785bc17SMauro Carvalho Chehab 
5633785bc17SMauro Carvalho Chehab 	/* bus specific part */
5643785bc17SMauro Carvalho Chehab 	fc_usb->udev = udev;
5653785bc17SMauro Carvalho Chehab 	fc_usb->uintf = intf;
5663785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_usb_init(fc_usb)) != 0)
5673785bc17SMauro Carvalho Chehab 		goto err_kfree;
5683785bc17SMauro Carvalho Chehab 
5693785bc17SMauro Carvalho Chehab 	/* init flexcop */
5703785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_device_initialize(fc)) != 0)
5713785bc17SMauro Carvalho Chehab 		goto err_usb_exit;
5723785bc17SMauro Carvalho Chehab 
5733785bc17SMauro Carvalho Chehab 	/* xfer init */
5743785bc17SMauro Carvalho Chehab 	if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
5753785bc17SMauro Carvalho Chehab 		goto err_fc_exit;
5763785bc17SMauro Carvalho Chehab 
5773785bc17SMauro Carvalho Chehab 	info("%s successfully initialized and connected.", DRIVER_NAME);
5783785bc17SMauro Carvalho Chehab 	return 0;
5793785bc17SMauro Carvalho Chehab 
5803785bc17SMauro Carvalho Chehab err_fc_exit:
5813785bc17SMauro Carvalho Chehab 	flexcop_device_exit(fc);
5823785bc17SMauro Carvalho Chehab err_usb_exit:
5833785bc17SMauro Carvalho Chehab 	flexcop_usb_exit(fc_usb);
5843785bc17SMauro Carvalho Chehab err_kfree:
5853785bc17SMauro Carvalho Chehab 	flexcop_device_kfree(fc);
5863785bc17SMauro Carvalho Chehab 	return ret;
5873785bc17SMauro Carvalho Chehab }
5883785bc17SMauro Carvalho Chehab 
5893785bc17SMauro Carvalho Chehab static void flexcop_usb_disconnect(struct usb_interface *intf)
5903785bc17SMauro Carvalho Chehab {
5913785bc17SMauro Carvalho Chehab 	struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
5923785bc17SMauro Carvalho Chehab 	flexcop_usb_transfer_exit(fc_usb);
5933785bc17SMauro Carvalho Chehab 	flexcop_device_exit(fc_usb->fc_dev);
5943785bc17SMauro Carvalho Chehab 	flexcop_usb_exit(fc_usb);
5953785bc17SMauro Carvalho Chehab 	flexcop_device_kfree(fc_usb->fc_dev);
5963785bc17SMauro Carvalho Chehab 	info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
5973785bc17SMauro Carvalho Chehab }
5983785bc17SMauro Carvalho Chehab 
5993785bc17SMauro Carvalho Chehab static struct usb_device_id flexcop_usb_table [] = {
6003785bc17SMauro Carvalho Chehab 	{ USB_DEVICE(0x0af7, 0x0101) },
6013785bc17SMauro Carvalho Chehab 	{ }
6023785bc17SMauro Carvalho Chehab };
6033785bc17SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
6043785bc17SMauro Carvalho Chehab 
6053785bc17SMauro Carvalho Chehab /* usb specific object needed to register this driver with the usb subsystem */
6063785bc17SMauro Carvalho Chehab static struct usb_driver flexcop_usb_driver = {
6073785bc17SMauro Carvalho Chehab 	.name		= "b2c2_flexcop_usb",
6083785bc17SMauro Carvalho Chehab 	.probe		= flexcop_usb_probe,
6093785bc17SMauro Carvalho Chehab 	.disconnect = flexcop_usb_disconnect,
6103785bc17SMauro Carvalho Chehab 	.id_table	= flexcop_usb_table,
6113785bc17SMauro Carvalho Chehab };
6123785bc17SMauro Carvalho Chehab 
6133785bc17SMauro Carvalho Chehab module_usb_driver(flexcop_usb_driver);
6143785bc17SMauro Carvalho Chehab 
6153785bc17SMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
6163785bc17SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_NAME);
6173785bc17SMauro Carvalho Chehab MODULE_LICENSE("GPL");
618