#define RQ_VENDOR_RESET_DEVICE 0x23 /* Try to reset the device */ #define RQ_VENDOR_QUERY_FW_CONFIG 0x24
#define RQ_VENDOR_GET_VERSION 0x81 /* Get firmware version */ #define RQ_VENDOR_GET_PAGE 0x82 /* Read flash page */ #define RQ_VENDOR_GET_ROM_PROC 0x83 /* Get ROM process state */
#define RQ_VENDOR_GET_INQUEUE 0x84 /* Data in input buffer */ #define RQ_VENDOR_GET_OUTQUEUE 0x85 /* Data in output buffer */
#define RQ_VENDOR_GET_MSR 0x86 /* Get modem status register */
/* Definitions for UPort event type */ #define UPORT_EVENT_NONE 0 /* None */ #define UPORT_EVENT_TXBUF_THRESHOLD 1 /* Tx buffer threshold */ #define UPORT_EVENT_SEND_NEXT 2 /* Send next */ #define UPORT_EVENT_MSR 3 /* Modem status */ #define UPORT_EVENT_LSR 4 /* Line status */ #define UPORT_EVENT_MCR 5 /* Modem control */
/* Definitions for serial event type */ #define SERIAL_EV_CTS 0x0008 /* CTS changed state */ #define SERIAL_EV_DSR 0x0010 /* DSR changed state */ #define SERIAL_EV_RLSD 0x0020 /* RLSD changed state */
/* Definitions for modem control event type */ #define SERIAL_EV_XOFF 0x40 /* XOFF received */
/* Definitions for line control of communication */ #define MX_WORDLENGTH_5 5 #define MX_WORDLENGTH_6 6 #define MX_WORDLENGTH_7 7 #define MX_WORDLENGTH_8 8
/* This structure holds all of the local port information */ struct mxuport_port {
u8 mcr_state; /* Last MCR state */
u8 msr_state; /* Last MSR state */ struct mutex mutex; /* Protects mcr_state */
spinlock_t spinlock; /* Protects msr_state */
};
/* * Add a four byte header containing the port number and the number of * bytes of data in the message. Return the number of bytes in the * buffer.
*/ staticint mxuport_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size)
{
u8 *buf = dest; int count;
/* Read the given buffer in from the control pipe. */ staticint mxuport_recv_ctrl_urb(struct usb_serial *serial,
u8 request, u16 value, u16 index,
u8 *data, size_t size)
{ int status;
if (status != size) {
dev_err(&serial->interface->dev, "%s - short read (%d / %zd)\n",
__func__, status, size); return -EIO;
}
return status;
}
/* Write the given buffer out to the control pipe. */ staticint mxuport_send_ctrl_data_urb(struct usb_serial *serial,
u8 request,
u16 value, u16 index,
u8 *data, size_t size)
{ int status;
/* Send a vendor request without any data */ staticint mxuport_send_ctrl_urb(struct usb_serial *serial,
u8 request, u16 value, u16 index)
{ return mxuport_send_ctrl_data_urb(serial, request, value, index,
NULL, 0);
}
/* * mxuport_throttle - throttle function of driver * * This function is called by the tty driver when it wants to stop the * data being read from the port. Since all the data comes over one * bulk in endpoint, we cannot stop submitting urbs by setting * port->throttle. Instead tell the device to stop sending us data for * the port.
*/ staticvoid mxuport_throttle(struct tty_struct *tty)
{ struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial;
/* * mxuport_unthrottle - unthrottle function of driver * * This function is called by the tty driver when it wants to resume * the data being read from the port. Tell the device it can resume * sending us received data from the port.
*/ staticvoid mxuport_unthrottle(struct tty_struct *tty)
{
/* * Processes one chunk of data received for a port. Mostly a copy of * usb_serial_generic_process_read_urb().
*/ staticvoid mxuport_process_read_urb_data(struct usb_serial_port *port, char *data, int size)
{ int i;
if (port->sysrq) { for (i = 0; i < size; i++, data++) { if (!usb_serial_handle_sysrq_char(port, *data))
tty_insert_flip_char(&port->port, *data,
TTY_NORMAL);
}
} else {
tty_insert_flip_string(&port->port, data, size);
}
tty_flip_buffer_push(&port->port);
}
/* * When something interesting happens, modem control lines XON/XOFF * etc, the device sends an event. Process these events.
*/ staticvoid mxuport_process_read_urb_event(struct usb_serial_port *port,
u8 buf[4], u32 event)
{
dev_dbg(&port->dev, "%s - receive event : %04x\n", __func__, event);
switch (event) { case UPORT_EVENT_SEND_NEXT: /* * Sent as part of the flow control on device buffers. * Not currently used.
*/ break; case UPORT_EVENT_MSR:
mxuport_msr_event(port, buf); break; case UPORT_EVENT_LSR:
mxuport_lsr_event(port, buf); break; case UPORT_EVENT_MCR: /* * Event to indicate a change in XON/XOFF from the * peer. Currently not used. We just continue * sending the device data and it will buffer it if * needed. This event could be used for flow control * between the host and the device.
*/ break; default:
dev_dbg(&port->dev, "Unexpected event\n"); break;
}
}
/* * One URB can contain data for multiple ports. Demultiplex the data, * checking the port exists, is opened and the message is valid.
*/ staticvoid mxuport_process_read_urb_demux_data(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial;
u8 *data = urb->transfer_buffer;
u8 *end = data + urb->actual_length; struct usb_serial_port *demux_port;
u8 *ch;
u16 rcv_port;
u16 rcv_len;
while (data < end) { if (data + HEADER_SIZE > end) {
dev_warn(&port->dev, "%s - message with short header\n",
__func__); return;
}
rcv_port = get_unaligned_be16(data); if (rcv_port >= serial->num_ports) {
dev_warn(&port->dev, "%s - message for invalid port\n",
__func__); return;
}
demux_port = serial->port[rcv_port];
rcv_len = get_unaligned_be16(data + 2); if (!rcv_len || data + HEADER_SIZE + rcv_len > end) {
dev_warn(&port->dev, "%s - short data\n", __func__); return;
}
if (tty_port_initialized(&demux_port->port)) {
ch = data + HEADER_SIZE;
mxuport_process_read_urb_data(demux_port, ch, rcv_len);
} else {
dev_dbg(&demux_port->dev, "%s - data for closed port\n",
__func__);
}
data += HEADER_SIZE + rcv_len;
}
}
/* * One URB can contain events for multiple ports. Demultiplex the event, * checking the port exists, and is opened.
*/ staticvoid mxuport_process_read_urb_demux_event(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial;
u8 *data = urb->transfer_buffer;
u8 *end = data + urb->actual_length; struct usb_serial_port *demux_port;
u8 *ch;
u16 rcv_port;
u16 rcv_event;
while (data < end) { if (data + EVENT_LENGTH > end) {
dev_warn(&port->dev, "%s - message with short event\n",
__func__); return;
}
rcv_port = get_unaligned_be16(data); if (rcv_port >= serial->num_ports) {
dev_warn(&port->dev, "%s - message for invalid port\n",
__func__); return;
}
demux_port = serial->port[rcv_port]; if (tty_port_initialized(&demux_port->port)) {
ch = data + HEADER_SIZE;
rcv_event = get_unaligned_be16(data + 2);
mxuport_process_read_urb_event(demux_port, ch,
rcv_event);
} else {
dev_dbg(&demux_port->dev, "%s - event for closed port\n", __func__);
}
data += EVENT_LENGTH;
}
}
/* * This is called when we have received data on the bulk in * endpoint. Depending on which port it was received on, it can * contain serial data or events.
*/ staticvoid mxuport_process_read_urb(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial;
if (port == serial->port[0])
mxuport_process_read_urb_demux_data(urb);
if (port == serial->port[1])
mxuport_process_read_urb_demux_event(urb);
}
/* * Ask the device how many bytes it has queued to be sent out. If * there are none, return true.
*/ staticbool mxuport_tx_empty(struct usb_serial_port *port)
{ struct usb_serial *serial = port->serial; bool is_empty = true;
u32 txlen;
u8 *len_buf; int err;
len_buf = kzalloc(4, GFP_KERNEL); if (!len_buf) goto out;
if (old_termios &&
!tty_termios_hw_change(&tty->termios, old_termios) &&
tty->termios.c_iflag == old_termios->c_iflag) {
dev_dbg(&port->dev, "%s - nothing to change\n", __func__); return;
}
buf = kmalloc(4, GFP_KERNEL); if (!buf) return;
/* Set data bit of termios */ switch (C_CSIZE(tty)) { case CS5:
data_bits = MX_WORDLENGTH_5; break; case CS6:
data_bits = MX_WORDLENGTH_6; break; case CS7:
data_bits = MX_WORDLENGTH_7; break; case CS8: default:
data_bits = MX_WORDLENGTH_8; break;
}
/* Set parity of termios */ if (C_PARENB(tty)) { if (C_CMSPAR(tty)) { if (C_PARODD(tty))
parity = MX_PARITY_MARK; else
parity = MX_PARITY_SPACE;
} else { if (C_PARODD(tty))
parity = MX_PARITY_ODD; else
parity = MX_PARITY_EVEN;
}
} else {
parity = MX_PARITY_NONE;
}
/* Set stop bit of termios */ if (C_CSTOPB(tty))
stop_bits = MX_STOP_BITS_2; else
stop_bits = MX_STOP_BITS_1;
/* * Determine how many ports this device has dynamically. It will be * called after the probe() callback is called, but before attach().
*/ staticint mxuport_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds)
{ unsignedlong features = (unsignedlong)usb_get_serial_data(serial); int num_ports; int i;
/* * Setup bulk-out endpoint multiplexing. All ports share the same * bulk-out endpoint.
*/
BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16);
for (i = 1; i < num_ports; ++i)
epds->bulk_out[i] = epds->bulk_out[0];
epds->num_bulk_out = num_ports;
return num_ports;
}
/* Get the version of the firmware currently running. */ staticint mxuport_get_fw_version(struct usb_serial *serial, u32 *version)
{
u8 *ver_buf; int err;
ver_buf = kzalloc(4, GFP_KERNEL); if (!ver_buf) return -ENOMEM;
/* Get firmware version from SDRAM */
err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_VERSION, 0, 0,
ver_buf, 4); if (err != 4) {
err = -EIO; goto out;
}
/* Given a firmware blob, download it to the device. */ staticint mxuport_download_fw(struct usb_serial *serial, conststruct firmware *fw_p)
{
u8 *fw_buf;
size_t txlen;
size_t fwidx; int err;
fw_buf = kmalloc(DOWN_BLOCK_SIZE, GFP_KERNEL); if (!fw_buf) return -ENOMEM;
/* * Contains the features of this hardware. Store away for * later use, eg, number of ports.
*/
usb_set_serial_data(serial, (void *)id->driver_info);
out: if (fw_p)
release_firmware(fw_p); return err;
}
/* * All data from the ports is received on the first bulk in * endpoint, with a multiplex header. The second bulk in is * used for events. * * Start to read from the device.
*/
err = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL); if (err) return err;
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_AUTHOR("<support@moxa.com>");
MODULE_DESCRIPTION("Moxa UPORT USB Serial driver");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-04-29)
¤
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.