18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
48c2ecf20Sopenharmony_ci * flexcop-usb.c - covers the USB part
58c2ecf20Sopenharmony_ci * see flexcop.c for copyright information
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#define FC_LOG_PREFIX "flexcop_usb"
88c2ecf20Sopenharmony_ci#include "flexcop-usb.h"
98c2ecf20Sopenharmony_ci#include "flexcop-common.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/* Version information */
128c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.1"
138c2ecf20Sopenharmony_ci#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
148c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* debug */
178c2ecf20Sopenharmony_ci#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
188c2ecf20Sopenharmony_ci#define dprintk(level,args...) \
198c2ecf20Sopenharmony_ci	do { if ((debug & level)) printk(args); } while (0)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define debug_dump(b, l, method) do {\
228c2ecf20Sopenharmony_ci	int i; \
238c2ecf20Sopenharmony_ci	for (i = 0; i < l; i++) \
248c2ecf20Sopenharmony_ci		method("%02x ", b[i]); \
258c2ecf20Sopenharmony_ci	method("\n"); \
268c2ecf20Sopenharmony_ci} while (0)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define DEBSTATUS ""
298c2ecf20Sopenharmony_ci#else
308c2ecf20Sopenharmony_ci#define dprintk(level, args...)
318c2ecf20Sopenharmony_ci#define debug_dump(b, l, method)
328c2ecf20Sopenharmony_ci#define DEBSTATUS " (debugging is not enabled)"
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int debug;
368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
388c2ecf20Sopenharmony_ci#undef DEBSTATUS
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define deb_info(args...) dprintk(0x01, args)
418c2ecf20Sopenharmony_ci#define deb_ts(args...) dprintk(0x02, args)
428c2ecf20Sopenharmony_ci#define deb_ctrl(args...) dprintk(0x04, args)
438c2ecf20Sopenharmony_ci#define deb_i2c(args...) dprintk(0x08, args)
448c2ecf20Sopenharmony_ci#define deb_v8(args...) dprintk(0x10, args)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
478c2ecf20Sopenharmony_ci * in the IBI address, to make the V8 code simpler.
488c2ecf20Sopenharmony_ci * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
498c2ecf20Sopenharmony_ci *                  in general: 0000 0HHH 000L LL00
508c2ecf20Sopenharmony_ci * IBI ADDRESS FORMAT:                    RHHH BLLL
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * where R is the read(1)/write(0) bit, B is the busy bit
538c2ecf20Sopenharmony_ci * and HHH and LLL are the two sets of three bits from the PCI address.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ci#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
568c2ecf20Sopenharmony_ci	(((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
578c2ecf20Sopenharmony_ci#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
588c2ecf20Sopenharmony_ci	(((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * DKT 020228
628c2ecf20Sopenharmony_ci * - forget about this VENDOR_BUFFER_SIZE, read and write register
638c2ecf20Sopenharmony_ci *   deal with DWORD or 4 bytes, that should be should from now on
648c2ecf20Sopenharmony_ci * - from now on, we don't support anything older than firm 1.00
658c2ecf20Sopenharmony_ci *   I eliminated the write register as a 2 trip of writing hi word and lo word
668c2ecf20Sopenharmony_ci *   and force this to write only 4 bytes at a time.
678c2ecf20Sopenharmony_ci *   NOTE: this should work with all the firmware from 1.00 and newer
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct flexcop_usb *fc_usb = fc->bus_specific;
728c2ecf20Sopenharmony_ci	u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
738c2ecf20Sopenharmony_ci	u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
748c2ecf20Sopenharmony_ci	u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
758c2ecf20Sopenharmony_ci		(read ? 0x80 : 0);
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	mutex_lock(&fc_usb->data_mutex);
798c2ecf20Sopenharmony_ci	if (!read)
808c2ecf20Sopenharmony_ci		memcpy(fc_usb->data, val, sizeof(*val));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ret = usb_control_msg(fc_usb->udev,
838c2ecf20Sopenharmony_ci			read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
848c2ecf20Sopenharmony_ci			request,
858c2ecf20Sopenharmony_ci			request_type, /* 0xc0 read or 0x40 write */
868c2ecf20Sopenharmony_ci			wAddress,
878c2ecf20Sopenharmony_ci			0,
888c2ecf20Sopenharmony_ci			fc_usb->data,
898c2ecf20Sopenharmony_ci			sizeof(u32),
908c2ecf20Sopenharmony_ci			B2C2_WAIT_FOR_OPERATION_RDW);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (ret != sizeof(u32)) {
938c2ecf20Sopenharmony_ci		err("error while %s dword from %d (%d).", read ? "reading" :
948c2ecf20Sopenharmony_ci				"writing", wAddress, wRegOffsPCI);
958c2ecf20Sopenharmony_ci		if (ret >= 0)
968c2ecf20Sopenharmony_ci			ret = -EIO;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (read && ret >= 0)
1008c2ecf20Sopenharmony_ci		memcpy(val, fc_usb->data, sizeof(*val));
1018c2ecf20Sopenharmony_ci	mutex_unlock(&fc_usb->data_mutex);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * DKT 010817 - add support for V8 memory read/write and flash update
1078c2ecf20Sopenharmony_ci */
1088c2ecf20Sopenharmony_cistatic int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
1098c2ecf20Sopenharmony_ci		flexcop_usb_request_t req, u8 page, u16 wAddress,
1108c2ecf20Sopenharmony_ci		u8 *pbBuffer, u32 buflen)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	u8 request_type = USB_TYPE_VENDOR;
1138c2ecf20Sopenharmony_ci	u16 wIndex;
1148c2ecf20Sopenharmony_ci	int nWaitTime, pipe, ret;
1158c2ecf20Sopenharmony_ci	wIndex = page << 8;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (buflen > sizeof(fc_usb->data)) {
1188c2ecf20Sopenharmony_ci		err("Buffer size bigger than max URB control message\n");
1198c2ecf20Sopenharmony_ci		return -EIO;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	switch (req) {
1238c2ecf20Sopenharmony_ci	case B2C2_USB_READ_V8_MEM:
1248c2ecf20Sopenharmony_ci		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
1258c2ecf20Sopenharmony_ci		request_type |= USB_DIR_IN;
1268c2ecf20Sopenharmony_ci		pipe = B2C2_USB_CTRL_PIPE_IN;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case B2C2_USB_WRITE_V8_MEM:
1298c2ecf20Sopenharmony_ci		wIndex |= pbBuffer[0];
1308c2ecf20Sopenharmony_ci		request_type |= USB_DIR_OUT;
1318c2ecf20Sopenharmony_ci		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
1328c2ecf20Sopenharmony_ci		pipe = B2C2_USB_CTRL_PIPE_OUT;
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	case B2C2_USB_FLASH_BLOCK:
1358c2ecf20Sopenharmony_ci		request_type |= USB_DIR_OUT;
1368c2ecf20Sopenharmony_ci		nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
1378c2ecf20Sopenharmony_ci		pipe = B2C2_USB_CTRL_PIPE_OUT;
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	default:
1408c2ecf20Sopenharmony_ci		deb_info("unsupported request for v8_mem_req %x.\n", req);
1418c2ecf20Sopenharmony_ci		return -EINVAL;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
1448c2ecf20Sopenharmony_ci			wAddress, wIndex, buflen);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mutex_lock(&fc_usb->data_mutex);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
1498c2ecf20Sopenharmony_ci		memcpy(fc_usb->data, pbBuffer, buflen);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ret = usb_control_msg(fc_usb->udev, pipe,
1528c2ecf20Sopenharmony_ci			req,
1538c2ecf20Sopenharmony_ci			request_type,
1548c2ecf20Sopenharmony_ci			wAddress,
1558c2ecf20Sopenharmony_ci			wIndex,
1568c2ecf20Sopenharmony_ci			fc_usb->data,
1578c2ecf20Sopenharmony_ci			buflen,
1588c2ecf20Sopenharmony_ci			nWaitTime);
1598c2ecf20Sopenharmony_ci	if (ret != buflen)
1608c2ecf20Sopenharmony_ci		ret = -EIO;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (ret >= 0) {
1638c2ecf20Sopenharmony_ci		ret = 0;
1648c2ecf20Sopenharmony_ci		if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
1658c2ecf20Sopenharmony_ci			memcpy(pbBuffer, fc_usb->data, buflen);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	mutex_unlock(&fc_usb->data_mutex);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	debug_dump(pbBuffer, ret, deb_v8);
1718c2ecf20Sopenharmony_ci	return ret;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define bytes_left_to_read_on_page(paddr,buflen) \
1758c2ecf20Sopenharmony_ci	((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
1768c2ecf20Sopenharmony_ci	 ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
1798c2ecf20Sopenharmony_ci		flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
1808c2ecf20Sopenharmony_ci		u32 addr, int extended, u8 *buf, u32 len)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	int i,ret = 0;
1838c2ecf20Sopenharmony_ci	u16 wMax;
1848c2ecf20Sopenharmony_ci	u32 pagechunk = 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	switch(req) {
1878c2ecf20Sopenharmony_ci	case B2C2_USB_READ_V8_MEM:
1888c2ecf20Sopenharmony_ci		wMax = USB_MEM_READ_MAX;
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci	case B2C2_USB_WRITE_V8_MEM:
1918c2ecf20Sopenharmony_ci		wMax = USB_MEM_WRITE_MAX;
1928c2ecf20Sopenharmony_ci		break;
1938c2ecf20Sopenharmony_ci	case B2C2_USB_FLASH_BLOCK:
1948c2ecf20Sopenharmony_ci		wMax = USB_FLASH_MAX;
1958c2ecf20Sopenharmony_ci		break;
1968c2ecf20Sopenharmony_ci	default:
1978c2ecf20Sopenharmony_ci		return -EINVAL;
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	for (i = 0; i < len;) {
2018c2ecf20Sopenharmony_ci		pagechunk =
2028c2ecf20Sopenharmony_ci			wMax < bytes_left_to_read_on_page(addr, len) ?
2038c2ecf20Sopenharmony_ci				wMax :
2048c2ecf20Sopenharmony_ci				bytes_left_to_read_on_page(addr, len);
2058c2ecf20Sopenharmony_ci		deb_info("%x\n",
2068c2ecf20Sopenharmony_ci			(addr & V8_MEMORY_PAGE_MASK) |
2078c2ecf20Sopenharmony_ci				(V8_MEMORY_EXTENDED*extended));
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		ret = flexcop_usb_v8_memory_req(fc_usb, req,
2108c2ecf20Sopenharmony_ci			page_start + (addr / V8_MEMORY_PAGE_SIZE),
2118c2ecf20Sopenharmony_ci			(addr & V8_MEMORY_PAGE_MASK) |
2128c2ecf20Sopenharmony_ci				(V8_MEMORY_EXTENDED*extended),
2138c2ecf20Sopenharmony_ci			&buf[i], pagechunk);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		if (ret < 0)
2168c2ecf20Sopenharmony_ci			return ret;
2178c2ecf20Sopenharmony_ci		addr += pagechunk;
2188c2ecf20Sopenharmony_ci		len -= pagechunk;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
2268c2ecf20Sopenharmony_ci		V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
2278c2ecf20Sopenharmony_ci		fc->dvb_adapter.proposed_mac, 6);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/* usb i2c stuff */
2318c2ecf20Sopenharmony_cistatic int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
2328c2ecf20Sopenharmony_ci		flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
2338c2ecf20Sopenharmony_ci		u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
2368c2ecf20Sopenharmony_ci	u16 wValue, wIndex;
2378c2ecf20Sopenharmony_ci	int nWaitTime, pipe, ret;
2388c2ecf20Sopenharmony_ci	u8 request_type = USB_TYPE_VENDOR;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (buflen > sizeof(fc_usb->data)) {
2418c2ecf20Sopenharmony_ci		err("Buffer size bigger than max URB control message\n");
2428c2ecf20Sopenharmony_ci		return -EIO;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	switch (func) {
2468c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_WRITE:
2478c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_MULTIWRITE:
2488c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_REPEATWRITE:
2498c2ecf20Sopenharmony_ci		/* DKT 020208 - add this to support special case of DiSEqC */
2508c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_CHECKWRITE:
2518c2ecf20Sopenharmony_ci		pipe = B2C2_USB_CTRL_PIPE_OUT;
2528c2ecf20Sopenharmony_ci		nWaitTime = 2000;
2538c2ecf20Sopenharmony_ci		request_type |= USB_DIR_OUT;
2548c2ecf20Sopenharmony_ci		break;
2558c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_READ:
2568c2ecf20Sopenharmony_ci	case USB_FUNC_I2C_REPEATREAD:
2578c2ecf20Sopenharmony_ci		pipe = B2C2_USB_CTRL_PIPE_IN;
2588c2ecf20Sopenharmony_ci		nWaitTime = 2000;
2598c2ecf20Sopenharmony_ci		request_type |= USB_DIR_IN;
2608c2ecf20Sopenharmony_ci		break;
2618c2ecf20Sopenharmony_ci	default:
2628c2ecf20Sopenharmony_ci		deb_info("unsupported function for i2c_req %x\n", func);
2638c2ecf20Sopenharmony_ci		return -EINVAL;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	wValue = (func << 8) | (i2c->port << 4);
2668c2ecf20Sopenharmony_ci	wIndex = (chipaddr << 8 ) | addr;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
2698c2ecf20Sopenharmony_ci			func, request_type, req,
2708c2ecf20Sopenharmony_ci			wValue & 0xff, wValue >> 8,
2718c2ecf20Sopenharmony_ci			wIndex & 0xff, wIndex >> 8);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	mutex_lock(&fc_usb->data_mutex);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
2768c2ecf20Sopenharmony_ci		memcpy(fc_usb->data, buf, buflen);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	ret = usb_control_msg(fc_usb->udev, pipe,
2798c2ecf20Sopenharmony_ci			req,
2808c2ecf20Sopenharmony_ci			request_type,
2818c2ecf20Sopenharmony_ci			wValue,
2828c2ecf20Sopenharmony_ci			wIndex,
2838c2ecf20Sopenharmony_ci			fc_usb->data,
2848c2ecf20Sopenharmony_ci			buflen,
2858c2ecf20Sopenharmony_ci			nWaitTime);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (ret != buflen)
2888c2ecf20Sopenharmony_ci		ret = -EIO;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (ret >= 0) {
2918c2ecf20Sopenharmony_ci		ret = 0;
2928c2ecf20Sopenharmony_ci		if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
2938c2ecf20Sopenharmony_ci			memcpy(buf, fc_usb->data, buflen);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	mutex_unlock(&fc_usb->data_mutex);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return ret;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* actual bus specific access functions,
3028c2ecf20Sopenharmony_ci   make sure prototype are/will be equal to pci */
3038c2ecf20Sopenharmony_cistatic flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
3048c2ecf20Sopenharmony_ci	flexcop_ibi_register reg)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	flexcop_ibi_value val;
3078c2ecf20Sopenharmony_ci	val.raw = 0;
3088c2ecf20Sopenharmony_ci	flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
3098c2ecf20Sopenharmony_ci	return val;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
3138c2ecf20Sopenharmony_ci		flexcop_ibi_register reg, flexcop_ibi_value val)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
3198c2ecf20Sopenharmony_ci		flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	if (op == FC_READ)
3228c2ecf20Sopenharmony_ci		return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3238c2ecf20Sopenharmony_ci				USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
3248c2ecf20Sopenharmony_ci	else
3258c2ecf20Sopenharmony_ci		return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
3268c2ecf20Sopenharmony_ci				USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
3308c2ecf20Sopenharmony_ci	u8 *buffer, int buffer_length)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u8 *b;
3338c2ecf20Sopenharmony_ci	int l;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
3368c2ecf20Sopenharmony_ci		fc_usb->tmp_buffer_length, buffer_length);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (fc_usb->tmp_buffer_length > 0) {
3398c2ecf20Sopenharmony_ci		memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
3408c2ecf20Sopenharmony_ci				buffer_length);
3418c2ecf20Sopenharmony_ci		fc_usb->tmp_buffer_length += buffer_length;
3428c2ecf20Sopenharmony_ci		b = fc_usb->tmp_buffer;
3438c2ecf20Sopenharmony_ci		l = fc_usb->tmp_buffer_length;
3448c2ecf20Sopenharmony_ci	} else {
3458c2ecf20Sopenharmony_ci		b=buffer;
3468c2ecf20Sopenharmony_ci		l=buffer_length;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	while (l >= 190) {
3508c2ecf20Sopenharmony_ci		if (*b == 0xff) {
3518c2ecf20Sopenharmony_ci			switch (*(b+1) & 0x03) {
3528c2ecf20Sopenharmony_ci			case 0x01: /* media packet */
3538c2ecf20Sopenharmony_ci				if (*(b+2) == 0x47)
3548c2ecf20Sopenharmony_ci					flexcop_pass_dmx_packets(
3558c2ecf20Sopenharmony_ci							fc_usb->fc_dev, b+2, 1);
3568c2ecf20Sopenharmony_ci				else
3578c2ecf20Sopenharmony_ci					deb_ts("not ts packet %*ph\n", 4, b+2);
3588c2ecf20Sopenharmony_ci				b += 190;
3598c2ecf20Sopenharmony_ci				l -= 190;
3608c2ecf20Sopenharmony_ci				break;
3618c2ecf20Sopenharmony_ci			default:
3628c2ecf20Sopenharmony_ci				deb_ts("wrong packet type\n");
3638c2ecf20Sopenharmony_ci				l = 0;
3648c2ecf20Sopenharmony_ci				break;
3658c2ecf20Sopenharmony_ci			}
3668c2ecf20Sopenharmony_ci		} else {
3678c2ecf20Sopenharmony_ci			deb_ts("wrong header\n");
3688c2ecf20Sopenharmony_ci			l = 0;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (l>0)
3738c2ecf20Sopenharmony_ci		memcpy(fc_usb->tmp_buffer, b, l);
3748c2ecf20Sopenharmony_ci	fc_usb->tmp_buffer_length = l;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void flexcop_usb_urb_complete(struct urb *urb)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct flexcop_usb *fc_usb = urb->context;
3808c2ecf20Sopenharmony_ci	int i;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (urb->actual_length > 0)
3838c2ecf20Sopenharmony_ci		deb_ts("urb completed, bufsize: %d actlen; %d\n",
3848c2ecf20Sopenharmony_ci			urb->transfer_buffer_length, urb->actual_length);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
3878c2ecf20Sopenharmony_ci		if (urb->iso_frame_desc[i].status < 0) {
3888c2ecf20Sopenharmony_ci			err("iso frame descriptor %d has an error: %d\n", i,
3898c2ecf20Sopenharmony_ci				urb->iso_frame_desc[i].status);
3908c2ecf20Sopenharmony_ci		} else
3918c2ecf20Sopenharmony_ci			if (urb->iso_frame_desc[i].actual_length > 0) {
3928c2ecf20Sopenharmony_ci				deb_ts("passed %d bytes to the demux\n",
3938c2ecf20Sopenharmony_ci					urb->iso_frame_desc[i].actual_length);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci				flexcop_usb_process_frame(fc_usb,
3968c2ecf20Sopenharmony_ci					urb->transfer_buffer +
3978c2ecf20Sopenharmony_ci						urb->iso_frame_desc[i].offset,
3988c2ecf20Sopenharmony_ci					urb->iso_frame_desc[i].actual_length);
3998c2ecf20Sopenharmony_ci			}
4008c2ecf20Sopenharmony_ci		urb->iso_frame_desc[i].status = 0;
4018c2ecf20Sopenharmony_ci		urb->iso_frame_desc[i].actual_length = 0;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci	usb_submit_urb(urb,GFP_ATOMIC);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	/* submit/kill iso packets */
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	int i;
4158c2ecf20Sopenharmony_ci	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
4168c2ecf20Sopenharmony_ci		if (fc_usb->iso_urb[i] != NULL) {
4178c2ecf20Sopenharmony_ci			deb_ts("unlinking/killing urb no. %d\n",i);
4188c2ecf20Sopenharmony_ci			usb_kill_urb(fc_usb->iso_urb[i]);
4198c2ecf20Sopenharmony_ci			usb_free_urb(fc_usb->iso_urb[i]);
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	usb_free_coherent(fc_usb->udev, fc_usb->buffer_size,
4238c2ecf20Sopenharmony_ci			  fc_usb->iso_buffer, fc_usb->dma_addr);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	u16 frame_size = le16_to_cpu(
4308c2ecf20Sopenharmony_ci		fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
4318c2ecf20Sopenharmony_ci	int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
4328c2ecf20Sopenharmony_ci		frame_size, i, j, ret;
4338c2ecf20Sopenharmony_ci	int buffer_offset = 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
4368c2ecf20Sopenharmony_ci	       B2C2_USB_NUM_ISO_URB,
4378c2ecf20Sopenharmony_ci			B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
4408c2ecf20Sopenharmony_ci			bufsize, GFP_KERNEL, &fc_usb->dma_addr);
4418c2ecf20Sopenharmony_ci	if (fc_usb->iso_buffer == NULL)
4428c2ecf20Sopenharmony_ci		return -ENOMEM;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	memset(fc_usb->iso_buffer, 0, bufsize);
4458c2ecf20Sopenharmony_ci	fc_usb->buffer_size = bufsize;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* creating iso urbs */
4488c2ecf20Sopenharmony_ci	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4498c2ecf20Sopenharmony_ci		fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
4508c2ecf20Sopenharmony_ci			GFP_ATOMIC);
4518c2ecf20Sopenharmony_ci		if (fc_usb->iso_urb[i] == NULL) {
4528c2ecf20Sopenharmony_ci			ret = -ENOMEM;
4538c2ecf20Sopenharmony_ci			goto urb_error;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* initialising and submitting iso urbs */
4588c2ecf20Sopenharmony_ci	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
4598c2ecf20Sopenharmony_ci		int frame_offset = 0;
4608c2ecf20Sopenharmony_ci		struct urb *urb = fc_usb->iso_urb[i];
4618c2ecf20Sopenharmony_ci		deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",
4628c2ecf20Sopenharmony_ci		       i, buffer_offset);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		urb->dev = fc_usb->udev;
4658c2ecf20Sopenharmony_ci		urb->context = fc_usb;
4668c2ecf20Sopenharmony_ci		urb->complete = flexcop_usb_urb_complete;
4678c2ecf20Sopenharmony_ci		urb->pipe = B2C2_USB_DATA_PIPE;
4688c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP;
4698c2ecf20Sopenharmony_ci		urb->interval = 1;
4708c2ecf20Sopenharmony_ci		urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
4718c2ecf20Sopenharmony_ci		urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
4728c2ecf20Sopenharmony_ci		urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
4758c2ecf20Sopenharmony_ci		for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
4768c2ecf20Sopenharmony_ci			deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
4778c2ecf20Sopenharmony_ci					i, j, frame_offset);
4788c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].offset = frame_offset;
4798c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].length = frame_size;
4808c2ecf20Sopenharmony_ci			frame_offset += frame_size;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
4848c2ecf20Sopenharmony_ci			err("submitting urb %d failed with %d.", i, ret);
4858c2ecf20Sopenharmony_ci			goto urb_error;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		deb_ts("submitted urb no. %d.\n",i);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* SRAM */
4918c2ecf20Sopenharmony_ci	flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
4928c2ecf20Sopenharmony_ci			FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
4938c2ecf20Sopenharmony_ci			FC_SRAM_DEST_TARGET_WAN_USB);
4948c2ecf20Sopenharmony_ci	flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
4958c2ecf20Sopenharmony_ci	flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ciurb_error:
4998c2ecf20Sopenharmony_ci	flexcop_usb_transfer_exit(fc_usb);
5008c2ecf20Sopenharmony_ci	return ret;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic int flexcop_usb_init(struct flexcop_usb *fc_usb)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	/* use the alternate setting with the larges buffer */
5068c2ecf20Sopenharmony_ci	int ret = usb_set_interface(fc_usb->udev, 0, 1);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (ret) {
5098c2ecf20Sopenharmony_ci		err("set interface failed.");
5108c2ecf20Sopenharmony_ci		return ret;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1)
5148c2ecf20Sopenharmony_ci		return -ENODEV;
5158c2ecf20Sopenharmony_ci	if (!usb_endpoint_is_isoc_in(&fc_usb->uintf->cur_altsetting->endpoint[0].desc))
5168c2ecf20Sopenharmony_ci		return -ENODEV;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	switch (fc_usb->udev->speed) {
5198c2ecf20Sopenharmony_ci	case USB_SPEED_LOW:
5208c2ecf20Sopenharmony_ci		err("cannot handle USB speed because it is too slow.");
5218c2ecf20Sopenharmony_ci		return -ENODEV;
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
5248c2ecf20Sopenharmony_ci		info("running at FULL speed.");
5258c2ecf20Sopenharmony_ci		break;
5268c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
5278c2ecf20Sopenharmony_ci		info("running at HIGH speed.");
5288c2ecf20Sopenharmony_ci		break;
5298c2ecf20Sopenharmony_ci	case USB_SPEED_UNKNOWN:
5308c2ecf20Sopenharmony_ci	default:
5318c2ecf20Sopenharmony_ci		err("cannot handle USB speed because it is unknown.");
5328c2ecf20Sopenharmony_ci		return -ENODEV;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	usb_set_intfdata(fc_usb->uintf, fc_usb);
5358c2ecf20Sopenharmony_ci	return 0;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic void flexcop_usb_exit(struct flexcop_usb *fc_usb)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	usb_set_intfdata(fc_usb->uintf, NULL);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int flexcop_usb_probe(struct usb_interface *intf,
5448c2ecf20Sopenharmony_ci		const struct usb_device_id *id)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
5478c2ecf20Sopenharmony_ci	struct flexcop_usb *fc_usb = NULL;
5488c2ecf20Sopenharmony_ci	struct flexcop_device *fc = NULL;
5498c2ecf20Sopenharmony_ci	int ret;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
5528c2ecf20Sopenharmony_ci		err("out of memory\n");
5538c2ecf20Sopenharmony_ci		return -ENOMEM;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* general flexcop init */
5578c2ecf20Sopenharmony_ci	fc_usb = fc->bus_specific;
5588c2ecf20Sopenharmony_ci	fc_usb->fc_dev = fc;
5598c2ecf20Sopenharmony_ci	mutex_init(&fc_usb->data_mutex);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	fc->read_ibi_reg  = flexcop_usb_read_ibi_reg;
5628c2ecf20Sopenharmony_ci	fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
5638c2ecf20Sopenharmony_ci	fc->i2c_request = flexcop_usb_i2c_request;
5648c2ecf20Sopenharmony_ci	fc->get_mac_addr = flexcop_usb_get_mac_addr;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	fc->stream_control = flexcop_usb_stream_control;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	fc->pid_filtering = 1;
5698c2ecf20Sopenharmony_ci	fc->bus_type = FC_USB;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	fc->dev = &udev->dev;
5728c2ecf20Sopenharmony_ci	fc->owner = THIS_MODULE;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* bus specific part */
5758c2ecf20Sopenharmony_ci	fc_usb->udev = udev;
5768c2ecf20Sopenharmony_ci	fc_usb->uintf = intf;
5778c2ecf20Sopenharmony_ci	if ((ret = flexcop_usb_init(fc_usb)) != 0)
5788c2ecf20Sopenharmony_ci		goto err_kfree;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/* init flexcop */
5818c2ecf20Sopenharmony_ci	if ((ret = flexcop_device_initialize(fc)) != 0)
5828c2ecf20Sopenharmony_ci		goto err_usb_exit;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* xfer init */
5858c2ecf20Sopenharmony_ci	if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
5868c2ecf20Sopenharmony_ci		goto err_fc_exit;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	info("%s successfully initialized and connected.", DRIVER_NAME);
5898c2ecf20Sopenharmony_ci	return 0;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cierr_fc_exit:
5928c2ecf20Sopenharmony_ci	flexcop_device_exit(fc);
5938c2ecf20Sopenharmony_cierr_usb_exit:
5948c2ecf20Sopenharmony_ci	flexcop_usb_exit(fc_usb);
5958c2ecf20Sopenharmony_cierr_kfree:
5968c2ecf20Sopenharmony_ci	flexcop_device_kfree(fc);
5978c2ecf20Sopenharmony_ci	return ret;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic void flexcop_usb_disconnect(struct usb_interface *intf)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
6038c2ecf20Sopenharmony_ci	flexcop_usb_transfer_exit(fc_usb);
6048c2ecf20Sopenharmony_ci	flexcop_device_exit(fc_usb->fc_dev);
6058c2ecf20Sopenharmony_ci	flexcop_usb_exit(fc_usb);
6068c2ecf20Sopenharmony_ci	flexcop_device_kfree(fc_usb->fc_dev);
6078c2ecf20Sopenharmony_ci	info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic const struct usb_device_id flexcop_usb_table[] = {
6118c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0af7, 0x0101) },
6128c2ecf20Sopenharmony_ci	{ }
6138c2ecf20Sopenharmony_ci};
6148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (usb, flexcop_usb_table);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */
6178c2ecf20Sopenharmony_cistatic struct usb_driver flexcop_usb_driver = {
6188c2ecf20Sopenharmony_ci	.name		= "b2c2_flexcop_usb",
6198c2ecf20Sopenharmony_ci	.probe		= flexcop_usb_probe,
6208c2ecf20Sopenharmony_ci	.disconnect = flexcop_usb_disconnect,
6218c2ecf20Sopenharmony_ci	.id_table	= flexcop_usb_table,
6228c2ecf20Sopenharmony_ci};
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cimodule_usb_driver(flexcop_usb_driver);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
6278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_NAME);
6288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
629