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