18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mos7720.c 48c2ecf20Sopenharmony_ci * Controls the Moschip 7720 usb to dual port serial converter 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2006 Moschip Semiconductor Tech. Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Developed by: 98c2ecf20Sopenharmony_ci * Vijaya Kumar <vijaykumar.gn@gmail.com> 108c2ecf20Sopenharmony_ci * Ajay Kumar <naanuajay@yahoo.com> 118c2ecf20Sopenharmony_ci * Gurudeva <ngurudeva@yahoo.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Cleaned up from the original by: 148c2ecf20Sopenharmony_ci * Greg Kroah-Hartman <gregkh@suse.de> 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is: 178c2ecf20Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved. 188c2ecf20Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/tty.h> 248c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 258c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/serial.h> 298c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 308c2ecf20Sopenharmony_ci#include <linux/usb.h> 318c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci#include <linux/parport.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Aspire Communications pvt Ltd." 368c2ecf20Sopenharmony_ci#define DRIVER_DESC "Moschip USB Serial Driver" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* default urb timeout */ 398c2ecf20Sopenharmony_ci#define MOS_WDR_TIMEOUT 5000 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MOS_MAX_PORT 0x02 428c2ecf20Sopenharmony_ci#define MOS_WRITE 0x0E 438c2ecf20Sopenharmony_ci#define MOS_READ 0x0D 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Interrupt Routines Defines */ 468c2ecf20Sopenharmony_ci#define SERIAL_IIR_RLS 0x06 478c2ecf20Sopenharmony_ci#define SERIAL_IIR_RDA 0x04 488c2ecf20Sopenharmony_ci#define SERIAL_IIR_CTI 0x0c 498c2ecf20Sopenharmony_ci#define SERIAL_IIR_THR 0x02 508c2ecf20Sopenharmony_ci#define SERIAL_IIR_MS 0x00 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define NUM_URBS 16 /* URB Count */ 538c2ecf20Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* This structure holds all of the local serial port information */ 568c2ecf20Sopenharmony_cistruct moschip_port { 578c2ecf20Sopenharmony_ci __u8 shadowLCR; /* last LCR value received */ 588c2ecf20Sopenharmony_ci __u8 shadowMCR; /* last MCR value received */ 598c2ecf20Sopenharmony_ci __u8 shadowMSR; /* last MSR value received */ 608c2ecf20Sopenharmony_ci char open; 618c2ecf20Sopenharmony_ci struct usb_serial_port *port; /* loop back to the owner */ 628c2ecf20Sopenharmony_ci struct urb *write_urb_pool[NUM_URBS]; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_MOSCHIP 0x9710 668c2ecf20Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7720 0x7720 678c2ecf20Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7715 0x7715 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 708c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) }, 718c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) }, 728c2ecf20Sopenharmony_ci { } /* terminating entry */ 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* initial values for parport regs */ 798c2ecf20Sopenharmony_ci#define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */ 808c2ecf20Sopenharmony_ci#define ECR_INIT_VAL 0x00 /* SPP mode */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct urbtracker { 838c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 848c2ecf20Sopenharmony_ci struct list_head urblist_entry; 858c2ecf20Sopenharmony_ci struct kref ref_count; 868c2ecf20Sopenharmony_ci struct urb *urb; 878c2ecf20Sopenharmony_ci struct usb_ctrlrequest *setup; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cienum mos7715_pp_modes { 918c2ecf20Sopenharmony_ci SPP = 0<<5, 928c2ecf20Sopenharmony_ci PS2 = 1<<5, /* moschip calls this 'NIBBLE' mode */ 938c2ecf20Sopenharmony_ci PPF = 2<<5, /* moschip calls this 'CB-FIFO mode */ 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct mos7715_parport { 978c2ecf20Sopenharmony_ci struct parport *pp; /* back to containing struct */ 988c2ecf20Sopenharmony_ci struct kref ref_count; /* to instance of this struct */ 998c2ecf20Sopenharmony_ci struct list_head deferred_urbs; /* list deferred async urbs */ 1008c2ecf20Sopenharmony_ci struct list_head active_urbs; /* list async urbs in flight */ 1018c2ecf20Sopenharmony_ci spinlock_t listlock; /* protects list access */ 1028c2ecf20Sopenharmony_ci bool msg_pending; /* usb sync call pending */ 1038c2ecf20Sopenharmony_ci struct completion syncmsg_compl; /* usb sync call completed */ 1048c2ecf20Sopenharmony_ci struct tasklet_struct urb_tasklet; /* for sending deferred urbs */ 1058c2ecf20Sopenharmony_ci struct usb_serial *serial; /* back to containing struct */ 1068c2ecf20Sopenharmony_ci __u8 shadowECR; /* parallel port regs... */ 1078c2ecf20Sopenharmony_ci __u8 shadowDCR; 1088c2ecf20Sopenharmony_ci atomic_t shadowDSR; /* updated in int-in callback */ 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* lock guards against dereferencing NULL ptr in parport ops callbacks */ 1128c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(release_lock); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const unsigned int dummy; /* for clarity in register access fns */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cienum mos_regs { 1198c2ecf20Sopenharmony_ci MOS7720_THR, /* serial port regs */ 1208c2ecf20Sopenharmony_ci MOS7720_RHR, 1218c2ecf20Sopenharmony_ci MOS7720_IER, 1228c2ecf20Sopenharmony_ci MOS7720_FCR, 1238c2ecf20Sopenharmony_ci MOS7720_ISR, 1248c2ecf20Sopenharmony_ci MOS7720_LCR, 1258c2ecf20Sopenharmony_ci MOS7720_MCR, 1268c2ecf20Sopenharmony_ci MOS7720_LSR, 1278c2ecf20Sopenharmony_ci MOS7720_MSR, 1288c2ecf20Sopenharmony_ci MOS7720_SPR, 1298c2ecf20Sopenharmony_ci MOS7720_DLL, 1308c2ecf20Sopenharmony_ci MOS7720_DLM, 1318c2ecf20Sopenharmony_ci MOS7720_DPR, /* parallel port regs */ 1328c2ecf20Sopenharmony_ci MOS7720_DSR, 1338c2ecf20Sopenharmony_ci MOS7720_DCR, 1348c2ecf20Sopenharmony_ci MOS7720_ECR, 1358c2ecf20Sopenharmony_ci MOS7720_SP1_REG, /* device control regs */ 1368c2ecf20Sopenharmony_ci MOS7720_SP2_REG, /* serial port 2 (7720 only) */ 1378c2ecf20Sopenharmony_ci MOS7720_PP_REG, 1388c2ecf20Sopenharmony_ci MOS7720_SP_CONTROL_REG, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Return the correct value for the Windex field of the setup packet 1438c2ecf20Sopenharmony_ci * for a control endpoint message. See the 7715 datasheet. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic inline __u16 get_reg_index(enum mos_regs reg) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci static const __u16 mos7715_index_lookup_table[] = { 1488c2ecf20Sopenharmony_ci 0x00, /* MOS7720_THR */ 1498c2ecf20Sopenharmony_ci 0x00, /* MOS7720_RHR */ 1508c2ecf20Sopenharmony_ci 0x01, /* MOS7720_IER */ 1518c2ecf20Sopenharmony_ci 0x02, /* MOS7720_FCR */ 1528c2ecf20Sopenharmony_ci 0x02, /* MOS7720_ISR */ 1538c2ecf20Sopenharmony_ci 0x03, /* MOS7720_LCR */ 1548c2ecf20Sopenharmony_ci 0x04, /* MOS7720_MCR */ 1558c2ecf20Sopenharmony_ci 0x05, /* MOS7720_LSR */ 1568c2ecf20Sopenharmony_ci 0x06, /* MOS7720_MSR */ 1578c2ecf20Sopenharmony_ci 0x07, /* MOS7720_SPR */ 1588c2ecf20Sopenharmony_ci 0x00, /* MOS7720_DLL */ 1598c2ecf20Sopenharmony_ci 0x01, /* MOS7720_DLM */ 1608c2ecf20Sopenharmony_ci 0x00, /* MOS7720_DPR */ 1618c2ecf20Sopenharmony_ci 0x01, /* MOS7720_DSR */ 1628c2ecf20Sopenharmony_ci 0x02, /* MOS7720_DCR */ 1638c2ecf20Sopenharmony_ci 0x0a, /* MOS7720_ECR */ 1648c2ecf20Sopenharmony_ci 0x01, /* MOS7720_SP1_REG */ 1658c2ecf20Sopenharmony_ci 0x02, /* MOS7720_SP2_REG (7720 only) */ 1668c2ecf20Sopenharmony_ci 0x04, /* MOS7720_PP_REG (7715 only) */ 1678c2ecf20Sopenharmony_ci 0x08, /* MOS7720_SP_CONTROL_REG */ 1688c2ecf20Sopenharmony_ci }; 1698c2ecf20Sopenharmony_ci return mos7715_index_lookup_table[reg]; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * Return the correct value for the upper byte of the Wvalue field of 1748c2ecf20Sopenharmony_ci * the setup packet for a control endpoint message. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic inline __u16 get_reg_value(enum mos_regs reg, 1778c2ecf20Sopenharmony_ci unsigned int serial_portnum) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (reg >= MOS7720_SP1_REG) /* control reg */ 1808c2ecf20Sopenharmony_ci return 0x0000; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci else if (reg >= MOS7720_DPR) /* parallel port reg (7715 only) */ 1838c2ecf20Sopenharmony_ci return 0x0100; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci else /* serial port reg */ 1868c2ecf20Sopenharmony_ci return (serial_portnum + 2) << 8; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Write data byte to the specified device register. The data is embedded in 1918c2ecf20Sopenharmony_ci * the value field of the setup packet. serial_portnum is ignored for registers 1928c2ecf20Sopenharmony_ci * not specific to a particular serial port. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_cistatic int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, 1958c2ecf20Sopenharmony_ci enum mos_regs reg, __u8 data) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct usb_device *usbdev = serial->dev; 1988c2ecf20Sopenharmony_ci unsigned int pipe = usb_sndctrlpipe(usbdev, 0); 1998c2ecf20Sopenharmony_ci __u8 request = (__u8)0x0e; 2008c2ecf20Sopenharmony_ci __u8 requesttype = (__u8)0x40; 2018c2ecf20Sopenharmony_ci __u16 index = get_reg_index(reg); 2028c2ecf20Sopenharmony_ci __u16 value = get_reg_value(reg, serial_portnum) + data; 2038c2ecf20Sopenharmony_ci int status = usb_control_msg(usbdev, pipe, request, requesttype, value, 2048c2ecf20Sopenharmony_ci index, NULL, 0, MOS_WDR_TIMEOUT); 2058c2ecf20Sopenharmony_ci if (status < 0) 2068c2ecf20Sopenharmony_ci dev_err(&usbdev->dev, 2078c2ecf20Sopenharmony_ci "mos7720: usb_control_msg() failed: %d\n", status); 2088c2ecf20Sopenharmony_ci return status; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Read data byte from the specified device register. The data returned by the 2138c2ecf20Sopenharmony_ci * device is embedded in the value field of the setup packet. serial_portnum is 2148c2ecf20Sopenharmony_ci * ignored for registers that are not specific to a particular serial port. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, 2178c2ecf20Sopenharmony_ci enum mos_regs reg, __u8 *data) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct usb_device *usbdev = serial->dev; 2208c2ecf20Sopenharmony_ci unsigned int pipe = usb_rcvctrlpipe(usbdev, 0); 2218c2ecf20Sopenharmony_ci __u8 request = (__u8)0x0d; 2228c2ecf20Sopenharmony_ci __u8 requesttype = (__u8)0xc0; 2238c2ecf20Sopenharmony_ci __u16 index = get_reg_index(reg); 2248c2ecf20Sopenharmony_ci __u16 value = get_reg_value(reg, serial_portnum); 2258c2ecf20Sopenharmony_ci u8 *buf; 2268c2ecf20Sopenharmony_ci int status; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci buf = kmalloc(1, GFP_KERNEL); 2298c2ecf20Sopenharmony_ci if (!buf) { 2308c2ecf20Sopenharmony_ci *data = 0; 2318c2ecf20Sopenharmony_ci return -ENOMEM; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci status = usb_control_msg(usbdev, pipe, request, requesttype, value, 2358c2ecf20Sopenharmony_ci index, buf, 1, MOS_WDR_TIMEOUT); 2368c2ecf20Sopenharmony_ci if (status == 1) { 2378c2ecf20Sopenharmony_ci *data = *buf; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci dev_err(&usbdev->dev, 2408c2ecf20Sopenharmony_ci "mos7720: usb_control_msg() failed: %d\n", status); 2418c2ecf20Sopenharmony_ci if (status >= 0) 2428c2ecf20Sopenharmony_ci status = -EIO; 2438c2ecf20Sopenharmony_ci *data = 0; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci kfree(buf); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return status; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic inline int mos7715_change_mode(struct mos7715_parport *mos_parport, 2548c2ecf20Sopenharmony_ci enum mos7715_pp_modes mode) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci mos_parport->shadowECR = mode; 2578c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, 2588c2ecf20Sopenharmony_ci mos_parport->shadowECR); 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void destroy_mos_parport(struct kref *kref) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = 2658c2ecf20Sopenharmony_ci container_of(kref, struct mos7715_parport, ref_count); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci kfree(mos_parport); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void destroy_urbtracker(struct kref *kref) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct urbtracker *urbtrack = 2738c2ecf20Sopenharmony_ci container_of(kref, struct urbtracker, ref_count); 2748c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = urbtrack->mos_parport; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci usb_free_urb(urbtrack->urb); 2778c2ecf20Sopenharmony_ci kfree(urbtrack->setup); 2788c2ecf20Sopenharmony_ci kfree(urbtrack); 2798c2ecf20Sopenharmony_ci kref_put(&mos_parport->ref_count, destroy_mos_parport); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* 2838c2ecf20Sopenharmony_ci * This runs as a tasklet when sending an urb in a non-blocking parallel 2848c2ecf20Sopenharmony_ci * port callback had to be deferred because the disconnect mutex could not be 2858c2ecf20Sopenharmony_ci * obtained at the time. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic void send_deferred_urbs(struct tasklet_struct *t) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci int ret_val; 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = from_tasklet(mos_parport, t, 2928c2ecf20Sopenharmony_ci urb_tasklet); 2938c2ecf20Sopenharmony_ci struct urbtracker *urbtrack, *tmp; 2948c2ecf20Sopenharmony_ci struct list_head *cursor, *next; 2958c2ecf20Sopenharmony_ci struct device *dev; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* if release function ran, game over */ 2988c2ecf20Sopenharmony_ci if (unlikely(mos_parport->serial == NULL)) 2998c2ecf20Sopenharmony_ci return; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci dev = &mos_parport->serial->dev->dev; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* try again to get the mutex */ 3048c2ecf20Sopenharmony_ci if (!mutex_trylock(&mos_parport->serial->disc_mutex)) { 3058c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: rescheduling tasklet\n", __func__); 3068c2ecf20Sopenharmony_ci tasklet_schedule(&mos_parport->urb_tasklet); 3078c2ecf20Sopenharmony_ci return; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* if device disconnected, game over */ 3118c2ecf20Sopenharmony_ci if (unlikely(mos_parport->serial->disconnected)) { 3128c2ecf20Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 3138c2ecf20Sopenharmony_ci return; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos_parport->listlock, flags); 3178c2ecf20Sopenharmony_ci if (list_empty(&mos_parport->deferred_urbs)) { 3188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 3198c2ecf20Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 3208c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: deferred_urbs list empty\n", __func__); 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* move contents of deferred_urbs list to active_urbs list and submit */ 3258c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &mos_parport->deferred_urbs) 3268c2ecf20Sopenharmony_ci list_move_tail(cursor, &mos_parport->active_urbs); 3278c2ecf20Sopenharmony_ci list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs, 3288c2ecf20Sopenharmony_ci urblist_entry) { 3298c2ecf20Sopenharmony_ci ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); 3308c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: urb submitted\n", __func__); 3318c2ecf20Sopenharmony_ci if (ret_val) { 3328c2ecf20Sopenharmony_ci dev_err(dev, "usb_submit_urb() failed: %d\n", ret_val); 3338c2ecf20Sopenharmony_ci list_del(&urbtrack->urblist_entry); 3348c2ecf20Sopenharmony_ci kref_put(&urbtrack->ref_count, destroy_urbtracker); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 3388c2ecf20Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* callback for parallel port control urbs submitted asynchronously */ 3428c2ecf20Sopenharmony_cistatic void async_complete(struct urb *urb) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct urbtracker *urbtrack = urb->context; 3458c2ecf20Sopenharmony_ci int status = urb->status; 3468c2ecf20Sopenharmony_ci unsigned long flags; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (unlikely(status)) 3498c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* remove the urbtracker from the active_urbs list */ 3528c2ecf20Sopenharmony_ci spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags); 3538c2ecf20Sopenharmony_ci list_del(&urbtrack->urblist_entry); 3548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags); 3558c2ecf20Sopenharmony_ci kref_put(&urbtrack->ref_count, destroy_urbtracker); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, 3598c2ecf20Sopenharmony_ci enum mos_regs reg, __u8 data) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct urbtracker *urbtrack; 3628c2ecf20Sopenharmony_ci int ret_val; 3638c2ecf20Sopenharmony_ci unsigned long flags; 3648c2ecf20Sopenharmony_ci struct usb_serial *serial = mos_parport->serial; 3658c2ecf20Sopenharmony_ci struct usb_device *usbdev = serial->dev; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* create and initialize the control urb and containing urbtracker */ 3688c2ecf20Sopenharmony_ci urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC); 3698c2ecf20Sopenharmony_ci if (!urbtrack) 3708c2ecf20Sopenharmony_ci return -ENOMEM; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC); 3738c2ecf20Sopenharmony_ci if (!urbtrack->urb) { 3748c2ecf20Sopenharmony_ci kfree(urbtrack); 3758c2ecf20Sopenharmony_ci return -ENOMEM; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC); 3788c2ecf20Sopenharmony_ci if (!urbtrack->setup) { 3798c2ecf20Sopenharmony_ci usb_free_urb(urbtrack->urb); 3808c2ecf20Sopenharmony_ci kfree(urbtrack); 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci urbtrack->setup->bRequestType = (__u8)0x40; 3848c2ecf20Sopenharmony_ci urbtrack->setup->bRequest = (__u8)0x0e; 3858c2ecf20Sopenharmony_ci urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy)); 3868c2ecf20Sopenharmony_ci urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg)); 3878c2ecf20Sopenharmony_ci urbtrack->setup->wLength = 0; 3888c2ecf20Sopenharmony_ci usb_fill_control_urb(urbtrack->urb, usbdev, 3898c2ecf20Sopenharmony_ci usb_sndctrlpipe(usbdev, 0), 3908c2ecf20Sopenharmony_ci (unsigned char *)urbtrack->setup, 3918c2ecf20Sopenharmony_ci NULL, 0, async_complete, urbtrack); 3928c2ecf20Sopenharmony_ci kref_get(&mos_parport->ref_count); 3938c2ecf20Sopenharmony_ci urbtrack->mos_parport = mos_parport; 3948c2ecf20Sopenharmony_ci kref_init(&urbtrack->ref_count); 3958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&urbtrack->urblist_entry); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * get the disconnect mutex, or add tracker to the deferred_urbs list 3998c2ecf20Sopenharmony_ci * and schedule a tasklet to try again later 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci if (!mutex_trylock(&serial->disc_mutex)) { 4028c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos_parport->listlock, flags); 4038c2ecf20Sopenharmony_ci list_add_tail(&urbtrack->urblist_entry, 4048c2ecf20Sopenharmony_ci &mos_parport->deferred_urbs); 4058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 4068c2ecf20Sopenharmony_ci tasklet_schedule(&mos_parport->urb_tasklet); 4078c2ecf20Sopenharmony_ci dev_dbg(&usbdev->dev, "tasklet scheduled\n"); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* bail if device disconnected */ 4128c2ecf20Sopenharmony_ci if (serial->disconnected) { 4138c2ecf20Sopenharmony_ci kref_put(&urbtrack->ref_count, destroy_urbtracker); 4148c2ecf20Sopenharmony_ci mutex_unlock(&serial->disc_mutex); 4158c2ecf20Sopenharmony_ci return -ENODEV; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* add the tracker to the active_urbs list and submit */ 4198c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos_parport->listlock, flags); 4208c2ecf20Sopenharmony_ci list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs); 4218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 4228c2ecf20Sopenharmony_ci ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); 4238c2ecf20Sopenharmony_ci mutex_unlock(&serial->disc_mutex); 4248c2ecf20Sopenharmony_ci if (ret_val) { 4258c2ecf20Sopenharmony_ci dev_err(&usbdev->dev, 4268c2ecf20Sopenharmony_ci "%s: submit_urb() failed: %d\n", __func__, ret_val); 4278c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos_parport->listlock, flags); 4288c2ecf20Sopenharmony_ci list_del(&urbtrack->urblist_entry); 4298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 4308c2ecf20Sopenharmony_ci kref_put(&urbtrack->ref_count, destroy_urbtracker); 4318c2ecf20Sopenharmony_ci return ret_val; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * This is the the common top part of all parallel port callback operations that 4388c2ecf20Sopenharmony_ci * send synchronous messages to the device. This implements convoluted locking 4398c2ecf20Sopenharmony_ci * that avoids two scenarios: (1) a port operation is called after usbserial 4408c2ecf20Sopenharmony_ci * has called our release function, at which point struct mos7715_parport has 4418c2ecf20Sopenharmony_ci * been destroyed, and (2) the device has been disconnected, but usbserial has 4428c2ecf20Sopenharmony_ci * not called the release function yet because someone has a serial port open. 4438c2ecf20Sopenharmony_ci * The shared release_lock prevents the first, and the mutex and disconnected 4448c2ecf20Sopenharmony_ci * flag maintained by usbserial covers the second. We also use the msg_pending 4458c2ecf20Sopenharmony_ci * flag to ensure that all synchronous usb message calls have completed before 4468c2ecf20Sopenharmony_ci * our release function can return. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_cistatic int parport_prologue(struct parport *pp) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci spin_lock(&release_lock); 4538c2ecf20Sopenharmony_ci mos_parport = pp->private_data; 4548c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) { 4558c2ecf20Sopenharmony_ci /* release fn called, port struct destroyed */ 4568c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 4578c2ecf20Sopenharmony_ci return -1; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci mos_parport->msg_pending = true; /* synch usb call pending */ 4608c2ecf20Sopenharmony_ci reinit_completion(&mos_parport->syncmsg_compl); 4618c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci mutex_lock(&mos_parport->serial->disc_mutex); 4648c2ecf20Sopenharmony_ci if (mos_parport->serial->disconnected) { 4658c2ecf20Sopenharmony_ci /* device disconnected */ 4668c2ecf20Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 4678c2ecf20Sopenharmony_ci mos_parport->msg_pending = false; 4688c2ecf20Sopenharmony_ci complete(&mos_parport->syncmsg_compl); 4698c2ecf20Sopenharmony_ci return -1; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* 4768c2ecf20Sopenharmony_ci * This is the common bottom part of all parallel port functions that send 4778c2ecf20Sopenharmony_ci * synchronous messages to the device. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_cistatic inline void parport_epilogue(struct parport *pp) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 4828c2ecf20Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 4838c2ecf20Sopenharmony_ci mos_parport->msg_pending = false; 4848c2ecf20Sopenharmony_ci complete(&mos_parport->syncmsg_compl); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void parport_mos7715_write_data(struct parport *pp, unsigned char d) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 4928c2ecf20Sopenharmony_ci return; 4938c2ecf20Sopenharmony_ci mos7715_change_mode(mos_parport, SPP); 4948c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, (__u8)d); 4958c2ecf20Sopenharmony_ci parport_epilogue(pp); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_data(struct parport *pp) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 5018c2ecf20Sopenharmony_ci unsigned char d; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci read_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, &d); 5068c2ecf20Sopenharmony_ci parport_epilogue(pp); 5078c2ecf20Sopenharmony_ci return d; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void parport_mos7715_write_control(struct parport *pp, unsigned char d) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 5138c2ecf20Sopenharmony_ci __u8 data; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0); 5188c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, data); 5198c2ecf20Sopenharmony_ci mos_parport->shadowDCR = data; 5208c2ecf20Sopenharmony_ci parport_epilogue(pp); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_control(struct parport *pp) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 5268c2ecf20Sopenharmony_ci __u8 dcr; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci spin_lock(&release_lock); 5298c2ecf20Sopenharmony_ci mos_parport = pp->private_data; 5308c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) { 5318c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci dcr = mos_parport->shadowDCR & 0x0f; 5358c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 5368c2ecf20Sopenharmony_ci return dcr; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_frob_control(struct parport *pp, 5408c2ecf20Sopenharmony_ci unsigned char mask, 5418c2ecf20Sopenharmony_ci unsigned char val) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 5448c2ecf20Sopenharmony_ci __u8 dcr; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci mask &= 0x0f; 5478c2ecf20Sopenharmony_ci val &= 0x0f; 5488c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val; 5518c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 5528c2ecf20Sopenharmony_ci mos_parport->shadowDCR); 5538c2ecf20Sopenharmony_ci dcr = mos_parport->shadowDCR & 0x0f; 5548c2ecf20Sopenharmony_ci parport_epilogue(pp); 5558c2ecf20Sopenharmony_ci return dcr; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_status(struct parport *pp) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci unsigned char status; 5618c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci spin_lock(&release_lock); 5648c2ecf20Sopenharmony_ci mos_parport = pp->private_data; 5658c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 5668c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci status = atomic_read(&mos_parport->shadowDSR) & 0xf8; 5708c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 5718c2ecf20Sopenharmony_ci return status; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void parport_mos7715_enable_irq(struct parport *pp) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void parport_mos7715_disable_irq(struct parport *pp) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic void parport_mos7715_data_forward(struct parport *pp) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci mos7715_change_mode(mos_parport, PS2); 5898c2ecf20Sopenharmony_ci mos_parport->shadowDCR &= ~0x20; 5908c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 5918c2ecf20Sopenharmony_ci mos_parport->shadowDCR); 5928c2ecf20Sopenharmony_ci parport_epilogue(pp); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void parport_mos7715_data_reverse(struct parport *pp) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_ci mos7715_change_mode(mos_parport, PS2); 6028c2ecf20Sopenharmony_ci mos_parport->shadowDCR |= 0x20; 6038c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 6048c2ecf20Sopenharmony_ci mos_parport->shadowDCR); 6058c2ecf20Sopenharmony_ci parport_epilogue(pp); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void parport_mos7715_init_state(struct pardevice *dev, 6098c2ecf20Sopenharmony_ci struct parport_state *s) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci s->u.pc.ctr = DCR_INIT_VAL; 6128c2ecf20Sopenharmony_ci s->u.pc.ecr = ECR_INIT_VAL; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* N.B. Parport core code requires that this function not block */ 6168c2ecf20Sopenharmony_cistatic void parport_mos7715_save_state(struct parport *pp, 6178c2ecf20Sopenharmony_ci struct parport_state *s) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci spin_lock(&release_lock); 6228c2ecf20Sopenharmony_ci mos_parport = pp->private_data; 6238c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 6248c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 6258c2ecf20Sopenharmony_ci return; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci s->u.pc.ctr = mos_parport->shadowDCR; 6288c2ecf20Sopenharmony_ci s->u.pc.ecr = mos_parport->shadowECR; 6298c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* N.B. Parport core code requires that this function not block */ 6338c2ecf20Sopenharmony_cistatic void parport_mos7715_restore_state(struct parport *pp, 6348c2ecf20Sopenharmony_ci struct parport_state *s) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock(&release_lock); 6398c2ecf20Sopenharmony_ci mos_parport = pp->private_data; 6408c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 6418c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci mos_parport->shadowDCR = s->u.pc.ctr; 6458c2ecf20Sopenharmony_ci mos_parport->shadowECR = s->u.pc.ecr; 6468c2ecf20Sopenharmony_ci write_parport_reg_nonblock(mos_parport, MOS7720_DCR, 6478c2ecf20Sopenharmony_ci mos_parport->shadowDCR); 6488c2ecf20Sopenharmony_ci write_parport_reg_nonblock(mos_parport, MOS7720_ECR, 6498c2ecf20Sopenharmony_ci mos_parport->shadowECR); 6508c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic size_t parport_mos7715_write_compat(struct parport *pp, 6548c2ecf20Sopenharmony_ci const void *buffer, 6558c2ecf20Sopenharmony_ci size_t len, int flags) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci int retval; 6588c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 6598c2ecf20Sopenharmony_ci int actual_len; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (parport_prologue(pp) < 0) 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci mos7715_change_mode(mos_parport, PPF); 6648c2ecf20Sopenharmony_ci retval = usb_bulk_msg(mos_parport->serial->dev, 6658c2ecf20Sopenharmony_ci usb_sndbulkpipe(mos_parport->serial->dev, 2), 6668c2ecf20Sopenharmony_ci (void *)buffer, len, &actual_len, 6678c2ecf20Sopenharmony_ci MOS_WDR_TIMEOUT); 6688c2ecf20Sopenharmony_ci parport_epilogue(pp); 6698c2ecf20Sopenharmony_ci if (retval) { 6708c2ecf20Sopenharmony_ci dev_err(&mos_parport->serial->dev->dev, 6718c2ecf20Sopenharmony_ci "mos7720: usb_bulk_msg() failed: %d\n", retval); 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci return actual_len; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic struct parport_operations parport_mos7715_ops = { 6788c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6798c2ecf20Sopenharmony_ci .write_data = parport_mos7715_write_data, 6808c2ecf20Sopenharmony_ci .read_data = parport_mos7715_read_data, 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci .write_control = parport_mos7715_write_control, 6838c2ecf20Sopenharmony_ci .read_control = parport_mos7715_read_control, 6848c2ecf20Sopenharmony_ci .frob_control = parport_mos7715_frob_control, 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci .read_status = parport_mos7715_read_status, 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci .enable_irq = parport_mos7715_enable_irq, 6898c2ecf20Sopenharmony_ci .disable_irq = parport_mos7715_disable_irq, 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci .data_forward = parport_mos7715_data_forward, 6928c2ecf20Sopenharmony_ci .data_reverse = parport_mos7715_data_reverse, 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci .init_state = parport_mos7715_init_state, 6958c2ecf20Sopenharmony_ci .save_state = parport_mos7715_save_state, 6968c2ecf20Sopenharmony_ci .restore_state = parport_mos7715_restore_state, 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci .compat_write_data = parport_mos7715_write_compat, 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci .nibble_read_data = parport_ieee1284_read_nibble, 7018c2ecf20Sopenharmony_ci .byte_read_data = parport_ieee1284_read_byte, 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* 7058c2ecf20Sopenharmony_ci * Allocate and initialize parallel port control struct, initialize 7068c2ecf20Sopenharmony_ci * the parallel port hardware device, and register with the parport subsystem. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_cistatic int mos7715_parport_init(struct usb_serial *serial) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* allocate and initialize parallel port control struct */ 7138c2ecf20Sopenharmony_ci mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); 7148c2ecf20Sopenharmony_ci if (!mos_parport) 7158c2ecf20Sopenharmony_ci return -ENOMEM; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci mos_parport->msg_pending = false; 7188c2ecf20Sopenharmony_ci kref_init(&mos_parport->ref_count); 7198c2ecf20Sopenharmony_ci spin_lock_init(&mos_parport->listlock); 7208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mos_parport->active_urbs); 7218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mos_parport->deferred_urbs); 7228c2ecf20Sopenharmony_ci usb_set_serial_data(serial, mos_parport); /* hijack private pointer */ 7238c2ecf20Sopenharmony_ci mos_parport->serial = serial; 7248c2ecf20Sopenharmony_ci tasklet_setup(&mos_parport->urb_tasklet, send_deferred_urbs); 7258c2ecf20Sopenharmony_ci init_completion(&mos_parport->syncmsg_compl); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* cycle parallel port reset bit */ 7288c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x80); 7298c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x00); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* initialize device registers */ 7328c2ecf20Sopenharmony_ci mos_parport->shadowDCR = DCR_INIT_VAL; 7338c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 7348c2ecf20Sopenharmony_ci mos_parport->shadowDCR); 7358c2ecf20Sopenharmony_ci mos_parport->shadowECR = ECR_INIT_VAL; 7368c2ecf20Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, 7378c2ecf20Sopenharmony_ci mos_parport->shadowECR); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* register with parport core */ 7408c2ecf20Sopenharmony_ci mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE, 7418c2ecf20Sopenharmony_ci PARPORT_DMA_NONE, 7428c2ecf20Sopenharmony_ci &parport_mos7715_ops); 7438c2ecf20Sopenharmony_ci if (mos_parport->pp == NULL) { 7448c2ecf20Sopenharmony_ci dev_err(&serial->interface->dev, 7458c2ecf20Sopenharmony_ci "Could not register parport\n"); 7468c2ecf20Sopenharmony_ci kref_put(&mos_parport->ref_count, destroy_mos_parport); 7478c2ecf20Sopenharmony_ci return -EIO; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci mos_parport->pp->private_data = mos_parport; 7508c2ecf20Sopenharmony_ci mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP; 7518c2ecf20Sopenharmony_ci mos_parport->pp->dev = &serial->interface->dev; 7528c2ecf20Sopenharmony_ci parport_announce_port(mos_parport->pp); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/* 7598c2ecf20Sopenharmony_ci * mos7720_interrupt_callback 7608c2ecf20Sopenharmony_ci * this is the callback function for when we have received data on the 7618c2ecf20Sopenharmony_ci * interrupt endpoint. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_cistatic void mos7720_interrupt_callback(struct urb *urb) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci int result; 7668c2ecf20Sopenharmony_ci int length; 7678c2ecf20Sopenharmony_ci int status = urb->status; 7688c2ecf20Sopenharmony_ci struct device *dev = &urb->dev->dev; 7698c2ecf20Sopenharmony_ci __u8 *data; 7708c2ecf20Sopenharmony_ci __u8 sp1; 7718c2ecf20Sopenharmony_ci __u8 sp2; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci switch (status) { 7748c2ecf20Sopenharmony_ci case 0: 7758c2ecf20Sopenharmony_ci /* success */ 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci case -ECONNRESET: 7788c2ecf20Sopenharmony_ci case -ENOENT: 7798c2ecf20Sopenharmony_ci case -ESHUTDOWN: 7808c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 7818c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); 7828c2ecf20Sopenharmony_ci return; 7838c2ecf20Sopenharmony_ci default: 7848c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); 7858c2ecf20Sopenharmony_ci goto exit; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci length = urb->actual_length; 7898c2ecf20Sopenharmony_ci data = urb->transfer_buffer; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Moschip get 4 bytes 7928c2ecf20Sopenharmony_ci * Byte 1 IIR Port 1 (port.number is 0) 7938c2ecf20Sopenharmony_ci * Byte 2 IIR Port 2 (port.number is 1) 7948c2ecf20Sopenharmony_ci * Byte 3 -------------- 7958c2ecf20Sopenharmony_ci * Byte 4 FIFO status for both */ 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* the above description is inverted 7988c2ecf20Sopenharmony_ci * oneukum 2007-03-14 */ 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (unlikely(length != 4)) { 8018c2ecf20Sopenharmony_ci dev_dbg(dev, "Wrong data !!!\n"); 8028c2ecf20Sopenharmony_ci return; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci sp1 = data[3]; 8068c2ecf20Sopenharmony_ci sp2 = data[2]; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if ((sp1 | sp2) & 0x01) { 8098c2ecf20Sopenharmony_ci /* No Interrupt Pending in both the ports */ 8108c2ecf20Sopenharmony_ci dev_dbg(dev, "No Interrupt !!!\n"); 8118c2ecf20Sopenharmony_ci } else { 8128c2ecf20Sopenharmony_ci switch (sp1 & 0x0f) { 8138c2ecf20Sopenharmony_ci case SERIAL_IIR_RLS: 8148c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n"); 8158c2ecf20Sopenharmony_ci break; 8168c2ecf20Sopenharmony_ci case SERIAL_IIR_CTI: 8178c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port 1: Receiver time out\n"); 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci case SERIAL_IIR_MS: 8208c2ecf20Sopenharmony_ci /* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */ 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci switch (sp2 & 0x0f) { 8258c2ecf20Sopenharmony_ci case SERIAL_IIR_RLS: 8268c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n"); 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci case SERIAL_IIR_CTI: 8298c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port 2: Receiver time out\n"); 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci case SERIAL_IIR_MS: 8328c2ecf20Sopenharmony_ci /* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */ 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ciexit: 8388c2ecf20Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 8398c2ecf20Sopenharmony_ci if (result) 8408c2ecf20Sopenharmony_ci dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result); 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci/* 8448c2ecf20Sopenharmony_ci * mos7715_interrupt_callback 8458c2ecf20Sopenharmony_ci * this is the 7715's callback function for when we have received data on 8468c2ecf20Sopenharmony_ci * the interrupt endpoint. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_cistatic void mos7715_interrupt_callback(struct urb *urb) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci int result; 8518c2ecf20Sopenharmony_ci int length; 8528c2ecf20Sopenharmony_ci int status = urb->status; 8538c2ecf20Sopenharmony_ci struct device *dev = &urb->dev->dev; 8548c2ecf20Sopenharmony_ci __u8 *data; 8558c2ecf20Sopenharmony_ci __u8 iir; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci switch (status) { 8588c2ecf20Sopenharmony_ci case 0: 8598c2ecf20Sopenharmony_ci /* success */ 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci case -ECONNRESET: 8628c2ecf20Sopenharmony_ci case -ENOENT: 8638c2ecf20Sopenharmony_ci case -ESHUTDOWN: 8648c2ecf20Sopenharmony_ci case -ENODEV: 8658c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 8668c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); 8678c2ecf20Sopenharmony_ci return; 8688c2ecf20Sopenharmony_ci default: 8698c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); 8708c2ecf20Sopenharmony_ci goto exit; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci length = urb->actual_length; 8748c2ecf20Sopenharmony_ci data = urb->transfer_buffer; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* Structure of data from 7715 device: 8778c2ecf20Sopenharmony_ci * Byte 1: IIR serial Port 8788c2ecf20Sopenharmony_ci * Byte 2: unused 8798c2ecf20Sopenharmony_ci * Byte 2: DSR parallel port 8808c2ecf20Sopenharmony_ci * Byte 4: FIFO status for both */ 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (unlikely(length != 4)) { 8838c2ecf20Sopenharmony_ci dev_dbg(dev, "Wrong data !!!\n"); 8848c2ecf20Sopenharmony_ci return; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci iir = data[0]; 8888c2ecf20Sopenharmony_ci if (!(iir & 0x01)) { /* serial port interrupt pending */ 8898c2ecf20Sopenharmony_ci switch (iir & 0x0f) { 8908c2ecf20Sopenharmony_ci case SERIAL_IIR_RLS: 8918c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n"); 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case SERIAL_IIR_CTI: 8948c2ecf20Sopenharmony_ci dev_dbg(dev, "Serial Port: Receiver time out\n"); 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci case SERIAL_IIR_MS: 8978c2ecf20Sopenharmony_ci /* dev_dbg(dev, "Serial Port: Modem status change\n"); */ 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 9038c2ecf20Sopenharmony_ci { /* update local copy of DSR reg */ 9048c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 9058c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = port->serial->private; 9068c2ecf20Sopenharmony_ci if (unlikely(mos_parport == NULL)) 9078c2ecf20Sopenharmony_ci return; 9088c2ecf20Sopenharmony_ci atomic_set(&mos_parport->shadowDSR, data[2]); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci#endif 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ciexit: 9138c2ecf20Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 9148c2ecf20Sopenharmony_ci if (result) 9158c2ecf20Sopenharmony_ci dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result); 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/* 9198c2ecf20Sopenharmony_ci * mos7720_bulk_in_callback 9208c2ecf20Sopenharmony_ci * this is the callback function for when we have received data on the 9218c2ecf20Sopenharmony_ci * bulk in endpoint. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_cistatic void mos7720_bulk_in_callback(struct urb *urb) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci int retval; 9268c2ecf20Sopenharmony_ci unsigned char *data ; 9278c2ecf20Sopenharmony_ci struct usb_serial_port *port; 9288c2ecf20Sopenharmony_ci int status = urb->status; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (status) { 9318c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status); 9328c2ecf20Sopenharmony_ci return; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci port = urb->context; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Entering...%s\n", __func__); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci data = urb->transfer_buffer; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (urb->actual_length) { 9428c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, data, urb->actual_length); 9438c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 9478c2ecf20Sopenharmony_ci retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); 9488c2ecf20Sopenharmony_ci if (retval) 9498c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval); 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * mos7720_bulk_out_data_callback 9558c2ecf20Sopenharmony_ci * this is the callback function for when we have finished sending serial 9568c2ecf20Sopenharmony_ci * data on the bulk out endpoint. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_cistatic void mos7720_bulk_out_data_callback(struct urb *urb) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 9618c2ecf20Sopenharmony_ci int status = urb->status; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (status) { 9648c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status); 9658c2ecf20Sopenharmony_ci return; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci mos7720_port = urb->context; 9698c2ecf20Sopenharmony_ci if (!mos7720_port) { 9708c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n"); 9718c2ecf20Sopenharmony_ci return ; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (mos7720_port->open) 9758c2ecf20Sopenharmony_ci tty_port_tty_wakeup(&mos7720_port->port->port); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic int mos77xx_calc_num_ports(struct usb_serial *serial, 9798c2ecf20Sopenharmony_ci struct usb_serial_endpoints *epds) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (product == MOSCHIP_DEVICE_ID_7715) { 9848c2ecf20Sopenharmony_ci /* 9858c2ecf20Sopenharmony_ci * The 7715 uses the first bulk in/out endpoint pair for the 9868c2ecf20Sopenharmony_ci * parallel port, and the second for the serial port. We swap 9878c2ecf20Sopenharmony_ci * the endpoint descriptors here so that the the first and 9888c2ecf20Sopenharmony_ci * only registered port structure uses the serial-port 9898c2ecf20Sopenharmony_ci * endpoints. 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_ci swap(epds->bulk_in[0], epds->bulk_in[1]); 9928c2ecf20Sopenharmony_ci swap(epds->bulk_out[0], epds->bulk_out[1]); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return 1; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 2; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct usb_serial *serial; 10038c2ecf20Sopenharmony_ci struct urb *urb; 10048c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 10058c2ecf20Sopenharmony_ci int response; 10068c2ecf20Sopenharmony_ci int port_number; 10078c2ecf20Sopenharmony_ci __u8 data; 10088c2ecf20Sopenharmony_ci int allocated_urbs = 0; 10098c2ecf20Sopenharmony_ci int j; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci serial = port->serial; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 10148c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 10158c2ecf20Sopenharmony_ci return -ENODEV; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->write_urb->pipe); 10188c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->read_urb->pipe); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* Initialising the write urb pool */ 10218c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 10228c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 10238c2ecf20Sopenharmony_ci mos7720_port->write_urb_pool[j] = urb; 10248c2ecf20Sopenharmony_ci if (!urb) 10258c2ecf20Sopenharmony_ci continue; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 10288c2ecf20Sopenharmony_ci GFP_KERNEL); 10298c2ecf20Sopenharmony_ci if (!urb->transfer_buffer) { 10308c2ecf20Sopenharmony_ci usb_free_urb(mos7720_port->write_urb_pool[j]); 10318c2ecf20Sopenharmony_ci mos7720_port->write_urb_pool[j] = NULL; 10328c2ecf20Sopenharmony_ci continue; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci allocated_urbs++; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (!allocated_urbs) 10388c2ecf20Sopenharmony_ci return -ENOMEM; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* Initialize MCS7720 -- Write Init values to corresponding Registers 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * Register Index 10438c2ecf20Sopenharmony_ci * 0 : MOS7720_THR/MOS7720_RHR 10448c2ecf20Sopenharmony_ci * 1 : MOS7720_IER 10458c2ecf20Sopenharmony_ci * 2 : MOS7720_FCR 10468c2ecf20Sopenharmony_ci * 3 : MOS7720_LCR 10478c2ecf20Sopenharmony_ci * 4 : MOS7720_MCR 10488c2ecf20Sopenharmony_ci * 5 : MOS7720_LSR 10498c2ecf20Sopenharmony_ci * 6 : MOS7720_MSR 10508c2ecf20Sopenharmony_ci * 7 : MOS7720_SPR 10518c2ecf20Sopenharmony_ci * 10528c2ecf20Sopenharmony_ci * 0x08 : SP1/2 Control Reg 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_ci port_number = port->port_number; 10558c2ecf20Sopenharmony_ci read_mos_reg(serial, port_number, MOS7720_LSR, &data); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP1_REG, 0x02); 10608c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP2_REG, 0x02); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 10638c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 10668c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = 0x03; 10678c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 10688c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 10698c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 10708c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 10718c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_SP_CONTROL_REG, 0x00); 10748c2ecf20Sopenharmony_ci read_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, &data); 10758c2ecf20Sopenharmony_ci data = data | (port->port_number + 1); 10768c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, data); 10778c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = 0x83; 10788c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 10798c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 10808c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_THR, 0x0c); 10818c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 10828c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = 0x03; 10838c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 10848c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 10858c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci response = usb_submit_urb(port->read_urb, GFP_KERNEL); 10888c2ecf20Sopenharmony_ci if (response) 10898c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s - Error %d submitting read urb\n", 10908c2ecf20Sopenharmony_ci __func__, response); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* initialize our port settings */ 10938c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */ 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* send a open port command */ 10968c2ecf20Sopenharmony_ci mos7720_port->open = 1; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* 11028c2ecf20Sopenharmony_ci * mos7720_chars_in_buffer 11038c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 11048c2ecf20Sopenharmony_ci * bytes of data we currently have outstanding in the port (data that has 11058c2ecf20Sopenharmony_ci * been written, but hasn't made it out the port yet) 11068c2ecf20Sopenharmony_ci * If successful, we return the number of bytes left to be written in the 11078c2ecf20Sopenharmony_ci * system, 11088c2ecf20Sopenharmony_ci * Otherwise we return a negative error number. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_cistatic int mos7720_chars_in_buffer(struct tty_struct *tty) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 11138c2ecf20Sopenharmony_ci int i; 11148c2ecf20Sopenharmony_ci int chars = 0; 11158c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 11188c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 11198c2ecf20Sopenharmony_ci return 0; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 11228c2ecf20Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 11238c2ecf20Sopenharmony_ci mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) 11248c2ecf20Sopenharmony_ci chars += URB_TRANSFER_BUFFER_SIZE; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); 11278c2ecf20Sopenharmony_ci return chars; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void mos7720_close(struct usb_serial_port *port) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct usb_serial *serial; 11338c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 11348c2ecf20Sopenharmony_ci int j; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci serial = port->serial; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 11398c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 11408c2ecf20Sopenharmony_ci return; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) 11438c2ecf20Sopenharmony_ci usb_kill_urb(mos7720_port->write_urb_pool[j]); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* Freeing Write URBs */ 11468c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 11478c2ecf20Sopenharmony_ci if (mos7720_port->write_urb_pool[j]) { 11488c2ecf20Sopenharmony_ci kfree(mos7720_port->write_urb_pool[j]->transfer_buffer); 11498c2ecf20Sopenharmony_ci usb_free_urb(mos7720_port->write_urb_pool[j]); 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* While closing port, shutdown all bulk read, write * 11548c2ecf20Sopenharmony_ci * and interrupt read if they exists, otherwise nop */ 11558c2ecf20Sopenharmony_ci usb_kill_urb(port->write_urb); 11568c2ecf20Sopenharmony_ci usb_kill_urb(port->read_urb); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci write_mos_reg(serial, port->port_number, MOS7720_MCR, 0x00); 11598c2ecf20Sopenharmony_ci write_mos_reg(serial, port->port_number, MOS7720_IER, 0x00); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci mos7720_port->open = 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic void mos7720_break(struct tty_struct *tty, int break_state) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 11678c2ecf20Sopenharmony_ci unsigned char data; 11688c2ecf20Sopenharmony_ci struct usb_serial *serial; 11698c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci serial = port->serial; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 11748c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 11758c2ecf20Sopenharmony_ci return; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (break_state == -1) 11788c2ecf20Sopenharmony_ci data = mos7720_port->shadowLCR | UART_LCR_SBC; 11798c2ecf20Sopenharmony_ci else 11808c2ecf20Sopenharmony_ci data = mos7720_port->shadowLCR & ~UART_LCR_SBC; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = data; 11838c2ecf20Sopenharmony_ci write_mos_reg(serial, port->port_number, MOS7720_LCR, 11848c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* 11888c2ecf20Sopenharmony_ci * mos7720_write_room 11898c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 11908c2ecf20Sopenharmony_ci * bytes of data we can accept for a specific port. 11918c2ecf20Sopenharmony_ci * If successful, we return the amount of room that we have for this port 11928c2ecf20Sopenharmony_ci * Otherwise we return a negative error number. 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_cistatic int mos7720_write_room(struct tty_struct *tty) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 11978c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 11988c2ecf20Sopenharmony_ci int room = 0; 11998c2ecf20Sopenharmony_ci int i; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 12028c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 12038c2ecf20Sopenharmony_ci return -ENODEV; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* FIXME: Locking */ 12068c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 12078c2ecf20Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 12088c2ecf20Sopenharmony_ci mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) 12098c2ecf20Sopenharmony_ci room += URB_TRANSFER_BUFFER_SIZE; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); 12138c2ecf20Sopenharmony_ci return room; 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, 12178c2ecf20Sopenharmony_ci const unsigned char *data, int count) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci int status; 12208c2ecf20Sopenharmony_ci int i; 12218c2ecf20Sopenharmony_ci int bytes_sent = 0; 12228c2ecf20Sopenharmony_ci int transfer_size; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 12258c2ecf20Sopenharmony_ci struct usb_serial *serial; 12268c2ecf20Sopenharmony_ci struct urb *urb; 12278c2ecf20Sopenharmony_ci const unsigned char *current_position = data; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci serial = port->serial; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 12328c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 12338c2ecf20Sopenharmony_ci return -ENODEV; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* try to find a free urb in the list */ 12368c2ecf20Sopenharmony_ci urb = NULL; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 12398c2ecf20Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 12408c2ecf20Sopenharmony_ci mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { 12418c2ecf20Sopenharmony_ci urb = mos7720_port->write_urb_pool[i]; 12428c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "URB:%d\n", i); 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (urb == NULL) { 12488c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - no more free urbs\n", __func__); 12498c2ecf20Sopenharmony_ci goto exit; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (urb->transfer_buffer == NULL) { 12538c2ecf20Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 12548c2ecf20Sopenharmony_ci GFP_ATOMIC); 12558c2ecf20Sopenharmony_ci if (!urb->transfer_buffer) { 12568c2ecf20Sopenharmony_ci bytes_sent = -ENOMEM; 12578c2ecf20Sopenharmony_ci goto exit; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci memcpy(urb->transfer_buffer, current_position, transfer_size); 12638c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, transfer_size, 12648c2ecf20Sopenharmony_ci urb->transfer_buffer); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* fill urb with data and submit */ 12678c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, serial->dev, 12688c2ecf20Sopenharmony_ci usb_sndbulkpipe(serial->dev, 12698c2ecf20Sopenharmony_ci port->bulk_out_endpointAddress), 12708c2ecf20Sopenharmony_ci urb->transfer_buffer, transfer_size, 12718c2ecf20Sopenharmony_ci mos7720_bulk_out_data_callback, mos7720_port); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci /* send it down the pipe */ 12748c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 12758c2ecf20Sopenharmony_ci if (status) { 12768c2ecf20Sopenharmony_ci dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " 12778c2ecf20Sopenharmony_ci "with status = %d\n", __func__, status); 12788c2ecf20Sopenharmony_ci bytes_sent = status; 12798c2ecf20Sopenharmony_ci goto exit; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci bytes_sent = transfer_size; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ciexit: 12848c2ecf20Sopenharmony_ci return bytes_sent; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void mos7720_throttle(struct tty_struct *tty) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 12908c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 12918c2ecf20Sopenharmony_ci int status; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 12968c2ecf20Sopenharmony_ci return; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (!mos7720_port->open) { 12998c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 13008c2ecf20Sopenharmony_ci return; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci /* if we are implementing XON/XOFF, send the stop character */ 13048c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 13058c2ecf20Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 13068c2ecf20Sopenharmony_ci status = mos7720_write(tty, port, &stop_char, 1); 13078c2ecf20Sopenharmony_ci if (status <= 0) 13088c2ecf20Sopenharmony_ci return; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 13128c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 13138c2ecf20Sopenharmony_ci mos7720_port->shadowMCR &= ~UART_MCR_RTS; 13148c2ecf20Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 13158c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic void mos7720_unthrottle(struct tty_struct *tty) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 13228c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 13238c2ecf20Sopenharmony_ci int status; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 13268c2ecf20Sopenharmony_ci return; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (!mos7720_port->open) { 13298c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 13308c2ecf20Sopenharmony_ci return; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* if we are implementing XON/XOFF, send the start character */ 13348c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 13358c2ecf20Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 13368c2ecf20Sopenharmony_ci status = mos7720_write(tty, port, &start_char, 1); 13378c2ecf20Sopenharmony_ci if (status <= 0) 13388c2ecf20Sopenharmony_ci return; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 13428c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 13438c2ecf20Sopenharmony_ci mos7720_port->shadowMCR |= UART_MCR_RTS; 13448c2ecf20Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 13458c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci} 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci/* FIXME: this function does not work */ 13508c2ecf20Sopenharmony_cistatic int set_higher_rates(struct moschip_port *mos7720_port, 13518c2ecf20Sopenharmony_ci unsigned int baud) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci struct usb_serial_port *port; 13548c2ecf20Sopenharmony_ci struct usb_serial *serial; 13558c2ecf20Sopenharmony_ci int port_number; 13568c2ecf20Sopenharmony_ci enum mos_regs sp_reg; 13578c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 13588c2ecf20Sopenharmony_ci return -EINVAL; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci port = mos7720_port->port; 13618c2ecf20Sopenharmony_ci serial = port->serial; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /*********************************************** 13648c2ecf20Sopenharmony_ci * Init Sequence for higher rates 13658c2ecf20Sopenharmony_ci ***********************************************/ 13668c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Sending Setting Commands ..........\n"); 13678c2ecf20Sopenharmony_ci port_number = port->port_number; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 13708c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 13718c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 13728c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 13738c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 13748c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 13758c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x00); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /*********************************************** 13788c2ecf20Sopenharmony_ci * Set for higher rates * 13798c2ecf20Sopenharmony_ci ***********************************************/ 13808c2ecf20Sopenharmony_ci /* writing baud rate verbatum into uart clock field clearly not right */ 13818c2ecf20Sopenharmony_ci if (port_number == 0) 13828c2ecf20Sopenharmony_ci sp_reg = MOS7720_SP1_REG; 13838c2ecf20Sopenharmony_ci else 13848c2ecf20Sopenharmony_ci sp_reg = MOS7720_SP2_REG; 13858c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, sp_reg, baud * 0x10); 13868c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x03); 13878c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = 0x2b; 13888c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 13898c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /*********************************************** 13928c2ecf20Sopenharmony_ci * Set DLL/DLM 13938c2ecf20Sopenharmony_ci ***********************************************/ 13948c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; 13958c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 13968c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 13978c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_DLL, 0x01); 13988c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_DLM, 0x00); 13998c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; 14008c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 14018c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci return 0; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci/* baud rate information */ 14078c2ecf20Sopenharmony_cistruct divisor_table_entry { 14088c2ecf20Sopenharmony_ci __u32 baudrate; 14098c2ecf20Sopenharmony_ci __u16 divisor; 14108c2ecf20Sopenharmony_ci}; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci/* Define table of divisors for moschip 7720 hardware * 14138c2ecf20Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and * 14148c2ecf20Sopenharmony_ci * MCR.7 = 0. */ 14158c2ecf20Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = { 14168c2ecf20Sopenharmony_ci { 50, 2304}, 14178c2ecf20Sopenharmony_ci { 110, 1047}, /* 2094.545455 => 230450 => .0217 % over */ 14188c2ecf20Sopenharmony_ci { 134, 857}, /* 1713.011152 => 230398.5 => .00065% under */ 14198c2ecf20Sopenharmony_ci { 150, 768}, 14208c2ecf20Sopenharmony_ci { 300, 384}, 14218c2ecf20Sopenharmony_ci { 600, 192}, 14228c2ecf20Sopenharmony_ci { 1200, 96}, 14238c2ecf20Sopenharmony_ci { 1800, 64}, 14248c2ecf20Sopenharmony_ci { 2400, 48}, 14258c2ecf20Sopenharmony_ci { 4800, 24}, 14268c2ecf20Sopenharmony_ci { 7200, 16}, 14278c2ecf20Sopenharmony_ci { 9600, 12}, 14288c2ecf20Sopenharmony_ci { 19200, 6}, 14298c2ecf20Sopenharmony_ci { 38400, 3}, 14308c2ecf20Sopenharmony_ci { 57600, 2}, 14318c2ecf20Sopenharmony_ci { 115200, 1}, 14328c2ecf20Sopenharmony_ci}; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci/***************************************************************************** 14358c2ecf20Sopenharmony_ci * calc_baud_rate_divisor 14368c2ecf20Sopenharmony_ci * this function calculates the proper baud rate divisor for the specified 14378c2ecf20Sopenharmony_ci * baud rate. 14388c2ecf20Sopenharmony_ci *****************************************************************************/ 14398c2ecf20Sopenharmony_cistatic int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci int i; 14428c2ecf20Sopenharmony_ci __u16 custom; 14438c2ecf20Sopenharmony_ci __u16 round1; 14448c2ecf20Sopenharmony_ci __u16 round; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { 14508c2ecf20Sopenharmony_ci if (divisor_table[i].baudrate == baudrate) { 14518c2ecf20Sopenharmony_ci *divisor = divisor_table[i].divisor; 14528c2ecf20Sopenharmony_ci return 0; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* After trying for all the standard baud rates * 14578c2ecf20Sopenharmony_ci * Try calculating the divisor for this baud rate */ 14588c2ecf20Sopenharmony_ci if (baudrate > 75 && baudrate < 230400) { 14598c2ecf20Sopenharmony_ci /* get the divisor */ 14608c2ecf20Sopenharmony_ci custom = (__u16)(230400L / baudrate); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* Check for round off */ 14638c2ecf20Sopenharmony_ci round1 = (__u16)(2304000L / baudrate); 14648c2ecf20Sopenharmony_ci round = (__u16)(round1 - (custom * 10)); 14658c2ecf20Sopenharmony_ci if (round > 4) 14668c2ecf20Sopenharmony_ci custom++; 14678c2ecf20Sopenharmony_ci *divisor = custom; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom); 14708c2ecf20Sopenharmony_ci return 0; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Baud calculation Failed...\n"); 14748c2ecf20Sopenharmony_ci return -EINVAL; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci/* 14788c2ecf20Sopenharmony_ci * send_cmd_write_baud_rate 14798c2ecf20Sopenharmony_ci * this function sends the proper command to change the baud rate of the 14808c2ecf20Sopenharmony_ci * specified port. 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_cistatic int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, 14838c2ecf20Sopenharmony_ci int baudrate) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct usb_serial_port *port; 14868c2ecf20Sopenharmony_ci struct usb_serial *serial; 14878c2ecf20Sopenharmony_ci int divisor; 14888c2ecf20Sopenharmony_ci int status; 14898c2ecf20Sopenharmony_ci unsigned char number; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 14928c2ecf20Sopenharmony_ci return -1; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci port = mos7720_port->port; 14958c2ecf20Sopenharmony_ci serial = port->serial; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci number = port->port_number; 14988c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* Calculate the Divisor */ 15018c2ecf20Sopenharmony_ci status = calc_baud_rate_divisor(port, baudrate, &divisor); 15028c2ecf20Sopenharmony_ci if (status) { 15038c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s - bad baud rate\n", __func__); 15048c2ecf20Sopenharmony_ci return status; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci /* Enable access to divisor latch */ 15088c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; 15098c2ecf20Sopenharmony_ci write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci /* Write the divisor */ 15128c2ecf20Sopenharmony_ci write_mos_reg(serial, number, MOS7720_DLL, (__u8)(divisor & 0xff)); 15138c2ecf20Sopenharmony_ci write_mos_reg(serial, number, MOS7720_DLM, 15148c2ecf20Sopenharmony_ci (__u8)((divisor & 0xff00) >> 8)); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* Disable access to divisor latch */ 15178c2ecf20Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; 15188c2ecf20Sopenharmony_ci write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return status; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci/* 15248c2ecf20Sopenharmony_ci * change_port_settings 15258c2ecf20Sopenharmony_ci * This routine is called to set the UART on the device to match 15268c2ecf20Sopenharmony_ci * the specified new settings. 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty, 15298c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port, 15308c2ecf20Sopenharmony_ci struct ktermios *old_termios) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci struct usb_serial_port *port; 15338c2ecf20Sopenharmony_ci struct usb_serial *serial; 15348c2ecf20Sopenharmony_ci int baud; 15358c2ecf20Sopenharmony_ci unsigned cflag; 15368c2ecf20Sopenharmony_ci __u8 lData; 15378c2ecf20Sopenharmony_ci __u8 lParity; 15388c2ecf20Sopenharmony_ci __u8 lStop; 15398c2ecf20Sopenharmony_ci int status; 15408c2ecf20Sopenharmony_ci int port_number; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 15438c2ecf20Sopenharmony_ci return ; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci port = mos7720_port->port; 15468c2ecf20Sopenharmony_ci serial = port->serial; 15478c2ecf20Sopenharmony_ci port_number = port->port_number; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (!mos7720_port->open) { 15508c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 15518c2ecf20Sopenharmony_ci return; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci lData = UART_LCR_WLEN8; 15558c2ecf20Sopenharmony_ci lStop = 0x00; /* 1 stop bit */ 15568c2ecf20Sopenharmony_ci lParity = 0x00; /* No parity */ 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci cflag = tty->termios.c_cflag; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci /* Change the number of bits */ 15618c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 15628c2ecf20Sopenharmony_ci case CS5: 15638c2ecf20Sopenharmony_ci lData = UART_LCR_WLEN5; 15648c2ecf20Sopenharmony_ci break; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci case CS6: 15678c2ecf20Sopenharmony_ci lData = UART_LCR_WLEN6; 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci case CS7: 15718c2ecf20Sopenharmony_ci lData = UART_LCR_WLEN7; 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci default: 15748c2ecf20Sopenharmony_ci case CS8: 15758c2ecf20Sopenharmony_ci lData = UART_LCR_WLEN8; 15768c2ecf20Sopenharmony_ci break; 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* Change the Parity bit */ 15808c2ecf20Sopenharmony_ci if (cflag & PARENB) { 15818c2ecf20Sopenharmony_ci if (cflag & PARODD) { 15828c2ecf20Sopenharmony_ci lParity = UART_LCR_PARITY; 15838c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = odd\n", __func__); 15848c2ecf20Sopenharmony_ci } else { 15858c2ecf20Sopenharmony_ci lParity = (UART_LCR_EPAR | UART_LCR_PARITY); 15868c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = even\n", __func__); 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci } else { 15908c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = none\n", __func__); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (cflag & CMSPAR) 15948c2ecf20Sopenharmony_ci lParity = lParity | 0x20; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* Change the Stop bit */ 15978c2ecf20Sopenharmony_ci if (cflag & CSTOPB) { 15988c2ecf20Sopenharmony_ci lStop = UART_LCR_STOP; 15998c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__); 16008c2ecf20Sopenharmony_ci } else { 16018c2ecf20Sopenharmony_ci lStop = 0x00; 16028c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__); 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ 16068c2ecf20Sopenharmony_ci#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ 16078c2ecf20Sopenharmony_ci#define LCR_PAR_MASK 0x38 /* Mask for parity field */ 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Update the LCR with the correct value */ 16108c2ecf20Sopenharmony_ci mos7720_port->shadowLCR &= 16118c2ecf20Sopenharmony_ci ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); 16128c2ecf20Sopenharmony_ci mos7720_port->shadowLCR |= (lData | lParity | lStop); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* Disable Interrupts */ 16168c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 16178c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 16188c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci /* Send the updated LCR value to the mos7720 */ 16218c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 16228c2ecf20Sopenharmony_ci mos7720_port->shadowLCR); 16238c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 16248c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 16258c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* set up the MCR register and send it to the mos7720 */ 16288c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = UART_MCR_OUT2; 16298c2ecf20Sopenharmony_ci if (cflag & CBAUD) 16308c2ecf20Sopenharmony_ci mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (cflag & CRTSCTS) { 16338c2ecf20Sopenharmony_ci mos7720_port->shadowMCR |= (UART_MCR_XONANY); 16348c2ecf20Sopenharmony_ci /* To set hardware flow control to the specified * 16358c2ecf20Sopenharmony_ci * serial port, in SP1/2_CONTROL_REG */ 16368c2ecf20Sopenharmony_ci if (port_number) 16378c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 16388c2ecf20Sopenharmony_ci 0x01); 16398c2ecf20Sopenharmony_ci else 16408c2ecf20Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 16418c2ecf20Sopenharmony_ci 0x02); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci } else 16448c2ecf20Sopenharmony_ci mos7720_port->shadowMCR &= ~(UART_MCR_XONANY); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 16478c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* Determine divisor based on baud rate */ 16508c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 16518c2ecf20Sopenharmony_ci if (!baud) { 16528c2ecf20Sopenharmony_ci /* pick a default, any default... */ 16538c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Picked default baud...\n"); 16548c2ecf20Sopenharmony_ci baud = 9600; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (baud >= 230400) { 16588c2ecf20Sopenharmony_ci set_higher_rates(mos7720_port, baud); 16598c2ecf20Sopenharmony_ci /* Enable Interrupts */ 16608c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 16618c2ecf20Sopenharmony_ci return; 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud); 16658c2ecf20Sopenharmony_ci status = send_cmd_write_baud_rate(mos7720_port, baud); 16668c2ecf20Sopenharmony_ci /* FIXME: needs to write actual resulting baud back not just 16678c2ecf20Sopenharmony_ci blindly do so */ 16688c2ecf20Sopenharmony_ci if (cflag & CBAUD) 16698c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 16708c2ecf20Sopenharmony_ci /* Enable Interrupts */ 16718c2ecf20Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 16748c2ecf20Sopenharmony_ci status = usb_submit_urb(port->read_urb, GFP_KERNEL); 16758c2ecf20Sopenharmony_ci if (status) 16768c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status); 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci/* 16818c2ecf20Sopenharmony_ci * mos7720_set_termios 16828c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to change the 16838c2ecf20Sopenharmony_ci * termios structure. 16848c2ecf20Sopenharmony_ci */ 16858c2ecf20Sopenharmony_cistatic void mos7720_set_termios(struct tty_struct *tty, 16868c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old_termios) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci int status; 16898c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 16948c2ecf20Sopenharmony_ci return; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (!mos7720_port->open) { 16978c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 16988c2ecf20Sopenharmony_ci return; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci /* change the port settings to the new ones specified */ 17028c2ecf20Sopenharmony_ci change_port_settings(tty, mos7720_port, old_termios); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 17058c2ecf20Sopenharmony_ci status = usb_submit_urb(port->read_urb, GFP_KERNEL); 17068c2ecf20Sopenharmony_ci if (status) 17078c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/* 17128c2ecf20Sopenharmony_ci * get_lsr_info - get line status register info 17138c2ecf20Sopenharmony_ci * 17148c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically 17158c2ecf20Sopenharmony_ci * is emptied. On bus types like RS485, the transmitter must 17168c2ecf20Sopenharmony_ci * release the bus after transmitting. This must be done when 17178c2ecf20Sopenharmony_ci * the transmit shift register is empty, not be done when the 17188c2ecf20Sopenharmony_ci * transmit holding register is empty. This functionality 17198c2ecf20Sopenharmony_ci * allows an RS485 driver to be written in user space. 17208c2ecf20Sopenharmony_ci */ 17218c2ecf20Sopenharmony_cistatic int get_lsr_info(struct tty_struct *tty, 17228c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port, unsigned int __user *value) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 17258c2ecf20Sopenharmony_ci unsigned int result = 0; 17268c2ecf20Sopenharmony_ci unsigned char data = 0; 17278c2ecf20Sopenharmony_ci int port_number = port->port_number; 17288c2ecf20Sopenharmony_ci int count; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci count = mos7720_chars_in_buffer(tty); 17318c2ecf20Sopenharmony_ci if (count == 0) { 17328c2ecf20Sopenharmony_ci read_mos_reg(port->serial, port_number, MOS7720_LSR, &data); 17338c2ecf20Sopenharmony_ci if ((data & (UART_LSR_TEMT | UART_LSR_THRE)) 17348c2ecf20Sopenharmony_ci == (UART_LSR_TEMT | UART_LSR_THRE)) { 17358c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s -- Empty\n", __func__); 17368c2ecf20Sopenharmony_ci result = TIOCSER_TEMT; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci if (copy_to_user(value, &result, sizeof(int))) 17408c2ecf20Sopenharmony_ci return -EFAULT; 17418c2ecf20Sopenharmony_ci return 0; 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic int mos7720_tiocmget(struct tty_struct *tty) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 17478c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 17488c2ecf20Sopenharmony_ci unsigned int result = 0; 17498c2ecf20Sopenharmony_ci unsigned int mcr ; 17508c2ecf20Sopenharmony_ci unsigned int msr ; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci mcr = mos7720_port->shadowMCR; 17538c2ecf20Sopenharmony_ci msr = mos7720_port->shadowMSR; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci result = ((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ 17568c2ecf20Sopenharmony_ci | ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ 17578c2ecf20Sopenharmony_ci | ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ 17588c2ecf20Sopenharmony_ci | ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) /* 0x040 */ 17598c2ecf20Sopenharmony_ci | ((msr & UART_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */ 17608c2ecf20Sopenharmony_ci | ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */ 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci return result; 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cistatic int mos7720_tiocmset(struct tty_struct *tty, 17668c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 17698c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 17708c2ecf20Sopenharmony_ci unsigned int mcr ; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci mcr = mos7720_port->shadowMCR; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 17758c2ecf20Sopenharmony_ci mcr |= UART_MCR_RTS; 17768c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 17778c2ecf20Sopenharmony_ci mcr |= UART_MCR_DTR; 17788c2ecf20Sopenharmony_ci if (set & TIOCM_LOOP) 17798c2ecf20Sopenharmony_ci mcr |= UART_MCR_LOOP; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 17828c2ecf20Sopenharmony_ci mcr &= ~UART_MCR_RTS; 17838c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 17848c2ecf20Sopenharmony_ci mcr &= ~UART_MCR_DTR; 17858c2ecf20Sopenharmony_ci if (clear & TIOCM_LOOP) 17868c2ecf20Sopenharmony_ci mcr &= ~UART_MCR_LOOP; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci mos7720_port->shadowMCR = mcr; 17898c2ecf20Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 17908c2ecf20Sopenharmony_ci mos7720_port->shadowMCR); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci return 0; 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic int get_serial_info(struct tty_struct *tty, 17968c2ecf20Sopenharmony_ci struct serial_struct *ss) 17978c2ecf20Sopenharmony_ci{ 17988c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 17998c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci ss->type = PORT_16550A; 18028c2ecf20Sopenharmony_ci ss->line = mos7720_port->port->minor; 18038c2ecf20Sopenharmony_ci ss->port = mos7720_port->port->port_number; 18048c2ecf20Sopenharmony_ci ss->irq = 0; 18058c2ecf20Sopenharmony_ci ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; 18068c2ecf20Sopenharmony_ci ss->baud_base = 9600; 18078c2ecf20Sopenharmony_ci ss->close_delay = 5*HZ; 18088c2ecf20Sopenharmony_ci ss->closing_wait = 30*HZ; 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic int mos7720_ioctl(struct tty_struct *tty, 18138c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 18168c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 18198c2ecf20Sopenharmony_ci if (mos7720_port == NULL) 18208c2ecf20Sopenharmony_ci return -ENODEV; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci switch (cmd) { 18238c2ecf20Sopenharmony_ci case TIOCSERGETLSR: 18248c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); 18258c2ecf20Sopenharmony_ci return get_lsr_info(tty, mos7720_port, 18268c2ecf20Sopenharmony_ci (unsigned int __user *)arg); 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 18308c2ecf20Sopenharmony_ci} 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic int mos7720_startup(struct usb_serial *serial) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci struct usb_device *dev; 18358c2ecf20Sopenharmony_ci char data; 18368c2ecf20Sopenharmony_ci u16 product; 18378c2ecf20Sopenharmony_ci int ret_val; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci product = le16_to_cpu(serial->dev->descriptor.idProduct); 18408c2ecf20Sopenharmony_ci dev = serial->dev; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (product == MOSCHIP_DEVICE_ID_7715) { 18438c2ecf20Sopenharmony_ci struct urb *urb = serial->port[0]->interrupt_in_urb; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci urb->complete = mos7715_interrupt_callback; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 18488c2ecf20Sopenharmony_ci ret_val = mos7715_parport_init(serial); 18498c2ecf20Sopenharmony_ci if (ret_val < 0) 18508c2ecf20Sopenharmony_ci return ret_val; 18518c2ecf20Sopenharmony_ci#endif 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci /* start the interrupt urb */ 18548c2ecf20Sopenharmony_ci ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); 18558c2ecf20Sopenharmony_ci if (ret_val) { 18568c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", 18578c2ecf20Sopenharmony_ci ret_val); 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* LSR For Port 1 */ 18618c2ecf20Sopenharmony_ci read_mos_reg(serial, 0, MOS7720_LSR, &data); 18628c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "LSR:%x\n", data); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci return 0; 18658c2ecf20Sopenharmony_ci} 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cistatic void mos7720_release(struct usb_serial *serial) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci usb_kill_urb(serial->port[0]->interrupt_in_urb); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 18728c2ecf20Sopenharmony_ci /* close the parallel port */ 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci if (le16_to_cpu(serial->dev->descriptor.idProduct) 18758c2ecf20Sopenharmony_ci == MOSCHIP_DEVICE_ID_7715) { 18768c2ecf20Sopenharmony_ci struct urbtracker *urbtrack; 18778c2ecf20Sopenharmony_ci unsigned long flags; 18788c2ecf20Sopenharmony_ci struct mos7715_parport *mos_parport = 18798c2ecf20Sopenharmony_ci usb_get_serial_data(serial); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* prevent NULL ptr dereference in port callbacks */ 18828c2ecf20Sopenharmony_ci spin_lock(&release_lock); 18838c2ecf20Sopenharmony_ci mos_parport->pp->private_data = NULL; 18848c2ecf20Sopenharmony_ci spin_unlock(&release_lock); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci /* wait for synchronous usb calls to return */ 18878c2ecf20Sopenharmony_ci if (mos_parport->msg_pending) 18888c2ecf20Sopenharmony_ci wait_for_completion_timeout(&mos_parport->syncmsg_compl, 18898c2ecf20Sopenharmony_ci msecs_to_jiffies(MOS_WDR_TIMEOUT)); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci parport_remove_port(mos_parport->pp); 18928c2ecf20Sopenharmony_ci usb_set_serial_data(serial, NULL); 18938c2ecf20Sopenharmony_ci mos_parport->serial = NULL; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* if tasklet currently scheduled, wait for it to complete */ 18968c2ecf20Sopenharmony_ci tasklet_kill(&mos_parport->urb_tasklet); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci /* unlink any urbs sent by the tasklet */ 18998c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos_parport->listlock, flags); 19008c2ecf20Sopenharmony_ci list_for_each_entry(urbtrack, 19018c2ecf20Sopenharmony_ci &mos_parport->active_urbs, 19028c2ecf20Sopenharmony_ci urblist_entry) 19038c2ecf20Sopenharmony_ci usb_unlink_urb(urbtrack->urb); 19048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos_parport->listlock, flags); 19058c2ecf20Sopenharmony_ci parport_del_port(mos_parport->pp); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci kref_put(&mos_parport->ref_count, destroy_mos_parport); 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci#endif 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic int mos7720_port_probe(struct usb_serial_port *port) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL); 19178c2ecf20Sopenharmony_ci if (!mos7720_port) 19188c2ecf20Sopenharmony_ci return -ENOMEM; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci mos7720_port->port = port; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, mos7720_port); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci return 0; 19258c2ecf20Sopenharmony_ci} 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_cistatic int mos7720_port_remove(struct usb_serial_port *port) 19288c2ecf20Sopenharmony_ci{ 19298c2ecf20Sopenharmony_ci struct moschip_port *mos7720_port; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 19328c2ecf20Sopenharmony_ci kfree(mos7720_port); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci return 0; 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistatic struct usb_serial_driver moschip7720_2port_driver = { 19388c2ecf20Sopenharmony_ci .driver = { 19398c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 19408c2ecf20Sopenharmony_ci .name = "moschip7720", 19418c2ecf20Sopenharmony_ci }, 19428c2ecf20Sopenharmony_ci .description = "Moschip 2 port adapter", 19438c2ecf20Sopenharmony_ci .id_table = id_table, 19448c2ecf20Sopenharmony_ci .num_bulk_in = 2, 19458c2ecf20Sopenharmony_ci .num_bulk_out = 2, 19468c2ecf20Sopenharmony_ci .num_interrupt_in = 1, 19478c2ecf20Sopenharmony_ci .calc_num_ports = mos77xx_calc_num_ports, 19488c2ecf20Sopenharmony_ci .open = mos7720_open, 19498c2ecf20Sopenharmony_ci .close = mos7720_close, 19508c2ecf20Sopenharmony_ci .throttle = mos7720_throttle, 19518c2ecf20Sopenharmony_ci .unthrottle = mos7720_unthrottle, 19528c2ecf20Sopenharmony_ci .attach = mos7720_startup, 19538c2ecf20Sopenharmony_ci .release = mos7720_release, 19548c2ecf20Sopenharmony_ci .port_probe = mos7720_port_probe, 19558c2ecf20Sopenharmony_ci .port_remove = mos7720_port_remove, 19568c2ecf20Sopenharmony_ci .ioctl = mos7720_ioctl, 19578c2ecf20Sopenharmony_ci .tiocmget = mos7720_tiocmget, 19588c2ecf20Sopenharmony_ci .tiocmset = mos7720_tiocmset, 19598c2ecf20Sopenharmony_ci .get_serial = get_serial_info, 19608c2ecf20Sopenharmony_ci .set_termios = mos7720_set_termios, 19618c2ecf20Sopenharmony_ci .write = mos7720_write, 19628c2ecf20Sopenharmony_ci .write_room = mos7720_write_room, 19638c2ecf20Sopenharmony_ci .chars_in_buffer = mos7720_chars_in_buffer, 19648c2ecf20Sopenharmony_ci .break_ctl = mos7720_break, 19658c2ecf20Sopenharmony_ci .read_bulk_callback = mos7720_bulk_in_callback, 19668c2ecf20Sopenharmony_ci .read_int_callback = mos7720_interrupt_callback, 19678c2ecf20Sopenharmony_ci}; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 19708c2ecf20Sopenharmony_ci &moschip7720_2port_driver, NULL 19718c2ecf20Sopenharmony_ci}; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 19768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 19778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1978