162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Edgeport USB Serial Converter driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved. 662306a36Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Supports the following devices: 962306a36Sopenharmony_ci * Edgeport/4 1062306a36Sopenharmony_ci * Edgeport/4t 1162306a36Sopenharmony_ci * Edgeport/2 1262306a36Sopenharmony_ci * Edgeport/4i 1362306a36Sopenharmony_ci * Edgeport/2i 1462306a36Sopenharmony_ci * Edgeport/421 1562306a36Sopenharmony_ci * Edgeport/21 1662306a36Sopenharmony_ci * Rapidport/4 1762306a36Sopenharmony_ci * Edgeport/8 1862306a36Sopenharmony_ci * Edgeport/2D8 1962306a36Sopenharmony_ci * Edgeport/4D8 2062306a36Sopenharmony_ci * Edgeport/8i 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * For questions or problems with this driver, contact Inside Out 2362306a36Sopenharmony_ci * Networks technical support, or Peter Berger <pberger@brimson.com>, 2462306a36Sopenharmony_ci * or Al Borchers <alborchers@steinerpoint.com>. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/jiffies.h> 3062306a36Sopenharmony_ci#include <linux/errno.h> 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci#include <linux/tty.h> 3362306a36Sopenharmony_ci#include <linux/tty_driver.h> 3462306a36Sopenharmony_ci#include <linux/tty_flip.h> 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/spinlock.h> 3762306a36Sopenharmony_ci#include <linux/serial.h> 3862306a36Sopenharmony_ci#include <linux/ioctl.h> 3962306a36Sopenharmony_ci#include <linux/wait.h> 4062306a36Sopenharmony_ci#include <linux/firmware.h> 4162306a36Sopenharmony_ci#include <linux/ihex.h> 4262306a36Sopenharmony_ci#include <linux/uaccess.h> 4362306a36Sopenharmony_ci#include <linux/usb.h> 4462306a36Sopenharmony_ci#include <linux/usb/serial.h> 4562306a36Sopenharmony_ci#include "io_edgeport.h" 4662306a36Sopenharmony_ci#include "io_ionsp.h" /* info for the iosp messages */ 4762306a36Sopenharmony_ci#include "io_16654.h" /* 16654 UART defines */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli" 5062306a36Sopenharmony_ci#define DRIVER_DESC "Edgeport USB Serial Driver" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define MAX_NAME_LEN 64 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define OPEN_TIMEOUT (5*HZ) /* 5 seconds */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct usb_device_id edgeport_2port_id_table[] = { 5762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, 5862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, 5962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, 6062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, 6162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, 6262306a36Sopenharmony_ci { } 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct usb_device_id edgeport_4port_id_table[] = { 6662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) }, 6762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) }, 6862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) }, 6962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) }, 7062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, 7162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, 7262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, 7362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, 7462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, 7562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, 7662306a36Sopenharmony_ci { } 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const struct usb_device_id edgeport_8port_id_table[] = { 8062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) }, 8162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, 8262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, 8362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, 8462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, 8562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, 8662306a36Sopenharmony_ci { } 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct usb_device_id Epic_port_id_table[] = { 9062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, 9162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, 9262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, 9362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, 9462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, 9562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, 9662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, 9762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, 9862306a36Sopenharmony_ci { } 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Devices that this driver supports */ 10262306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 10362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) }, 10462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) }, 10562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) }, 10662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) }, 10762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, 10862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, 10962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, 11062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, 11162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, 11262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, 11362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) }, 11462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, 11562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, 11662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, 11762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, 11862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, 11962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, 12062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, 12162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, 12262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, 12362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, 12462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, 12562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, 12662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, 12762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, 12862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, 12962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, 13062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, 13162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, 13262306a36Sopenharmony_ci { } /* Terminating entry */ 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* receive port state */ 13962306a36Sopenharmony_cienum RXSTATE { 14062306a36Sopenharmony_ci EXPECT_HDR1 = 0, /* Expect header byte 1 */ 14162306a36Sopenharmony_ci EXPECT_HDR2 = 1, /* Expect header byte 2 */ 14262306a36Sopenharmony_ci EXPECT_DATA = 2, /* Expect 'RxBytesRemaining' data */ 14362306a36Sopenharmony_ci EXPECT_HDR3 = 3, /* Expect header byte 3 (for status hdrs only) */ 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Transmit Fifo 14862306a36Sopenharmony_ci * This Transmit queue is an extension of the edgeport Rx buffer. 14962306a36Sopenharmony_ci * The maximum amount of data buffered in both the edgeport 15062306a36Sopenharmony_ci * Rx buffer (maxTxCredits) and this buffer will never exceed maxTxCredits. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistruct TxFifo { 15362306a36Sopenharmony_ci unsigned int head; /* index to head pointer (write) */ 15462306a36Sopenharmony_ci unsigned int tail; /* index to tail pointer (read) */ 15562306a36Sopenharmony_ci unsigned int count; /* Bytes in queue */ 15662306a36Sopenharmony_ci unsigned int size; /* Max size of queue (equal to Max number of TxCredits) */ 15762306a36Sopenharmony_ci unsigned char *fifo; /* allocated Buffer */ 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* This structure holds all of the local port information */ 16162306a36Sopenharmony_cistruct edgeport_port { 16262306a36Sopenharmony_ci __u16 txCredits; /* our current credits for this port */ 16362306a36Sopenharmony_ci __u16 maxTxCredits; /* the max size of the port */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci struct TxFifo txfifo; /* transmit fifo -- size will be maxTxCredits */ 16662306a36Sopenharmony_ci struct urb *write_urb; /* write URB for this port */ 16762306a36Sopenharmony_ci bool write_in_progress; /* 'true' while a write URB is outstanding */ 16862306a36Sopenharmony_ci spinlock_t ep_lock; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci __u8 shadowLCR; /* last LCR value received */ 17162306a36Sopenharmony_ci __u8 shadowMCR; /* last MCR value received */ 17262306a36Sopenharmony_ci __u8 shadowMSR; /* last MSR value received */ 17362306a36Sopenharmony_ci __u8 shadowLSR; /* last LSR value received */ 17462306a36Sopenharmony_ci __u8 shadowXonChar; /* last value set as XON char in Edgeport */ 17562306a36Sopenharmony_ci __u8 shadowXoffChar; /* last value set as XOFF char in Edgeport */ 17662306a36Sopenharmony_ci __u8 validDataMask; 17762306a36Sopenharmony_ci __u32 baudRate; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci bool open; 18062306a36Sopenharmony_ci bool openPending; 18162306a36Sopenharmony_ci bool commandPending; 18262306a36Sopenharmony_ci bool closePending; 18362306a36Sopenharmony_ci bool chaseResponsePending; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ 18662306a36Sopenharmony_ci wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */ 18762306a36Sopenharmony_ci wait_queue_head_t wait_command; /* for handling sleeping while waiting for command to finish */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci struct usb_serial_port *port; /* loop back to the owner of this object */ 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* This structure holds all of the individual device information */ 19462306a36Sopenharmony_cistruct edgeport_serial { 19562306a36Sopenharmony_ci char name[MAX_NAME_LEN+2]; /* string name of this device */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */ 19862306a36Sopenharmony_ci struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */ 19962306a36Sopenharmony_ci struct edgeport_product_info product_info; /* Product Info */ 20062306a36Sopenharmony_ci struct edge_compatibility_descriptor epic_descriptor; /* Edgeport compatible descriptor */ 20162306a36Sopenharmony_ci int is_epic; /* flag if EPiC device or not */ 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ 20462306a36Sopenharmony_ci unsigned char *interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ 20562306a36Sopenharmony_ci struct urb *interrupt_read_urb; /* our interrupt urb */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ 20862306a36Sopenharmony_ci unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ 20962306a36Sopenharmony_ci struct urb *read_urb; /* our bulk read urb */ 21062306a36Sopenharmony_ci bool read_in_progress; 21162306a36Sopenharmony_ci spinlock_t es_lock; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci __s16 rxBytesAvail; /* the number of bytes that we need to read from this device */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci enum RXSTATE rxState; /* the current state of the bulk receive processor */ 21862306a36Sopenharmony_ci __u8 rxHeader1; /* receive header byte 1 */ 21962306a36Sopenharmony_ci __u8 rxHeader2; /* receive header byte 2 */ 22062306a36Sopenharmony_ci __u8 rxHeader3; /* receive header byte 3 */ 22162306a36Sopenharmony_ci __u8 rxPort; /* the port that we are currently receiving data for */ 22262306a36Sopenharmony_ci __u8 rxStatusCode; /* the receive status code */ 22362306a36Sopenharmony_ci __u8 rxStatusParam; /* the receive status parameter */ 22462306a36Sopenharmony_ci __s16 rxBytesRemaining; /* the number of port bytes left to read */ 22562306a36Sopenharmony_ci struct usb_serial *serial; /* loop back to the owner of this object */ 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* baud rate information */ 22962306a36Sopenharmony_cistruct divisor_table_entry { 23062306a36Sopenharmony_ci __u32 BaudRate; 23162306a36Sopenharmony_ci __u16 Divisor; 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Define table of divisors for Rev A EdgePort/4 hardware 23662306a36Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and 23762306a36Sopenharmony_ci * MCR.7 = 0. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = { 24162306a36Sopenharmony_ci { 50, 4608}, 24262306a36Sopenharmony_ci { 75, 3072}, 24362306a36Sopenharmony_ci { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ 24462306a36Sopenharmony_ci { 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */ 24562306a36Sopenharmony_ci { 150, 1536}, 24662306a36Sopenharmony_ci { 300, 768}, 24762306a36Sopenharmony_ci { 600, 384}, 24862306a36Sopenharmony_ci { 1200, 192}, 24962306a36Sopenharmony_ci { 1800, 128}, 25062306a36Sopenharmony_ci { 2400, 96}, 25162306a36Sopenharmony_ci { 4800, 48}, 25262306a36Sopenharmony_ci { 7200, 32}, 25362306a36Sopenharmony_ci { 9600, 24}, 25462306a36Sopenharmony_ci { 14400, 16}, 25562306a36Sopenharmony_ci { 19200, 12}, 25662306a36Sopenharmony_ci { 38400, 6}, 25762306a36Sopenharmony_ci { 57600, 4}, 25862306a36Sopenharmony_ci { 115200, 2}, 25962306a36Sopenharmony_ci { 230400, 1}, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* Number of outstanding Command Write Urbs */ 26362306a36Sopenharmony_cistatic atomic_t CmdUrbs = ATOMIC_INIT(0); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* function prototypes */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void edge_close(struct usb_serial_port *port); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void process_rcvd_data(struct edgeport_serial *edge_serial, 27162306a36Sopenharmony_ci unsigned char *buffer, __u16 bufferLength); 27262306a36Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial, 27362306a36Sopenharmony_ci __u8 byte2, __u8 byte3); 27462306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data, 27562306a36Sopenharmony_ci int length); 27662306a36Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr); 27762306a36Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, 27862306a36Sopenharmony_ci __u8 lsr, __u8 data); 27962306a36Sopenharmony_cistatic int send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command, 28062306a36Sopenharmony_ci __u8 param); 28162306a36Sopenharmony_cistatic int calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor); 28262306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty, 28362306a36Sopenharmony_ci struct edgeport_port *edge_port, 28462306a36Sopenharmony_ci const struct ktermios *old_termios); 28562306a36Sopenharmony_cistatic int send_cmd_write_uart_register(struct edgeport_port *edge_port, 28662306a36Sopenharmony_ci __u8 regNum, __u8 regValue); 28762306a36Sopenharmony_cistatic int write_cmd_usb(struct edgeport_port *edge_port, 28862306a36Sopenharmony_ci unsigned char *buffer, int writeLength); 28962306a36Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial, 29062306a36Sopenharmony_ci struct edgeport_port *edge_port); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, 29362306a36Sopenharmony_ci __u16 length, const __u8 *data); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* ************************************************************************ */ 29662306a36Sopenharmony_ci/* ************************************************************************ */ 29762306a36Sopenharmony_ci/* ************************************************************************ */ 29862306a36Sopenharmony_ci/* ************************************************************************ */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/************************************************************************ 30162306a36Sopenharmony_ci * * 30262306a36Sopenharmony_ci * update_edgeport_E2PROM() Compare current versions of * 30362306a36Sopenharmony_ci * Boot ROM and Manufacture * 30462306a36Sopenharmony_ci * Descriptors with versions * 30562306a36Sopenharmony_ci * embedded in this driver * 30662306a36Sopenharmony_ci * * 30762306a36Sopenharmony_ci ************************************************************************/ 30862306a36Sopenharmony_cistatic void update_edgeport_E2PROM(struct edgeport_serial *edge_serial) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct device *dev = &edge_serial->serial->dev->dev; 31162306a36Sopenharmony_ci __u32 BootCurVer; 31262306a36Sopenharmony_ci __u32 BootNewVer; 31362306a36Sopenharmony_ci __u8 BootMajorVersion; 31462306a36Sopenharmony_ci __u8 BootMinorVersion; 31562306a36Sopenharmony_ci __u16 BootBuildNumber; 31662306a36Sopenharmony_ci __u32 Bootaddr; 31762306a36Sopenharmony_ci const struct ihex_binrec *rec; 31862306a36Sopenharmony_ci const struct firmware *fw; 31962306a36Sopenharmony_ci const char *fw_name; 32062306a36Sopenharmony_ci int response; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci switch (edge_serial->product_info.iDownloadFile) { 32362306a36Sopenharmony_ci case EDGE_DOWNLOAD_FILE_I930: 32462306a36Sopenharmony_ci fw_name = "edgeport/boot.fw"; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case EDGE_DOWNLOAD_FILE_80251: 32762306a36Sopenharmony_ci fw_name = "edgeport/boot2.fw"; 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci default: 33062306a36Sopenharmony_ci return; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci response = request_ihex_firmware(&fw, fw_name, 33462306a36Sopenharmony_ci &edge_serial->serial->dev->dev); 33562306a36Sopenharmony_ci if (response) { 33662306a36Sopenharmony_ci dev_err(dev, "Failed to load image \"%s\" err %d\n", 33762306a36Sopenharmony_ci fw_name, response); 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rec = (const struct ihex_binrec *)fw->data; 34262306a36Sopenharmony_ci BootMajorVersion = rec->data[0]; 34362306a36Sopenharmony_ci BootMinorVersion = rec->data[1]; 34462306a36Sopenharmony_ci BootBuildNumber = (rec->data[2] << 8) | rec->data[3]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Check Boot Image Version */ 34762306a36Sopenharmony_ci BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) + 34862306a36Sopenharmony_ci (edge_serial->boot_descriptor.MinorVersion << 16) + 34962306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.BuildNumber); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci BootNewVer = (BootMajorVersion << 24) + 35262306a36Sopenharmony_ci (BootMinorVersion << 16) + 35362306a36Sopenharmony_ci BootBuildNumber; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci dev_dbg(dev, "Current Boot Image version %d.%d.%d\n", 35662306a36Sopenharmony_ci edge_serial->boot_descriptor.MajorVersion, 35762306a36Sopenharmony_ci edge_serial->boot_descriptor.MinorVersion, 35862306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.BuildNumber)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (BootNewVer > BootCurVer) { 36262306a36Sopenharmony_ci dev_dbg(dev, "**Update Boot Image from %d.%d.%d to %d.%d.%d\n", 36362306a36Sopenharmony_ci edge_serial->boot_descriptor.MajorVersion, 36462306a36Sopenharmony_ci edge_serial->boot_descriptor.MinorVersion, 36562306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.BuildNumber), 36662306a36Sopenharmony_ci BootMajorVersion, BootMinorVersion, BootBuildNumber); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dev_dbg(dev, "Downloading new Boot Image\n"); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci for (rec = ihex_next_binrec(rec); rec; 37162306a36Sopenharmony_ci rec = ihex_next_binrec(rec)) { 37262306a36Sopenharmony_ci Bootaddr = be32_to_cpu(rec->addr); 37362306a36Sopenharmony_ci response = rom_write(edge_serial->serial, 37462306a36Sopenharmony_ci Bootaddr >> 16, 37562306a36Sopenharmony_ci Bootaddr & 0xFFFF, 37662306a36Sopenharmony_ci be16_to_cpu(rec->len), 37762306a36Sopenharmony_ci &rec->data[0]); 37862306a36Sopenharmony_ci if (response < 0) { 37962306a36Sopenharmony_ci dev_err(&edge_serial->serial->dev->dev, 38062306a36Sopenharmony_ci "rom_write failed (%x, %x, %d)\n", 38162306a36Sopenharmony_ci Bootaddr >> 16, Bootaddr & 0xFFFF, 38262306a36Sopenharmony_ci be16_to_cpu(rec->len)); 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } else { 38762306a36Sopenharmony_ci dev_dbg(dev, "Boot Image -- already up to date\n"); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci release_firmware(fw); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void dump_product_info(struct edgeport_serial *edge_serial, 39362306a36Sopenharmony_ci struct edgeport_product_info *product_info) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct device *dev = &edge_serial->serial->dev->dev; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Dump Product Info structure */ 39862306a36Sopenharmony_ci dev_dbg(dev, "**Product Information:\n"); 39962306a36Sopenharmony_ci dev_dbg(dev, " ProductId %x\n", product_info->ProductId); 40062306a36Sopenharmony_ci dev_dbg(dev, " NumPorts %d\n", product_info->NumPorts); 40162306a36Sopenharmony_ci dev_dbg(dev, " ProdInfoVer %d\n", product_info->ProdInfoVer); 40262306a36Sopenharmony_ci dev_dbg(dev, " IsServer %d\n", product_info->IsServer); 40362306a36Sopenharmony_ci dev_dbg(dev, " IsRS232 %d\n", product_info->IsRS232); 40462306a36Sopenharmony_ci dev_dbg(dev, " IsRS422 %d\n", product_info->IsRS422); 40562306a36Sopenharmony_ci dev_dbg(dev, " IsRS485 %d\n", product_info->IsRS485); 40662306a36Sopenharmony_ci dev_dbg(dev, " RomSize %d\n", product_info->RomSize); 40762306a36Sopenharmony_ci dev_dbg(dev, " RamSize %d\n", product_info->RamSize); 40862306a36Sopenharmony_ci dev_dbg(dev, " CpuRev %x\n", product_info->CpuRev); 40962306a36Sopenharmony_ci dev_dbg(dev, " BoardRev %x\n", product_info->BoardRev); 41062306a36Sopenharmony_ci dev_dbg(dev, " BootMajorVersion %d.%d.%d\n", 41162306a36Sopenharmony_ci product_info->BootMajorVersion, 41262306a36Sopenharmony_ci product_info->BootMinorVersion, 41362306a36Sopenharmony_ci le16_to_cpu(product_info->BootBuildNumber)); 41462306a36Sopenharmony_ci dev_dbg(dev, " FirmwareMajorVersion %d.%d.%d\n", 41562306a36Sopenharmony_ci product_info->FirmwareMajorVersion, 41662306a36Sopenharmony_ci product_info->FirmwareMinorVersion, 41762306a36Sopenharmony_ci le16_to_cpu(product_info->FirmwareBuildNumber)); 41862306a36Sopenharmony_ci dev_dbg(dev, " ManufactureDescDate %d/%d/%d\n", 41962306a36Sopenharmony_ci product_info->ManufactureDescDate[0], 42062306a36Sopenharmony_ci product_info->ManufactureDescDate[1], 42162306a36Sopenharmony_ci product_info->ManufactureDescDate[2]+1900); 42262306a36Sopenharmony_ci dev_dbg(dev, " iDownloadFile 0x%x\n", 42362306a36Sopenharmony_ci product_info->iDownloadFile); 42462306a36Sopenharmony_ci dev_dbg(dev, " EpicVer %d\n", product_info->EpicVer); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void get_product_info(struct edgeport_serial *edge_serial) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct edgeport_product_info *product_info = &edge_serial->product_info; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci memset(product_info, 0, sizeof(struct edgeport_product_info)); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP); 43462306a36Sopenharmony_ci product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts; 43562306a36Sopenharmony_ci product_info->ProdInfoVer = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci product_info->RomSize = edge_serial->manuf_descriptor.RomSize; 43862306a36Sopenharmony_ci product_info->RamSize = edge_serial->manuf_descriptor.RamSize; 43962306a36Sopenharmony_ci product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev; 44062306a36Sopenharmony_ci product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci product_info->BootMajorVersion = 44362306a36Sopenharmony_ci edge_serial->boot_descriptor.MajorVersion; 44462306a36Sopenharmony_ci product_info->BootMinorVersion = 44562306a36Sopenharmony_ci edge_serial->boot_descriptor.MinorVersion; 44662306a36Sopenharmony_ci product_info->BootBuildNumber = 44762306a36Sopenharmony_ci edge_serial->boot_descriptor.BuildNumber; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci memcpy(product_info->ManufactureDescDate, 45062306a36Sopenharmony_ci edge_serial->manuf_descriptor.DescDate, 45162306a36Sopenharmony_ci sizeof(edge_serial->manuf_descriptor.DescDate)); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* check if this is 2nd generation hardware */ 45462306a36Sopenharmony_ci if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) 45562306a36Sopenharmony_ci & ION_DEVICE_ID_80251_NETCHIP) 45662306a36Sopenharmony_ci product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251; 45762306a36Sopenharmony_ci else 45862306a36Sopenharmony_ci product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Determine Product type and set appropriate flags */ 46162306a36Sopenharmony_ci switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) { 46262306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_COMPATIBLE: 46362306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_4T: 46462306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_4: 46562306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_2: 46662306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU: 46762306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_8: 46862306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_421: 46962306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_21: 47062306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_2_DIN: 47162306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_4_DIN: 47262306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: 47362306a36Sopenharmony_ci product_info->IsRS232 = 1; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_2I: /* Edgeport/2 RS422/RS485 */ 47762306a36Sopenharmony_ci product_info->IsRS422 = 1; 47862306a36Sopenharmony_ci product_info->IsRS485 = 1; 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_8I: /* Edgeport/4 RS422 */ 48262306a36Sopenharmony_ci case ION_DEVICE_ID_EDGEPORT_4I: /* Edgeport/4 RS422 */ 48362306a36Sopenharmony_ci product_info->IsRS422 = 1; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci dump_product_info(edge_serial, product_info); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int get_epic_descriptor(struct edgeport_serial *ep) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int result; 49362306a36Sopenharmony_ci struct usb_serial *serial = ep->serial; 49462306a36Sopenharmony_ci struct edgeport_product_info *product_info = &ep->product_info; 49562306a36Sopenharmony_ci struct edge_compatibility_descriptor *epic; 49662306a36Sopenharmony_ci struct edge_compatibility_bits *bits; 49762306a36Sopenharmony_ci struct device *dev = &serial->dev->dev; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ep->is_epic = 0; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci epic = kmalloc(sizeof(*epic), GFP_KERNEL); 50262306a36Sopenharmony_ci if (!epic) 50362306a36Sopenharmony_ci return -ENOMEM; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 50662306a36Sopenharmony_ci USB_REQUEST_ION_GET_EPIC_DESC, 50762306a36Sopenharmony_ci 0xC0, 0x00, 0x00, 50862306a36Sopenharmony_ci epic, sizeof(*epic), 50962306a36Sopenharmony_ci 300); 51062306a36Sopenharmony_ci if (result == sizeof(*epic)) { 51162306a36Sopenharmony_ci ep->is_epic = 1; 51262306a36Sopenharmony_ci memcpy(&ep->epic_descriptor, epic, sizeof(*epic)); 51362306a36Sopenharmony_ci memset(product_info, 0, sizeof(struct edgeport_product_info)); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci product_info->NumPorts = epic->NumPorts; 51662306a36Sopenharmony_ci product_info->ProdInfoVer = 0; 51762306a36Sopenharmony_ci product_info->FirmwareMajorVersion = epic->MajorVersion; 51862306a36Sopenharmony_ci product_info->FirmwareMinorVersion = epic->MinorVersion; 51962306a36Sopenharmony_ci product_info->FirmwareBuildNumber = epic->BuildNumber; 52062306a36Sopenharmony_ci product_info->iDownloadFile = epic->iDownloadFile; 52162306a36Sopenharmony_ci product_info->EpicVer = epic->EpicVer; 52262306a36Sopenharmony_ci product_info->Epic = epic->Supports; 52362306a36Sopenharmony_ci product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE; 52462306a36Sopenharmony_ci dump_product_info(ep, product_info); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci bits = &ep->epic_descriptor.Supports; 52762306a36Sopenharmony_ci dev_dbg(dev, "**EPIC descriptor:\n"); 52862306a36Sopenharmony_ci dev_dbg(dev, " VendEnableSuspend: %s\n", bits->VendEnableSuspend ? "TRUE": "FALSE"); 52962306a36Sopenharmony_ci dev_dbg(dev, " IOSPOpen : %s\n", bits->IOSPOpen ? "TRUE": "FALSE"); 53062306a36Sopenharmony_ci dev_dbg(dev, " IOSPClose : %s\n", bits->IOSPClose ? "TRUE": "FALSE"); 53162306a36Sopenharmony_ci dev_dbg(dev, " IOSPChase : %s\n", bits->IOSPChase ? "TRUE": "FALSE"); 53262306a36Sopenharmony_ci dev_dbg(dev, " IOSPSetRxFlow : %s\n", bits->IOSPSetRxFlow ? "TRUE": "FALSE"); 53362306a36Sopenharmony_ci dev_dbg(dev, " IOSPSetTxFlow : %s\n", bits->IOSPSetTxFlow ? "TRUE": "FALSE"); 53462306a36Sopenharmony_ci dev_dbg(dev, " IOSPSetXChar : %s\n", bits->IOSPSetXChar ? "TRUE": "FALSE"); 53562306a36Sopenharmony_ci dev_dbg(dev, " IOSPRxCheck : %s\n", bits->IOSPRxCheck ? "TRUE": "FALSE"); 53662306a36Sopenharmony_ci dev_dbg(dev, " IOSPSetClrBreak : %s\n", bits->IOSPSetClrBreak ? "TRUE": "FALSE"); 53762306a36Sopenharmony_ci dev_dbg(dev, " IOSPWriteMCR : %s\n", bits->IOSPWriteMCR ? "TRUE": "FALSE"); 53862306a36Sopenharmony_ci dev_dbg(dev, " IOSPWriteLCR : %s\n", bits->IOSPWriteLCR ? "TRUE": "FALSE"); 53962306a36Sopenharmony_ci dev_dbg(dev, " IOSPSetBaudRate : %s\n", bits->IOSPSetBaudRate ? "TRUE": "FALSE"); 54062306a36Sopenharmony_ci dev_dbg(dev, " TrueEdgeport : %s\n", bits->TrueEdgeport ? "TRUE": "FALSE"); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci result = 0; 54362306a36Sopenharmony_ci } else if (result >= 0) { 54462306a36Sopenharmony_ci dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n", 54562306a36Sopenharmony_ci result); 54662306a36Sopenharmony_ci result = -EIO; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci kfree(epic); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return result; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/************************************************************************/ 55662306a36Sopenharmony_ci/************************************************************************/ 55762306a36Sopenharmony_ci/* U S B C A L L B A C K F U N C T I O N S */ 55862306a36Sopenharmony_ci/* U S B C A L L B A C K F U N C T I O N S */ 55962306a36Sopenharmony_ci/************************************************************************/ 56062306a36Sopenharmony_ci/************************************************************************/ 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/***************************************************************************** 56362306a36Sopenharmony_ci * edge_interrupt_callback 56462306a36Sopenharmony_ci * this is the callback function for when we have received data on the 56562306a36Sopenharmony_ci * interrupt endpoint. 56662306a36Sopenharmony_ci *****************************************************************************/ 56762306a36Sopenharmony_cistatic void edge_interrupt_callback(struct urb *urb) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct edgeport_serial *edge_serial = urb->context; 57062306a36Sopenharmony_ci struct device *dev; 57162306a36Sopenharmony_ci struct edgeport_port *edge_port; 57262306a36Sopenharmony_ci struct usb_serial_port *port; 57362306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 57462306a36Sopenharmony_ci int length = urb->actual_length; 57562306a36Sopenharmony_ci unsigned long flags; 57662306a36Sopenharmony_ci int bytes_avail; 57762306a36Sopenharmony_ci int position; 57862306a36Sopenharmony_ci int txCredits; 57962306a36Sopenharmony_ci int portNumber; 58062306a36Sopenharmony_ci int result; 58162306a36Sopenharmony_ci int status = urb->status; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci switch (status) { 58462306a36Sopenharmony_ci case 0: 58562306a36Sopenharmony_ci /* success */ 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci case -ECONNRESET: 58862306a36Sopenharmony_ci case -ENOENT: 58962306a36Sopenharmony_ci case -ESHUTDOWN: 59062306a36Sopenharmony_ci /* this urb is terminated, clean up */ 59162306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status); 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci default: 59462306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status); 59562306a36Sopenharmony_ci goto exit; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci dev = &edge_serial->serial->dev->dev; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* process this interrupt-read even if there are no ports open */ 60162306a36Sopenharmony_ci if (length) { 60262306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, length, data); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (length > 1) { 60562306a36Sopenharmony_ci bytes_avail = data[0] | (data[1] << 8); 60662306a36Sopenharmony_ci if (bytes_avail) { 60762306a36Sopenharmony_ci spin_lock_irqsave(&edge_serial->es_lock, flags); 60862306a36Sopenharmony_ci edge_serial->rxBytesAvail += bytes_avail; 60962306a36Sopenharmony_ci dev_dbg(dev, 61062306a36Sopenharmony_ci "%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n", 61162306a36Sopenharmony_ci __func__, bytes_avail, 61262306a36Sopenharmony_ci edge_serial->rxBytesAvail, 61362306a36Sopenharmony_ci edge_serial->read_in_progress); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (edge_serial->rxBytesAvail > 0 && 61662306a36Sopenharmony_ci !edge_serial->read_in_progress) { 61762306a36Sopenharmony_ci dev_dbg(dev, "%s - posting a read\n", __func__); 61862306a36Sopenharmony_ci edge_serial->read_in_progress = true; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* we have pending bytes on the 62162306a36Sopenharmony_ci bulk in pipe, send a request */ 62262306a36Sopenharmony_ci result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); 62362306a36Sopenharmony_ci if (result) { 62462306a36Sopenharmony_ci dev_err(dev, 62562306a36Sopenharmony_ci "%s - usb_submit_urb(read bulk) failed with result = %d\n", 62662306a36Sopenharmony_ci __func__, result); 62762306a36Sopenharmony_ci edge_serial->read_in_progress = false; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_serial->es_lock, 63162306a36Sopenharmony_ci flags); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci /* grab the txcredits for the ports if available */ 63562306a36Sopenharmony_ci position = 2; 63662306a36Sopenharmony_ci portNumber = 0; 63762306a36Sopenharmony_ci while ((position < length - 1) && 63862306a36Sopenharmony_ci (portNumber < edge_serial->serial->num_ports)) { 63962306a36Sopenharmony_ci txCredits = data[position] | (data[position+1] << 8); 64062306a36Sopenharmony_ci if (txCredits) { 64162306a36Sopenharmony_ci port = edge_serial->serial->port[portNumber]; 64262306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 64362306a36Sopenharmony_ci if (edge_port && edge_port->open) { 64462306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, 64562306a36Sopenharmony_ci flags); 64662306a36Sopenharmony_ci edge_port->txCredits += txCredits; 64762306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, 64862306a36Sopenharmony_ci flags); 64962306a36Sopenharmony_ci dev_dbg(dev, "%s - txcredits for port%d = %d\n", 65062306a36Sopenharmony_ci __func__, portNumber, 65162306a36Sopenharmony_ci edge_port->txCredits); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* tell the tty driver that something 65462306a36Sopenharmony_ci has changed */ 65562306a36Sopenharmony_ci tty_port_tty_wakeup(&edge_port->port->port); 65662306a36Sopenharmony_ci /* Since we have more credit, check 65762306a36Sopenharmony_ci if more data can be sent */ 65862306a36Sopenharmony_ci send_more_port_data(edge_serial, 65962306a36Sopenharmony_ci edge_port); 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci position += 2; 66362306a36Sopenharmony_ci ++portNumber; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciexit: 66862306a36Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 66962306a36Sopenharmony_ci if (result) 67062306a36Sopenharmony_ci dev_err(&urb->dev->dev, 67162306a36Sopenharmony_ci "%s - Error %d submitting control urb\n", 67262306a36Sopenharmony_ci __func__, result); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/***************************************************************************** 67762306a36Sopenharmony_ci * edge_bulk_in_callback 67862306a36Sopenharmony_ci * this is the callback function for when we have received data on the 67962306a36Sopenharmony_ci * bulk in endpoint. 68062306a36Sopenharmony_ci *****************************************************************************/ 68162306a36Sopenharmony_cistatic void edge_bulk_in_callback(struct urb *urb) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct edgeport_serial *edge_serial = urb->context; 68462306a36Sopenharmony_ci struct device *dev; 68562306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 68662306a36Sopenharmony_ci int retval; 68762306a36Sopenharmony_ci __u16 raw_data_length; 68862306a36Sopenharmony_ci int status = urb->status; 68962306a36Sopenharmony_ci unsigned long flags; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (status) { 69262306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", 69362306a36Sopenharmony_ci __func__, status); 69462306a36Sopenharmony_ci edge_serial->read_in_progress = false; 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (urb->actual_length == 0) { 69962306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - read bulk callback with no data\n", __func__); 70062306a36Sopenharmony_ci edge_serial->read_in_progress = false; 70162306a36Sopenharmony_ci return; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci dev = &edge_serial->serial->dev->dev; 70562306a36Sopenharmony_ci raw_data_length = urb->actual_length; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, raw_data_length, data); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci spin_lock_irqsave(&edge_serial->es_lock, flags); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* decrement our rxBytes available by the number that we just got */ 71262306a36Sopenharmony_ci edge_serial->rxBytesAvail -= raw_data_length; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dev_dbg(dev, "%s - Received = %d, rxBytesAvail %d\n", __func__, 71562306a36Sopenharmony_ci raw_data_length, edge_serial->rxBytesAvail); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci process_rcvd_data(edge_serial, data, urb->actual_length); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* check to see if there's any more data for us to read */ 72062306a36Sopenharmony_ci if (edge_serial->rxBytesAvail > 0) { 72162306a36Sopenharmony_ci dev_dbg(dev, "%s - posting a read\n", __func__); 72262306a36Sopenharmony_ci retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); 72362306a36Sopenharmony_ci if (retval) { 72462306a36Sopenharmony_ci dev_err(dev, 72562306a36Sopenharmony_ci "%s - usb_submit_urb(read bulk) failed, retval = %d\n", 72662306a36Sopenharmony_ci __func__, retval); 72762306a36Sopenharmony_ci edge_serial->read_in_progress = false; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci edge_serial->read_in_progress = false; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_serial->es_lock, flags); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/***************************************************************************** 73862306a36Sopenharmony_ci * edge_bulk_out_data_callback 73962306a36Sopenharmony_ci * this is the callback function for when we have finished sending 74062306a36Sopenharmony_ci * serial data on the bulk out endpoint. 74162306a36Sopenharmony_ci *****************************************************************************/ 74262306a36Sopenharmony_cistatic void edge_bulk_out_data_callback(struct urb *urb) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct edgeport_port *edge_port = urb->context; 74562306a36Sopenharmony_ci int status = urb->status; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (status) { 74862306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, 74962306a36Sopenharmony_ci "%s - nonzero write bulk status received: %d\n", 75062306a36Sopenharmony_ci __func__, status); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (edge_port->open) 75462306a36Sopenharmony_ci tty_port_tty_wakeup(&edge_port->port->port); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Release the Write URB */ 75762306a36Sopenharmony_ci edge_port->write_in_progress = false; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Check if more data needs to be sent */ 76062306a36Sopenharmony_ci send_more_port_data((struct edgeport_serial *) 76162306a36Sopenharmony_ci (usb_get_serial_data(edge_port->port->serial)), edge_port); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/***************************************************************************** 76662306a36Sopenharmony_ci * BulkOutCmdCallback 76762306a36Sopenharmony_ci * this is the callback function for when we have finished sending a 76862306a36Sopenharmony_ci * command on the bulk out endpoint. 76962306a36Sopenharmony_ci *****************************************************************************/ 77062306a36Sopenharmony_cistatic void edge_bulk_out_cmd_callback(struct urb *urb) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct edgeport_port *edge_port = urb->context; 77362306a36Sopenharmony_ci int status = urb->status; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci atomic_dec(&CmdUrbs); 77662306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - FREE URB %p (outstanding %d)\n", 77762306a36Sopenharmony_ci __func__, urb, atomic_read(&CmdUrbs)); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* clean up the transfer buffer */ 78162306a36Sopenharmony_ci kfree(urb->transfer_buffer); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Free the command urb */ 78462306a36Sopenharmony_ci usb_free_urb(urb); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (status) { 78762306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, 78862306a36Sopenharmony_ci "%s - nonzero write bulk status received: %d\n", 78962306a36Sopenharmony_ci __func__, status); 79062306a36Sopenharmony_ci return; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* tell the tty driver that something has changed */ 79462306a36Sopenharmony_ci if (edge_port->open) 79562306a36Sopenharmony_ci tty_port_tty_wakeup(&edge_port->port->port); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* we have completed the command */ 79862306a36Sopenharmony_ci edge_port->commandPending = false; 79962306a36Sopenharmony_ci wake_up(&edge_port->wait_command); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci/***************************************************************************** 80462306a36Sopenharmony_ci * Driver tty interface functions 80562306a36Sopenharmony_ci *****************************************************************************/ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/***************************************************************************** 80862306a36Sopenharmony_ci * SerialOpen 80962306a36Sopenharmony_ci * this function is called by the tty driver when a port is opened 81062306a36Sopenharmony_ci * If successful, we return 0 81162306a36Sopenharmony_ci * Otherwise we return a negative error number. 81262306a36Sopenharmony_ci *****************************************************************************/ 81362306a36Sopenharmony_cistatic int edge_open(struct tty_struct *tty, struct usb_serial_port *port) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 81662306a36Sopenharmony_ci struct device *dev = &port->dev; 81762306a36Sopenharmony_ci struct usb_serial *serial; 81862306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 81962306a36Sopenharmony_ci int response; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (edge_port == NULL) 82262306a36Sopenharmony_ci return -ENODEV; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* see if we've set up our endpoint info yet (can't set it up 82562306a36Sopenharmony_ci in edge_startup as the structures were not set up at that time.) */ 82662306a36Sopenharmony_ci serial = port->serial; 82762306a36Sopenharmony_ci edge_serial = usb_get_serial_data(serial); 82862306a36Sopenharmony_ci if (edge_serial == NULL) 82962306a36Sopenharmony_ci return -ENODEV; 83062306a36Sopenharmony_ci if (edge_serial->interrupt_in_buffer == NULL) { 83162306a36Sopenharmony_ci struct usb_serial_port *port0 = serial->port[0]; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* not set up yet, so do it now */ 83462306a36Sopenharmony_ci edge_serial->interrupt_in_buffer = 83562306a36Sopenharmony_ci port0->interrupt_in_buffer; 83662306a36Sopenharmony_ci edge_serial->interrupt_in_endpoint = 83762306a36Sopenharmony_ci port0->interrupt_in_endpointAddress; 83862306a36Sopenharmony_ci edge_serial->interrupt_read_urb = port0->interrupt_in_urb; 83962306a36Sopenharmony_ci edge_serial->bulk_in_buffer = port0->bulk_in_buffer; 84062306a36Sopenharmony_ci edge_serial->bulk_in_endpoint = 84162306a36Sopenharmony_ci port0->bulk_in_endpointAddress; 84262306a36Sopenharmony_ci edge_serial->read_urb = port0->read_urb; 84362306a36Sopenharmony_ci edge_serial->bulk_out_endpoint = 84462306a36Sopenharmony_ci port0->bulk_out_endpointAddress; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* set up our interrupt urb */ 84762306a36Sopenharmony_ci usb_fill_int_urb(edge_serial->interrupt_read_urb, 84862306a36Sopenharmony_ci serial->dev, 84962306a36Sopenharmony_ci usb_rcvintpipe(serial->dev, 85062306a36Sopenharmony_ci port0->interrupt_in_endpointAddress), 85162306a36Sopenharmony_ci port0->interrupt_in_buffer, 85262306a36Sopenharmony_ci edge_serial->interrupt_read_urb->transfer_buffer_length, 85362306a36Sopenharmony_ci edge_interrupt_callback, edge_serial, 85462306a36Sopenharmony_ci edge_serial->interrupt_read_urb->interval); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* set up our bulk in urb */ 85762306a36Sopenharmony_ci usb_fill_bulk_urb(edge_serial->read_urb, serial->dev, 85862306a36Sopenharmony_ci usb_rcvbulkpipe(serial->dev, 85962306a36Sopenharmony_ci port0->bulk_in_endpointAddress), 86062306a36Sopenharmony_ci port0->bulk_in_buffer, 86162306a36Sopenharmony_ci edge_serial->read_urb->transfer_buffer_length, 86262306a36Sopenharmony_ci edge_bulk_in_callback, edge_serial); 86362306a36Sopenharmony_ci edge_serial->read_in_progress = false; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* start interrupt read for this edgeport 86662306a36Sopenharmony_ci * this interrupt will continue as long 86762306a36Sopenharmony_ci * as the edgeport is connected */ 86862306a36Sopenharmony_ci response = usb_submit_urb(edge_serial->interrupt_read_urb, 86962306a36Sopenharmony_ci GFP_KERNEL); 87062306a36Sopenharmony_ci if (response) { 87162306a36Sopenharmony_ci dev_err(dev, "%s - Error %d submitting control urb\n", 87262306a36Sopenharmony_ci __func__, response); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* initialize our wait queues */ 87762306a36Sopenharmony_ci init_waitqueue_head(&edge_port->wait_open); 87862306a36Sopenharmony_ci init_waitqueue_head(&edge_port->wait_chase); 87962306a36Sopenharmony_ci init_waitqueue_head(&edge_port->wait_command); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* initialize our port settings */ 88262306a36Sopenharmony_ci edge_port->txCredits = 0; /* Can't send any data yet */ 88362306a36Sopenharmony_ci /* Must always set this bit to enable ints! */ 88462306a36Sopenharmony_ci edge_port->shadowMCR = MCR_MASTER_IE; 88562306a36Sopenharmony_ci edge_port->chaseResponsePending = false; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* send a open port command */ 88862306a36Sopenharmony_ci edge_port->openPending = true; 88962306a36Sopenharmony_ci edge_port->open = false; 89062306a36Sopenharmony_ci response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (response < 0) { 89362306a36Sopenharmony_ci dev_err(dev, "%s - error sending open port command\n", __func__); 89462306a36Sopenharmony_ci edge_port->openPending = false; 89562306a36Sopenharmony_ci return -ENODEV; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* now wait for the port to be completely opened */ 89962306a36Sopenharmony_ci wait_event_timeout(edge_port->wait_open, !edge_port->openPending, 90062306a36Sopenharmony_ci OPEN_TIMEOUT); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (!edge_port->open) { 90362306a36Sopenharmony_ci /* open timed out */ 90462306a36Sopenharmony_ci dev_dbg(dev, "%s - open timeout\n", __func__); 90562306a36Sopenharmony_ci edge_port->openPending = false; 90662306a36Sopenharmony_ci return -ENODEV; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* create the txfifo */ 91062306a36Sopenharmony_ci edge_port->txfifo.head = 0; 91162306a36Sopenharmony_ci edge_port->txfifo.tail = 0; 91262306a36Sopenharmony_ci edge_port->txfifo.count = 0; 91362306a36Sopenharmony_ci edge_port->txfifo.size = edge_port->maxTxCredits; 91462306a36Sopenharmony_ci edge_port->txfifo.fifo = kmalloc(edge_port->maxTxCredits, GFP_KERNEL); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (!edge_port->txfifo.fifo) { 91762306a36Sopenharmony_ci edge_close(port); 91862306a36Sopenharmony_ci return -ENOMEM; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* Allocate a URB for the write */ 92262306a36Sopenharmony_ci edge_port->write_urb = usb_alloc_urb(0, GFP_KERNEL); 92362306a36Sopenharmony_ci edge_port->write_in_progress = false; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (!edge_port->write_urb) { 92662306a36Sopenharmony_ci edge_close(port); 92762306a36Sopenharmony_ci return -ENOMEM; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci dev_dbg(dev, "%s - Initialize TX fifo to %d bytes\n", 93162306a36Sopenharmony_ci __func__, edge_port->maxTxCredits); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci return 0; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/************************************************************************ 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * block_until_chase_response 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * This function will block the close until one of the following: 94262306a36Sopenharmony_ci * 1. Response to our Chase comes from Edgeport 94362306a36Sopenharmony_ci * 2. A timeout of 10 seconds without activity has expired 94462306a36Sopenharmony_ci * (1K of Edgeport data @ 2400 baud ==> 4 sec to empty) 94562306a36Sopenharmony_ci * 94662306a36Sopenharmony_ci ************************************************************************/ 94762306a36Sopenharmony_cistatic void block_until_chase_response(struct edgeport_port *edge_port) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 95062306a36Sopenharmony_ci DEFINE_WAIT(wait); 95162306a36Sopenharmony_ci __u16 lastCredits; 95262306a36Sopenharmony_ci int timeout = 1*HZ; 95362306a36Sopenharmony_ci int loop = 10; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci while (1) { 95662306a36Sopenharmony_ci /* Save Last credits */ 95762306a36Sopenharmony_ci lastCredits = edge_port->txCredits; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* Did we get our Chase response */ 96062306a36Sopenharmony_ci if (!edge_port->chaseResponsePending) { 96162306a36Sopenharmony_ci dev_dbg(dev, "%s - Got Chase Response\n", __func__); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* did we get all of our credit back? */ 96462306a36Sopenharmony_ci if (edge_port->txCredits == edge_port->maxTxCredits) { 96562306a36Sopenharmony_ci dev_dbg(dev, "%s - Got all credits\n", __func__); 96662306a36Sopenharmony_ci return; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Block the thread for a while */ 97162306a36Sopenharmony_ci prepare_to_wait(&edge_port->wait_chase, &wait, 97262306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 97362306a36Sopenharmony_ci schedule_timeout(timeout); 97462306a36Sopenharmony_ci finish_wait(&edge_port->wait_chase, &wait); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (lastCredits == edge_port->txCredits) { 97762306a36Sopenharmony_ci /* No activity.. count down. */ 97862306a36Sopenharmony_ci loop--; 97962306a36Sopenharmony_ci if (loop == 0) { 98062306a36Sopenharmony_ci edge_port->chaseResponsePending = false; 98162306a36Sopenharmony_ci dev_dbg(dev, "%s - Chase TIMEOUT\n", __func__); 98262306a36Sopenharmony_ci return; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci } else { 98562306a36Sopenharmony_ci /* Reset timeout value back to 10 seconds */ 98662306a36Sopenharmony_ci dev_dbg(dev, "%s - Last %d, Current %d\n", __func__, 98762306a36Sopenharmony_ci lastCredits, edge_port->txCredits); 98862306a36Sopenharmony_ci loop = 10; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci/************************************************************************ 99562306a36Sopenharmony_ci * 99662306a36Sopenharmony_ci * block_until_tx_empty 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci * This function will block the close until one of the following: 99962306a36Sopenharmony_ci * 1. TX count are 0 100062306a36Sopenharmony_ci * 2. The edgeport has stopped 100162306a36Sopenharmony_ci * 3. A timeout of 3 seconds without activity has expired 100262306a36Sopenharmony_ci * 100362306a36Sopenharmony_ci ************************************************************************/ 100462306a36Sopenharmony_cistatic void block_until_tx_empty(struct edgeport_port *edge_port) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 100762306a36Sopenharmony_ci DEFINE_WAIT(wait); 100862306a36Sopenharmony_ci struct TxFifo *fifo = &edge_port->txfifo; 100962306a36Sopenharmony_ci __u32 lastCount; 101062306a36Sopenharmony_ci int timeout = HZ/10; 101162306a36Sopenharmony_ci int loop = 30; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci while (1) { 101462306a36Sopenharmony_ci /* Save Last count */ 101562306a36Sopenharmony_ci lastCount = fifo->count; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* Is the Edgeport Buffer empty? */ 101862306a36Sopenharmony_ci if (lastCount == 0) { 101962306a36Sopenharmony_ci dev_dbg(dev, "%s - TX Buffer Empty\n", __func__); 102062306a36Sopenharmony_ci return; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* Block the thread for a while */ 102462306a36Sopenharmony_ci prepare_to_wait(&edge_port->wait_chase, &wait, 102562306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 102662306a36Sopenharmony_ci schedule_timeout(timeout); 102762306a36Sopenharmony_ci finish_wait(&edge_port->wait_chase, &wait); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci dev_dbg(dev, "%s wait\n", __func__); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (lastCount == fifo->count) { 103262306a36Sopenharmony_ci /* No activity.. count down. */ 103362306a36Sopenharmony_ci loop--; 103462306a36Sopenharmony_ci if (loop == 0) { 103562306a36Sopenharmony_ci dev_dbg(dev, "%s - TIMEOUT\n", __func__); 103662306a36Sopenharmony_ci return; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci } else { 103962306a36Sopenharmony_ci /* Reset timeout value back to seconds */ 104062306a36Sopenharmony_ci loop = 30; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/***************************************************************************** 104762306a36Sopenharmony_ci * edge_close 104862306a36Sopenharmony_ci * this function is called by the tty driver when a port is closed 104962306a36Sopenharmony_ci *****************************************************************************/ 105062306a36Sopenharmony_cistatic void edge_close(struct usb_serial_port *port) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 105362306a36Sopenharmony_ci struct edgeport_port *edge_port; 105462306a36Sopenharmony_ci int status; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci edge_serial = usb_get_serial_data(port->serial); 105762306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 105862306a36Sopenharmony_ci if (edge_serial == NULL || edge_port == NULL) 105962306a36Sopenharmony_ci return; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* block until tx is empty */ 106262306a36Sopenharmony_ci block_until_tx_empty(edge_port); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci edge_port->closePending = true; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (!edge_serial->is_epic || 106762306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPChase) { 106862306a36Sopenharmony_ci /* flush and chase */ 106962306a36Sopenharmony_ci edge_port->chaseResponsePending = true; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__); 107262306a36Sopenharmony_ci status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0); 107362306a36Sopenharmony_ci if (status == 0) 107462306a36Sopenharmony_ci /* block until chase finished */ 107562306a36Sopenharmony_ci block_until_chase_response(edge_port); 107662306a36Sopenharmony_ci else 107762306a36Sopenharmony_ci edge_port->chaseResponsePending = false; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!edge_serial->is_epic || 108162306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPClose) { 108262306a36Sopenharmony_ci /* close the port */ 108362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__); 108462306a36Sopenharmony_ci send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0); 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci /* port->close = true; */ 108862306a36Sopenharmony_ci edge_port->closePending = false; 108962306a36Sopenharmony_ci edge_port->open = false; 109062306a36Sopenharmony_ci edge_port->openPending = false; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci usb_kill_urb(edge_port->write_urb); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (edge_port->write_urb) { 109562306a36Sopenharmony_ci /* if this urb had a transfer buffer already 109662306a36Sopenharmony_ci (old transfer) free it */ 109762306a36Sopenharmony_ci kfree(edge_port->write_urb->transfer_buffer); 109862306a36Sopenharmony_ci usb_free_urb(edge_port->write_urb); 109962306a36Sopenharmony_ci edge_port->write_urb = NULL; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci kfree(edge_port->txfifo.fifo); 110262306a36Sopenharmony_ci edge_port->txfifo.fifo = NULL; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci/***************************************************************************** 110662306a36Sopenharmony_ci * SerialWrite 110762306a36Sopenharmony_ci * this function is called by the tty driver when data should be written 110862306a36Sopenharmony_ci * to the port. 110962306a36Sopenharmony_ci * If successful, we return the number of bytes written, otherwise we 111062306a36Sopenharmony_ci * return a negative error number. 111162306a36Sopenharmony_ci *****************************************************************************/ 111262306a36Sopenharmony_cistatic int edge_write(struct tty_struct *tty, struct usb_serial_port *port, 111362306a36Sopenharmony_ci const unsigned char *data, int count) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 111662306a36Sopenharmony_ci struct TxFifo *fifo; 111762306a36Sopenharmony_ci int copySize; 111862306a36Sopenharmony_ci int bytesleft; 111962306a36Sopenharmony_ci int firsthalf; 112062306a36Sopenharmony_ci int secondhalf; 112162306a36Sopenharmony_ci unsigned long flags; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (edge_port == NULL) 112462306a36Sopenharmony_ci return -ENODEV; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* get a pointer to the Tx fifo */ 112762306a36Sopenharmony_ci fifo = &edge_port->txfifo; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* calculate number of bytes to put in fifo */ 113262306a36Sopenharmony_ci copySize = min((unsigned int)count, 113362306a36Sopenharmony_ci (edge_port->txCredits - fifo->count)); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s of %d byte(s) Fifo room %d -- will copy %d bytes\n", 113662306a36Sopenharmony_ci __func__, count, edge_port->txCredits - fifo->count, copySize); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* catch writes of 0 bytes which the tty driver likes to give us, 113962306a36Sopenharmony_ci and when txCredits is empty */ 114062306a36Sopenharmony_ci if (copySize == 0) { 114162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - copySize = Zero\n", __func__); 114262306a36Sopenharmony_ci goto finish_write; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* queue the data 114662306a36Sopenharmony_ci * since we can never overflow the buffer we do not have to check for a 114762306a36Sopenharmony_ci * full condition 114862306a36Sopenharmony_ci * 114962306a36Sopenharmony_ci * the copy is done is two parts -- first fill to the end of the buffer 115062306a36Sopenharmony_ci * then copy the reset from the start of the buffer 115162306a36Sopenharmony_ci */ 115262306a36Sopenharmony_ci bytesleft = fifo->size - fifo->head; 115362306a36Sopenharmony_ci firsthalf = min(bytesleft, copySize); 115462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - copy %d bytes of %d into fifo \n", __func__, 115562306a36Sopenharmony_ci firsthalf, bytesleft); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* now copy our data */ 115862306a36Sopenharmony_ci memcpy(&fifo->fifo[fifo->head], data, firsthalf); 115962306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* update the index and size */ 116262306a36Sopenharmony_ci fifo->head += firsthalf; 116362306a36Sopenharmony_ci fifo->count += firsthalf; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* wrap the index */ 116662306a36Sopenharmony_ci if (fifo->head == fifo->size) 116762306a36Sopenharmony_ci fifo->head = 0; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci secondhalf = copySize-firsthalf; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (secondhalf) { 117262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - copy rest of data %d\n", __func__, secondhalf); 117362306a36Sopenharmony_ci memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf); 117462306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]); 117562306a36Sopenharmony_ci /* update the index and size */ 117662306a36Sopenharmony_ci fifo->count += secondhalf; 117762306a36Sopenharmony_ci fifo->head += secondhalf; 117862306a36Sopenharmony_ci /* No need to check for wrap since we can not get to end of 117962306a36Sopenharmony_ci * the fifo in this part 118062306a36Sopenharmony_ci */ 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cifinish_write: 118462306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci send_more_port_data((struct edgeport_serial *) 118762306a36Sopenharmony_ci usb_get_serial_data(port->serial), edge_port); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s wrote %d byte(s) TxCredits %d, Fifo %d\n", 119062306a36Sopenharmony_ci __func__, copySize, edge_port->txCredits, fifo->count); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return copySize; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/************************************************************************ 119762306a36Sopenharmony_ci * 119862306a36Sopenharmony_ci * send_more_port_data() 119962306a36Sopenharmony_ci * 120062306a36Sopenharmony_ci * This routine attempts to write additional UART transmit data 120162306a36Sopenharmony_ci * to a port over the USB bulk pipe. It is called (1) when new 120262306a36Sopenharmony_ci * data has been written to a port's TxBuffer from higher layers 120362306a36Sopenharmony_ci * (2) when the peripheral sends us additional TxCredits indicating 120462306a36Sopenharmony_ci * that it can accept more Tx data for a given port; and (3) when 120562306a36Sopenharmony_ci * a bulk write completes successfully and we want to see if we 120662306a36Sopenharmony_ci * can transmit more. 120762306a36Sopenharmony_ci * 120862306a36Sopenharmony_ci ************************************************************************/ 120962306a36Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial, 121062306a36Sopenharmony_ci struct edgeport_port *edge_port) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci struct TxFifo *fifo = &edge_port->txfifo; 121362306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 121462306a36Sopenharmony_ci struct urb *urb; 121562306a36Sopenharmony_ci unsigned char *buffer; 121662306a36Sopenharmony_ci int status; 121762306a36Sopenharmony_ci int count; 121862306a36Sopenharmony_ci int bytesleft; 121962306a36Sopenharmony_ci int firsthalf; 122062306a36Sopenharmony_ci int secondhalf; 122162306a36Sopenharmony_ci unsigned long flags; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (edge_port->write_in_progress || 122662306a36Sopenharmony_ci !edge_port->open || 122762306a36Sopenharmony_ci (fifo->count == 0)) { 122862306a36Sopenharmony_ci dev_dbg(dev, "%s EXIT - fifo %d, PendingWrite = %d\n", 122962306a36Sopenharmony_ci __func__, fifo->count, edge_port->write_in_progress); 123062306a36Sopenharmony_ci goto exit_send; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* since the amount of data in the fifo will always fit into the 123462306a36Sopenharmony_ci * edgeport buffer we do not need to check the write length 123562306a36Sopenharmony_ci * 123662306a36Sopenharmony_ci * Do we have enough credits for this port to make it worthwhile 123762306a36Sopenharmony_ci * to bother queueing a write. If it's too small, say a few bytes, 123862306a36Sopenharmony_ci * it's better to wait for more credits so we can do a larger write. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ci if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) { 124162306a36Sopenharmony_ci dev_dbg(dev, "%s Not enough credit - fifo %d TxCredit %d\n", 124262306a36Sopenharmony_ci __func__, fifo->count, edge_port->txCredits); 124362306a36Sopenharmony_ci goto exit_send; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci /* lock this write */ 124762306a36Sopenharmony_ci edge_port->write_in_progress = true; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* get a pointer to the write_urb */ 125062306a36Sopenharmony_ci urb = edge_port->write_urb; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* make sure transfer buffer is freed */ 125362306a36Sopenharmony_ci kfree(urb->transfer_buffer); 125462306a36Sopenharmony_ci urb->transfer_buffer = NULL; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* build the data header for the buffer and port that we are about 125762306a36Sopenharmony_ci to send out */ 125862306a36Sopenharmony_ci count = fifo->count; 125962306a36Sopenharmony_ci buffer = kmalloc(count+2, GFP_ATOMIC); 126062306a36Sopenharmony_ci if (!buffer) { 126162306a36Sopenharmony_ci edge_port->write_in_progress = false; 126262306a36Sopenharmony_ci goto exit_send; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->port_number, count); 126562306a36Sopenharmony_ci buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->port_number, count); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* now copy our data */ 126862306a36Sopenharmony_ci bytesleft = fifo->size - fifo->tail; 126962306a36Sopenharmony_ci firsthalf = min(bytesleft, count); 127062306a36Sopenharmony_ci memcpy(&buffer[2], &fifo->fifo[fifo->tail], firsthalf); 127162306a36Sopenharmony_ci fifo->tail += firsthalf; 127262306a36Sopenharmony_ci fifo->count -= firsthalf; 127362306a36Sopenharmony_ci if (fifo->tail == fifo->size) 127462306a36Sopenharmony_ci fifo->tail = 0; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci secondhalf = count-firsthalf; 127762306a36Sopenharmony_ci if (secondhalf) { 127862306a36Sopenharmony_ci memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail], 127962306a36Sopenharmony_ci secondhalf); 128062306a36Sopenharmony_ci fifo->tail += secondhalf; 128162306a36Sopenharmony_ci fifo->count -= secondhalf; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (count) 128562306a36Sopenharmony_ci usb_serial_debug_data(&edge_port->port->dev, __func__, count, &buffer[2]); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* fill up the urb with all of our data and submit it */ 128862306a36Sopenharmony_ci usb_fill_bulk_urb(urb, edge_serial->serial->dev, 128962306a36Sopenharmony_ci usb_sndbulkpipe(edge_serial->serial->dev, 129062306a36Sopenharmony_ci edge_serial->bulk_out_endpoint), 129162306a36Sopenharmony_ci buffer, count+2, 129262306a36Sopenharmony_ci edge_bulk_out_data_callback, edge_port); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* decrement the number of credits we have by the number we just sent */ 129562306a36Sopenharmony_ci edge_port->txCredits -= count; 129662306a36Sopenharmony_ci edge_port->port->icount.tx += count; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 129962306a36Sopenharmony_ci if (status) { 130062306a36Sopenharmony_ci /* something went wrong */ 130162306a36Sopenharmony_ci dev_err_console(edge_port->port, 130262306a36Sopenharmony_ci "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", 130362306a36Sopenharmony_ci __func__, status); 130462306a36Sopenharmony_ci edge_port->write_in_progress = false; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* revert the credits as something bad happened. */ 130762306a36Sopenharmony_ci edge_port->txCredits += count; 130862306a36Sopenharmony_ci edge_port->port->icount.tx -= count; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n", 131162306a36Sopenharmony_ci __func__, count, edge_port->txCredits, fifo->count); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ciexit_send: 131462306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci/***************************************************************************** 131962306a36Sopenharmony_ci * edge_write_room 132062306a36Sopenharmony_ci * this function is called by the tty driver when it wants to know how 132162306a36Sopenharmony_ci * many bytes of data we can accept for a specific port. 132262306a36Sopenharmony_ci *****************************************************************************/ 132362306a36Sopenharmony_cistatic unsigned int edge_write_room(struct tty_struct *tty) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 132662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 132762306a36Sopenharmony_ci unsigned int room; 132862306a36Sopenharmony_ci unsigned long flags; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* total of both buffers is still txCredit */ 133162306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 133262306a36Sopenharmony_ci room = edge_port->txCredits - edge_port->txfifo.count; 133362306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, room); 133662306a36Sopenharmony_ci return room; 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci/***************************************************************************** 134162306a36Sopenharmony_ci * edge_chars_in_buffer 134262306a36Sopenharmony_ci * this function is called by the tty driver when it wants to know how 134362306a36Sopenharmony_ci * many bytes of data we currently have outstanding in the port (data that 134462306a36Sopenharmony_ci * has been written, but hasn't made it out the port yet) 134562306a36Sopenharmony_ci *****************************************************************************/ 134662306a36Sopenharmony_cistatic unsigned int edge_chars_in_buffer(struct tty_struct *tty) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 134962306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 135062306a36Sopenharmony_ci unsigned int num_chars; 135162306a36Sopenharmony_ci unsigned long flags; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 135462306a36Sopenharmony_ci num_chars = edge_port->maxTxCredits - edge_port->txCredits + 135562306a36Sopenharmony_ci edge_port->txfifo.count; 135662306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 135762306a36Sopenharmony_ci if (num_chars) { 135862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, num_chars); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci return num_chars; 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci/***************************************************************************** 136662306a36Sopenharmony_ci * SerialThrottle 136762306a36Sopenharmony_ci * this function is called by the tty driver when it wants to stop the data 136862306a36Sopenharmony_ci * being read from the port. 136962306a36Sopenharmony_ci *****************************************************************************/ 137062306a36Sopenharmony_cistatic void edge_throttle(struct tty_struct *tty) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 137362306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 137462306a36Sopenharmony_ci int status; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (edge_port == NULL) 137762306a36Sopenharmony_ci return; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (!edge_port->open) { 138062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 138162306a36Sopenharmony_ci return; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the stop character */ 138562306a36Sopenharmony_ci if (I_IXOFF(tty)) { 138662306a36Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 138762306a36Sopenharmony_ci status = edge_write(tty, port, &stop_char, 1); 138862306a36Sopenharmony_ci if (status <= 0) 138962306a36Sopenharmony_ci return; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 139362306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 139462306a36Sopenharmony_ci edge_port->shadowMCR &= ~MCR_RTS; 139562306a36Sopenharmony_ci status = send_cmd_write_uart_register(edge_port, MCR, 139662306a36Sopenharmony_ci edge_port->shadowMCR); 139762306a36Sopenharmony_ci if (status != 0) 139862306a36Sopenharmony_ci return; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci/***************************************************************************** 140462306a36Sopenharmony_ci * edge_unthrottle 140562306a36Sopenharmony_ci * this function is called by the tty driver when it wants to resume the 140662306a36Sopenharmony_ci * data being read from the port (called after SerialThrottle is called) 140762306a36Sopenharmony_ci *****************************************************************************/ 140862306a36Sopenharmony_cistatic void edge_unthrottle(struct tty_struct *tty) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 141162306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 141262306a36Sopenharmony_ci int status; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (edge_port == NULL) 141562306a36Sopenharmony_ci return; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (!edge_port->open) { 141862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 141962306a36Sopenharmony_ci return; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the start character */ 142362306a36Sopenharmony_ci if (I_IXOFF(tty)) { 142462306a36Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 142562306a36Sopenharmony_ci status = edge_write(tty, port, &start_char, 1); 142662306a36Sopenharmony_ci if (status <= 0) 142762306a36Sopenharmony_ci return; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 143062306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 143162306a36Sopenharmony_ci edge_port->shadowMCR |= MCR_RTS; 143262306a36Sopenharmony_ci send_cmd_write_uart_register(edge_port, MCR, 143362306a36Sopenharmony_ci edge_port->shadowMCR); 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci/***************************************************************************** 143962306a36Sopenharmony_ci * SerialSetTermios 144062306a36Sopenharmony_ci * this function is called by the tty driver when it wants to change 144162306a36Sopenharmony_ci * the termios structure 144262306a36Sopenharmony_ci *****************************************************************************/ 144362306a36Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty, 144462306a36Sopenharmony_ci struct usb_serial_port *port, 144562306a36Sopenharmony_ci const struct ktermios *old_termios) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci if (edge_port == NULL) 145062306a36Sopenharmony_ci return; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!edge_port->open) { 145362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 145462306a36Sopenharmony_ci return; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* change the port settings to the new ones specified */ 145862306a36Sopenharmony_ci change_port_settings(tty, edge_port, old_termios); 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci/***************************************************************************** 146362306a36Sopenharmony_ci * get_lsr_info - get line status register info 146462306a36Sopenharmony_ci * 146562306a36Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically 146662306a36Sopenharmony_ci * is emptied. On bus types like RS485, the transmitter must 146762306a36Sopenharmony_ci * release the bus after transmitting. This must be done when 146862306a36Sopenharmony_ci * the transmit shift register is empty, not be done when the 146962306a36Sopenharmony_ci * transmit holding register is empty. This functionality 147062306a36Sopenharmony_ci * allows an RS485 driver to be written in user space. 147162306a36Sopenharmony_ci *****************************************************************************/ 147262306a36Sopenharmony_cistatic int get_lsr_info(struct edgeport_port *edge_port, 147362306a36Sopenharmony_ci unsigned int __user *value) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci unsigned int result = 0; 147662306a36Sopenharmony_ci unsigned long flags; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 147962306a36Sopenharmony_ci if (edge_port->maxTxCredits == edge_port->txCredits && 148062306a36Sopenharmony_ci edge_port->txfifo.count == 0) { 148162306a36Sopenharmony_ci dev_dbg(&edge_port->port->dev, "%s -- Empty\n", __func__); 148262306a36Sopenharmony_ci result = TIOCSER_TEMT; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (copy_to_user(value, &result, sizeof(int))) 148762306a36Sopenharmony_ci return -EFAULT; 148862306a36Sopenharmony_ci return 0; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_cistatic int edge_tiocmset(struct tty_struct *tty, 149262306a36Sopenharmony_ci unsigned int set, unsigned int clear) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 149562306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 149662306a36Sopenharmony_ci unsigned int mcr; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci mcr = edge_port->shadowMCR; 149962306a36Sopenharmony_ci if (set & TIOCM_RTS) 150062306a36Sopenharmony_ci mcr |= MCR_RTS; 150162306a36Sopenharmony_ci if (set & TIOCM_DTR) 150262306a36Sopenharmony_ci mcr |= MCR_DTR; 150362306a36Sopenharmony_ci if (set & TIOCM_LOOP) 150462306a36Sopenharmony_ci mcr |= MCR_LOOPBACK; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (clear & TIOCM_RTS) 150762306a36Sopenharmony_ci mcr &= ~MCR_RTS; 150862306a36Sopenharmony_ci if (clear & TIOCM_DTR) 150962306a36Sopenharmony_ci mcr &= ~MCR_DTR; 151062306a36Sopenharmony_ci if (clear & TIOCM_LOOP) 151162306a36Sopenharmony_ci mcr &= ~MCR_LOOPBACK; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci edge_port->shadowMCR = mcr; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci return 0; 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic int edge_tiocmget(struct tty_struct *tty) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 152362306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 152462306a36Sopenharmony_ci unsigned int result = 0; 152562306a36Sopenharmony_ci unsigned int msr; 152662306a36Sopenharmony_ci unsigned int mcr; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci msr = edge_port->shadowMSR; 152962306a36Sopenharmony_ci mcr = edge_port->shadowMCR; 153062306a36Sopenharmony_ci result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0) /* 0x002 */ 153162306a36Sopenharmony_ci | ((mcr & MCR_RTS) ? TIOCM_RTS: 0) /* 0x004 */ 153262306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_CTS) ? TIOCM_CTS: 0) /* 0x020 */ 153362306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_CD) ? TIOCM_CAR: 0) /* 0x040 */ 153462306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_RI) ? TIOCM_RI: 0) /* 0x080 */ 153562306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */ 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci return result; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci/***************************************************************************** 154162306a36Sopenharmony_ci * SerialIoctl 154262306a36Sopenharmony_ci * this function handles any ioctl calls to the driver 154362306a36Sopenharmony_ci *****************************************************************************/ 154462306a36Sopenharmony_cistatic int edge_ioctl(struct tty_struct *tty, 154562306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 154862306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci switch (cmd) { 155162306a36Sopenharmony_ci case TIOCSERGETLSR: 155262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); 155362306a36Sopenharmony_ci return get_lsr_info(edge_port, (unsigned int __user *) arg); 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci return -ENOIOCTLCMD; 155662306a36Sopenharmony_ci} 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci/***************************************************************************** 156062306a36Sopenharmony_ci * SerialBreak 156162306a36Sopenharmony_ci * this function sends a break to the port 156262306a36Sopenharmony_ci *****************************************************************************/ 156362306a36Sopenharmony_cistatic int edge_break(struct tty_struct *tty, int break_state) 156462306a36Sopenharmony_ci{ 156562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 156662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 156762306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); 156862306a36Sopenharmony_ci int status = 0; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (!edge_serial->is_epic || 157162306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPChase) { 157262306a36Sopenharmony_ci /* flush and chase */ 157362306a36Sopenharmony_ci edge_port->chaseResponsePending = true; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__); 157662306a36Sopenharmony_ci status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0); 157762306a36Sopenharmony_ci if (status == 0) { 157862306a36Sopenharmony_ci /* block until chase finished */ 157962306a36Sopenharmony_ci block_until_chase_response(edge_port); 158062306a36Sopenharmony_ci } else { 158162306a36Sopenharmony_ci edge_port->chaseResponsePending = false; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci } 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (!edge_serial->is_epic || 158662306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) { 158762306a36Sopenharmony_ci if (break_state == -1) { 158862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__); 158962306a36Sopenharmony_ci status = send_iosp_ext_cmd(edge_port, 159062306a36Sopenharmony_ci IOSP_CMD_SET_BREAK, 0); 159162306a36Sopenharmony_ci } else { 159262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLEAR_BREAK\n", __func__); 159362306a36Sopenharmony_ci status = send_iosp_ext_cmd(edge_port, 159462306a36Sopenharmony_ci IOSP_CMD_CLEAR_BREAK, 0); 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci if (status) 159762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - error sending break set/clear command.\n", 159862306a36Sopenharmony_ci __func__); 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci return status; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci/***************************************************************************** 160662306a36Sopenharmony_ci * process_rcvd_data 160762306a36Sopenharmony_ci * this function handles the data received on the bulk in pipe. 160862306a36Sopenharmony_ci *****************************************************************************/ 160962306a36Sopenharmony_cistatic void process_rcvd_data(struct edgeport_serial *edge_serial, 161062306a36Sopenharmony_ci unsigned char *buffer, __u16 bufferLength) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci struct usb_serial *serial = edge_serial->serial; 161362306a36Sopenharmony_ci struct device *dev = &serial->dev->dev; 161462306a36Sopenharmony_ci struct usb_serial_port *port; 161562306a36Sopenharmony_ci struct edgeport_port *edge_port; 161662306a36Sopenharmony_ci __u16 lastBufferLength; 161762306a36Sopenharmony_ci __u16 rxLen; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci lastBufferLength = bufferLength + 1; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci while (bufferLength > 0) { 162262306a36Sopenharmony_ci /* failsafe incase we get a message that we don't understand */ 162362306a36Sopenharmony_ci if (lastBufferLength == bufferLength) { 162462306a36Sopenharmony_ci dev_dbg(dev, "%s - stuck in loop, exiting it.\n", __func__); 162562306a36Sopenharmony_ci break; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci lastBufferLength = bufferLength; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci switch (edge_serial->rxState) { 163062306a36Sopenharmony_ci case EXPECT_HDR1: 163162306a36Sopenharmony_ci edge_serial->rxHeader1 = *buffer; 163262306a36Sopenharmony_ci ++buffer; 163362306a36Sopenharmony_ci --bufferLength; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci if (bufferLength == 0) { 163662306a36Sopenharmony_ci edge_serial->rxState = EXPECT_HDR2; 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci fallthrough; 164062306a36Sopenharmony_ci case EXPECT_HDR2: 164162306a36Sopenharmony_ci edge_serial->rxHeader2 = *buffer; 164262306a36Sopenharmony_ci ++buffer; 164362306a36Sopenharmony_ci --bufferLength; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci dev_dbg(dev, "%s - Hdr1=%02X Hdr2=%02X\n", __func__, 164662306a36Sopenharmony_ci edge_serial->rxHeader1, edge_serial->rxHeader2); 164762306a36Sopenharmony_ci /* Process depending on whether this header is 164862306a36Sopenharmony_ci * data or status */ 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) { 165162306a36Sopenharmony_ci /* Decode this status header and go to 165262306a36Sopenharmony_ci * EXPECT_HDR1 (if we can process the status 165362306a36Sopenharmony_ci * with only 2 bytes), or go to EXPECT_HDR3 to 165462306a36Sopenharmony_ci * get the third byte. */ 165562306a36Sopenharmony_ci edge_serial->rxPort = 165662306a36Sopenharmony_ci IOSP_GET_HDR_PORT(edge_serial->rxHeader1); 165762306a36Sopenharmony_ci edge_serial->rxStatusCode = 165862306a36Sopenharmony_ci IOSP_GET_STATUS_CODE( 165962306a36Sopenharmony_ci edge_serial->rxHeader1); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (!IOSP_STATUS_IS_2BYTE( 166262306a36Sopenharmony_ci edge_serial->rxStatusCode)) { 166362306a36Sopenharmony_ci /* This status needs additional bytes. 166462306a36Sopenharmony_ci * Save what we have and then wait for 166562306a36Sopenharmony_ci * more data. 166662306a36Sopenharmony_ci */ 166762306a36Sopenharmony_ci edge_serial->rxStatusParam 166862306a36Sopenharmony_ci = edge_serial->rxHeader2; 166962306a36Sopenharmony_ci edge_serial->rxState = EXPECT_HDR3; 167062306a36Sopenharmony_ci break; 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci /* We have all the header bytes, process the 167362306a36Sopenharmony_ci status now */ 167462306a36Sopenharmony_ci process_rcvd_status(edge_serial, 167562306a36Sopenharmony_ci edge_serial->rxHeader2, 0); 167662306a36Sopenharmony_ci edge_serial->rxState = EXPECT_HDR1; 167762306a36Sopenharmony_ci break; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1); 168162306a36Sopenharmony_ci edge_serial->rxBytesRemaining = IOSP_GET_HDR_DATA_LEN(edge_serial->rxHeader1, 168262306a36Sopenharmony_ci edge_serial->rxHeader2); 168362306a36Sopenharmony_ci dev_dbg(dev, "%s - Data for Port %u Len %u\n", __func__, 168462306a36Sopenharmony_ci edge_serial->rxPort, 168562306a36Sopenharmony_ci edge_serial->rxBytesRemaining); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci if (bufferLength == 0) { 168862306a36Sopenharmony_ci edge_serial->rxState = EXPECT_DATA; 168962306a36Sopenharmony_ci break; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci fallthrough; 169262306a36Sopenharmony_ci case EXPECT_DATA: /* Expect data */ 169362306a36Sopenharmony_ci if (bufferLength < edge_serial->rxBytesRemaining) { 169462306a36Sopenharmony_ci rxLen = bufferLength; 169562306a36Sopenharmony_ci /* Expect data to start next buffer */ 169662306a36Sopenharmony_ci edge_serial->rxState = EXPECT_DATA; 169762306a36Sopenharmony_ci } else { 169862306a36Sopenharmony_ci /* BufLen >= RxBytesRemaining */ 169962306a36Sopenharmony_ci rxLen = edge_serial->rxBytesRemaining; 170062306a36Sopenharmony_ci /* Start another header next time */ 170162306a36Sopenharmony_ci edge_serial->rxState = EXPECT_HDR1; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci bufferLength -= rxLen; 170562306a36Sopenharmony_ci edge_serial->rxBytesRemaining -= rxLen; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci /* spit this data back into the tty driver if this 170862306a36Sopenharmony_ci port is open */ 170962306a36Sopenharmony_ci if (rxLen && edge_serial->rxPort < serial->num_ports) { 171062306a36Sopenharmony_ci port = serial->port[edge_serial->rxPort]; 171162306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 171262306a36Sopenharmony_ci if (edge_port && edge_port->open) { 171362306a36Sopenharmony_ci dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n", 171462306a36Sopenharmony_ci __func__, rxLen, 171562306a36Sopenharmony_ci edge_serial->rxPort); 171662306a36Sopenharmony_ci edge_tty_recv(edge_port->port, buffer, 171762306a36Sopenharmony_ci rxLen); 171862306a36Sopenharmony_ci edge_port->port->icount.rx += rxLen; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci buffer += rxLen; 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci case EXPECT_HDR3: /* Expect 3rd byte of status header */ 172562306a36Sopenharmony_ci edge_serial->rxHeader3 = *buffer; 172662306a36Sopenharmony_ci ++buffer; 172762306a36Sopenharmony_ci --bufferLength; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* We have all the header bytes, process the 173062306a36Sopenharmony_ci status now */ 173162306a36Sopenharmony_ci process_rcvd_status(edge_serial, 173262306a36Sopenharmony_ci edge_serial->rxStatusParam, 173362306a36Sopenharmony_ci edge_serial->rxHeader3); 173462306a36Sopenharmony_ci edge_serial->rxState = EXPECT_HDR1; 173562306a36Sopenharmony_ci break; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/***************************************************************************** 174262306a36Sopenharmony_ci * process_rcvd_status 174362306a36Sopenharmony_ci * this function handles the any status messages received on the 174462306a36Sopenharmony_ci * bulk in pipe. 174562306a36Sopenharmony_ci *****************************************************************************/ 174662306a36Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial, 174762306a36Sopenharmony_ci __u8 byte2, __u8 byte3) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci struct usb_serial_port *port; 175062306a36Sopenharmony_ci struct edgeport_port *edge_port; 175162306a36Sopenharmony_ci struct tty_struct *tty; 175262306a36Sopenharmony_ci struct device *dev; 175362306a36Sopenharmony_ci __u8 code = edge_serial->rxStatusCode; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci /* switch the port pointer to the one being currently talked about */ 175662306a36Sopenharmony_ci if (edge_serial->rxPort >= edge_serial->serial->num_ports) 175762306a36Sopenharmony_ci return; 175862306a36Sopenharmony_ci port = edge_serial->serial->port[edge_serial->rxPort]; 175962306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 176062306a36Sopenharmony_ci if (edge_port == NULL) { 176162306a36Sopenharmony_ci dev_err(&edge_serial->serial->dev->dev, 176262306a36Sopenharmony_ci "%s - edge_port == NULL for port %d\n", 176362306a36Sopenharmony_ci __func__, edge_serial->rxPort); 176462306a36Sopenharmony_ci return; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci dev = &port->dev; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (code == IOSP_EXT_STATUS) { 176962306a36Sopenharmony_ci switch (byte2) { 177062306a36Sopenharmony_ci case IOSP_EXT_STATUS_CHASE_RSP: 177162306a36Sopenharmony_ci /* we want to do EXT status regardless of port 177262306a36Sopenharmony_ci * open/closed */ 177362306a36Sopenharmony_ci dev_dbg(dev, "%s - Port %u EXT CHASE_RSP Data = %02x\n", 177462306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte3); 177562306a36Sopenharmony_ci /* Currently, the only EXT_STATUS is Chase, so process 177662306a36Sopenharmony_ci * here instead of one more call to one more subroutine 177762306a36Sopenharmony_ci * If/when more EXT_STATUS, there'll be more work to do 177862306a36Sopenharmony_ci * Also, we currently clear flag and close the port 177962306a36Sopenharmony_ci * regardless of content of above's Byte3. 178062306a36Sopenharmony_ci * We could choose to do something else when Byte3 says 178162306a36Sopenharmony_ci * Timeout on Chase from Edgeport, like wait longer in 178262306a36Sopenharmony_ci * block_until_chase_response, but for now we don't. 178362306a36Sopenharmony_ci */ 178462306a36Sopenharmony_ci edge_port->chaseResponsePending = false; 178562306a36Sopenharmony_ci wake_up(&edge_port->wait_chase); 178662306a36Sopenharmony_ci return; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci case IOSP_EXT_STATUS_RX_CHECK_RSP: 178962306a36Sopenharmony_ci dev_dbg(dev, "%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", 179062306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte3); 179162306a36Sopenharmony_ci /* Port->RxCheckRsp = true; */ 179262306a36Sopenharmony_ci return; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (code == IOSP_STATUS_OPEN_RSP) { 179762306a36Sopenharmony_ci edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3); 179862306a36Sopenharmony_ci edge_port->maxTxCredits = edge_port->txCredits; 179962306a36Sopenharmony_ci dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n", 180062306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte2, edge_port->txCredits); 180162306a36Sopenharmony_ci handle_new_msr(edge_port, byte2); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci /* send the current line settings to the port so we are 180462306a36Sopenharmony_ci in sync with any further termios calls */ 180562306a36Sopenharmony_ci tty = tty_port_tty_get(&edge_port->port->port); 180662306a36Sopenharmony_ci if (tty) { 180762306a36Sopenharmony_ci change_port_settings(tty, 180862306a36Sopenharmony_ci edge_port, &tty->termios); 180962306a36Sopenharmony_ci tty_kref_put(tty); 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* we have completed the open */ 181362306a36Sopenharmony_ci edge_port->openPending = false; 181462306a36Sopenharmony_ci edge_port->open = true; 181562306a36Sopenharmony_ci wake_up(&edge_port->wait_open); 181662306a36Sopenharmony_ci return; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* If port is closed, silently discard all rcvd status. We can 182062306a36Sopenharmony_ci * have cases where buffered status is received AFTER the close 182162306a36Sopenharmony_ci * port command is sent to the Edgeport. 182262306a36Sopenharmony_ci */ 182362306a36Sopenharmony_ci if (!edge_port->open || edge_port->closePending) 182462306a36Sopenharmony_ci return; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci switch (code) { 182762306a36Sopenharmony_ci /* Not currently sent by Edgeport */ 182862306a36Sopenharmony_ci case IOSP_STATUS_LSR: 182962306a36Sopenharmony_ci dev_dbg(dev, "%s - Port %u LSR Status = %02x\n", 183062306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte2); 183162306a36Sopenharmony_ci handle_new_lsr(edge_port, false, byte2, 0); 183262306a36Sopenharmony_ci break; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci case IOSP_STATUS_LSR_DATA: 183562306a36Sopenharmony_ci dev_dbg(dev, "%s - Port %u LSR Status = %02x, Data = %02x\n", 183662306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte2, byte3); 183762306a36Sopenharmony_ci /* byte2 is LSR Register */ 183862306a36Sopenharmony_ci /* byte3 is broken data byte */ 183962306a36Sopenharmony_ci handle_new_lsr(edge_port, true, byte2, byte3); 184062306a36Sopenharmony_ci break; 184162306a36Sopenharmony_ci /* 184262306a36Sopenharmony_ci * case IOSP_EXT_4_STATUS: 184362306a36Sopenharmony_ci * dev_dbg(dev, "%s - Port %u LSR Status = %02x Data = %02x\n", 184462306a36Sopenharmony_ci * __func__, edge_serial->rxPort, byte2, byte3); 184562306a36Sopenharmony_ci * break; 184662306a36Sopenharmony_ci */ 184762306a36Sopenharmony_ci case IOSP_STATUS_MSR: 184862306a36Sopenharmony_ci dev_dbg(dev, "%s - Port %u MSR Status = %02x\n", 184962306a36Sopenharmony_ci __func__, edge_serial->rxPort, byte2); 185062306a36Sopenharmony_ci /* 185162306a36Sopenharmony_ci * Process this new modem status and generate appropriate 185262306a36Sopenharmony_ci * events, etc, based on the new status. This routine 185362306a36Sopenharmony_ci * also saves the MSR in Port->ShadowMsr. 185462306a36Sopenharmony_ci */ 185562306a36Sopenharmony_ci handle_new_msr(edge_port, byte2); 185662306a36Sopenharmony_ci break; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci default: 185962306a36Sopenharmony_ci dev_dbg(dev, "%s - Unrecognized IOSP status code %u\n", __func__, code); 186062306a36Sopenharmony_ci break; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci/***************************************************************************** 186662306a36Sopenharmony_ci * edge_tty_recv 186762306a36Sopenharmony_ci * this function passes data on to the tty flip buffer 186862306a36Sopenharmony_ci *****************************************************************************/ 186962306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data, 187062306a36Sopenharmony_ci int length) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci int cnt; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci cnt = tty_insert_flip_string(&port->port, data, length); 187562306a36Sopenharmony_ci if (cnt < length) { 187662306a36Sopenharmony_ci dev_err(&port->dev, "%s - dropping data, %d bytes lost\n", 187762306a36Sopenharmony_ci __func__, length - cnt); 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci data += cnt; 188062306a36Sopenharmony_ci length -= cnt; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 188362306a36Sopenharmony_ci} 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci/***************************************************************************** 188762306a36Sopenharmony_ci * handle_new_msr 188862306a36Sopenharmony_ci * this function handles any change to the msr register for a port. 188962306a36Sopenharmony_ci *****************************************************************************/ 189062306a36Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci struct async_icount *icount; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | 189562306a36Sopenharmony_ci EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { 189662306a36Sopenharmony_ci icount = &edge_port->port->icount; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci /* update input line counters */ 189962306a36Sopenharmony_ci if (newMsr & EDGEPORT_MSR_DELTA_CTS) 190062306a36Sopenharmony_ci icount->cts++; 190162306a36Sopenharmony_ci if (newMsr & EDGEPORT_MSR_DELTA_DSR) 190262306a36Sopenharmony_ci icount->dsr++; 190362306a36Sopenharmony_ci if (newMsr & EDGEPORT_MSR_DELTA_CD) 190462306a36Sopenharmony_ci icount->dcd++; 190562306a36Sopenharmony_ci if (newMsr & EDGEPORT_MSR_DELTA_RI) 190662306a36Sopenharmony_ci icount->rng++; 190762306a36Sopenharmony_ci wake_up_interruptible(&edge_port->port->port.delta_msr_wait); 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci /* Save the new modem status */ 191162306a36Sopenharmony_ci edge_port->shadowMSR = newMsr & 0xf0; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci/***************************************************************************** 191662306a36Sopenharmony_ci * handle_new_lsr 191762306a36Sopenharmony_ci * this function handles any change to the lsr register for a port. 191862306a36Sopenharmony_ci *****************************************************************************/ 191962306a36Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, 192062306a36Sopenharmony_ci __u8 lsr, __u8 data) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci __u8 newLsr = (__u8) (lsr & (__u8) 192362306a36Sopenharmony_ci (LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); 192462306a36Sopenharmony_ci struct async_icount *icount; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci edge_port->shadowLSR = lsr; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci if (newLsr & LSR_BREAK) { 192962306a36Sopenharmony_ci /* 193062306a36Sopenharmony_ci * Parity and Framing errors only count if they 193162306a36Sopenharmony_ci * occur exclusive of a break being 193262306a36Sopenharmony_ci * received. 193362306a36Sopenharmony_ci */ 193462306a36Sopenharmony_ci newLsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci /* Place LSR data byte into Rx buffer */ 193862306a36Sopenharmony_ci if (lsrData) 193962306a36Sopenharmony_ci edge_tty_recv(edge_port->port, &data, 1); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci /* update input line counters */ 194262306a36Sopenharmony_ci icount = &edge_port->port->icount; 194362306a36Sopenharmony_ci if (newLsr & LSR_BREAK) 194462306a36Sopenharmony_ci icount->brk++; 194562306a36Sopenharmony_ci if (newLsr & LSR_OVER_ERR) 194662306a36Sopenharmony_ci icount->overrun++; 194762306a36Sopenharmony_ci if (newLsr & LSR_PAR_ERR) 194862306a36Sopenharmony_ci icount->parity++; 194962306a36Sopenharmony_ci if (newLsr & LSR_FRM_ERR) 195062306a36Sopenharmony_ci icount->frame++; 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci/**************************************************************************** 195562306a36Sopenharmony_ci * sram_write 195662306a36Sopenharmony_ci * writes a number of bytes to the Edgeport device's sram starting at the 195762306a36Sopenharmony_ci * given address. 195862306a36Sopenharmony_ci * If successful returns the number of bytes written, otherwise it returns 195962306a36Sopenharmony_ci * a negative error number of the problem. 196062306a36Sopenharmony_ci ****************************************************************************/ 196162306a36Sopenharmony_cistatic int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, 196262306a36Sopenharmony_ci __u16 length, const __u8 *data) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci int result; 196562306a36Sopenharmony_ci __u16 current_length; 196662306a36Sopenharmony_ci unsigned char *transfer_buffer; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci transfer_buffer = kmalloc(64, GFP_KERNEL); 197162306a36Sopenharmony_ci if (!transfer_buffer) 197262306a36Sopenharmony_ci return -ENOMEM; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* need to split these writes up into 64 byte chunks */ 197562306a36Sopenharmony_ci result = 0; 197662306a36Sopenharmony_ci while (length > 0) { 197762306a36Sopenharmony_ci if (length > 64) 197862306a36Sopenharmony_ci current_length = 64; 197962306a36Sopenharmony_ci else 198062306a36Sopenharmony_ci current_length = length; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci/* dev_dbg(&serial->dev->dev, "%s - writing %x, %x, %d\n", __func__, extAddr, addr, current_length); */ 198362306a36Sopenharmony_ci memcpy(transfer_buffer, data, current_length); 198462306a36Sopenharmony_ci result = usb_control_msg(serial->dev, 198562306a36Sopenharmony_ci usb_sndctrlpipe(serial->dev, 0), 198662306a36Sopenharmony_ci USB_REQUEST_ION_WRITE_RAM, 198762306a36Sopenharmony_ci 0x40, addr, extAddr, transfer_buffer, 198862306a36Sopenharmony_ci current_length, 300); 198962306a36Sopenharmony_ci if (result < 0) 199062306a36Sopenharmony_ci break; 199162306a36Sopenharmony_ci length -= current_length; 199262306a36Sopenharmony_ci addr += current_length; 199362306a36Sopenharmony_ci data += current_length; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci kfree(transfer_buffer); 199762306a36Sopenharmony_ci return result; 199862306a36Sopenharmony_ci} 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci/**************************************************************************** 200262306a36Sopenharmony_ci * rom_write 200362306a36Sopenharmony_ci * writes a number of bytes to the Edgeport device's ROM starting at the 200462306a36Sopenharmony_ci * given address. 200562306a36Sopenharmony_ci * If successful returns the number of bytes written, otherwise it returns 200662306a36Sopenharmony_ci * a negative error number of the problem. 200762306a36Sopenharmony_ci ****************************************************************************/ 200862306a36Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, 200962306a36Sopenharmony_ci __u16 length, const __u8 *data) 201062306a36Sopenharmony_ci{ 201162306a36Sopenharmony_ci int result; 201262306a36Sopenharmony_ci __u16 current_length; 201362306a36Sopenharmony_ci unsigned char *transfer_buffer; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci transfer_buffer = kmalloc(64, GFP_KERNEL); 201662306a36Sopenharmony_ci if (!transfer_buffer) 201762306a36Sopenharmony_ci return -ENOMEM; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci /* need to split these writes up into 64 byte chunks */ 202062306a36Sopenharmony_ci result = 0; 202162306a36Sopenharmony_ci while (length > 0) { 202262306a36Sopenharmony_ci if (length > 64) 202362306a36Sopenharmony_ci current_length = 64; 202462306a36Sopenharmony_ci else 202562306a36Sopenharmony_ci current_length = length; 202662306a36Sopenharmony_ci memcpy(transfer_buffer, data, current_length); 202762306a36Sopenharmony_ci result = usb_control_msg(serial->dev, 202862306a36Sopenharmony_ci usb_sndctrlpipe(serial->dev, 0), 202962306a36Sopenharmony_ci USB_REQUEST_ION_WRITE_ROM, 0x40, 203062306a36Sopenharmony_ci addr, extAddr, 203162306a36Sopenharmony_ci transfer_buffer, current_length, 300); 203262306a36Sopenharmony_ci if (result < 0) 203362306a36Sopenharmony_ci break; 203462306a36Sopenharmony_ci length -= current_length; 203562306a36Sopenharmony_ci addr += current_length; 203662306a36Sopenharmony_ci data += current_length; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci kfree(transfer_buffer); 204062306a36Sopenharmony_ci return result; 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci/**************************************************************************** 204562306a36Sopenharmony_ci * rom_read 204662306a36Sopenharmony_ci * reads a number of bytes from the Edgeport device starting at the given 204762306a36Sopenharmony_ci * address. 204862306a36Sopenharmony_ci * Returns zero on success or a negative error number. 204962306a36Sopenharmony_ci ****************************************************************************/ 205062306a36Sopenharmony_cistatic int rom_read(struct usb_serial *serial, __u16 extAddr, 205162306a36Sopenharmony_ci __u16 addr, __u16 length, __u8 *data) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci int result; 205462306a36Sopenharmony_ci __u16 current_length; 205562306a36Sopenharmony_ci unsigned char *transfer_buffer; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci transfer_buffer = kmalloc(64, GFP_KERNEL); 205862306a36Sopenharmony_ci if (!transfer_buffer) 205962306a36Sopenharmony_ci return -ENOMEM; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci /* need to split these reads up into 64 byte chunks */ 206262306a36Sopenharmony_ci result = 0; 206362306a36Sopenharmony_ci while (length > 0) { 206462306a36Sopenharmony_ci if (length > 64) 206562306a36Sopenharmony_ci current_length = 64; 206662306a36Sopenharmony_ci else 206762306a36Sopenharmony_ci current_length = length; 206862306a36Sopenharmony_ci result = usb_control_msg(serial->dev, 206962306a36Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), 207062306a36Sopenharmony_ci USB_REQUEST_ION_READ_ROM, 207162306a36Sopenharmony_ci 0xC0, addr, extAddr, transfer_buffer, 207262306a36Sopenharmony_ci current_length, 300); 207362306a36Sopenharmony_ci if (result < current_length) { 207462306a36Sopenharmony_ci if (result >= 0) 207562306a36Sopenharmony_ci result = -EIO; 207662306a36Sopenharmony_ci break; 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci memcpy(data, transfer_buffer, current_length); 207962306a36Sopenharmony_ci length -= current_length; 208062306a36Sopenharmony_ci addr += current_length; 208162306a36Sopenharmony_ci data += current_length; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci result = 0; 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci kfree(transfer_buffer); 208762306a36Sopenharmony_ci return result; 208862306a36Sopenharmony_ci} 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci/**************************************************************************** 209262306a36Sopenharmony_ci * send_iosp_ext_cmd 209362306a36Sopenharmony_ci * Is used to send a IOSP message to the Edgeport device 209462306a36Sopenharmony_ci ****************************************************************************/ 209562306a36Sopenharmony_cistatic int send_iosp_ext_cmd(struct edgeport_port *edge_port, 209662306a36Sopenharmony_ci __u8 command, __u8 param) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci unsigned char *buffer; 209962306a36Sopenharmony_ci unsigned char *currentCommand; 210062306a36Sopenharmony_ci int length = 0; 210162306a36Sopenharmony_ci int status = 0; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci buffer = kmalloc(10, GFP_ATOMIC); 210462306a36Sopenharmony_ci if (!buffer) 210562306a36Sopenharmony_ci return -ENOMEM; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci currentCommand = buffer; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci MAKE_CMD_EXT_CMD(¤tCommand, &length, edge_port->port->port_number, 211062306a36Sopenharmony_ci command, param); 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci status = write_cmd_usb(edge_port, buffer, length); 211362306a36Sopenharmony_ci if (status) { 211462306a36Sopenharmony_ci /* something bad happened, let's free up the memory */ 211562306a36Sopenharmony_ci kfree(buffer); 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci return status; 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci/***************************************************************************** 212362306a36Sopenharmony_ci * write_cmd_usb 212462306a36Sopenharmony_ci * this function writes the given buffer out to the bulk write endpoint. 212562306a36Sopenharmony_ci *****************************************************************************/ 212662306a36Sopenharmony_cistatic int write_cmd_usb(struct edgeport_port *edge_port, 212762306a36Sopenharmony_ci unsigned char *buffer, int length) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci struct edgeport_serial *edge_serial = 213062306a36Sopenharmony_ci usb_get_serial_data(edge_port->port->serial); 213162306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 213262306a36Sopenharmony_ci int status = 0; 213362306a36Sopenharmony_ci struct urb *urb; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, length, buffer); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci /* Allocate our next urb */ 213862306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 213962306a36Sopenharmony_ci if (!urb) 214062306a36Sopenharmony_ci return -ENOMEM; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci atomic_inc(&CmdUrbs); 214362306a36Sopenharmony_ci dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n", 214462306a36Sopenharmony_ci __func__, urb, atomic_read(&CmdUrbs)); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci usb_fill_bulk_urb(urb, edge_serial->serial->dev, 214762306a36Sopenharmony_ci usb_sndbulkpipe(edge_serial->serial->dev, 214862306a36Sopenharmony_ci edge_serial->bulk_out_endpoint), 214962306a36Sopenharmony_ci buffer, length, edge_bulk_out_cmd_callback, edge_port); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci edge_port->commandPending = true; 215262306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (status) { 215562306a36Sopenharmony_ci /* something went wrong */ 215662306a36Sopenharmony_ci dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n", 215762306a36Sopenharmony_ci __func__, status); 215862306a36Sopenharmony_ci usb_free_urb(urb); 215962306a36Sopenharmony_ci atomic_dec(&CmdUrbs); 216062306a36Sopenharmony_ci return status; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci#if 0 216462306a36Sopenharmony_ci wait_event(&edge_port->wait_command, !edge_port->commandPending); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (edge_port->commandPending) { 216762306a36Sopenharmony_ci /* command timed out */ 216862306a36Sopenharmony_ci dev_dbg(dev, "%s - command timed out\n", __func__); 216962306a36Sopenharmony_ci status = -EINVAL; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci#endif 217262306a36Sopenharmony_ci return status; 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci/***************************************************************************** 217762306a36Sopenharmony_ci * send_cmd_write_baud_rate 217862306a36Sopenharmony_ci * this function sends the proper command to change the baud rate of the 217962306a36Sopenharmony_ci * specified port. 218062306a36Sopenharmony_ci *****************************************************************************/ 218162306a36Sopenharmony_cistatic int send_cmd_write_baud_rate(struct edgeport_port *edge_port, 218262306a36Sopenharmony_ci int baudRate) 218362306a36Sopenharmony_ci{ 218462306a36Sopenharmony_ci struct edgeport_serial *edge_serial = 218562306a36Sopenharmony_ci usb_get_serial_data(edge_port->port->serial); 218662306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 218762306a36Sopenharmony_ci unsigned char *cmdBuffer; 218862306a36Sopenharmony_ci unsigned char *currCmd; 218962306a36Sopenharmony_ci int cmdLen = 0; 219062306a36Sopenharmony_ci int divisor; 219162306a36Sopenharmony_ci int status; 219262306a36Sopenharmony_ci u32 number = edge_port->port->port_number; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci if (edge_serial->is_epic && 219562306a36Sopenharmony_ci !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) { 219662306a36Sopenharmony_ci dev_dbg(dev, "SendCmdWriteBaudRate - NOT Setting baud rate for port, baud = %d\n", 219762306a36Sopenharmony_ci baudRate); 219862306a36Sopenharmony_ci return 0; 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci dev_dbg(dev, "%s - baud = %d\n", __func__, baudRate); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci status = calc_baud_rate_divisor(dev, baudRate, &divisor); 220462306a36Sopenharmony_ci if (status) { 220562306a36Sopenharmony_ci dev_err(dev, "%s - bad baud rate\n", __func__); 220662306a36Sopenharmony_ci return status; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci /* Alloc memory for the string of commands. */ 221062306a36Sopenharmony_ci cmdBuffer = kmalloc(0x100, GFP_ATOMIC); 221162306a36Sopenharmony_ci if (!cmdBuffer) 221262306a36Sopenharmony_ci return -ENOMEM; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci currCmd = cmdBuffer; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci /* Enable access to divisor latch */ 221762306a36Sopenharmony_ci MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* Write the divisor itself */ 222062306a36Sopenharmony_ci MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLL, LOW8(divisor)); 222162306a36Sopenharmony_ci MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLM, HIGH8(divisor)); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* Restore original value to disable access to divisor latch */ 222462306a36Sopenharmony_ci MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, 222562306a36Sopenharmony_ci edge_port->shadowLCR); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci status = write_cmd_usb(edge_port, cmdBuffer, cmdLen); 222862306a36Sopenharmony_ci if (status) { 222962306a36Sopenharmony_ci /* something bad happened, let's free up the memory */ 223062306a36Sopenharmony_ci kfree(cmdBuffer); 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci return status; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci/***************************************************************************** 223862306a36Sopenharmony_ci * calc_baud_rate_divisor 223962306a36Sopenharmony_ci * this function calculates the proper baud rate divisor for the specified 224062306a36Sopenharmony_ci * baud rate. 224162306a36Sopenharmony_ci *****************************************************************************/ 224262306a36Sopenharmony_cistatic int calc_baud_rate_divisor(struct device *dev, int baudrate, int *divisor) 224362306a36Sopenharmony_ci{ 224462306a36Sopenharmony_ci int i; 224562306a36Sopenharmony_ci __u16 custom; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { 224862306a36Sopenharmony_ci if (divisor_table[i].BaudRate == baudrate) { 224962306a36Sopenharmony_ci *divisor = divisor_table[i].Divisor; 225062306a36Sopenharmony_ci return 0; 225162306a36Sopenharmony_ci } 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci /* We have tried all of the standard baud rates 225562306a36Sopenharmony_ci * lets try to calculate the divisor for this baud rate 225662306a36Sopenharmony_ci * Make sure the baud rate is reasonable */ 225762306a36Sopenharmony_ci if (baudrate > 50 && baudrate < 230400) { 225862306a36Sopenharmony_ci /* get divisor */ 225962306a36Sopenharmony_ci custom = (__u16)((230400L + baudrate/2) / baudrate); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci *divisor = custom; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci dev_dbg(dev, "%s - Baud %d = %d\n", __func__, baudrate, custom); 226462306a36Sopenharmony_ci return 0; 226562306a36Sopenharmony_ci } 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci return -1; 226862306a36Sopenharmony_ci} 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci/***************************************************************************** 227262306a36Sopenharmony_ci * send_cmd_write_uart_register 227362306a36Sopenharmony_ci * this function builds up a uart register message and sends to the device. 227462306a36Sopenharmony_ci *****************************************************************************/ 227562306a36Sopenharmony_cistatic int send_cmd_write_uart_register(struct edgeport_port *edge_port, 227662306a36Sopenharmony_ci __u8 regNum, __u8 regValue) 227762306a36Sopenharmony_ci{ 227862306a36Sopenharmony_ci struct edgeport_serial *edge_serial = 227962306a36Sopenharmony_ci usb_get_serial_data(edge_port->port->serial); 228062306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 228162306a36Sopenharmony_ci unsigned char *cmdBuffer; 228262306a36Sopenharmony_ci unsigned char *currCmd; 228362306a36Sopenharmony_ci unsigned long cmdLen = 0; 228462306a36Sopenharmony_ci int status; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci dev_dbg(dev, "%s - write to %s register 0x%02x\n", 228762306a36Sopenharmony_ci (regNum == MCR) ? "MCR" : "LCR", __func__, regValue); 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (edge_serial->is_epic && 229062306a36Sopenharmony_ci !edge_serial->epic_descriptor.Supports.IOSPWriteMCR && 229162306a36Sopenharmony_ci regNum == MCR) { 229262306a36Sopenharmony_ci dev_dbg(dev, "SendCmdWriteUartReg - Not writing to MCR Register\n"); 229362306a36Sopenharmony_ci return 0; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci if (edge_serial->is_epic && 229762306a36Sopenharmony_ci !edge_serial->epic_descriptor.Supports.IOSPWriteLCR && 229862306a36Sopenharmony_ci regNum == LCR) { 229962306a36Sopenharmony_ci dev_dbg(dev, "SendCmdWriteUartReg - Not writing to LCR Register\n"); 230062306a36Sopenharmony_ci return 0; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci /* Alloc memory for the string of commands. */ 230462306a36Sopenharmony_ci cmdBuffer = kmalloc(0x10, GFP_ATOMIC); 230562306a36Sopenharmony_ci if (cmdBuffer == NULL) 230662306a36Sopenharmony_ci return -ENOMEM; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci currCmd = cmdBuffer; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci /* Build a cmd in the buffer to write the given register */ 231162306a36Sopenharmony_ci MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->port_number, 231262306a36Sopenharmony_ci regNum, regValue); 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci status = write_cmd_usb(edge_port, cmdBuffer, cmdLen); 231562306a36Sopenharmony_ci if (status) { 231662306a36Sopenharmony_ci /* something bad happened, let's free up the memory */ 231762306a36Sopenharmony_ci kfree(cmdBuffer); 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci return status; 232162306a36Sopenharmony_ci} 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci/***************************************************************************** 232562306a36Sopenharmony_ci * change_port_settings 232662306a36Sopenharmony_ci * This routine is called to set the UART on the device to match the 232762306a36Sopenharmony_ci * specified new settings. 232862306a36Sopenharmony_ci *****************************************************************************/ 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty, 233162306a36Sopenharmony_ci struct edgeport_port *edge_port, const struct ktermios *old_termios) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 233462306a36Sopenharmony_ci struct edgeport_serial *edge_serial = 233562306a36Sopenharmony_ci usb_get_serial_data(edge_port->port->serial); 233662306a36Sopenharmony_ci int baud; 233762306a36Sopenharmony_ci unsigned cflag; 233862306a36Sopenharmony_ci __u8 mask = 0xff; 233962306a36Sopenharmony_ci __u8 lData; 234062306a36Sopenharmony_ci __u8 lParity; 234162306a36Sopenharmony_ci __u8 lStop; 234262306a36Sopenharmony_ci __u8 rxFlow; 234362306a36Sopenharmony_ci __u8 txFlow; 234462306a36Sopenharmony_ci int status; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci if (!edge_port->open && 234762306a36Sopenharmony_ci !edge_port->openPending) { 234862306a36Sopenharmony_ci dev_dbg(dev, "%s - port not opened\n", __func__); 234962306a36Sopenharmony_ci return; 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci cflag = tty->termios.c_cflag; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci switch (cflag & CSIZE) { 235562306a36Sopenharmony_ci case CS5: 235662306a36Sopenharmony_ci lData = LCR_BITS_5; mask = 0x1f; 235762306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 5\n", __func__); 235862306a36Sopenharmony_ci break; 235962306a36Sopenharmony_ci case CS6: 236062306a36Sopenharmony_ci lData = LCR_BITS_6; mask = 0x3f; 236162306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 6\n", __func__); 236262306a36Sopenharmony_ci break; 236362306a36Sopenharmony_ci case CS7: 236462306a36Sopenharmony_ci lData = LCR_BITS_7; mask = 0x7f; 236562306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 7\n", __func__); 236662306a36Sopenharmony_ci break; 236762306a36Sopenharmony_ci default: 236862306a36Sopenharmony_ci case CS8: 236962306a36Sopenharmony_ci lData = LCR_BITS_8; 237062306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 8\n", __func__); 237162306a36Sopenharmony_ci break; 237262306a36Sopenharmony_ci } 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci lParity = LCR_PAR_NONE; 237562306a36Sopenharmony_ci if (cflag & PARENB) { 237662306a36Sopenharmony_ci if (cflag & CMSPAR) { 237762306a36Sopenharmony_ci if (cflag & PARODD) { 237862306a36Sopenharmony_ci lParity = LCR_PAR_MARK; 237962306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = mark\n", __func__); 238062306a36Sopenharmony_ci } else { 238162306a36Sopenharmony_ci lParity = LCR_PAR_SPACE; 238262306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = space\n", __func__); 238362306a36Sopenharmony_ci } 238462306a36Sopenharmony_ci } else if (cflag & PARODD) { 238562306a36Sopenharmony_ci lParity = LCR_PAR_ODD; 238662306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = odd\n", __func__); 238762306a36Sopenharmony_ci } else { 238862306a36Sopenharmony_ci lParity = LCR_PAR_EVEN; 238962306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = even\n", __func__); 239062306a36Sopenharmony_ci } 239162306a36Sopenharmony_ci } else { 239262306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = none\n", __func__); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci if (cflag & CSTOPB) { 239662306a36Sopenharmony_ci lStop = LCR_STOP_2; 239762306a36Sopenharmony_ci dev_dbg(dev, "%s - stop bits = 2\n", __func__); 239862306a36Sopenharmony_ci } else { 239962306a36Sopenharmony_ci lStop = LCR_STOP_1; 240062306a36Sopenharmony_ci dev_dbg(dev, "%s - stop bits = 1\n", __func__); 240162306a36Sopenharmony_ci } 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci /* figure out the flow control settings */ 240462306a36Sopenharmony_ci rxFlow = txFlow = 0x00; 240562306a36Sopenharmony_ci if (cflag & CRTSCTS) { 240662306a36Sopenharmony_ci rxFlow |= IOSP_RX_FLOW_RTS; 240762306a36Sopenharmony_ci txFlow |= IOSP_TX_FLOW_CTS; 240862306a36Sopenharmony_ci dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__); 240962306a36Sopenharmony_ci } else { 241062306a36Sopenharmony_ci dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__); 241162306a36Sopenharmony_ci } 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci /* if we are implementing XON/XOFF, set the start and stop character 241462306a36Sopenharmony_ci in the device */ 241562306a36Sopenharmony_ci if (I_IXOFF(tty) || I_IXON(tty)) { 241662306a36Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 241762306a36Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci if (!edge_serial->is_epic || 242062306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPSetXChar) { 242162306a36Sopenharmony_ci send_iosp_ext_cmd(edge_port, 242262306a36Sopenharmony_ci IOSP_CMD_SET_XON_CHAR, start_char); 242362306a36Sopenharmony_ci send_iosp_ext_cmd(edge_port, 242462306a36Sopenharmony_ci IOSP_CMD_SET_XOFF_CHAR, stop_char); 242562306a36Sopenharmony_ci } 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci /* if we are implementing INBOUND XON/XOFF */ 242862306a36Sopenharmony_ci if (I_IXOFF(tty)) { 242962306a36Sopenharmony_ci rxFlow |= IOSP_RX_FLOW_XON_XOFF; 243062306a36Sopenharmony_ci dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n", 243162306a36Sopenharmony_ci __func__, start_char, stop_char); 243262306a36Sopenharmony_ci } else { 243362306a36Sopenharmony_ci dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__); 243462306a36Sopenharmony_ci } 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci /* if we are implementing OUTBOUND XON/XOFF */ 243762306a36Sopenharmony_ci if (I_IXON(tty)) { 243862306a36Sopenharmony_ci txFlow |= IOSP_TX_FLOW_XON_XOFF; 243962306a36Sopenharmony_ci dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n", 244062306a36Sopenharmony_ci __func__, start_char, stop_char); 244162306a36Sopenharmony_ci } else { 244262306a36Sopenharmony_ci dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__); 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci } 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci /* Set flow control to the configured value */ 244762306a36Sopenharmony_ci if (!edge_serial->is_epic || 244862306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPSetRxFlow) 244962306a36Sopenharmony_ci send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow); 245062306a36Sopenharmony_ci if (!edge_serial->is_epic || 245162306a36Sopenharmony_ci edge_serial->epic_descriptor.Supports.IOSPSetTxFlow) 245262306a36Sopenharmony_ci send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow); 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); 245662306a36Sopenharmony_ci edge_port->shadowLCR |= (lData | lParity | lStop); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci edge_port->validDataMask = mask; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci /* Send the updated LCR value to the EdgePort */ 246162306a36Sopenharmony_ci status = send_cmd_write_uart_register(edge_port, LCR, 246262306a36Sopenharmony_ci edge_port->shadowLCR); 246362306a36Sopenharmony_ci if (status != 0) 246462306a36Sopenharmony_ci return; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci /* set up the MCR register and send it to the EdgePort */ 246762306a36Sopenharmony_ci edge_port->shadowMCR = MCR_MASTER_IE; 246862306a36Sopenharmony_ci if (cflag & CBAUD) 246962306a36Sopenharmony_ci edge_port->shadowMCR |= (MCR_DTR | MCR_RTS); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci status = send_cmd_write_uart_register(edge_port, MCR, 247262306a36Sopenharmony_ci edge_port->shadowMCR); 247362306a36Sopenharmony_ci if (status != 0) 247462306a36Sopenharmony_ci return; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci /* Determine divisor based on baud rate */ 247762306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 247862306a36Sopenharmony_ci if (!baud) { 247962306a36Sopenharmony_ci /* pick a default, any default... */ 248062306a36Sopenharmony_ci baud = 9600; 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud); 248462306a36Sopenharmony_ci status = send_cmd_write_baud_rate(edge_port, baud); 248562306a36Sopenharmony_ci if (status == -1) { 248662306a36Sopenharmony_ci /* Speed change was not possible - put back the old speed */ 248762306a36Sopenharmony_ci baud = tty_termios_baud_rate(old_termios); 248862306a36Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 248962306a36Sopenharmony_ci } 249062306a36Sopenharmony_ci} 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci/**************************************************************************** 249462306a36Sopenharmony_ci * unicode_to_ascii 249562306a36Sopenharmony_ci * Turns a string from Unicode into ASCII. 249662306a36Sopenharmony_ci * Doesn't do a good job with any characters that are outside the normal 249762306a36Sopenharmony_ci * ASCII range, but it's only for debugging... 249862306a36Sopenharmony_ci * NOTE: expects the unicode in LE format 249962306a36Sopenharmony_ci ****************************************************************************/ 250062306a36Sopenharmony_cistatic void unicode_to_ascii(char *string, int buflen, 250162306a36Sopenharmony_ci __le16 *unicode, int unicode_size) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci int i; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci if (buflen <= 0) /* never happens, but... */ 250662306a36Sopenharmony_ci return; 250762306a36Sopenharmony_ci --buflen; /* space for nul */ 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci for (i = 0; i < unicode_size; i++) { 251062306a36Sopenharmony_ci if (i >= buflen) 251162306a36Sopenharmony_ci break; 251262306a36Sopenharmony_ci string[i] = (char)(le16_to_cpu(unicode[i])); 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci string[i] = 0x00; 251562306a36Sopenharmony_ci} 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci/**************************************************************************** 251962306a36Sopenharmony_ci * get_manufacturing_desc 252062306a36Sopenharmony_ci * reads in the manufacturing descriptor and stores it into the serial 252162306a36Sopenharmony_ci * structure. 252262306a36Sopenharmony_ci ****************************************************************************/ 252362306a36Sopenharmony_cistatic void get_manufacturing_desc(struct edgeport_serial *edge_serial) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci struct device *dev = &edge_serial->serial->dev->dev; 252662306a36Sopenharmony_ci int response; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci dev_dbg(dev, "getting manufacturer descriptor\n"); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci response = rom_read(edge_serial->serial, 253162306a36Sopenharmony_ci (EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16, 253262306a36Sopenharmony_ci (__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff), 253362306a36Sopenharmony_ci EDGE_MANUF_DESC_LEN, 253462306a36Sopenharmony_ci (__u8 *)(&edge_serial->manuf_descriptor)); 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci if (response < 0) { 253762306a36Sopenharmony_ci dev_err(dev, "error in getting manufacturer descriptor: %d\n", 253862306a36Sopenharmony_ci response); 253962306a36Sopenharmony_ci } else { 254062306a36Sopenharmony_ci char string[30]; 254162306a36Sopenharmony_ci dev_dbg(dev, "**Manufacturer Descriptor\n"); 254262306a36Sopenharmony_ci dev_dbg(dev, " RomSize: %dK\n", 254362306a36Sopenharmony_ci edge_serial->manuf_descriptor.RomSize); 254462306a36Sopenharmony_ci dev_dbg(dev, " RamSize: %dK\n", 254562306a36Sopenharmony_ci edge_serial->manuf_descriptor.RamSize); 254662306a36Sopenharmony_ci dev_dbg(dev, " CpuRev: %d\n", 254762306a36Sopenharmony_ci edge_serial->manuf_descriptor.CpuRev); 254862306a36Sopenharmony_ci dev_dbg(dev, " BoardRev: %d\n", 254962306a36Sopenharmony_ci edge_serial->manuf_descriptor.BoardRev); 255062306a36Sopenharmony_ci dev_dbg(dev, " NumPorts: %d\n", 255162306a36Sopenharmony_ci edge_serial->manuf_descriptor.NumPorts); 255262306a36Sopenharmony_ci dev_dbg(dev, " DescDate: %d/%d/%d\n", 255362306a36Sopenharmony_ci edge_serial->manuf_descriptor.DescDate[0], 255462306a36Sopenharmony_ci edge_serial->manuf_descriptor.DescDate[1], 255562306a36Sopenharmony_ci edge_serial->manuf_descriptor.DescDate[2]+1900); 255662306a36Sopenharmony_ci unicode_to_ascii(string, sizeof(string), 255762306a36Sopenharmony_ci edge_serial->manuf_descriptor.SerialNumber, 255862306a36Sopenharmony_ci edge_serial->manuf_descriptor.SerNumLength/2); 255962306a36Sopenharmony_ci dev_dbg(dev, " SerialNumber: %s\n", string); 256062306a36Sopenharmony_ci unicode_to_ascii(string, sizeof(string), 256162306a36Sopenharmony_ci edge_serial->manuf_descriptor.AssemblyNumber, 256262306a36Sopenharmony_ci edge_serial->manuf_descriptor.AssemblyNumLength/2); 256362306a36Sopenharmony_ci dev_dbg(dev, " AssemblyNumber: %s\n", string); 256462306a36Sopenharmony_ci unicode_to_ascii(string, sizeof(string), 256562306a36Sopenharmony_ci edge_serial->manuf_descriptor.OemAssyNumber, 256662306a36Sopenharmony_ci edge_serial->manuf_descriptor.OemAssyNumLength/2); 256762306a36Sopenharmony_ci dev_dbg(dev, " OemAssyNumber: %s\n", string); 256862306a36Sopenharmony_ci dev_dbg(dev, " UartType: %d\n", 256962306a36Sopenharmony_ci edge_serial->manuf_descriptor.UartType); 257062306a36Sopenharmony_ci dev_dbg(dev, " IonPid: %d\n", 257162306a36Sopenharmony_ci edge_serial->manuf_descriptor.IonPid); 257262306a36Sopenharmony_ci dev_dbg(dev, " IonConfig: %d\n", 257362306a36Sopenharmony_ci edge_serial->manuf_descriptor.IonConfig); 257462306a36Sopenharmony_ci } 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci/**************************************************************************** 257962306a36Sopenharmony_ci * get_boot_desc 258062306a36Sopenharmony_ci * reads in the bootloader descriptor and stores it into the serial 258162306a36Sopenharmony_ci * structure. 258262306a36Sopenharmony_ci ****************************************************************************/ 258362306a36Sopenharmony_cistatic void get_boot_desc(struct edgeport_serial *edge_serial) 258462306a36Sopenharmony_ci{ 258562306a36Sopenharmony_ci struct device *dev = &edge_serial->serial->dev->dev; 258662306a36Sopenharmony_ci int response; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci dev_dbg(dev, "getting boot descriptor\n"); 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci response = rom_read(edge_serial->serial, 259162306a36Sopenharmony_ci (EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16, 259262306a36Sopenharmony_ci (__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff), 259362306a36Sopenharmony_ci EDGE_BOOT_DESC_LEN, 259462306a36Sopenharmony_ci (__u8 *)(&edge_serial->boot_descriptor)); 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci if (response < 0) { 259762306a36Sopenharmony_ci dev_err(dev, "error in getting boot descriptor: %d\n", 259862306a36Sopenharmony_ci response); 259962306a36Sopenharmony_ci } else { 260062306a36Sopenharmony_ci dev_dbg(dev, "**Boot Descriptor:\n"); 260162306a36Sopenharmony_ci dev_dbg(dev, " BootCodeLength: %d\n", 260262306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength)); 260362306a36Sopenharmony_ci dev_dbg(dev, " MajorVersion: %d\n", 260462306a36Sopenharmony_ci edge_serial->boot_descriptor.MajorVersion); 260562306a36Sopenharmony_ci dev_dbg(dev, " MinorVersion: %d\n", 260662306a36Sopenharmony_ci edge_serial->boot_descriptor.MinorVersion); 260762306a36Sopenharmony_ci dev_dbg(dev, " BuildNumber: %d\n", 260862306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.BuildNumber)); 260962306a36Sopenharmony_ci dev_dbg(dev, " Capabilities: 0x%x\n", 261062306a36Sopenharmony_ci le16_to_cpu(edge_serial->boot_descriptor.Capabilities)); 261162306a36Sopenharmony_ci dev_dbg(dev, " UConfig0: %d\n", 261262306a36Sopenharmony_ci edge_serial->boot_descriptor.UConfig0); 261362306a36Sopenharmony_ci dev_dbg(dev, " UConfig1: %d\n", 261462306a36Sopenharmony_ci edge_serial->boot_descriptor.UConfig1); 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci} 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci/**************************************************************************** 262062306a36Sopenharmony_ci * load_application_firmware 262162306a36Sopenharmony_ci * This is called to load the application firmware to the device 262262306a36Sopenharmony_ci ****************************************************************************/ 262362306a36Sopenharmony_cistatic void load_application_firmware(struct edgeport_serial *edge_serial) 262462306a36Sopenharmony_ci{ 262562306a36Sopenharmony_ci struct device *dev = &edge_serial->serial->dev->dev; 262662306a36Sopenharmony_ci const struct ihex_binrec *rec; 262762306a36Sopenharmony_ci const struct firmware *fw; 262862306a36Sopenharmony_ci const char *fw_name; 262962306a36Sopenharmony_ci const char *fw_info; 263062306a36Sopenharmony_ci int response; 263162306a36Sopenharmony_ci __u32 Operaddr; 263262306a36Sopenharmony_ci __u16 build; 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci switch (edge_serial->product_info.iDownloadFile) { 263562306a36Sopenharmony_ci case EDGE_DOWNLOAD_FILE_I930: 263662306a36Sopenharmony_ci fw_info = "downloading firmware version (930)"; 263762306a36Sopenharmony_ci fw_name = "edgeport/down.fw"; 263862306a36Sopenharmony_ci break; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci case EDGE_DOWNLOAD_FILE_80251: 264162306a36Sopenharmony_ci fw_info = "downloading firmware version (80251)"; 264262306a36Sopenharmony_ci fw_name = "edgeport/down2.fw"; 264362306a36Sopenharmony_ci break; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci case EDGE_DOWNLOAD_FILE_NONE: 264662306a36Sopenharmony_ci dev_dbg(dev, "No download file specified, skipping download\n"); 264762306a36Sopenharmony_ci return; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci default: 265062306a36Sopenharmony_ci return; 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci response = request_ihex_firmware(&fw, fw_name, 265462306a36Sopenharmony_ci &edge_serial->serial->dev->dev); 265562306a36Sopenharmony_ci if (response) { 265662306a36Sopenharmony_ci dev_err(dev, "Failed to load image \"%s\" err %d\n", 265762306a36Sopenharmony_ci fw_name, response); 265862306a36Sopenharmony_ci return; 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci rec = (const struct ihex_binrec *)fw->data; 266262306a36Sopenharmony_ci build = (rec->data[2] << 8) | rec->data[3]; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci dev_dbg(dev, "%s %d.%d.%d\n", fw_info, rec->data[0], rec->data[1], build); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci edge_serial->product_info.FirmwareMajorVersion = rec->data[0]; 266762306a36Sopenharmony_ci edge_serial->product_info.FirmwareMinorVersion = rec->data[1]; 266862306a36Sopenharmony_ci edge_serial->product_info.FirmwareBuildNumber = cpu_to_le16(build); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci for (rec = ihex_next_binrec(rec); rec; 267162306a36Sopenharmony_ci rec = ihex_next_binrec(rec)) { 267262306a36Sopenharmony_ci Operaddr = be32_to_cpu(rec->addr); 267362306a36Sopenharmony_ci response = sram_write(edge_serial->serial, 267462306a36Sopenharmony_ci Operaddr >> 16, 267562306a36Sopenharmony_ci Operaddr & 0xFFFF, 267662306a36Sopenharmony_ci be16_to_cpu(rec->len), 267762306a36Sopenharmony_ci &rec->data[0]); 267862306a36Sopenharmony_ci if (response < 0) { 267962306a36Sopenharmony_ci dev_err(&edge_serial->serial->dev->dev, 268062306a36Sopenharmony_ci "sram_write failed (%x, %x, %d)\n", 268162306a36Sopenharmony_ci Operaddr >> 16, Operaddr & 0xFFFF, 268262306a36Sopenharmony_ci be16_to_cpu(rec->len)); 268362306a36Sopenharmony_ci break; 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci } 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci dev_dbg(dev, "sending exec_dl_code\n"); 268862306a36Sopenharmony_ci response = usb_control_msg (edge_serial->serial->dev, 268962306a36Sopenharmony_ci usb_sndctrlpipe(edge_serial->serial->dev, 0), 269062306a36Sopenharmony_ci USB_REQUEST_ION_EXEC_DL_CODE, 269162306a36Sopenharmony_ci 0x40, 0x4000, 0x0001, NULL, 0, 3000); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci release_firmware(fw); 269462306a36Sopenharmony_ci} 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci/**************************************************************************** 269862306a36Sopenharmony_ci * edge_startup 269962306a36Sopenharmony_ci ****************************************************************************/ 270062306a36Sopenharmony_cistatic int edge_startup(struct usb_serial *serial) 270162306a36Sopenharmony_ci{ 270262306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 270362306a36Sopenharmony_ci struct usb_device *dev; 270462306a36Sopenharmony_ci struct device *ddev = &serial->dev->dev; 270562306a36Sopenharmony_ci int i; 270662306a36Sopenharmony_ci int response; 270762306a36Sopenharmony_ci bool interrupt_in_found; 270862306a36Sopenharmony_ci bool bulk_in_found; 270962306a36Sopenharmony_ci bool bulk_out_found; 271062306a36Sopenharmony_ci static const __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0, 271162306a36Sopenharmony_ci EDGE_COMPATIBILITY_MASK1, 271262306a36Sopenharmony_ci EDGE_COMPATIBILITY_MASK2 }; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci dev = serial->dev; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci /* create our private serial structure */ 271762306a36Sopenharmony_ci edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); 271862306a36Sopenharmony_ci if (!edge_serial) 271962306a36Sopenharmony_ci return -ENOMEM; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci spin_lock_init(&edge_serial->es_lock); 272262306a36Sopenharmony_ci edge_serial->serial = serial; 272362306a36Sopenharmony_ci usb_set_serial_data(serial, edge_serial); 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci /* get the name for the device from the device */ 272662306a36Sopenharmony_ci i = usb_string(dev, dev->descriptor.iManufacturer, 272762306a36Sopenharmony_ci &edge_serial->name[0], MAX_NAME_LEN+1); 272862306a36Sopenharmony_ci if (i < 0) 272962306a36Sopenharmony_ci i = 0; 273062306a36Sopenharmony_ci edge_serial->name[i++] = ' '; 273162306a36Sopenharmony_ci usb_string(dev, dev->descriptor.iProduct, 273262306a36Sopenharmony_ci &edge_serial->name[i], MAX_NAME_LEN+2 - i); 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci /* Read the epic descriptor */ 273762306a36Sopenharmony_ci if (get_epic_descriptor(edge_serial) < 0) { 273862306a36Sopenharmony_ci /* memcpy descriptor to Supports structures */ 273962306a36Sopenharmony_ci memcpy(&edge_serial->epic_descriptor.Supports, descriptor, 274062306a36Sopenharmony_ci sizeof(struct edge_compatibility_bits)); 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci /* get the manufacturing descriptor for this device */ 274362306a36Sopenharmony_ci get_manufacturing_desc(edge_serial); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* get the boot descriptor */ 274662306a36Sopenharmony_ci get_boot_desc(edge_serial); 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci get_product_info(edge_serial); 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci /* set the number of ports from the manufacturing description */ 275262306a36Sopenharmony_ci /* serial->num_ports = serial->product_info.NumPorts; */ 275362306a36Sopenharmony_ci if ((!edge_serial->is_epic) && 275462306a36Sopenharmony_ci (edge_serial->product_info.NumPorts != serial->num_ports)) { 275562306a36Sopenharmony_ci dev_warn(ddev, 275662306a36Sopenharmony_ci "Device Reported %d serial ports vs. core thinking we have %d ports, email greg@kroah.com this information.\n", 275762306a36Sopenharmony_ci edge_serial->product_info.NumPorts, 275862306a36Sopenharmony_ci serial->num_ports); 275962306a36Sopenharmony_ci } 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci dev_dbg(ddev, "%s - time 1 %ld\n", __func__, jiffies); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci /* If not an EPiC device */ 276462306a36Sopenharmony_ci if (!edge_serial->is_epic) { 276562306a36Sopenharmony_ci /* now load the application firmware into this device */ 276662306a36Sopenharmony_ci load_application_firmware(edge_serial); 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci dev_dbg(ddev, "%s - time 2 %ld\n", __func__, jiffies); 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci /* Check current Edgeport EEPROM and update if necessary */ 277162306a36Sopenharmony_ci update_edgeport_E2PROM(edge_serial); 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci dev_dbg(ddev, "%s - time 3 %ld\n", __func__, jiffies); 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci /* set the configuration to use #1 */ 277662306a36Sopenharmony_ci/* dev_dbg(ddev, "set_configuration 1\n"); */ 277762306a36Sopenharmony_ci/* usb_set_configuration (dev, 1); */ 277862306a36Sopenharmony_ci } 277962306a36Sopenharmony_ci dev_dbg(ddev, " FirmwareMajorVersion %d.%d.%d\n", 278062306a36Sopenharmony_ci edge_serial->product_info.FirmwareMajorVersion, 278162306a36Sopenharmony_ci edge_serial->product_info.FirmwareMinorVersion, 278262306a36Sopenharmony_ci le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber)); 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci /* we set up the pointers to the endpoints in the edge_open function, 278562306a36Sopenharmony_ci * as the structures aren't created yet. */ 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci response = 0; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci if (edge_serial->is_epic) { 279062306a36Sopenharmony_ci struct usb_host_interface *alt; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci alt = serial->interface->cur_altsetting; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci /* EPIC thing, set up our interrupt polling now and our read 279562306a36Sopenharmony_ci * urb, so that the device knows it really is connected. */ 279662306a36Sopenharmony_ci interrupt_in_found = bulk_in_found = bulk_out_found = false; 279762306a36Sopenharmony_ci for (i = 0; i < alt->desc.bNumEndpoints; ++i) { 279862306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 279962306a36Sopenharmony_ci int buffer_size; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci endpoint = &alt->endpoint[i].desc; 280262306a36Sopenharmony_ci buffer_size = usb_endpoint_maxp(endpoint); 280362306a36Sopenharmony_ci if (!interrupt_in_found && 280462306a36Sopenharmony_ci (usb_endpoint_is_int_in(endpoint))) { 280562306a36Sopenharmony_ci /* we found a interrupt in endpoint */ 280662306a36Sopenharmony_ci dev_dbg(ddev, "found interrupt in\n"); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci /* not set up yet, so do it now */ 280962306a36Sopenharmony_ci edge_serial->interrupt_read_urb = 281062306a36Sopenharmony_ci usb_alloc_urb(0, GFP_KERNEL); 281162306a36Sopenharmony_ci if (!edge_serial->interrupt_read_urb) { 281262306a36Sopenharmony_ci response = -ENOMEM; 281362306a36Sopenharmony_ci break; 281462306a36Sopenharmony_ci } 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci edge_serial->interrupt_in_buffer = 281762306a36Sopenharmony_ci kmalloc(buffer_size, GFP_KERNEL); 281862306a36Sopenharmony_ci if (!edge_serial->interrupt_in_buffer) { 281962306a36Sopenharmony_ci response = -ENOMEM; 282062306a36Sopenharmony_ci break; 282162306a36Sopenharmony_ci } 282262306a36Sopenharmony_ci edge_serial->interrupt_in_endpoint = 282362306a36Sopenharmony_ci endpoint->bEndpointAddress; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci /* set up our interrupt urb */ 282662306a36Sopenharmony_ci usb_fill_int_urb( 282762306a36Sopenharmony_ci edge_serial->interrupt_read_urb, 282862306a36Sopenharmony_ci dev, 282962306a36Sopenharmony_ci usb_rcvintpipe(dev, 283062306a36Sopenharmony_ci endpoint->bEndpointAddress), 283162306a36Sopenharmony_ci edge_serial->interrupt_in_buffer, 283262306a36Sopenharmony_ci buffer_size, 283362306a36Sopenharmony_ci edge_interrupt_callback, 283462306a36Sopenharmony_ci edge_serial, 283562306a36Sopenharmony_ci endpoint->bInterval); 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci interrupt_in_found = true; 283862306a36Sopenharmony_ci } 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci if (!bulk_in_found && 284162306a36Sopenharmony_ci (usb_endpoint_is_bulk_in(endpoint))) { 284262306a36Sopenharmony_ci /* we found a bulk in endpoint */ 284362306a36Sopenharmony_ci dev_dbg(ddev, "found bulk in\n"); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci /* not set up yet, so do it now */ 284662306a36Sopenharmony_ci edge_serial->read_urb = 284762306a36Sopenharmony_ci usb_alloc_urb(0, GFP_KERNEL); 284862306a36Sopenharmony_ci if (!edge_serial->read_urb) { 284962306a36Sopenharmony_ci response = -ENOMEM; 285062306a36Sopenharmony_ci break; 285162306a36Sopenharmony_ci } 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci edge_serial->bulk_in_buffer = 285462306a36Sopenharmony_ci kmalloc(buffer_size, GFP_KERNEL); 285562306a36Sopenharmony_ci if (!edge_serial->bulk_in_buffer) { 285662306a36Sopenharmony_ci response = -ENOMEM; 285762306a36Sopenharmony_ci break; 285862306a36Sopenharmony_ci } 285962306a36Sopenharmony_ci edge_serial->bulk_in_endpoint = 286062306a36Sopenharmony_ci endpoint->bEndpointAddress; 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci /* set up our bulk in urb */ 286362306a36Sopenharmony_ci usb_fill_bulk_urb(edge_serial->read_urb, dev, 286462306a36Sopenharmony_ci usb_rcvbulkpipe(dev, 286562306a36Sopenharmony_ci endpoint->bEndpointAddress), 286662306a36Sopenharmony_ci edge_serial->bulk_in_buffer, 286762306a36Sopenharmony_ci usb_endpoint_maxp(endpoint), 286862306a36Sopenharmony_ci edge_bulk_in_callback, 286962306a36Sopenharmony_ci edge_serial); 287062306a36Sopenharmony_ci bulk_in_found = true; 287162306a36Sopenharmony_ci } 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci if (!bulk_out_found && 287462306a36Sopenharmony_ci (usb_endpoint_is_bulk_out(endpoint))) { 287562306a36Sopenharmony_ci /* we found a bulk out endpoint */ 287662306a36Sopenharmony_ci dev_dbg(ddev, "found bulk out\n"); 287762306a36Sopenharmony_ci edge_serial->bulk_out_endpoint = 287862306a36Sopenharmony_ci endpoint->bEndpointAddress; 287962306a36Sopenharmony_ci bulk_out_found = true; 288062306a36Sopenharmony_ci } 288162306a36Sopenharmony_ci } 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci if (response || !interrupt_in_found || !bulk_in_found || 288462306a36Sopenharmony_ci !bulk_out_found) { 288562306a36Sopenharmony_ci if (!response) { 288662306a36Sopenharmony_ci dev_err(ddev, "expected endpoints not found\n"); 288762306a36Sopenharmony_ci response = -ENODEV; 288862306a36Sopenharmony_ci } 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci goto error; 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci /* start interrupt read for this edgeport this interrupt will 289462306a36Sopenharmony_ci * continue as long as the edgeport is connected */ 289562306a36Sopenharmony_ci response = usb_submit_urb(edge_serial->interrupt_read_urb, 289662306a36Sopenharmony_ci GFP_KERNEL); 289762306a36Sopenharmony_ci if (response) { 289862306a36Sopenharmony_ci dev_err(ddev, "%s - Error %d submitting control urb\n", 289962306a36Sopenharmony_ci __func__, response); 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci goto error; 290262306a36Sopenharmony_ci } 290362306a36Sopenharmony_ci } 290462306a36Sopenharmony_ci return response; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_cierror: 290762306a36Sopenharmony_ci usb_free_urb(edge_serial->interrupt_read_urb); 290862306a36Sopenharmony_ci kfree(edge_serial->interrupt_in_buffer); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci usb_free_urb(edge_serial->read_urb); 291162306a36Sopenharmony_ci kfree(edge_serial->bulk_in_buffer); 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci kfree(edge_serial); 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci return response; 291662306a36Sopenharmony_ci} 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci/**************************************************************************** 292062306a36Sopenharmony_ci * edge_disconnect 292162306a36Sopenharmony_ci * This function is called whenever the device is removed from the usb bus. 292262306a36Sopenharmony_ci ****************************************************************************/ 292362306a36Sopenharmony_cistatic void edge_disconnect(struct usb_serial *serial) 292462306a36Sopenharmony_ci{ 292562306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (edge_serial->is_epic) { 292862306a36Sopenharmony_ci usb_kill_urb(edge_serial->interrupt_read_urb); 292962306a36Sopenharmony_ci usb_kill_urb(edge_serial->read_urb); 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci} 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci/**************************************************************************** 293562306a36Sopenharmony_ci * edge_release 293662306a36Sopenharmony_ci * This function is called when the device structure is deallocated. 293762306a36Sopenharmony_ci ****************************************************************************/ 293862306a36Sopenharmony_cistatic void edge_release(struct usb_serial *serial) 293962306a36Sopenharmony_ci{ 294062306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci if (edge_serial->is_epic) { 294362306a36Sopenharmony_ci usb_kill_urb(edge_serial->interrupt_read_urb); 294462306a36Sopenharmony_ci usb_free_urb(edge_serial->interrupt_read_urb); 294562306a36Sopenharmony_ci kfree(edge_serial->interrupt_in_buffer); 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci usb_kill_urb(edge_serial->read_urb); 294862306a36Sopenharmony_ci usb_free_urb(edge_serial->read_urb); 294962306a36Sopenharmony_ci kfree(edge_serial->bulk_in_buffer); 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci kfree(edge_serial); 295362306a36Sopenharmony_ci} 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_cistatic int edge_port_probe(struct usb_serial_port *port) 295662306a36Sopenharmony_ci{ 295762306a36Sopenharmony_ci struct edgeport_port *edge_port; 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); 296062306a36Sopenharmony_ci if (!edge_port) 296162306a36Sopenharmony_ci return -ENOMEM; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci spin_lock_init(&edge_port->ep_lock); 296462306a36Sopenharmony_ci edge_port->port = port; 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci usb_set_serial_port_data(port, edge_port); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci return 0; 296962306a36Sopenharmony_ci} 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_cistatic void edge_port_remove(struct usb_serial_port *port) 297262306a36Sopenharmony_ci{ 297362306a36Sopenharmony_ci struct edgeport_port *edge_port; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 297662306a36Sopenharmony_ci kfree(edge_port); 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_2port_device = { 298062306a36Sopenharmony_ci .driver = { 298162306a36Sopenharmony_ci .owner = THIS_MODULE, 298262306a36Sopenharmony_ci .name = "edgeport_2", 298362306a36Sopenharmony_ci }, 298462306a36Sopenharmony_ci .description = "Edgeport 2 port adapter", 298562306a36Sopenharmony_ci .id_table = edgeport_2port_id_table, 298662306a36Sopenharmony_ci .num_ports = 2, 298762306a36Sopenharmony_ci .num_bulk_in = 1, 298862306a36Sopenharmony_ci .num_bulk_out = 1, 298962306a36Sopenharmony_ci .num_interrupt_in = 1, 299062306a36Sopenharmony_ci .open = edge_open, 299162306a36Sopenharmony_ci .close = edge_close, 299262306a36Sopenharmony_ci .throttle = edge_throttle, 299362306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 299462306a36Sopenharmony_ci .attach = edge_startup, 299562306a36Sopenharmony_ci .disconnect = edge_disconnect, 299662306a36Sopenharmony_ci .release = edge_release, 299762306a36Sopenharmony_ci .port_probe = edge_port_probe, 299862306a36Sopenharmony_ci .port_remove = edge_port_remove, 299962306a36Sopenharmony_ci .ioctl = edge_ioctl, 300062306a36Sopenharmony_ci .set_termios = edge_set_termios, 300162306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 300262306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 300362306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 300462306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 300562306a36Sopenharmony_ci .write = edge_write, 300662306a36Sopenharmony_ci .write_room = edge_write_room, 300762306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 300862306a36Sopenharmony_ci .break_ctl = edge_break, 300962306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 301062306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 301162306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_data_callback, 301262306a36Sopenharmony_ci}; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_4port_device = { 301562306a36Sopenharmony_ci .driver = { 301662306a36Sopenharmony_ci .owner = THIS_MODULE, 301762306a36Sopenharmony_ci .name = "edgeport_4", 301862306a36Sopenharmony_ci }, 301962306a36Sopenharmony_ci .description = "Edgeport 4 port adapter", 302062306a36Sopenharmony_ci .id_table = edgeport_4port_id_table, 302162306a36Sopenharmony_ci .num_ports = 4, 302262306a36Sopenharmony_ci .num_bulk_in = 1, 302362306a36Sopenharmony_ci .num_bulk_out = 1, 302462306a36Sopenharmony_ci .num_interrupt_in = 1, 302562306a36Sopenharmony_ci .open = edge_open, 302662306a36Sopenharmony_ci .close = edge_close, 302762306a36Sopenharmony_ci .throttle = edge_throttle, 302862306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 302962306a36Sopenharmony_ci .attach = edge_startup, 303062306a36Sopenharmony_ci .disconnect = edge_disconnect, 303162306a36Sopenharmony_ci .release = edge_release, 303262306a36Sopenharmony_ci .port_probe = edge_port_probe, 303362306a36Sopenharmony_ci .port_remove = edge_port_remove, 303462306a36Sopenharmony_ci .ioctl = edge_ioctl, 303562306a36Sopenharmony_ci .set_termios = edge_set_termios, 303662306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 303762306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 303862306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 303962306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 304062306a36Sopenharmony_ci .write = edge_write, 304162306a36Sopenharmony_ci .write_room = edge_write_room, 304262306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 304362306a36Sopenharmony_ci .break_ctl = edge_break, 304462306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 304562306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 304662306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_data_callback, 304762306a36Sopenharmony_ci}; 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_8port_device = { 305062306a36Sopenharmony_ci .driver = { 305162306a36Sopenharmony_ci .owner = THIS_MODULE, 305262306a36Sopenharmony_ci .name = "edgeport_8", 305362306a36Sopenharmony_ci }, 305462306a36Sopenharmony_ci .description = "Edgeport 8 port adapter", 305562306a36Sopenharmony_ci .id_table = edgeport_8port_id_table, 305662306a36Sopenharmony_ci .num_ports = 8, 305762306a36Sopenharmony_ci .num_bulk_in = 1, 305862306a36Sopenharmony_ci .num_bulk_out = 1, 305962306a36Sopenharmony_ci .num_interrupt_in = 1, 306062306a36Sopenharmony_ci .open = edge_open, 306162306a36Sopenharmony_ci .close = edge_close, 306262306a36Sopenharmony_ci .throttle = edge_throttle, 306362306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 306462306a36Sopenharmony_ci .attach = edge_startup, 306562306a36Sopenharmony_ci .disconnect = edge_disconnect, 306662306a36Sopenharmony_ci .release = edge_release, 306762306a36Sopenharmony_ci .port_probe = edge_port_probe, 306862306a36Sopenharmony_ci .port_remove = edge_port_remove, 306962306a36Sopenharmony_ci .ioctl = edge_ioctl, 307062306a36Sopenharmony_ci .set_termios = edge_set_termios, 307162306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 307262306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 307362306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 307462306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 307562306a36Sopenharmony_ci .write = edge_write, 307662306a36Sopenharmony_ci .write_room = edge_write_room, 307762306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 307862306a36Sopenharmony_ci .break_ctl = edge_break, 307962306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 308062306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 308162306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_data_callback, 308262306a36Sopenharmony_ci}; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_cistatic struct usb_serial_driver epic_device = { 308562306a36Sopenharmony_ci .driver = { 308662306a36Sopenharmony_ci .owner = THIS_MODULE, 308762306a36Sopenharmony_ci .name = "epic", 308862306a36Sopenharmony_ci }, 308962306a36Sopenharmony_ci .description = "EPiC device", 309062306a36Sopenharmony_ci .id_table = Epic_port_id_table, 309162306a36Sopenharmony_ci .num_ports = 1, 309262306a36Sopenharmony_ci .num_bulk_in = 1, 309362306a36Sopenharmony_ci .num_bulk_out = 1, 309462306a36Sopenharmony_ci .num_interrupt_in = 1, 309562306a36Sopenharmony_ci .open = edge_open, 309662306a36Sopenharmony_ci .close = edge_close, 309762306a36Sopenharmony_ci .throttle = edge_throttle, 309862306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 309962306a36Sopenharmony_ci .attach = edge_startup, 310062306a36Sopenharmony_ci .disconnect = edge_disconnect, 310162306a36Sopenharmony_ci .release = edge_release, 310262306a36Sopenharmony_ci .port_probe = edge_port_probe, 310362306a36Sopenharmony_ci .port_remove = edge_port_remove, 310462306a36Sopenharmony_ci .ioctl = edge_ioctl, 310562306a36Sopenharmony_ci .set_termios = edge_set_termios, 310662306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 310762306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 310862306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 310962306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 311062306a36Sopenharmony_ci .write = edge_write, 311162306a36Sopenharmony_ci .write_room = edge_write_room, 311262306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 311362306a36Sopenharmony_ci .break_ctl = edge_break, 311462306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 311562306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 311662306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_data_callback, 311762306a36Sopenharmony_ci}; 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 312062306a36Sopenharmony_ci &edgeport_2port_device, &edgeport_4port_device, 312162306a36Sopenharmony_ci &edgeport_8port_device, &epic_device, NULL 312262306a36Sopenharmony_ci}; 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 312762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 312862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 312962306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot.fw"); 313062306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot2.fw"); 313162306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/down.fw"); 313262306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/down2.fw"); 3133