brintos

brintos / linux-shallow public Read only

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