18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Abilis Systems Single DVB-T Receiver
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci#include <linux/usb.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "as102_drv.h"
148c2ecf20Sopenharmony_ci#include "as102_usb_drv.h"
158c2ecf20Sopenharmony_ci#include "as102_fw.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic void as102_usb_disconnect(struct usb_interface *interface);
188c2ecf20Sopenharmony_cistatic int as102_usb_probe(struct usb_interface *interface,
198c2ecf20Sopenharmony_ci			   const struct usb_device_id *id);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int as102_usb_start_stream(struct as102_dev_t *dev);
228c2ecf20Sopenharmony_cistatic void as102_usb_stop_stream(struct as102_dev_t *dev);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int as102_open(struct inode *inode, struct file *file);
258c2ecf20Sopenharmony_cistatic int as102_release(struct inode *inode, struct file *file);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const struct usb_device_id as102_usb_id_table[] = {
288c2ecf20Sopenharmony_ci	{ USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) },
298c2ecf20Sopenharmony_ci	{ USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) },
308c2ecf20Sopenharmony_ci	{ USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) },
318c2ecf20Sopenharmony_ci	{ USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) },
328c2ecf20Sopenharmony_ci	{ USB_DEVICE(SKY_IT_DIGITAL_KEY_USB_VID, SKY_IT_DIGITAL_KEY_USB_PID) },
338c2ecf20Sopenharmony_ci	{ } /* Terminating entry */
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Note that this table must always have the same number of entries as the
378c2ecf20Sopenharmony_ci   as102_usb_id_table struct */
388c2ecf20Sopenharmony_cistatic const char * const as102_device_names[] = {
398c2ecf20Sopenharmony_ci	AS102_REFERENCE_DESIGN,
408c2ecf20Sopenharmony_ci	AS102_PCTV_74E,
418c2ecf20Sopenharmony_ci	AS102_ELGATO_EYETV_DTT_NAME,
428c2ecf20Sopenharmony_ci	AS102_NBOX_DVBT_DONGLE_NAME,
438c2ecf20Sopenharmony_ci	AS102_SKY_IT_DIGITAL_KEY_NAME,
448c2ecf20Sopenharmony_ci	NULL /* Terminating entry */
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* eLNA configuration: devices built on the reference design work best
488c2ecf20Sopenharmony_ci   with 0xA0, while custom designs seem to require 0xC0 */
498c2ecf20Sopenharmony_cistatic uint8_t const as102_elna_cfg[] = {
508c2ecf20Sopenharmony_ci	0xA0,
518c2ecf20Sopenharmony_ci	0xC0,
528c2ecf20Sopenharmony_ci	0xC0,
538c2ecf20Sopenharmony_ci	0xA0,
548c2ecf20Sopenharmony_ci	0xA0,
558c2ecf20Sopenharmony_ci	0x00 /* Terminating entry */
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct usb_driver as102_usb_driver = {
598c2ecf20Sopenharmony_ci	.name		= DRIVER_FULL_NAME,
608c2ecf20Sopenharmony_ci	.probe		= as102_usb_probe,
618c2ecf20Sopenharmony_ci	.disconnect	= as102_usb_disconnect,
628c2ecf20Sopenharmony_ci	.id_table	= as102_usb_id_table
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct file_operations as102_dev_fops = {
668c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
678c2ecf20Sopenharmony_ci	.open		= as102_open,
688c2ecf20Sopenharmony_ci	.release	= as102_release,
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct usb_class_driver as102_usb_class_driver = {
728c2ecf20Sopenharmony_ci	.name		= "aton2-%d",
738c2ecf20Sopenharmony_ci	.fops		= &as102_dev_fops,
748c2ecf20Sopenharmony_ci	.minor_base	= AS102_DEVICE_MAJOR,
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int as102_usb_xfer_cmd(struct as10x_bus_adapter_t *bus_adap,
788c2ecf20Sopenharmony_ci			      unsigned char *send_buf, int send_buf_len,
798c2ecf20Sopenharmony_ci			      unsigned char *recv_buf, int recv_buf_len)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int ret = 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (send_buf != NULL) {
848c2ecf20Sopenharmony_ci		ret = usb_control_msg(bus_adap->usb_dev,
858c2ecf20Sopenharmony_ci				      usb_sndctrlpipe(bus_adap->usb_dev, 0),
868c2ecf20Sopenharmony_ci				      AS102_USB_DEVICE_TX_CTRL_CMD,
878c2ecf20Sopenharmony_ci				      USB_DIR_OUT | USB_TYPE_VENDOR |
888c2ecf20Sopenharmony_ci				      USB_RECIP_DEVICE,
898c2ecf20Sopenharmony_ci				      bus_adap->cmd_xid, /* value */
908c2ecf20Sopenharmony_ci				      0, /* index */
918c2ecf20Sopenharmony_ci				      send_buf, send_buf_len,
928c2ecf20Sopenharmony_ci				      USB_CTRL_SET_TIMEOUT /* 200 */);
938c2ecf20Sopenharmony_ci		if (ret < 0) {
948c2ecf20Sopenharmony_ci			dev_dbg(&bus_adap->usb_dev->dev,
958c2ecf20Sopenharmony_ci				"usb_control_msg(send) failed, err %i\n", ret);
968c2ecf20Sopenharmony_ci			return ret;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		if (ret != send_buf_len) {
1008c2ecf20Sopenharmony_ci			dev_dbg(&bus_adap->usb_dev->dev,
1018c2ecf20Sopenharmony_ci			"only wrote %d of %d bytes\n", ret, send_buf_len);
1028c2ecf20Sopenharmony_ci			return -1;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (recv_buf != NULL) {
1078c2ecf20Sopenharmony_ci#ifdef TRACE
1088c2ecf20Sopenharmony_ci		dev_dbg(bus_adap->usb_dev->dev,
1098c2ecf20Sopenharmony_ci			"want to read: %d bytes\n", recv_buf_len);
1108c2ecf20Sopenharmony_ci#endif
1118c2ecf20Sopenharmony_ci		ret = usb_control_msg(bus_adap->usb_dev,
1128c2ecf20Sopenharmony_ci				      usb_rcvctrlpipe(bus_adap->usb_dev, 0),
1138c2ecf20Sopenharmony_ci				      AS102_USB_DEVICE_RX_CTRL_CMD,
1148c2ecf20Sopenharmony_ci				      USB_DIR_IN | USB_TYPE_VENDOR |
1158c2ecf20Sopenharmony_ci				      USB_RECIP_DEVICE,
1168c2ecf20Sopenharmony_ci				      bus_adap->cmd_xid, /* value */
1178c2ecf20Sopenharmony_ci				      0, /* index */
1188c2ecf20Sopenharmony_ci				      recv_buf, recv_buf_len,
1198c2ecf20Sopenharmony_ci				      USB_CTRL_GET_TIMEOUT /* 200 */);
1208c2ecf20Sopenharmony_ci		if (ret < 0) {
1218c2ecf20Sopenharmony_ci			dev_dbg(&bus_adap->usb_dev->dev,
1228c2ecf20Sopenharmony_ci				"usb_control_msg(recv) failed, err %i\n", ret);
1238c2ecf20Sopenharmony_ci			return ret;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci#ifdef TRACE
1268c2ecf20Sopenharmony_ci		dev_dbg(bus_adap->usb_dev->dev,
1278c2ecf20Sopenharmony_ci			"read %d bytes\n", recv_buf_len);
1288c2ecf20Sopenharmony_ci#endif
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return ret;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int as102_send_ep1(struct as10x_bus_adapter_t *bus_adap,
1358c2ecf20Sopenharmony_ci			  unsigned char *send_buf,
1368c2ecf20Sopenharmony_ci			  int send_buf_len,
1378c2ecf20Sopenharmony_ci			  int swap32)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	int ret, actual_len;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(bus_adap->usb_dev,
1428c2ecf20Sopenharmony_ci			   usb_sndbulkpipe(bus_adap->usb_dev, 1),
1438c2ecf20Sopenharmony_ci			   send_buf, send_buf_len, &actual_len, 200);
1448c2ecf20Sopenharmony_ci	if (ret) {
1458c2ecf20Sopenharmony_ci		dev_dbg(&bus_adap->usb_dev->dev,
1468c2ecf20Sopenharmony_ci			"usb_bulk_msg(send) failed, err %i\n", ret);
1478c2ecf20Sopenharmony_ci		return ret;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (actual_len != send_buf_len) {
1518c2ecf20Sopenharmony_ci		dev_dbg(&bus_adap->usb_dev->dev, "only wrote %d of %d bytes\n",
1528c2ecf20Sopenharmony_ci			actual_len, send_buf_len);
1538c2ecf20Sopenharmony_ci		return -1;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	return actual_len;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int as102_read_ep2(struct as10x_bus_adapter_t *bus_adap,
1598c2ecf20Sopenharmony_ci		   unsigned char *recv_buf, int recv_buf_len)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int ret, actual_len;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (recv_buf == NULL)
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(bus_adap->usb_dev,
1678c2ecf20Sopenharmony_ci			   usb_rcvbulkpipe(bus_adap->usb_dev, 2),
1688c2ecf20Sopenharmony_ci			   recv_buf, recv_buf_len, &actual_len, 200);
1698c2ecf20Sopenharmony_ci	if (ret) {
1708c2ecf20Sopenharmony_ci		dev_dbg(&bus_adap->usb_dev->dev,
1718c2ecf20Sopenharmony_ci			"usb_bulk_msg(recv) failed, err %i\n", ret);
1728c2ecf20Sopenharmony_ci		return ret;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (actual_len != recv_buf_len) {
1768c2ecf20Sopenharmony_ci		dev_dbg(&bus_adap->usb_dev->dev, "only read %d of %d bytes\n",
1778c2ecf20Sopenharmony_ci			actual_len, recv_buf_len);
1788c2ecf20Sopenharmony_ci		return -1;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	return actual_len;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const struct as102_priv_ops_t as102_priv_ops = {
1848c2ecf20Sopenharmony_ci	.upload_fw_pkt	= as102_send_ep1,
1858c2ecf20Sopenharmony_ci	.xfer_cmd	= as102_usb_xfer_cmd,
1868c2ecf20Sopenharmony_ci	.as102_read_ep2	= as102_read_ep2,
1878c2ecf20Sopenharmony_ci	.start_stream	= as102_usb_start_stream,
1888c2ecf20Sopenharmony_ci	.stop_stream	= as102_usb_stop_stream,
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int err;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb,
1968c2ecf20Sopenharmony_ci			  dev->bus_adap.usb_dev,
1978c2ecf20Sopenharmony_ci			  usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2),
1988c2ecf20Sopenharmony_ci			  urb->transfer_buffer,
1998c2ecf20Sopenharmony_ci			  AS102_USB_BUF_SIZE,
2008c2ecf20Sopenharmony_ci			  as102_urb_stream_irq,
2018c2ecf20Sopenharmony_ci			  dev);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	err = usb_submit_urb(urb, GFP_ATOMIC);
2048c2ecf20Sopenharmony_ci	if (err)
2058c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev,
2068c2ecf20Sopenharmony_ci			"%s: usb_submit_urb failed\n", __func__);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return err;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_civoid as102_urb_stream_irq(struct urb *urb)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct as102_dev_t *as102_dev = urb->context;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (urb->actual_length > 0) {
2168c2ecf20Sopenharmony_ci		dvb_dmx_swfilter(&as102_dev->dvb_dmx,
2178c2ecf20Sopenharmony_ci				 urb->transfer_buffer,
2188c2ecf20Sopenharmony_ci				 urb->actual_length);
2198c2ecf20Sopenharmony_ci	} else {
2208c2ecf20Sopenharmony_ci		if (urb->actual_length == 0)
2218c2ecf20Sopenharmony_ci			memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* is not stopped, re-submit urb */
2258c2ecf20Sopenharmony_ci	if (as102_dev->streaming)
2268c2ecf20Sopenharmony_ci		as102_submit_urb_stream(as102_dev, urb);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void as102_free_usb_stream_buffer(struct as102_dev_t *dev)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int i;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_STREAM_URB; i++)
2348c2ecf20Sopenharmony_ci		usb_free_urb(dev->stream_urb[i]);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	usb_free_coherent(dev->bus_adap.usb_dev,
2378c2ecf20Sopenharmony_ci			MAX_STREAM_URB * AS102_USB_BUF_SIZE,
2388c2ecf20Sopenharmony_ci			dev->stream,
2398c2ecf20Sopenharmony_ci			dev->dma_addr);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	int i;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev,
2478c2ecf20Sopenharmony_ci				       MAX_STREAM_URB * AS102_USB_BUF_SIZE,
2488c2ecf20Sopenharmony_ci				       GFP_KERNEL,
2498c2ecf20Sopenharmony_ci				       &dev->dma_addr);
2508c2ecf20Sopenharmony_ci	if (!dev->stream) {
2518c2ecf20Sopenharmony_ci		dev_dbg(&dev->bus_adap.usb_dev->dev,
2528c2ecf20Sopenharmony_ci			"%s: usb_buffer_alloc failed\n", __func__);
2538c2ecf20Sopenharmony_ci		return -ENOMEM;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* init urb buffers */
2598c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_STREAM_URB; i++) {
2608c2ecf20Sopenharmony_ci		struct urb *urb;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_ATOMIC);
2638c2ecf20Sopenharmony_ci		if (urb == NULL) {
2648c2ecf20Sopenharmony_ci			as102_free_usb_stream_buffer(dev);
2658c2ecf20Sopenharmony_ci			return -ENOMEM;
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE);
2698c2ecf20Sopenharmony_ci		urb->transfer_dma = dev->dma_addr + (i * AS102_USB_BUF_SIZE);
2708c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
2718c2ecf20Sopenharmony_ci		urb->transfer_buffer_length = AS102_USB_BUF_SIZE;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		dev->stream_urb[i] = urb;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void as102_usb_stop_stream(struct as102_dev_t *dev)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int i;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_STREAM_URB; i++)
2838c2ecf20Sopenharmony_ci		usb_kill_urb(dev->stream_urb[i]);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int as102_usb_start_stream(struct as102_dev_t *dev)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	int i, ret = 0;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_STREAM_URB; i++) {
2918c2ecf20Sopenharmony_ci		ret = as102_submit_urb_stream(dev, dev->stream_urb[i]);
2928c2ecf20Sopenharmony_ci		if (ret) {
2938c2ecf20Sopenharmony_ci			as102_usb_stop_stream(dev);
2948c2ecf20Sopenharmony_ci			return ret;
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void as102_usb_release(struct kref *kref)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct as102_dev_t *as102_dev;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	as102_dev = container_of(kref, struct as102_dev_t, kref);
3068c2ecf20Sopenharmony_ci	if (as102_dev != NULL) {
3078c2ecf20Sopenharmony_ci		usb_put_dev(as102_dev->bus_adap.usb_dev);
3088c2ecf20Sopenharmony_ci		kfree(as102_dev);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void as102_usb_disconnect(struct usb_interface *intf)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct as102_dev_t *as102_dev;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* extract as102_dev_t from usb_device private data */
3178c2ecf20Sopenharmony_ci	as102_dev = usb_get_intfdata(intf);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* unregister dvb layer */
3208c2ecf20Sopenharmony_ci	as102_dvb_unregister(as102_dev);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* free usb buffers */
3238c2ecf20Sopenharmony_ci	as102_free_usb_stream_buffer(as102_dev);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* usb unregister device */
3288c2ecf20Sopenharmony_ci	usb_deregister_dev(intf, &as102_usb_class_driver);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* decrement usage counter */
3318c2ecf20Sopenharmony_ci	kref_put(&as102_dev->kref, as102_usb_release);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	pr_info("%s: device has been disconnected\n", DRIVER_NAME);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int as102_usb_probe(struct usb_interface *intf,
3378c2ecf20Sopenharmony_ci			   const struct usb_device_id *id)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	int ret;
3408c2ecf20Sopenharmony_ci	struct as102_dev_t *as102_dev;
3418c2ecf20Sopenharmony_ci	int i;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* This should never actually happen */
3448c2ecf20Sopenharmony_ci	if (ARRAY_SIZE(as102_usb_id_table) !=
3458c2ecf20Sopenharmony_ci	    (sizeof(as102_device_names) / sizeof(const char *))) {
3468c2ecf20Sopenharmony_ci		pr_err("Device names table invalid size");
3478c2ecf20Sopenharmony_ci		return -EINVAL;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL);
3518c2ecf20Sopenharmony_ci	if (as102_dev == NULL)
3528c2ecf20Sopenharmony_ci		return -ENOMEM;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* Assign the user-friendly device name */
3558c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) {
3568c2ecf20Sopenharmony_ci		if (id == &as102_usb_id_table[i]) {
3578c2ecf20Sopenharmony_ci			as102_dev->name = as102_device_names[i];
3588c2ecf20Sopenharmony_ci			as102_dev->elna_cfg = as102_elna_cfg[i];
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (as102_dev->name == NULL)
3638c2ecf20Sopenharmony_ci		as102_dev->name = "Unknown AS102 device";
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* set private callback functions */
3668c2ecf20Sopenharmony_ci	as102_dev->bus_adap.ops = &as102_priv_ops;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* init cmd token for usb bus */
3698c2ecf20Sopenharmony_ci	as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c;
3708c2ecf20Sopenharmony_ci	as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* init kernel device reference */
3738c2ecf20Sopenharmony_ci	kref_init(&as102_dev->kref);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* store as102 device to usb_device private data */
3768c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, (void *) as102_dev);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* store in as102 device the usb_device pointer */
3798c2ecf20Sopenharmony_ci	as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf));
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* we can register the device now, as it is ready */
3828c2ecf20Sopenharmony_ci	ret = usb_register_dev(intf, &as102_usb_class_driver);
3838c2ecf20Sopenharmony_ci	if (ret < 0) {
3848c2ecf20Sopenharmony_ci		/* something prevented us from registering this driver */
3858c2ecf20Sopenharmony_ci		dev_err(&intf->dev,
3868c2ecf20Sopenharmony_ci			"%s: usb_register_dev() failed (errno = %d)\n",
3878c2ecf20Sopenharmony_ci			__func__, ret);
3888c2ecf20Sopenharmony_ci		goto failed;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	pr_info("%s: device has been detected\n", DRIVER_NAME);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* request buffer allocation for streaming */
3948c2ecf20Sopenharmony_ci	ret = as102_alloc_usb_stream_buffer(as102_dev);
3958c2ecf20Sopenharmony_ci	if (ret != 0)
3968c2ecf20Sopenharmony_ci		goto failed_stream;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* register dvb layer */
3998c2ecf20Sopenharmony_ci	ret = as102_dvb_register(as102_dev);
4008c2ecf20Sopenharmony_ci	if (ret != 0)
4018c2ecf20Sopenharmony_ci		goto failed_dvb;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return ret;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cifailed_dvb:
4068c2ecf20Sopenharmony_ci	as102_free_usb_stream_buffer(as102_dev);
4078c2ecf20Sopenharmony_cifailed_stream:
4088c2ecf20Sopenharmony_ci	usb_deregister_dev(intf, &as102_usb_class_driver);
4098c2ecf20Sopenharmony_cifailed:
4108c2ecf20Sopenharmony_ci	usb_put_dev(as102_dev->bus_adap.usb_dev);
4118c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
4128c2ecf20Sopenharmony_ci	kfree(as102_dev);
4138c2ecf20Sopenharmony_ci	return ret;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int as102_open(struct inode *inode, struct file *file)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	int ret = 0, minor = 0;
4198c2ecf20Sopenharmony_ci	struct usb_interface *intf = NULL;
4208c2ecf20Sopenharmony_ci	struct as102_dev_t *dev = NULL;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* read minor from inode */
4238c2ecf20Sopenharmony_ci	minor = iminor(inode);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* fetch device from usb interface */
4268c2ecf20Sopenharmony_ci	intf = usb_find_interface(&as102_usb_driver, minor);
4278c2ecf20Sopenharmony_ci	if (intf == NULL) {
4288c2ecf20Sopenharmony_ci		pr_err("%s: can't find device for minor %d\n",
4298c2ecf20Sopenharmony_ci		       __func__, minor);
4308c2ecf20Sopenharmony_ci		ret = -ENODEV;
4318c2ecf20Sopenharmony_ci		goto exit;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* get our device */
4358c2ecf20Sopenharmony_ci	dev = usb_get_intfdata(intf);
4368c2ecf20Sopenharmony_ci	if (dev == NULL) {
4378c2ecf20Sopenharmony_ci		ret = -EFAULT;
4388c2ecf20Sopenharmony_ci		goto exit;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* save our device object in the file's private structure */
4428c2ecf20Sopenharmony_ci	file->private_data = dev;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* increment our usage count for the device */
4458c2ecf20Sopenharmony_ci	kref_get(&dev->kref);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ciexit:
4488c2ecf20Sopenharmony_ci	return ret;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int as102_release(struct inode *inode, struct file *file)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct as102_dev_t *dev = NULL;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	dev = file->private_data;
4568c2ecf20Sopenharmony_ci	if (dev != NULL) {
4578c2ecf20Sopenharmony_ci		/* decrement the count on our device */
4588c2ecf20Sopenharmony_ci		kref_put(&dev->kref, as102_usb_release);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, as102_usb_id_table);
465