/* This structure holds all of the local serial port information */ struct moschip_port {
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
__u8 shadowMSR; /* last MSR value received */ char open; struct usb_serial_port *port; /* loop back to the owner */ struct urb *write_urb_pool[NUM_URBS];
};
/* * Return the correct value for the upper byte of the Wvalue field of * the setup packet for a control endpoint message.
*/ staticinline __u16 get_reg_value(enum mos_regs reg, unsignedint serial_portnum)
{ if (reg >= MOS7720_SP1_REG) /* control reg */ return 0x0000;
else/* serial port reg */ return (serial_portnum + 2) << 8;
}
/* * Write data byte to the specified device register. The data is embedded in * the value field of the setup packet. serial_portnum is ignored for registers * not specific to a particular serial port.
*/ staticint write_mos_reg(struct usb_serial *serial, unsignedint serial_portnum, enum mos_regs reg, __u8 data)
{ struct usb_device *usbdev = serial->dev; unsignedint pipe = usb_sndctrlpipe(usbdev, 0);
__u8 request = (__u8)0x0e;
__u8 requesttype = (__u8)0x40;
__u16 index = get_reg_index(reg);
__u16 value = get_reg_value(reg, serial_portnum) + data; int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
index, NULL, 0, MOS_WDR_TIMEOUT); if (status < 0)
dev_err(&usbdev->dev, "mos7720: usb_control_msg() failed: %d\n", status); return status;
}
/* * Read data byte from the specified device register. The data returned by the * device is embedded in the value field of the setup packet. serial_portnum is * ignored for registers that are not specific to a particular serial port.
*/ staticint read_mos_reg(struct usb_serial *serial, unsignedint serial_portnum, enum mos_regs reg, __u8 *data)
{ struct usb_device *usbdev = serial->dev; unsignedint pipe = usb_rcvctrlpipe(usbdev, 0);
__u8 request = (__u8)0x0d;
__u8 requesttype = (__u8)0xc0;
__u16 index = get_reg_index(reg);
__u16 value = get_reg_value(reg, serial_portnum);
u8 *buf; int status;
/* * This is the common top part of all parallel port callback operations that * send synchronous messages to the device. This implements convoluted locking * that avoids two scenarios: (1) a port operation is called after usbserial * has called our release function, at which point struct mos7715_parport has * been destroyed, and (2) the device has been disconnected, but usbserial has * not called the release function yet because someone has a serial port open. * The shared release_lock prevents the first, and the mutex and disconnected * flag maintained by usbserial covers the second. We also use the msg_pending * flag to ensure that all synchronous usb message calls have completed before * our release function can return.
*/ staticint parport_prologue(struct parport *pp)
{ struct mos7715_parport *mos_parport;
/* * This is the common bottom part of all parallel port functions that send * synchronous messages to the device.
*/ staticinlinevoid parport_epilogue(struct parport *pp)
{ struct mos7715_parport *mos_parport = pp->private_data;
mutex_unlock(&mos_parport->serial->disc_mutex);
mos_parport->msg_pending = false;
complete(&mos_parport->syncmsg_compl);
}
/* * Allocate and initialize parallel port control struct, initialize * the parallel port hardware device, and register with the parport subsystem.
*/ staticint mos7715_parport_init(struct usb_serial *serial)
{ struct mos7715_parport *mos_parport;
/* allocate and initialize parallel port control struct */
mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); if (!mos_parport) return -ENOMEM;
/* * mos7720_interrupt_callback * this is the callback function for when we have received data on the * interrupt endpoint.
*/ staticvoid mos7720_interrupt_callback(struct urb *urb)
{ int result; int length; int status = urb->status; struct device *dev = &urb->dev->dev;
__u8 *data;
__u8 sp1;
__u8 sp2;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default:
dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); gotoexit;
}
length = urb->actual_length;
data = urb->transfer_buffer;
/* Moschip get 4 bytes * Byte 1 IIR Port 1 (port.number is 0) * Byte 2 IIR Port 2 (port.number is 1) * Byte 3 --------------
* Byte 4 FIFO status for both */
/* the above description is inverted
* oneukum 2007-03-14 */
if (unlikely(length != 4)) {
dev_dbg(dev, "Wrong data !!!\n"); return;
}
sp1 = data[3];
sp2 = data[2];
if ((sp1 | sp2) & 0x01) { /* No Interrupt Pending in both the ports */
dev_dbg(dev, "No Interrupt !!!\n");
} else { switch (sp1 & 0x0f) { case SERIAL_IIR_RLS:
dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI:
dev_dbg(dev, "Serial Port 1: Receiver time out\n"); break; case SERIAL_IIR_MS: /* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */ break;
}
switch (sp2 & 0x0f) { case SERIAL_IIR_RLS:
dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI:
dev_dbg(dev, "Serial Port 2: Receiver time out\n"); break; case SERIAL_IIR_MS: /* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */ break;
}
}
exit:
result = usb_submit_urb(urb, GFP_ATOMIC); if (result)
dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
}
/* * mos7715_interrupt_callback * this is the 7715's callback function for when we have received data on * the interrupt endpoint.
*/ staticvoid mos7715_interrupt_callback(struct urb *urb)
{ int result; int length; int status = urb->status; struct device *dev = &urb->dev->dev;
__u8 *data;
__u8 iir;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: case -ENODEV: /* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default:
dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); gotoexit;
}
length = urb->actual_length;
data = urb->transfer_buffer;
/* Structure of data from 7715 device: * Byte 1: IIR serial Port * Byte 2: unused * Byte 2: DSR parallel port
* Byte 4: FIFO status for both */
if (unlikely(length != 4)) {
dev_dbg(dev, "Wrong data !!!\n"); return;
}
iir = data[0]; if (!(iir & 0x01)) { /* serial port interrupt pending */ switch (iir & 0x0f) { case SERIAL_IIR_RLS:
dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI:
dev_dbg(dev, "Serial Port: Receiver time out\n"); break; case SERIAL_IIR_MS: /* dev_dbg(dev, "Serial Port: Modem status change\n"); */ break;
}
}
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
{ /* update local copy of DSR reg */ struct usb_serial_port *port = urb->context; struct mos7715_parport *mos_parport = port->serial->private; if (unlikely(mos_parport == NULL)) return;
atomic_set(&mos_parport->shadowDSR, data[2]);
} #endif
exit:
result = usb_submit_urb(urb, GFP_ATOMIC); if (result)
dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
}
/* * mos7720_bulk_in_callback * this is the callback function for when we have received data on the * bulk in endpoint.
*/ staticvoid mos7720_bulk_in_callback(struct urb *urb)
{ int retval; unsignedchar *data ; struct usb_serial_port *port; int status = urb->status;
if (status) {
dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status); return;
}
port = urb->context;
dev_dbg(&port->dev, "Entering...%s\n", __func__);
data = urb->transfer_buffer;
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
/* * mos7720_bulk_out_data_callback * this is the callback function for when we have finished sending serial * data on the bulk out endpoint.
*/ staticvoid mos7720_bulk_out_data_callback(struct urb *urb)
{ struct moschip_port *mos7720_port; int status = urb->status;
if (status) {
dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status); return;
}
if (product == MOSCHIP_DEVICE_ID_7715) { /* * The 7715 uses the first bulk in/out endpoint pair for the * parallel port, and the second for the serial port. We swap * the endpoint descriptors here so that the first and * only registered port structure uses the serial-port * endpoints.
*/
swap(epds->bulk_in[0], epds->bulk_in[1]);
swap(epds->bulk_out[0], epds->bulk_out[1]);
return 1;
}
return 2;
}
staticint mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
{ struct usb_serial *serial; struct urb *urb; struct moschip_port *mos7720_port; int response; int port_number;
__u8 data; int allocated_urbs = 0; int j;
serial = port->serial;
mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) return -ENODEV;
/* initialize our port settings */
mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
/* send a open port command */
mos7720_port->open = 1;
return 0;
}
/* * mos7720_chars_in_buffer * this function is called by the tty driver when it wants to know how many * bytes of data we currently have outstanding in the port (data that has * been written, but hasn't made it out the port yet)
*/ staticunsignedint mos7720_chars_in_buffer(struct tty_struct *tty)
{ struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); int i; unsignedint chars = 0;
for (i = 0; i < NUM_URBS; ++i) { if (mos7720_port->write_urb_pool[i] &&
mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars); return chars;
}
mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) return;
for (j = 0; j < NUM_URBS; ++j)
usb_kill_urb(mos7720_port->write_urb_pool[j]);
/* Freeing Write URBs */ for (j = 0; j < NUM_URBS; ++j) { if (mos7720_port->write_urb_pool[j]) {
kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
usb_free_urb(mos7720_port->write_urb_pool[j]);
}
}
/* While closing port, shutdown all bulk read, write *
* and interrupt read if they exists, otherwise nop */
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
/* * mos7720_write_room * this function is called by the tty driver when it wants to know how many * bytes of data we can accept for a specific port.
*/ staticunsignedint mos7720_write_room(struct tty_struct *tty)
{ struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); unsignedint room = 0; int i;
/* FIXME: Locking */ for (i = 0; i < NUM_URBS; ++i) { if (mos7720_port->write_urb_pool[i] &&
mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
room += URB_TRANSFER_BUFFER_SIZE;
}
staticint mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, constunsignedchar *data, int count)
{ int status; int i; int bytes_sent = 0; int transfer_size;
/* fill urb with data and submit */
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev,
port->bulk_out_endpointAddress),
urb->transfer_buffer, transfer_size,
mos7720_bulk_out_data_callback, mos7720_port);
/* send it down the pipe */
status = usb_submit_urb(urb, GFP_ATOMIC); if (status) {
dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " "with status = %d\n", __func__, status);
bytes_sent = status; gotoexit;
}
bytes_sent = transfer_size;
if (!mos7720_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__); return;
}
/* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsignedchar stop_char = STOP_CHAR(tty);
status = mos7720_write(tty, port, &stop_char, 1); if (status <= 0) return;
}
/* if we are implementing RTS/CTS, toggle that line */ if (C_CRTSCTS(tty)) {
mos7720_port->shadowMCR &= ~UART_MCR_RTS;
write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
mos7720_port->shadowMCR);
}
}
if (!mos7720_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__); return;
}
/* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsignedchar start_char = START_CHAR(tty);
status = mos7720_write(tty, port, &start_char, 1); if (status <= 0) return;
}
/* if we are implementing RTS/CTS, toggle that line */ if (C_CRTSCTS(tty)) {
mos7720_port->shadowMCR |= UART_MCR_RTS;
write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
mos7720_port->shadowMCR);
}
}
/* FIXME: this function does not work */ staticint set_higher_rates(struct moschip_port *mos7720_port, unsignedint baud)
{ struct usb_serial_port *port; struct usb_serial *serial; int port_number; enum mos_regs sp_reg; if (mos7720_port == NULL) return -EINVAL;
/***************************************************************************** * calc_baud_rate_divisor * this function calculates the proper baud rate divisor for the specified * baud rate.
*****************************************************************************/ staticint calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor)
{ int i;
__u16 custom;
__u16 round1;
__u16 round;
for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { if (divisor_table[i].baudrate == baudrate) {
*divisor = divisor_table[i].divisor; return 0;
}
}
/* After trying for all the standard baud rates *
* Try calculating the divisor for this baud rate */ if (baudrate > 75 && baudrate < 230400) { /* get the divisor */
custom = (__u16)(230400L / baudrate);
/* Check for round off */
round1 = (__u16)(2304000L / baudrate);
round = (__u16)(round1 - (custom * 10)); if (round > 4)
custom++;
*divisor = custom;
/* * send_cmd_write_baud_rate * this function sends the proper command to change the baud rate of the * specified port.
*/ staticint send_cmd_write_baud_rate(struct moschip_port *mos7720_port, int baudrate)
{ struct usb_serial_port *port; struct usb_serial *serial; int divisor; int status; unsignedchar number;
/* * change_port_settings * This routine is called to set the UART on the device to match * the specified new settings.
*/ staticvoid change_port_settings(struct tty_struct *tty, struct moschip_port *mos7720_port, conststruct ktermios *old_termios)
{ struct usb_serial_port *port; struct usb_serial *serial; int baud; unsigned cflag;
__u8 lData;
__u8 lParity;
__u8 lStop; int status; int port_number;
if (mos7720_port == NULL) return ;
port = mos7720_port->port;
serial = port->serial;
port_number = port->port_number;
if (!mos7720_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__); return;
}
lStop = 0x00; /* 1 stop bit */
lParity = 0x00; /* No parity */
#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ #define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ #define LCR_PAR_MASK 0x38 /* Mask for parity field */
/* Update the LCR with the correct value */
mos7720_port->shadowLCR &=
~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
mos7720_port->shadowLCR |= (lData | lParity | lStop);
/* Send the updated LCR value to the mos7720 */
write_mos_reg(serial, port_number, MOS7720_LCR,
mos7720_port->shadowLCR);
mos7720_port->shadowMCR = 0x0b;
write_mos_reg(serial, port_number, MOS7720_MCR,
mos7720_port->shadowMCR);
/* set up the MCR register and send it to the mos7720 */
mos7720_port->shadowMCR = UART_MCR_OUT2; if (cflag & CBAUD)
mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
if (cflag & CRTSCTS) {
mos7720_port->shadowMCR |= (UART_MCR_XONANY); /* To set hardware flow control to the specified *
* serial port, in SP1/2_CONTROL_REG */ if (port_number)
write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
0x01); else
write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
0x02);
dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
status = send_cmd_write_baud_rate(mos7720_port, baud); /* FIXME: needs to write actual resulting baud back not just
blindly do so */ if (cflag & CBAUD)
tty_encode_baud_rate(tty, baud, baud); /* Enable Interrupts */
write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
if (port->read_urb->status != -EINPROGRESS) {
status = usb_submit_urb(port->read_urb, GFP_KERNEL); if (status)
dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
}
}
/* * mos7720_set_termios * this function is called by the tty driver when it wants to change the * termios structure.
*/ staticvoid mos7720_set_termios(struct tty_struct *tty, struct usb_serial_port *port, conststruct ktermios *old_termios)
{ int status; struct moschip_port *mos7720_port;
mos7720_port = usb_get_serial_port_data(port);
if (mos7720_port == NULL) return;
if (!mos7720_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__); return;
}
/* change the port settings to the new ones specified */
change_port_settings(tty, mos7720_port, old_termios);
if (port->read_urb->status != -EINPROGRESS) {
status = usb_submit_urb(port->read_urb, GFP_KERNEL); if (status)
dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
}
}
/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space.
*/ staticint get_lsr_info(struct tty_struct *tty, struct moschip_port *mos7720_port, unsignedint __user *value)
{ struct usb_serial_port *port = tty->driver_data; unsignedint result = 0; unsignedchar data = 0; int port_number = port->port_number; int count;
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT /* close the parallel port */
if (le16_to_cpu(serial->dev->descriptor.idProduct)
== MOSCHIP_DEVICE_ID_7715) { struct mos7715_parport *mos_parport =
usb_get_serial_data(serial);
/* prevent NULL ptr dereference in port callbacks */
spin_lock(&release_lock);
mos_parport->pp->private_data = NULL;
spin_unlock(&release_lock);
/* wait for synchronous usb calls to return */ if (mos_parport->msg_pending)
wait_for_completion_timeout(&mos_parport->syncmsg_compl,
msecs_to_jiffies(MOS_WDR_TIMEOUT)); /* * If delayed work is currently scheduled, wait for it to * complete. This also implies barriers that ensure the * below serial clearing is not hoisted above the ->work.
*/
cancel_work_sync(&mos_parport->work);
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.