// SPDX-License-Identifier: GPL-2.0+ /* *USBCypressM8driver * *Copyright(C)2004 *LonnieMendez(dignome@gmail.com) *Copyright(C)2003,2004 *NeilWhelchel(koyama@firstlight.net) * *SeeDocumentation/usb/usb-serial.rstformoreinformationonusingthis *driver * *Seehttp://geocities.com/i0xox0i for information on this driver and the *earthmateusbdevice.
*/
/* Thanks to Neil Whelchel for writing the first cypress m8 implementation
for linux. */ /* Thanks to cypress for providing references for the hid reports. */ /* Thanks to Jiang Zhang for providing links and for general help. */ /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/
struct cypress_private {
spinlock_t lock; /* private lock */ int chiptype; /* identifier of device, for quirks/etc */ int bytes_in; /* used for statistics */ int bytes_out; /* used for statistics */ int cmd_count; /* used for statistics */ int cmd_ctrl; /* always set this to 1 before issuing a command */ struct kfifo write_fifo; /* write fifo */ int write_urb_in_use; /* write urb in use indicator */ int write_urb_interval; /* interval to use for write urb */ int read_urb_interval; /* interval to use for read urb */ int comm_is_ok; /* true if communication is (still) ok */
__u8 line_control; /* holds dtr / rts value */
__u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */
__u8 current_config; /* stores the current configuration byte */
__u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */ enum packet_format pkt_fmt; /* format to use for packet send / receive */ int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */ int baud_rate; /* stores current baud rate in
integer form */ char prev_status; /* used for TIOCMIWAIT */
};
/* *ThegeneralpurposefirmwarefortheCypressM8allowsfor *amaximumspeedof57600bps(IhavenoideawhetherDeLorme *chosetousethegeneralpurposefirmwareornot),ifyou *needtomodifythisspeedsettingforyourownproject *pleaseaddyourownchiptypeandmodifythecodelikewise. *TheCypressHID->COMdevicewillworksuccessfullyupto *115200bps(buttheactualthroughputisaround3kBps).
*/ if (port->serial->dev->speed == USB_SPEED_LOW) { /* *MikeIsely<isely@pobox.com>2-Feb-2008:The *Cypressappnotethatdescribesthismechanism *statesthatthelow-speedpartcan'thandlemore *than800bytes/sec,inwhichcase4800baudisthe *safestspeedforapartlikethat.
*/ if (new_rate > 4800) {
dev_dbg(&port->dev, "%s - failed setting baud rate, device incapable speed %d\n",
__func__, new_rate); return -1;
}
} switch (priv->chiptype) { case CT_EARTHMATE: if (new_rate <= 600) { /* 300 and 600 baud rates are supported under *thegenericfirmware,butarenotusedwith
* NMEA and SiRF protocols */
dev_dbg(&port->dev, "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
__func__, new_rate); return -1;
} break; default: break;
} return new_rate;
}
/* This function can either set or retrieve the current serial line settings */ staticint cypress_serial_control(struct tty_struct *tty, struct usb_serial_port *port, speed_t baud_rate, int data_bits, int stop_bits, int parity_enable, int parity_type, int reset, int cypress_request_type)
{ int new_baudrate = 0, retval = 0, tries = 0; struct cypress_private *priv; struct device *dev = &port->dev;
u8 *feature_buffer; constunsignedint feature_len = 5; unsignedlong flags;
priv = usb_get_serial_port_data(port);
if (!priv->comm_is_ok) return -ENODEV;
feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL); if (!feature_buffer) return -ENOMEM;
switch (cypress_request_type) { case CYPRESS_SET_CONFIG: /* 0 means 'Hang up' so doesn't change the true bit rate */
new_baudrate = priv->baud_rate; if (baud_rate && baud_rate != priv->baud_rate) {
dev_dbg(dev, "%s - baud rate is changing\n", __func__);
retval = analyze_baud_rate(port, baud_rate); if (retval >= 0) {
new_baudrate = retval;
dev_dbg(dev, "%s - New baud rate set to %d\n",
__func__, new_baudrate);
}
}
dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
new_baudrate);
/* fill the feature_buffer with new configuration */
put_unaligned_le32(new_baudrate, feature_buffer);
feature_buffer[4] |= data_bits - 5; /* assign data bits in 2 bit space ( max 3 ) */ /* 1 bit gap */
feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */
feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */
feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ /* 1 bit gap */
feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */
dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
feature_buffer[0], feature_buffer[1],
feature_buffer[2], feature_buffer[3],
feature_buffer[4]);
} while (retval != feature_len &&
retval != -ENODEV);
if (retval != feature_len) {
dev_err(dev, "%s - failed sending serial line settings - %d\n",
__func__, retval);
cypress_set_dead(port);
} else {
spin_lock_irqsave(&priv->lock, flags);
priv->baud_rate = new_baudrate;
priv->current_config = feature_buffer[4];
spin_unlock_irqrestore(&priv->lock, flags); /* If we asked for a speed change encode it */ if (baud_rate)
tty_encode_baud_rate(tty,
new_baudrate, new_baudrate);
} break; case CYPRESS_GET_CONFIG: if (priv->get_cfg_unsafe) { /* Not implemented for this device, andifwetrytodoitwe'relikely
to crash the hardware. */
retval = -ENOTTY; goto out;
}
dev_dbg(dev, "%s - retrieving serial line settings\n", __func__); do {
retval = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
HID_REQ_GET_REPORT,
USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, 0x0300, 0, feature_buffer,
feature_len, 500);
if (tries++ >= 3) break;
} while (retval != feature_len
&& retval != -ENODEV);
if (retval != feature_len) {
dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
__func__, retval);
cypress_set_dead(port); goto out;
} else {
spin_lock_irqsave(&priv->lock, flags); /* store the config in one byte, and later
use bit masks to check values */
priv->current_config = feature_buffer[4];
priv->baud_rate = get_unaligned_le32(feature_buffer);
spin_unlock_irqrestore(&priv->lock, flags);
}
}
spin_lock_irqsave(&priv->lock, flags);
++priv->cmd_count;
spin_unlock_irqrestore(&priv->lock, flags);
out:
kfree(feature_buffer); return retval;
} /* cypress_serial_control */
/* Skip reset for FRWD device. It is a workaound: devicehangsifitreceivesSET_CONFIGUREinConfigured
state. */ if (!is_frwd(serial->dev))
usb_reset_configuration(serial->dev);
priv->cmd_ctrl = 0;
priv->line_control = 0;
priv->rx_flags = 0; /* Default packet format setting is determined by packet size. Anythingwithasizelargerthen9musthaveaseparate countfieldsincethe3bitcountfieldisotherwisetoo small.Otherwisewecanusetheslightlymorecompact format.Thisisinaccordancewiththecypress_m8serial
converter app note. */ if (port->interrupt_out_size > 9)
priv->pkt_fmt = packet_format_1; else
priv->pkt_fmt = packet_format_2;
ret = cypress_generic_port_probe(port); if (ret) {
dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); return ret;
}
priv = usb_get_serial_port_data(port);
priv->chiptype = CT_EARTHMATE; /* All Earthmate devices use the separated-count packet
format! Idiotic. */
priv->pkt_fmt = packet_format_1; if (serial->dev->descriptor.idProduct !=
cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) { /* The old original USB Earthmate seemed able to handleGET_CONFIGrequests;everythingthey've producedsincethattimecrashesifthiscommandis
attempted :-( */
dev_dbg(&port->dev, "%s - Marking this device as unsafe for GET_CONFIG commands\n",
__func__);
priv->get_cfg_unsafe = !0;
}
return0;
}
staticint cypress_hidcom_port_probe(struct usb_serial_port *port)
{ struct cypress_private *priv; int ret;
ret = cypress_generic_port_probe(port); if (ret) {
dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); return ret;
}
/* setup the port and start reading from the device */
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
port->interrupt_in_urb->transfer_buffer_length,
cypress_read_int_callback, port, priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
/* line control commands, which need to be executed immediately, arenotputintothebufferforobviousreasons.
*/ if (priv->cmd_ctrl) {
count = 0; goto finish;
}
spin_lock_irqsave(&priv->lock, flags); switch (priv->pkt_fmt) { default: case packet_format_1: /* this is for the CY7C64013... */
offset = 2;
port->interrupt_out_buffer[0] = priv->line_control; break; case packet_format_2: /* this is for the CY7C63743... */
offset = 1;
port->interrupt_out_buffer[0] = priv->line_control; break;
}
if (priv->line_control & CONTROL_RESET)
priv->line_control &= ~CONTROL_RESET;
if (priv->cmd_ctrl) {
priv->cmd_count++;
dev_dbg(dev, "%s - line control command being issued\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags); goto send;
} else
spin_unlock_irqrestore(&priv->lock, flags);
/* returns how much space is available in the soft buffer */ staticunsignedint cypress_write_room(struct tty_struct *tty)
{ struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsignedint room; unsignedlong flags;
/* we perform a CYPRESS_GET_CONFIG so that the current settings are *filledintotheprivatestructurethisshouldconfirmthatallis
* working if it returns what we just set */
cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
/* Here we can define custom tty settings for devices; the main tty
* termios flag base comes from empeg.c */
spin_lock_irqsave(&priv->lock, flags); if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n"); /* define custom termios settings for NMEA protocol */
spin_lock_irqsave(&priv->lock, flags); /* check to see if status has changed */ if (priv->current_status != priv->prev_status) {
u8 delta = priv->current_status ^ priv->prev_status;
if (delta & UART_MSR_MASK) { if (delta & UART_CTS)
port->icount.cts++; if (delta & UART_DSR)
port->icount.dsr++; if (delta & UART_RI)
port->icount.rng++; if (delta & UART_CD)
port->icount.dcd++;
/* hangup, as defined in acm.c... this might be a bad place for it
* though */ if (tty && !C_CLOCAL(tty) && !(priv->current_status & UART_CD)) {
dev_dbg(dev, "%s - calling hangup\n", __func__);
tty_hangup(tty); goto continue_read;
}
/* There is one error bit... I'm assuming it is a parity error *indicatorasthegenericfirmwarewillsetthisbitto1ifa *parityerroroccurs.
* I can not find reference to any other error events. */
spin_lock_irqsave(&priv->lock, flags); if (priv->current_status & CYP_ERROR) {
spin_unlock_irqrestore(&priv->lock, flags);
tty_flag = TTY_PARITY;
dev_dbg(dev, "%s - Parity Error detected\n", __func__);
} else
spin_unlock_irqrestore(&priv->lock, flags);
/* process read if there is data other than line status */ if (bytes > i) {
tty_insert_flip_string_fixed_flag(&port->port, data + i,
tty_flag, bytes - i);
tty_flip_buffer_push(&port->port);
}
spin_lock_irqsave(&priv->lock, flags); /* control and status byte(s) are also counted */
priv->bytes_in += bytes;
spin_unlock_irqrestore(&priv->lock, flags);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.