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