1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
4*4882a593Smuzhiyun * flexcop-usb.c - covers the USB part
5*4882a593Smuzhiyun * see flexcop.c for copyright information
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun #define FC_LOG_PREFIX "flexcop_usb"
8*4882a593Smuzhiyun #include "flexcop-usb.h"
9*4882a593Smuzhiyun #include "flexcop-common.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun /* Version information */
12*4882a593Smuzhiyun #define DRIVER_VERSION "0.1"
13*4882a593Smuzhiyun #define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
14*4882a593Smuzhiyun #define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de>"
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun /* debug */
17*4882a593Smuzhiyun #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
18*4882a593Smuzhiyun #define dprintk(level,args...) \
19*4882a593Smuzhiyun do { if ((debug & level)) printk(args); } while (0)
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #define debug_dump(b, l, method) do {\
22*4882a593Smuzhiyun int i; \
23*4882a593Smuzhiyun for (i = 0; i < l; i++) \
24*4882a593Smuzhiyun method("%02x ", b[i]); \
25*4882a593Smuzhiyun method("\n"); \
26*4882a593Smuzhiyun } while (0)
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define DEBSTATUS ""
29*4882a593Smuzhiyun #else
30*4882a593Smuzhiyun #define dprintk(level, args...)
31*4882a593Smuzhiyun #define debug_dump(b, l, method)
32*4882a593Smuzhiyun #define DEBSTATUS " (debugging is not enabled)"
33*4882a593Smuzhiyun #endif
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static int debug;
36*4882a593Smuzhiyun module_param(debug, int, 0644);
37*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
38*4882a593Smuzhiyun #undef DEBSTATUS
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define deb_info(args...) dprintk(0x01, args)
41*4882a593Smuzhiyun #define deb_ts(args...) dprintk(0x02, args)
42*4882a593Smuzhiyun #define deb_ctrl(args...) dprintk(0x04, args)
43*4882a593Smuzhiyun #define deb_i2c(args...) dprintk(0x08, args)
44*4882a593Smuzhiyun #define deb_v8(args...) dprintk(0x10, args)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
47*4882a593Smuzhiyun * in the IBI address, to make the V8 code simpler.
48*4882a593Smuzhiyun * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
49*4882a593Smuzhiyun * in general: 0000 0HHH 000L LL00
50*4882a593Smuzhiyun * IBI ADDRESS FORMAT: RHHH BLLL
51*4882a593Smuzhiyun *
52*4882a593Smuzhiyun * where R is the read(1)/write(0) bit, B is the busy bit
53*4882a593Smuzhiyun * and HHH and LLL are the two sets of three bits from the PCI address.
54*4882a593Smuzhiyun */
55*4882a593Smuzhiyun #define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
56*4882a593Smuzhiyun (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
57*4882a593Smuzhiyun #define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
58*4882a593Smuzhiyun (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun /*
61*4882a593Smuzhiyun * DKT 020228
62*4882a593Smuzhiyun * - forget about this VENDOR_BUFFER_SIZE, read and write register
63*4882a593Smuzhiyun * deal with DWORD or 4 bytes, that should be should from now on
64*4882a593Smuzhiyun * - from now on, we don't support anything older than firm 1.00
65*4882a593Smuzhiyun * I eliminated the write register as a 2 trip of writing hi word and lo word
66*4882a593Smuzhiyun * and force this to write only 4 bytes at a time.
67*4882a593Smuzhiyun * NOTE: this should work with all the firmware from 1.00 and newer
68*4882a593Smuzhiyun */
flexcop_usb_readwrite_dw(struct flexcop_device * fc,u16 wRegOffsPCI,u32 * val,u8 read)69*4882a593Smuzhiyun static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun struct flexcop_usb *fc_usb = fc->bus_specific;
72*4882a593Smuzhiyun u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
73*4882a593Smuzhiyun u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
74*4882a593Smuzhiyun u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
75*4882a593Smuzhiyun (read ? 0x80 : 0);
76*4882a593Smuzhiyun int ret;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun mutex_lock(&fc_usb->data_mutex);
79*4882a593Smuzhiyun if (!read)
80*4882a593Smuzhiyun memcpy(fc_usb->data, val, sizeof(*val));
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun ret = usb_control_msg(fc_usb->udev,
83*4882a593Smuzhiyun read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
84*4882a593Smuzhiyun request,
85*4882a593Smuzhiyun request_type, /* 0xc0 read or 0x40 write */
86*4882a593Smuzhiyun wAddress,
87*4882a593Smuzhiyun 0,
88*4882a593Smuzhiyun fc_usb->data,
89*4882a593Smuzhiyun sizeof(u32),
90*4882a593Smuzhiyun B2C2_WAIT_FOR_OPERATION_RDW);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun if (ret != sizeof(u32)) {
93*4882a593Smuzhiyun err("error while %s dword from %d (%d).", read ? "reading" :
94*4882a593Smuzhiyun "writing", wAddress, wRegOffsPCI);
95*4882a593Smuzhiyun if (ret >= 0)
96*4882a593Smuzhiyun ret = -EIO;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (read && ret >= 0)
100*4882a593Smuzhiyun memcpy(val, fc_usb->data, sizeof(*val));
101*4882a593Smuzhiyun mutex_unlock(&fc_usb->data_mutex);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return ret;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun * DKT 010817 - add support for V8 memory read/write and flash update
107*4882a593Smuzhiyun */
flexcop_usb_v8_memory_req(struct flexcop_usb * fc_usb,flexcop_usb_request_t req,u8 page,u16 wAddress,u8 * pbBuffer,u32 buflen)108*4882a593Smuzhiyun static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
109*4882a593Smuzhiyun flexcop_usb_request_t req, u8 page, u16 wAddress,
110*4882a593Smuzhiyun u8 *pbBuffer, u32 buflen)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun u8 request_type = USB_TYPE_VENDOR;
113*4882a593Smuzhiyun u16 wIndex;
114*4882a593Smuzhiyun int nWaitTime, pipe, ret;
115*4882a593Smuzhiyun wIndex = page << 8;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (buflen > sizeof(fc_usb->data)) {
118*4882a593Smuzhiyun err("Buffer size bigger than max URB control message\n");
119*4882a593Smuzhiyun return -EIO;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun switch (req) {
123*4882a593Smuzhiyun case B2C2_USB_READ_V8_MEM:
124*4882a593Smuzhiyun nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
125*4882a593Smuzhiyun request_type |= USB_DIR_IN;
126*4882a593Smuzhiyun pipe = B2C2_USB_CTRL_PIPE_IN;
127*4882a593Smuzhiyun break;
128*4882a593Smuzhiyun case B2C2_USB_WRITE_V8_MEM:
129*4882a593Smuzhiyun wIndex |= pbBuffer[0];
130*4882a593Smuzhiyun request_type |= USB_DIR_OUT;
131*4882a593Smuzhiyun nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
132*4882a593Smuzhiyun pipe = B2C2_USB_CTRL_PIPE_OUT;
133*4882a593Smuzhiyun break;
134*4882a593Smuzhiyun case B2C2_USB_FLASH_BLOCK:
135*4882a593Smuzhiyun request_type |= USB_DIR_OUT;
136*4882a593Smuzhiyun nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
137*4882a593Smuzhiyun pipe = B2C2_USB_CTRL_PIPE_OUT;
138*4882a593Smuzhiyun break;
139*4882a593Smuzhiyun default:
140*4882a593Smuzhiyun deb_info("unsupported request for v8_mem_req %x.\n", req);
141*4882a593Smuzhiyun return -EINVAL;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
144*4882a593Smuzhiyun wAddress, wIndex, buflen);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun mutex_lock(&fc_usb->data_mutex);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
149*4882a593Smuzhiyun memcpy(fc_usb->data, pbBuffer, buflen);
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun ret = usb_control_msg(fc_usb->udev, pipe,
152*4882a593Smuzhiyun req,
153*4882a593Smuzhiyun request_type,
154*4882a593Smuzhiyun wAddress,
155*4882a593Smuzhiyun wIndex,
156*4882a593Smuzhiyun fc_usb->data,
157*4882a593Smuzhiyun buflen,
158*4882a593Smuzhiyun nWaitTime);
159*4882a593Smuzhiyun if (ret != buflen)
160*4882a593Smuzhiyun ret = -EIO;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (ret >= 0) {
163*4882a593Smuzhiyun ret = 0;
164*4882a593Smuzhiyun if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
165*4882a593Smuzhiyun memcpy(pbBuffer, fc_usb->data, buflen);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun mutex_unlock(&fc_usb->data_mutex);
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun debug_dump(pbBuffer, ret, deb_v8);
171*4882a593Smuzhiyun return ret;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun #define bytes_left_to_read_on_page(paddr,buflen) \
175*4882a593Smuzhiyun ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
176*4882a593Smuzhiyun ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
177*4882a593Smuzhiyun
flexcop_usb_memory_req(struct flexcop_usb * fc_usb,flexcop_usb_request_t req,flexcop_usb_mem_page_t page_start,u32 addr,int extended,u8 * buf,u32 len)178*4882a593Smuzhiyun static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
179*4882a593Smuzhiyun flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
180*4882a593Smuzhiyun u32 addr, int extended, u8 *buf, u32 len)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun int i,ret = 0;
183*4882a593Smuzhiyun u16 wMax;
184*4882a593Smuzhiyun u32 pagechunk = 0;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun switch(req) {
187*4882a593Smuzhiyun case B2C2_USB_READ_V8_MEM:
188*4882a593Smuzhiyun wMax = USB_MEM_READ_MAX;
189*4882a593Smuzhiyun break;
190*4882a593Smuzhiyun case B2C2_USB_WRITE_V8_MEM:
191*4882a593Smuzhiyun wMax = USB_MEM_WRITE_MAX;
192*4882a593Smuzhiyun break;
193*4882a593Smuzhiyun case B2C2_USB_FLASH_BLOCK:
194*4882a593Smuzhiyun wMax = USB_FLASH_MAX;
195*4882a593Smuzhiyun break;
196*4882a593Smuzhiyun default:
197*4882a593Smuzhiyun return -EINVAL;
198*4882a593Smuzhiyun break;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun for (i = 0; i < len;) {
201*4882a593Smuzhiyun pagechunk =
202*4882a593Smuzhiyun wMax < bytes_left_to_read_on_page(addr, len) ?
203*4882a593Smuzhiyun wMax :
204*4882a593Smuzhiyun bytes_left_to_read_on_page(addr, len);
205*4882a593Smuzhiyun deb_info("%x\n",
206*4882a593Smuzhiyun (addr & V8_MEMORY_PAGE_MASK) |
207*4882a593Smuzhiyun (V8_MEMORY_EXTENDED*extended));
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun ret = flexcop_usb_v8_memory_req(fc_usb, req,
210*4882a593Smuzhiyun page_start + (addr / V8_MEMORY_PAGE_SIZE),
211*4882a593Smuzhiyun (addr & V8_MEMORY_PAGE_MASK) |
212*4882a593Smuzhiyun (V8_MEMORY_EXTENDED*extended),
213*4882a593Smuzhiyun &buf[i], pagechunk);
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (ret < 0)
216*4882a593Smuzhiyun return ret;
217*4882a593Smuzhiyun addr += pagechunk;
218*4882a593Smuzhiyun len -= pagechunk;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun return 0;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
flexcop_usb_get_mac_addr(struct flexcop_device * fc,int extended)223*4882a593Smuzhiyun static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
226*4882a593Smuzhiyun V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
227*4882a593Smuzhiyun fc->dvb_adapter.proposed_mac, 6);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /* usb i2c stuff */
flexcop_usb_i2c_req(struct flexcop_i2c_adapter * i2c,flexcop_usb_request_t req,flexcop_usb_i2c_function_t func,u8 chipaddr,u8 addr,u8 * buf,u8 buflen)231*4882a593Smuzhiyun static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
232*4882a593Smuzhiyun flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
233*4882a593Smuzhiyun u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
236*4882a593Smuzhiyun u16 wValue, wIndex;
237*4882a593Smuzhiyun int nWaitTime, pipe, ret;
238*4882a593Smuzhiyun u8 request_type = USB_TYPE_VENDOR;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun if (buflen > sizeof(fc_usb->data)) {
241*4882a593Smuzhiyun err("Buffer size bigger than max URB control message\n");
242*4882a593Smuzhiyun return -EIO;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun switch (func) {
246*4882a593Smuzhiyun case USB_FUNC_I2C_WRITE:
247*4882a593Smuzhiyun case USB_FUNC_I2C_MULTIWRITE:
248*4882a593Smuzhiyun case USB_FUNC_I2C_REPEATWRITE:
249*4882a593Smuzhiyun /* DKT 020208 - add this to support special case of DiSEqC */
250*4882a593Smuzhiyun case USB_FUNC_I2C_CHECKWRITE:
251*4882a593Smuzhiyun pipe = B2C2_USB_CTRL_PIPE_OUT;
252*4882a593Smuzhiyun nWaitTime = 2000;
253*4882a593Smuzhiyun request_type |= USB_DIR_OUT;
254*4882a593Smuzhiyun break;
255*4882a593Smuzhiyun case USB_FUNC_I2C_READ:
256*4882a593Smuzhiyun case USB_FUNC_I2C_REPEATREAD:
257*4882a593Smuzhiyun pipe = B2C2_USB_CTRL_PIPE_IN;
258*4882a593Smuzhiyun nWaitTime = 2000;
259*4882a593Smuzhiyun request_type |= USB_DIR_IN;
260*4882a593Smuzhiyun break;
261*4882a593Smuzhiyun default:
262*4882a593Smuzhiyun deb_info("unsupported function for i2c_req %x\n", func);
263*4882a593Smuzhiyun return -EINVAL;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun wValue = (func << 8) | (i2c->port << 4);
266*4882a593Smuzhiyun wIndex = (chipaddr << 8 ) | addr;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
269*4882a593Smuzhiyun func, request_type, req,
270*4882a593Smuzhiyun wValue & 0xff, wValue >> 8,
271*4882a593Smuzhiyun wIndex & 0xff, wIndex >> 8);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun mutex_lock(&fc_usb->data_mutex);
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
276*4882a593Smuzhiyun memcpy(fc_usb->data, buf, buflen);
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun ret = usb_control_msg(fc_usb->udev, pipe,
279*4882a593Smuzhiyun req,
280*4882a593Smuzhiyun request_type,
281*4882a593Smuzhiyun wValue,
282*4882a593Smuzhiyun wIndex,
283*4882a593Smuzhiyun fc_usb->data,
284*4882a593Smuzhiyun buflen,
285*4882a593Smuzhiyun nWaitTime);
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun if (ret != buflen)
288*4882a593Smuzhiyun ret = -EIO;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun if (ret >= 0) {
291*4882a593Smuzhiyun ret = 0;
292*4882a593Smuzhiyun if ((request_type & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
293*4882a593Smuzhiyun memcpy(buf, fc_usb->data, buflen);
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun mutex_unlock(&fc_usb->data_mutex);
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return ret;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /* actual bus specific access functions,
302*4882a593Smuzhiyun make sure prototype are/will be equal to pci */
flexcop_usb_read_ibi_reg(struct flexcop_device * fc,flexcop_ibi_register reg)303*4882a593Smuzhiyun static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
304*4882a593Smuzhiyun flexcop_ibi_register reg)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun flexcop_ibi_value val;
307*4882a593Smuzhiyun val.raw = 0;
308*4882a593Smuzhiyun flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
309*4882a593Smuzhiyun return val;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
flexcop_usb_write_ibi_reg(struct flexcop_device * fc,flexcop_ibi_register reg,flexcop_ibi_value val)312*4882a593Smuzhiyun static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
313*4882a593Smuzhiyun flexcop_ibi_register reg, flexcop_ibi_value val)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
flexcop_usb_i2c_request(struct flexcop_i2c_adapter * i2c,flexcop_access_op_t op,u8 chipaddr,u8 addr,u8 * buf,u16 len)318*4882a593Smuzhiyun static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
319*4882a593Smuzhiyun flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun if (op == FC_READ)
322*4882a593Smuzhiyun return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
323*4882a593Smuzhiyun USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
324*4882a593Smuzhiyun else
325*4882a593Smuzhiyun return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
326*4882a593Smuzhiyun USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
flexcop_usb_process_frame(struct flexcop_usb * fc_usb,u8 * buffer,int buffer_length)329*4882a593Smuzhiyun static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
330*4882a593Smuzhiyun u8 *buffer, int buffer_length)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun u8 *b;
333*4882a593Smuzhiyun int l;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
336*4882a593Smuzhiyun fc_usb->tmp_buffer_length, buffer_length);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun if (fc_usb->tmp_buffer_length > 0) {
339*4882a593Smuzhiyun memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
340*4882a593Smuzhiyun buffer_length);
341*4882a593Smuzhiyun fc_usb->tmp_buffer_length += buffer_length;
342*4882a593Smuzhiyun b = fc_usb->tmp_buffer;
343*4882a593Smuzhiyun l = fc_usb->tmp_buffer_length;
344*4882a593Smuzhiyun } else {
345*4882a593Smuzhiyun b=buffer;
346*4882a593Smuzhiyun l=buffer_length;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun while (l >= 190) {
350*4882a593Smuzhiyun if (*b == 0xff) {
351*4882a593Smuzhiyun switch (*(b+1) & 0x03) {
352*4882a593Smuzhiyun case 0x01: /* media packet */
353*4882a593Smuzhiyun if (*(b+2) == 0x47)
354*4882a593Smuzhiyun flexcop_pass_dmx_packets(
355*4882a593Smuzhiyun fc_usb->fc_dev, b+2, 1);
356*4882a593Smuzhiyun else
357*4882a593Smuzhiyun deb_ts("not ts packet %*ph\n", 4, b+2);
358*4882a593Smuzhiyun b += 190;
359*4882a593Smuzhiyun l -= 190;
360*4882a593Smuzhiyun break;
361*4882a593Smuzhiyun default:
362*4882a593Smuzhiyun deb_ts("wrong packet type\n");
363*4882a593Smuzhiyun l = 0;
364*4882a593Smuzhiyun break;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun } else {
367*4882a593Smuzhiyun deb_ts("wrong header\n");
368*4882a593Smuzhiyun l = 0;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun if (l>0)
373*4882a593Smuzhiyun memcpy(fc_usb->tmp_buffer, b, l);
374*4882a593Smuzhiyun fc_usb->tmp_buffer_length = l;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
flexcop_usb_urb_complete(struct urb * urb)377*4882a593Smuzhiyun static void flexcop_usb_urb_complete(struct urb *urb)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun struct flexcop_usb *fc_usb = urb->context;
380*4882a593Smuzhiyun int i;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun if (urb->actual_length > 0)
383*4882a593Smuzhiyun deb_ts("urb completed, bufsize: %d actlen; %d\n",
384*4882a593Smuzhiyun urb->transfer_buffer_length, urb->actual_length);
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun for (i = 0; i < urb->number_of_packets; i++) {
387*4882a593Smuzhiyun if (urb->iso_frame_desc[i].status < 0) {
388*4882a593Smuzhiyun err("iso frame descriptor %d has an error: %d\n", i,
389*4882a593Smuzhiyun urb->iso_frame_desc[i].status);
390*4882a593Smuzhiyun } else
391*4882a593Smuzhiyun if (urb->iso_frame_desc[i].actual_length > 0) {
392*4882a593Smuzhiyun deb_ts("passed %d bytes to the demux\n",
393*4882a593Smuzhiyun urb->iso_frame_desc[i].actual_length);
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun flexcop_usb_process_frame(fc_usb,
396*4882a593Smuzhiyun urb->transfer_buffer +
397*4882a593Smuzhiyun urb->iso_frame_desc[i].offset,
398*4882a593Smuzhiyun urb->iso_frame_desc[i].actual_length);
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun urb->iso_frame_desc[i].status = 0;
401*4882a593Smuzhiyun urb->iso_frame_desc[i].actual_length = 0;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun usb_submit_urb(urb,GFP_ATOMIC);
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun
flexcop_usb_stream_control(struct flexcop_device * fc,int onoff)406*4882a593Smuzhiyun static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
407*4882a593Smuzhiyun {
408*4882a593Smuzhiyun /* submit/kill iso packets */
409*4882a593Smuzhiyun return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
flexcop_usb_transfer_exit(struct flexcop_usb * fc_usb)412*4882a593Smuzhiyun static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun int i;
415*4882a593Smuzhiyun for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
416*4882a593Smuzhiyun if (fc_usb->iso_urb[i] != NULL) {
417*4882a593Smuzhiyun deb_ts("unlinking/killing urb no. %d\n",i);
418*4882a593Smuzhiyun usb_kill_urb(fc_usb->iso_urb[i]);
419*4882a593Smuzhiyun usb_free_urb(fc_usb->iso_urb[i]);
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun usb_free_coherent(fc_usb->udev, fc_usb->buffer_size,
423*4882a593Smuzhiyun fc_usb->iso_buffer, fc_usb->dma_addr);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
flexcop_usb_transfer_init(struct flexcop_usb * fc_usb)427*4882a593Smuzhiyun static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun u16 frame_size = le16_to_cpu(
430*4882a593Smuzhiyun fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
431*4882a593Smuzhiyun int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
432*4882a593Smuzhiyun frame_size, i, j, ret;
433*4882a593Smuzhiyun int buffer_offset = 0;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
436*4882a593Smuzhiyun B2C2_USB_NUM_ISO_URB,
437*4882a593Smuzhiyun B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun fc_usb->iso_buffer = usb_alloc_coherent(fc_usb->udev,
440*4882a593Smuzhiyun bufsize, GFP_KERNEL, &fc_usb->dma_addr);
441*4882a593Smuzhiyun if (fc_usb->iso_buffer == NULL)
442*4882a593Smuzhiyun return -ENOMEM;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun memset(fc_usb->iso_buffer, 0, bufsize);
445*4882a593Smuzhiyun fc_usb->buffer_size = bufsize;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun /* creating iso urbs */
448*4882a593Smuzhiyun for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
449*4882a593Smuzhiyun fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
450*4882a593Smuzhiyun GFP_ATOMIC);
451*4882a593Smuzhiyun if (fc_usb->iso_urb[i] == NULL) {
452*4882a593Smuzhiyun ret = -ENOMEM;
453*4882a593Smuzhiyun goto urb_error;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun /* initialising and submitting iso urbs */
458*4882a593Smuzhiyun for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
459*4882a593Smuzhiyun int frame_offset = 0;
460*4882a593Smuzhiyun struct urb *urb = fc_usb->iso_urb[i];
461*4882a593Smuzhiyun deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",
462*4882a593Smuzhiyun i, buffer_offset);
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun urb->dev = fc_usb->udev;
465*4882a593Smuzhiyun urb->context = fc_usb;
466*4882a593Smuzhiyun urb->complete = flexcop_usb_urb_complete;
467*4882a593Smuzhiyun urb->pipe = B2C2_USB_DATA_PIPE;
468*4882a593Smuzhiyun urb->transfer_flags = URB_ISO_ASAP;
469*4882a593Smuzhiyun urb->interval = 1;
470*4882a593Smuzhiyun urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
471*4882a593Smuzhiyun urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
472*4882a593Smuzhiyun urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
475*4882a593Smuzhiyun for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
476*4882a593Smuzhiyun deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
477*4882a593Smuzhiyun i, j, frame_offset);
478*4882a593Smuzhiyun urb->iso_frame_desc[j].offset = frame_offset;
479*4882a593Smuzhiyun urb->iso_frame_desc[j].length = frame_size;
480*4882a593Smuzhiyun frame_offset += frame_size;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
484*4882a593Smuzhiyun err("submitting urb %d failed with %d.", i, ret);
485*4882a593Smuzhiyun goto urb_error;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun deb_ts("submitted urb no. %d.\n",i);
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun /* SRAM */
491*4882a593Smuzhiyun flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
492*4882a593Smuzhiyun FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
493*4882a593Smuzhiyun FC_SRAM_DEST_TARGET_WAN_USB);
494*4882a593Smuzhiyun flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
495*4882a593Smuzhiyun flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
496*4882a593Smuzhiyun return 0;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun urb_error:
499*4882a593Smuzhiyun flexcop_usb_transfer_exit(fc_usb);
500*4882a593Smuzhiyun return ret;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
flexcop_usb_init(struct flexcop_usb * fc_usb)503*4882a593Smuzhiyun static int flexcop_usb_init(struct flexcop_usb *fc_usb)
504*4882a593Smuzhiyun {
505*4882a593Smuzhiyun /* use the alternate setting with the larges buffer */
506*4882a593Smuzhiyun int ret = usb_set_interface(fc_usb->udev, 0, 1);
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun if (ret) {
509*4882a593Smuzhiyun err("set interface failed.");
510*4882a593Smuzhiyun return ret;
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1)
514*4882a593Smuzhiyun return -ENODEV;
515*4882a593Smuzhiyun if (!usb_endpoint_is_isoc_in(&fc_usb->uintf->cur_altsetting->endpoint[0].desc))
516*4882a593Smuzhiyun return -ENODEV;
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun switch (fc_usb->udev->speed) {
519*4882a593Smuzhiyun case USB_SPEED_LOW:
520*4882a593Smuzhiyun err("cannot handle USB speed because it is too slow.");
521*4882a593Smuzhiyun return -ENODEV;
522*4882a593Smuzhiyun break;
523*4882a593Smuzhiyun case USB_SPEED_FULL:
524*4882a593Smuzhiyun info("running at FULL speed.");
525*4882a593Smuzhiyun break;
526*4882a593Smuzhiyun case USB_SPEED_HIGH:
527*4882a593Smuzhiyun info("running at HIGH speed.");
528*4882a593Smuzhiyun break;
529*4882a593Smuzhiyun case USB_SPEED_UNKNOWN:
530*4882a593Smuzhiyun default:
531*4882a593Smuzhiyun err("cannot handle USB speed because it is unknown.");
532*4882a593Smuzhiyun return -ENODEV;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun usb_set_intfdata(fc_usb->uintf, fc_usb);
535*4882a593Smuzhiyun return 0;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
flexcop_usb_exit(struct flexcop_usb * fc_usb)538*4882a593Smuzhiyun static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
539*4882a593Smuzhiyun {
540*4882a593Smuzhiyun usb_set_intfdata(fc_usb->uintf, NULL);
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun
flexcop_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)543*4882a593Smuzhiyun static int flexcop_usb_probe(struct usb_interface *intf,
544*4882a593Smuzhiyun const struct usb_device_id *id)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun struct usb_device *udev = interface_to_usbdev(intf);
547*4882a593Smuzhiyun struct flexcop_usb *fc_usb = NULL;
548*4882a593Smuzhiyun struct flexcop_device *fc = NULL;
549*4882a593Smuzhiyun int ret;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
552*4882a593Smuzhiyun err("out of memory\n");
553*4882a593Smuzhiyun return -ENOMEM;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun /* general flexcop init */
557*4882a593Smuzhiyun fc_usb = fc->bus_specific;
558*4882a593Smuzhiyun fc_usb->fc_dev = fc;
559*4882a593Smuzhiyun mutex_init(&fc_usb->data_mutex);
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
562*4882a593Smuzhiyun fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
563*4882a593Smuzhiyun fc->i2c_request = flexcop_usb_i2c_request;
564*4882a593Smuzhiyun fc->get_mac_addr = flexcop_usb_get_mac_addr;
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun fc->stream_control = flexcop_usb_stream_control;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun fc->pid_filtering = 1;
569*4882a593Smuzhiyun fc->bus_type = FC_USB;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun fc->dev = &udev->dev;
572*4882a593Smuzhiyun fc->owner = THIS_MODULE;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun /* bus specific part */
575*4882a593Smuzhiyun fc_usb->udev = udev;
576*4882a593Smuzhiyun fc_usb->uintf = intf;
577*4882a593Smuzhiyun if ((ret = flexcop_usb_init(fc_usb)) != 0)
578*4882a593Smuzhiyun goto err_kfree;
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun /* init flexcop */
581*4882a593Smuzhiyun if ((ret = flexcop_device_initialize(fc)) != 0)
582*4882a593Smuzhiyun goto err_usb_exit;
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun /* xfer init */
585*4882a593Smuzhiyun if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
586*4882a593Smuzhiyun goto err_fc_exit;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun info("%s successfully initialized and connected.", DRIVER_NAME);
589*4882a593Smuzhiyun return 0;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun err_fc_exit:
592*4882a593Smuzhiyun flexcop_device_exit(fc);
593*4882a593Smuzhiyun err_usb_exit:
594*4882a593Smuzhiyun flexcop_usb_exit(fc_usb);
595*4882a593Smuzhiyun err_kfree:
596*4882a593Smuzhiyun flexcop_device_kfree(fc);
597*4882a593Smuzhiyun return ret;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun
flexcop_usb_disconnect(struct usb_interface * intf)600*4882a593Smuzhiyun static void flexcop_usb_disconnect(struct usb_interface *intf)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
603*4882a593Smuzhiyun flexcop_usb_transfer_exit(fc_usb);
604*4882a593Smuzhiyun flexcop_device_exit(fc_usb->fc_dev);
605*4882a593Smuzhiyun flexcop_usb_exit(fc_usb);
606*4882a593Smuzhiyun flexcop_device_kfree(fc_usb->fc_dev);
607*4882a593Smuzhiyun info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun static const struct usb_device_id flexcop_usb_table[] = {
611*4882a593Smuzhiyun { USB_DEVICE(0x0af7, 0x0101) },
612*4882a593Smuzhiyun { }
613*4882a593Smuzhiyun };
614*4882a593Smuzhiyun MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun /* usb specific object needed to register this driver with the usb subsystem */
617*4882a593Smuzhiyun static struct usb_driver flexcop_usb_driver = {
618*4882a593Smuzhiyun .name = "b2c2_flexcop_usb",
619*4882a593Smuzhiyun .probe = flexcop_usb_probe,
620*4882a593Smuzhiyun .disconnect = flexcop_usb_disconnect,
621*4882a593Smuzhiyun .id_table = flexcop_usb_table,
622*4882a593Smuzhiyun };
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun module_usb_driver(flexcop_usb_driver);
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun MODULE_AUTHOR(DRIVER_AUTHOR);
627*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_NAME);
628*4882a593Smuzhiyun MODULE_LICENSE("GPL");
629