| /* |
| * This code was developed for the Quatech USB line for linux, it used |
| * much of the code developed by Greg Kroah-Hartman for USB serial devices |
| * |
| */ |
| |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| #include <linux/tty.h> |
| #include <linux/tty_flip.h> |
| #include <linux/module.h> |
| #include <linux/usb.h> |
| #include <linux/wait.h> |
| #include <linux/types.h> |
| #include <linux/version.h> |
| #include <linux/uaccess.h> |
| |
| /* Use our own dbg macro */ |
| /* #define DEBUG_ON */ |
| /* #undef dbg */ |
| #ifdef DEBUG_ON |
| #define mydbg(const...) printk(const) |
| #else |
| #define mydbg(const...) |
| #endif |
| |
| /* parity check flag */ |
| #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) |
| |
| #define SERIAL_TTY_MAJOR 0 /* Nice legal number now */ |
| #define SERIAL_TTY_MINORS 255 /* loads of devices :) */ |
| #define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */ |
| #define PREFUFF_LEVEL_CONSERVATIVE 128 |
| #define ATC_DISABLED 0x00 |
| |
| #define RR_BITS 0x03 /* for clearing clock bits */ |
| #define DUPMODE_BITS 0xc0 |
| |
| #define RS232_MODE 0x00 |
| #define RTSCTS_TO_CONNECTOR 0x40 |
| #define CLKS_X4 0x02 |
| |
| #define LOOPMODE_BITS 0x41 /* LOOP1 = b6, LOOP0 = b0 (PORT B) */ |
| #define ALL_LOOPBACK 0x01 |
| #define MODEM_CTRL 0x40 |
| |
| #define THISCHAR data[i] |
| #define NEXTCHAR data[i + 1] |
| #define THIRDCHAR data[i + 2] |
| #define FOURTHCHAR data[i + 3] |
| |
| /* |
| * Useful defintions for port A, Port B and Port C |
| */ |
| #define FULLPWRBIT 0x00000080 |
| #define NEXT_BOARD_POWER_BIT 0x00000004 |
| |
| #define SERIAL_LSR_OE 0x02 |
| #define SERIAL_LSR_PE 0x04 |
| #define SERIAL_LSR_FE 0x08 |
| #define SERIAL_LSR_BI 0x10 |
| |
| #define SERIAL_LSR_TEMT 0x40 |
| |
| #define DIV_LATCH_LS 0x00 |
| #define XMT_HOLD_REGISTER 0x00 |
| #define XVR_BUFFER_REGISTER 0x00 |
| #define DIV_LATCH_MS 0x01 |
| #define FIFO_CONTROL_REGISTER 0x02 |
| #define LINE_CONTROL_REGISTER 0x03 |
| #define MODEM_CONTROL_REGISTER 0x04 |
| #define LINE_STATUS_REGISTER 0x05 |
| #define MODEM_STATUS_REGISTER 0x06 |
| |
| #define SERIAL_MCR_DTR 0x01 |
| #define SERIAL_MCR_RTS 0x02 |
| #define SERIAL_MCR_LOOP 0x10 |
| |
| #define SERIAL_MSR_CTS 0x10 |
| #define SERIAL_MSR_CD 0x80 |
| #define SERIAL_MSR_RI 0x40 |
| #define SERIAL_MSR_DSR 0x20 |
| #define SERIAL_MSR_MASK 0xf0 |
| |
| #define SERIAL_8_DATA 0x03 |
| #define SERIAL_7_DATA 0x02 |
| #define SERIAL_6_DATA 0x01 |
| #define SERIAL_5_DATA 0x00 |
| |
| #define SERIAL_ODD_PARITY 0X08 |
| #define SERIAL_EVEN_PARITY 0X18 |
| #define SERIAL_TWO_STOPB 0x04 |
| #define SERIAL_ONE_STOPB 0x00 |
| |
| #define MAX_BAUD_RATE 460800 |
| #define MAX_BAUD_REMAINDER 4608 |
| |
| #define QT_SET_GET_DEVICE 0xc2 |
| #define QT_OPEN_CLOSE_CHANNEL 0xca |
| #define QT_GET_SET_PREBUF_TRIG_LVL 0xcc |
| #define QT_SET_ATF 0xcd |
| #define QT_GET_SET_REGISTER 0xc0 |
| #define QT_GET_SET_UART 0xc1 |
| #define QT_HW_FLOW_CONTROL_MASK 0xc5 |
| #define QT_SW_FLOW_CONTROL_MASK 0xc6 |
| #define QT_SW_FLOW_CONTROL_DISABLE 0xc7 |
| #define QT_BREAK_CONTROL 0xc8 |
| |
| #define SERIALQT_PCI_IOC_MAGIC 'k' |
| #define SERIALQT_WRITE_QOPR _IOW(SERIALQT_PCI_IOC_MAGIC, 0, int) |
| #define SERIALQT_WRITE_QMCR _IOW(SERIALQT_PCI_IOC_MAGIC, 1, int) |
| #define SERIALQT_GET_NUMOF_UNITS _IOR(SERIALQT_PCI_IOC_MAGIC, 2, void *) |
| #define SERIALQT_GET_THIS_UNIT _IOR(SERIALQT_PCI_IOC_MAGIC, 3, void *) |
| #define SERIALQT_READ_QOPR _IOR(SERIALQT_PCI_IOC_MAGIC, 4, int) |
| #define SERIALQT_READ_QMCR _IOR(SERIALQT_PCI_IOC_MAGIC, 5, int) |
| #define SERIALQT_IS422_EXTENDED _IOR(SERIALQT_PCI_IOC_MAGIC, 6, int) /* returns successful if 422 extended */ |
| |
| #define USBD_TRANSFER_DIRECTION_IN 0xc0 |
| #define USBD_TRANSFER_DIRECTION_OUT 0x40 |
| |
| #define ATC_DISABLED 0x00 |
| #define ATC_RTS_ENABLED 0x02 |
| #define ATC_DTR_ENABLED 0x01 |
| |
| #define RR_BITS 0x03 /* for clearing clock bits */ |
| #define DUPMODE_BITS 0xc0 |
| |
| #define FULL_DUPLEX 0x00 |
| #define HALF_DUPLEX_RTS 0x40 |
| #define HALF_DUPLEX_DTR 0x80 |
| |
| #define QMCR_FULL_DUPLEX 0x00 |
| #define QMCR_HALF_DUPLEX_RTS 0x02 |
| #define QMCR_HALF_DUPLEX_DTR 0x01 |
| #define QMCR_HALF_DUPLEX_MASK 0x03 |
| #define QMCR_CONNECTOR_MASK 0x1C |
| |
| #define QMCR_RX_EN_MASK 0x20 |
| |
| #define QMCR_ALL_LOOPBACK 0x10 |
| #define QMCR_MODEM_CONTROL 0X00 |
| |
| #define SERIALQT_IOC_MAXNR 6 |
| |
| struct usb_serial_port { |
| struct usb_serial *serial; /* pointer back to the owner of this port */ |
| struct tty_struct *tty; /* the coresponding tty for this port */ |
| unsigned char number; |
| char active; /* someone has this device open */ |
| |
| unsigned char *interrupt_in_buffer; |
| struct urb *interrupt_in_urb; |
| __u8 interrupt_in_endpointAddress; |
| |
| unsigned char *bulk_in_buffer; |
| unsigned char *xfer_to_tty_buffer; |
| struct urb *read_urb; |
| __u8 bulk_in_endpointAddress; |
| |
| unsigned char *bulk_out_buffer; |
| int bulk_out_size; |
| struct urb *write_urb; |
| __u8 bulk_out_endpointAddress; |
| |
| wait_queue_head_t write_wait; |
| wait_queue_head_t wait; |
| struct work_struct work; |
| |
| int open_count; /* number of times this port has been opened */ |
| struct semaphore sem; /* locks this structure */ |
| |
| __u8 shadowLCR; /* last LCR value received */ |
| __u8 shadowMCR; /* last MCR value received */ |
| __u8 shadowMSR; /* last MSR value received */ |
| __u8 shadowLSR; /* last LSR value received */ |
| int RxHolding; |
| char closePending; |
| int ReadBulkStopped; |
| |
| void *private; /* data private to the specific port */ |
| }; |
| |
| struct identity { |
| int index; |
| int n_identity; |
| }; |
| |
| struct usb_serial { |
| struct usb_device *dev; |
| struct usb_interface *interface; /* the interface for this device */ |
| struct tty_driver *tty_driver; /* the tty_driver for this device */ |
| unsigned char minor; /* the starting minor number for this device */ |
| unsigned char num_ports; /* the number of ports this device has */ |
| char num_interrupt_in; /* number of interrupt in endpoints we have */ |
| char num_bulk_in; /* number of bulk in endpoints we have */ |
| char num_bulk_out; /* number of bulk out endpoints we have */ |
| unsigned char num_OpenCount; /* the number of ports this device has */ |
| |
| __u16 vendor; /* vendor id of this device */ |
| __u16 product; /* product id of this device */ |
| struct usb_serial_port port[MAX_NUM_PORTS]; |
| |
| void *private; /* data private to the specific driver */ |
| }; |
| |
| static inline int port_paranoia_check(struct usb_serial_port *port, |
| const char *function) |
| { |
| if (!port) { |
| dbg("%s - port == NULL", function); |
| return -1; |
| } |
| if (!port->serial) { |
| dbg("%s - port->serial == NULL\n", function); |
| return -1; |
| } |
| if (!port->tty) { |
| dbg("%s - port->tty == NULL\n", function); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Inline functions to check the sanity of a pointer that is passed to us */ |
| static inline int serial_paranoia_check(struct usb_serial *serial, |
| const char *function) |
| { |
| if (!serial) { |
| dbg("%s - serial == NULL\n", function); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static inline struct usb_serial *get_usb_serial(struct usb_serial_port *port, |
| const char *function) |
| { |
| /* if no port was specified, or it fails a paranoia check */ |
| if (!port || |
| port_paranoia_check(port, function) || |
| serial_paranoia_check(port->serial, function)) { |
| /* then say that we dont have a valid usb_serial thing, which will |
| * end up genrating -ENODEV return values */ |
| return NULL; |
| } |
| |
| return port->serial; |
| } |
| |
| struct qt_get_device_data { |
| __u8 porta; |
| __u8 portb; |
| __u8 portc; |
| }; |
| |
| struct qt_open_channel_data { |
| __u8 line_status; |
| __u8 modem_status; |
| }; |
| |
| static void ProcessLineStatus(struct usb_serial_port *port, |
| unsigned char line_status); |
| static void ProcessModemStatus(struct usb_serial_port *port, |
| unsigned char modem_status); |
| static void ProcessRxChar(struct usb_serial_port *port, unsigned char Data); |
| static struct usb_serial *get_free_serial(int num_ports, int *minor); |
| |
| static int serqt_probe(struct usb_interface *interface, |
| const struct usb_device_id *id); |
| |
| static void serqt_usb_disconnect(struct usb_interface *interface); |
| static int box_set_device(struct usb_serial *serial, |
| struct qt_get_device_data *pDeviceData); |
| static int box_get_device(struct usb_serial *serial, |
| struct qt_get_device_data *pDeviceData); |
| static int serial_open(struct tty_struct *tty, struct file *filp); |
| static void serial_close(struct tty_struct *tty, struct file *filp); |
| static int serial_write_room(struct tty_struct *tty); |
| static int serial_ioctl(struct tty_struct *tty, struct file *file, |
| unsigned int cmd, unsigned long arg); |
| static void serial_set_termios(struct tty_struct *tty, struct ktermios *old); |
| static int serial_write(struct tty_struct *tty, const unsigned char *buf, |
| int count); |
| |
| static void serial_throttle(struct tty_struct *tty); |
| static void serial_unthrottle(struct tty_struct *tty); |
| static int serial_break(struct tty_struct *tty, int break_state); |
| static int serial_chars_in_buffer(struct tty_struct *tty); |
| |
| static int qt_open(struct usb_serial_port *port, struct file *filp); |
| static int BoxSetPrebufferLevel(struct usb_serial *serial); |
| |
| static int BoxSetATC(struct usb_serial *serial, __u16 n_Mode); |
| static int BoxSetUart(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short default_divisor, |
| unsigned char default_LCR); |
| |
| static int BoxOPenCloseChannel(struct usb_serial *serial, __u16 Uart_Number, |
| __u16 OpenClose, |
| struct qt_open_channel_data *pDeviceData); |
| static void qt_close(struct usb_serial_port *port, struct file *filp); |
| static int BoxGetRegister(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short Register_Num, __u8 *pValue); |
| static int BoxSetRegister(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short Register_Num, unsigned short Value); |
| static void qt_write_bulk_callback(struct urb *urb); |
| static int qt_write(struct usb_serial_port *port, int from_user, |
| const unsigned char *buf, int count); |
| static void port_softint(struct work_struct *work); |
| static int qt_write_room(struct usb_serial_port *port); |
| static int qt_chars_in_buffer(struct usb_serial_port *port); |
| static int qt_ioctl(struct usb_serial_port *port, struct file *file, |
| unsigned int cmd, unsigned long arg); |
| static void qt_set_termios(struct usb_serial_port *port, |
| struct ktermios *old_termios); |
| static int BoxSetHW_FlowCtrl(struct usb_serial *serial, unsigned int UartNumber, |
| int bSet); |
| static int BoxDisable_SW_FlowCtrl(struct usb_serial *serial, __u16 UartNumber); |
| static int EmulateWriteQMCR_Reg(int index, unsigned uc_value); |
| static int EmulateReadQMCR_Reg(int index, unsigned *uc_value); |
| static struct usb_serial *find_the_box(unsigned int index); |
| static int ioctl_serial_usb(struct inode *innod, struct file *filp, unsigned int cmd, |
| unsigned long arg); |
| |
| static int BoxSetSW_FlowCtrl(struct usb_serial *serial, __u16 Uart, |
| unsigned char stop_char, unsigned char start_char); |
| static void qt_read_bulk_callback(struct urb *urb); |
| |
| static void port_sofrint(void *private); |
| |
| static void return_serial(struct usb_serial *serial); |
| |
| static int serial_tiocmset(struct tty_struct *tty, struct file *file, |
| unsigned int set, unsigned int clear); |
| static int serial_tiocmget(struct tty_struct *tty, struct file *file); |
| |
| static int qt_tiocmset(struct usb_serial_port *port, struct file *file, |
| unsigned int value); |
| |
| static int qt_tiocmget(struct usb_serial_port *port, struct file *file); |
| |
| /* Version Information */ |
| #define DRIVER_VERSION "v2.14" |
| #define DRIVER_AUTHOR "Tim Gobeli, Quatech, Inc" |
| #define DRIVER_DESC "Quatech USB to Serial Driver" |
| |
| #define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */ |
| #define DEVICE_ID_QUATECH_RS232_SINGLE_PORT 0xC020 /* SSU100 */ |
| #define DEVICE_ID_QUATECH_RS422_SINGLE_PORT 0xC030 /* SSU200 */ |
| #define DEVICE_ID_QUATECH_RS232_DUAL_PORT 0xC040 /* DSU100 */ |
| #define DEVICE_ID_QUATECH_RS422_DUAL_PORT 0xC050 /* DSU200 */ |
| #define DEVICE_ID_QUATECH_RS232_FOUR_PORT 0xC060 /* QSU100 */ |
| #define DEVICE_ID_QUATECH_RS422_FOUR_PORT 0xC070 /* QSU200 */ |
| #define DEVICE_ID_QUATECH_RS232_EIGHT_PORT_A 0xC080 /* ESU100A */ |
| #define DEVICE_ID_QUATECH_RS232_EIGHT_PORT_B 0xC081 /* ESU100B */ |
| #define DEVICE_ID_QUATECH_RS422_EIGHT_PORT_A 0xC0A0 /* ESU200A */ |
| #define DEVICE_ID_QUATECH_RS422_EIGHT_PORT_B 0xC0A1 /* ESU200B */ |
| #define DEVICE_ID_QUATECH_RS232_16_PORT_A 0xC090 /* HSU100A */ |
| #define DEVICE_ID_QUATECH_RS232_16_PORT_B 0xC091 /* HSU100B */ |
| #define DEVICE_ID_QUATECH_RS232_16_PORT_C 0xC092 /* HSU100C */ |
| #define DEVICE_ID_QUATECH_RS232_16_PORT_D 0xC093 /* HSU100D */ |
| #define DEVICE_ID_QUATECH_RS422_16_PORT_A 0xC0B0 /* HSU200A */ |
| #define DEVICE_ID_QUATECH_RS422_16_PORT_B 0xC0B1 /* HSU200B */ |
| #define DEVICE_ID_QUATECH_RS422_16_PORT_C 0xC0B2 /* HSU200C */ |
| #define DEVICE_ID_QUATECH_RS422_16_PORT_D 0xC0B3 /* HSU200D */ |
| |
| /* table of Quatech devices */ |
| static struct usb_device_id serqt_table[] = { |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_SINGLE_PORT)}, |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_SINGLE_PORT)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_DUAL_PORT)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_DUAL_PORT)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_FOUR_PORT)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_FOUR_PORT)}, |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_EIGHT_PORT_A)}, |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_EIGHT_PORT_B)}, |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_EIGHT_PORT_A)}, |
| {USB_DEVICE |
| (USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_EIGHT_PORT_B)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_16_PORT_A)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_16_PORT_B)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_16_PORT_C)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS232_16_PORT_D)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_16_PORT_A)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_16_PORT_B)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_16_PORT_C)}, |
| {USB_DEVICE(USB_VENDOR_ID_QUATECH, DEVICE_ID_QUATECH_RS422_16_PORT_D)}, |
| {} /* Terminating entry */ |
| }; |
| |
| MODULE_DEVICE_TABLE(usb, serqt_table); |
| |
| static int major_number; |
| static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ |
| |
| /* table of Quatech 422devices */ |
| static unsigned int serqt_422_table[] = { |
| DEVICE_ID_QUATECH_RS422_SINGLE_PORT, |
| DEVICE_ID_QUATECH_RS422_DUAL_PORT, |
| DEVICE_ID_QUATECH_RS422_FOUR_PORT, |
| DEVICE_ID_QUATECH_RS422_EIGHT_PORT_A, |
| DEVICE_ID_QUATECH_RS422_EIGHT_PORT_B, |
| DEVICE_ID_QUATECH_RS422_16_PORT_A, |
| DEVICE_ID_QUATECH_RS422_16_PORT_B, |
| DEVICE_ID_QUATECH_RS422_16_PORT_C, |
| DEVICE_ID_QUATECH_RS422_16_PORT_D, |
| 0 /* terminate with zero */ |
| }; |
| |
| /* usb specific object needed to register this driver with the usb subsystem */ |
| static struct usb_driver serqt_usb_driver = { |
| .name = "quatech-usb-serial", |
| .probe = serqt_probe, |
| .disconnect = serqt_usb_disconnect, |
| .id_table = serqt_table, |
| }; |
| |
| static struct ktermios *serial_termios[SERIAL_TTY_MINORS]; |
| static struct ktermios *serial_termios_locked[SERIAL_TTY_MINORS]; |
| |
| static const struct tty_operations serial_ops = { |
| .open = serial_open, |
| .close = serial_close, |
| .write = serial_write, |
| .write_room = serial_write_room, |
| .ioctl = serial_ioctl, |
| .set_termios = serial_set_termios, |
| .throttle = serial_throttle, |
| .unthrottle = serial_unthrottle, |
| .break_ctl = serial_break, |
| .chars_in_buffer = serial_chars_in_buffer, |
| .tiocmset = serial_tiocmset, |
| .tiocmget = serial_tiocmget, |
| }; |
| |
| static struct tty_driver serial_tty_driver = { |
| .magic = TTY_DRIVER_MAGIC, |
| .driver_name = "Quatech usb-serial", |
| .name = "ttyQT_USB", |
| .major = SERIAL_TTY_MAJOR, |
| .minor_start = 0, |
| .num = SERIAL_TTY_MINORS, |
| .type = TTY_DRIVER_TYPE_SERIAL, |
| .subtype = SERIAL_TYPE_NORMAL, |
| .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV, |
| |
| .termios = serial_termios, |
| .termios_locked = serial_termios_locked, |
| .init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL, |
| |
| .init_termios.c_iflag = ICRNL | IXON, |
| .init_termios.c_oflag = OPOST, |
| |
| .init_termios.c_lflag = |
| ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN, |
| }; |
| |
| /* fops for parent device */ |
| static const struct file_operations serialqt_usb_fops = { |
| .ioctl = ioctl_serial_usb, |
| }; |
| |
| /** |
| * serqt_probe |
| * |
| * Called by the usb core when a new device is connected that it thinks |
| * this driver might be interested in. |
| * |
| */ |
| static int serqt_probe(struct usb_interface *interface, |
| const struct usb_device_id *id) |
| { |
| struct usb_device *dev = interface_to_usbdev(interface); |
| struct usb_serial *serial = NULL; |
| struct usb_serial_port *port; |
| struct usb_endpoint_descriptor *endpoint; |
| struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS]; |
| struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS]; |
| struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; |
| int minor; |
| int buffer_size; |
| int i; |
| struct usb_host_interface *iface_desc; |
| int num_interrupt_in = 0; |
| int num_bulk_in = 0; |
| int num_bulk_out = 0; |
| int num_ports; |
| struct qt_get_device_data DeviceData; |
| int status; |
| |
| mydbg("In %s\n", __func__); |
| |
| /* let's find the endpoints needed */ |
| /* check out the endpoints */ |
| iface_desc = interface->cur_altsetting;; |
| for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
| endpoint = &iface_desc->endpoint[i].desc; |
| |
| if ((endpoint->bEndpointAddress & 0x80) && |
| ((endpoint->bmAttributes & 3) == 0x02)) { |
| /* we found a bulk in endpoint */ |
| mydbg("found bulk in"); |
| bulk_in_endpoint[num_bulk_in] = endpoint; |
| ++num_bulk_in; |
| } |
| |
| if (((endpoint->bEndpointAddress & 0x80) == 0x00) && |
| ((endpoint->bmAttributes & 3) == 0x02)) { |
| /* we found a bulk out endpoint */ |
| mydbg("found bulk out\n"); |
| bulk_out_endpoint[num_bulk_out] = endpoint; |
| ++num_bulk_out; |
| } |
| |
| if ((endpoint->bEndpointAddress & 0x80) && |
| ((endpoint->bmAttributes & 3) == 0x03)) { |
| /* we found a interrupt in endpoint */ |
| mydbg("found interrupt in\n"); |
| interrupt_in_endpoint[num_interrupt_in] = endpoint; |
| ++num_interrupt_in; |
| } |
| } |
| |
| /* found all that we need */ |
| dev_info(&interface->dev, "Quatech converter detected\n"); |
| num_ports = num_bulk_out; |
| if (num_ports == 0) { |
| err("Quatech device with no bulk out, not allowed."); |
| return -ENODEV; |
| |
| } |
| |
| serial = get_free_serial(num_ports, &minor); |
| if (serial == NULL) { |
| err("No more free serial devices"); |
| return -ENODEV; |
| } |
| |
| serial->dev = dev; |
| serial->interface = interface; |
| serial->minor = minor; |
| serial->num_ports = num_ports; |
| serial->num_bulk_in = num_bulk_in; |
| serial->num_bulk_out = num_bulk_out; |
| serial->num_interrupt_in = num_interrupt_in; |
| serial->vendor = dev->descriptor.idVendor; |
| serial->product = dev->descriptor.idProduct; |
| |
| /* set up the endpoint information */ |
| for (i = 0; i < num_bulk_in; ++i) { |
| endpoint = bulk_in_endpoint[i]; |
| port = &serial->port[i]; |
| port->read_urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!port->read_urb) { |
| err("No free urbs available"); |
| goto probe_error; |
| } |
| buffer_size = endpoint->wMaxPacketSize; |
| port->bulk_in_endpointAddress = endpoint->bEndpointAddress; |
| port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); |
| port->xfer_to_tty_buffer = kmalloc(buffer_size, GFP_KERNEL); |
| if (!port->bulk_in_buffer) { |
| err("Couldn't allocate bulk_in_buffer"); |
| goto probe_error; |
| } |
| usb_fill_bulk_urb(port->read_urb, dev, |
| usb_rcvbulkpipe(dev, |
| endpoint->bEndpointAddress), |
| port->bulk_in_buffer, buffer_size, |
| qt_read_bulk_callback, port); |
| } |
| |
| for (i = 0; i < num_bulk_out; ++i) { |
| endpoint = bulk_out_endpoint[i]; |
| port = &serial->port[i]; |
| port->write_urb = usb_alloc_urb(0, GFP_KERNEL); |
| if (!port->write_urb) { |
| err("No free urbs available"); |
| goto probe_error; |
| } |
| buffer_size = endpoint->wMaxPacketSize; |
| port->bulk_out_size = buffer_size; |
| port->bulk_out_endpointAddress = endpoint->bEndpointAddress; |
| port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); |
| if (!port->bulk_out_buffer) { |
| err("Couldn't allocate bulk_out_buffer"); |
| goto probe_error; |
| } |
| usb_fill_bulk_urb(port->write_urb, dev, |
| usb_sndbulkpipe(dev, |
| endpoint->bEndpointAddress), |
| port->bulk_out_buffer, buffer_size, |
| qt_write_bulk_callback, port); |
| |
| } |
| |
| /* For us numb of bulkin or out = number of ports */ |
| mydbg("%s - setting up %d port structures for this device\n", |
| __func__, num_bulk_in); |
| for (i = 0; i < num_bulk_in; ++i) { |
| port = &serial->port[i]; |
| port->number = i + serial->minor; |
| port->serial = serial; |
| |
| INIT_WORK(&port->work, port_softint); |
| |
| init_MUTEX(&port->sem); |
| |
| } |
| status = box_get_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_get_device failed"); |
| goto probe_error; |
| } |
| |
| mydbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb); |
| |
| DeviceData.portb &= ~FULLPWRBIT; |
| mydbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb); |
| |
| status = box_set_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_set_device failed\n"); |
| goto probe_error; |
| } |
| |
| /* initialize the devfs nodes for this device and let the user know what ports we are bound to */ |
| for (i = 0; i < serial->num_ports; ++i) { |
| dev_info(&interface->dev, |
| "Converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", |
| serial->port[i].number, serial->port[i].number); |
| } |
| |
| /* usb_serial_console_init (debug, minor); */ |
| |
| /***********TAG add start next board here ****/ |
| status = box_get_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_get_device failed"); |
| goto probe_error; |
| } |
| /* |
| * and before we power up lets initialiaze parnent device stuff here before |
| * we set thmem via any other method such as the property pages |
| */ |
| switch (serial->product) { |
| case DEVICE_ID_QUATECH_RS232_SINGLE_PORT: |
| case DEVICE_ID_QUATECH_RS232_DUAL_PORT: |
| case DEVICE_ID_QUATECH_RS232_FOUR_PORT: |
| case DEVICE_ID_QUATECH_RS232_EIGHT_PORT_A: |
| case DEVICE_ID_QUATECH_RS232_EIGHT_PORT_B: |
| case DEVICE_ID_QUATECH_RS232_16_PORT_A: |
| case DEVICE_ID_QUATECH_RS232_16_PORT_B: |
| case DEVICE_ID_QUATECH_RS232_16_PORT_C: |
| case DEVICE_ID_QUATECH_RS232_16_PORT_D: |
| DeviceData.porta &= ~(RR_BITS | DUPMODE_BITS); |
| DeviceData.porta |= CLKS_X4; |
| DeviceData.portb &= ~(LOOPMODE_BITS); |
| DeviceData.portb |= RS232_MODE; |
| break; |
| |
| case DEVICE_ID_QUATECH_RS422_SINGLE_PORT: |
| case DEVICE_ID_QUATECH_RS422_DUAL_PORT: |
| case DEVICE_ID_QUATECH_RS422_FOUR_PORT: |
| case DEVICE_ID_QUATECH_RS422_EIGHT_PORT_A: |
| case DEVICE_ID_QUATECH_RS422_EIGHT_PORT_B: |
| case DEVICE_ID_QUATECH_RS422_16_PORT_A: |
| case DEVICE_ID_QUATECH_RS422_16_PORT_B: |
| case DEVICE_ID_QUATECH_RS422_16_PORT_C: |
| case DEVICE_ID_QUATECH_RS422_16_PORT_D: |
| DeviceData.porta &= ~(RR_BITS | DUPMODE_BITS); |
| DeviceData.porta |= CLKS_X4; |
| DeviceData.portb &= ~(LOOPMODE_BITS); |
| DeviceData.portb |= ALL_LOOPBACK; |
| break; |
| default: |
| DeviceData.porta &= ~(RR_BITS | DUPMODE_BITS); |
| DeviceData.porta |= CLKS_X4; |
| DeviceData.portb &= ~(LOOPMODE_BITS); |
| DeviceData.portb |= RS232_MODE; |
| break; |
| |
| } |
| status = BoxSetPrebufferLevel(serial); /* sets to default vaue */ |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetPrebufferLevel failed\n"); |
| goto probe_error; |
| } |
| |
| status = BoxSetATC(serial, ATC_DISABLED); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetATC failed\n"); |
| goto probe_error; |
| } |
| /**********************************************************/ |
| mydbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb); |
| |
| DeviceData.portb |= NEXT_BOARD_POWER_BIT; |
| mydbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb); |
| |
| status = box_set_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_set_device failed\n"); |
| goto probe_error; |
| } |
| |
| mydbg("Exit Success %s\n", __func__); |
| |
| usb_set_intfdata(interface, serial); |
| return 0; |
| |
| probe_error: |
| |
| for (i = 0; i < num_bulk_in; ++i) { |
| port = &serial->port[i]; |
| usb_free_urb(port->read_urb); |
| kfree(port->bulk_in_buffer); |
| } |
| for (i = 0; i < num_bulk_out; ++i) { |
| port = &serial->port[i]; |
| usb_free_urb(port->write_urb); |
| kfree(port->bulk_out_buffer); |
| kfree(port->xfer_to_tty_buffer); |
| } |
| for (i = 0; i < num_interrupt_in; ++i) { |
| port = &serial->port[i]; |
| usb_free_urb(port->interrupt_in_urb); |
| kfree(port->interrupt_in_buffer); |
| } |
| |
| /* return the minor range that this device had */ |
| return_serial(serial); |
| mydbg("Exit fail %s\n", __func__); |
| |
| /* free up any memory that we allocated */ |
| kfree(serial); |
| return -EIO; |
| } |
| |
| /* |
| * returns the serial_table array pointers that are taken |
| * up in consecutive positions for each port to a common usb_serial structure |
| * back to NULL |
| */ |
| static void return_serial(struct usb_serial *serial) |
| { |
| int i; |
| |
| mydbg("%s\n", __func__); |
| |
| if (serial == NULL) |
| return; |
| |
| for (i = 0; i < serial->num_ports; ++i) |
| serial_table[serial->minor + i] = NULL; |
| |
| return; |
| } |
| |
| /* |
| * Finds the first locatio int the serial_table array where it can fit |
| * num_ports number of consecutive points to a common usb_serial |
| * structure,allocates a stucture points to it in all the structures, and |
| * returns the index to the first location in the array in the "minor" |
| * variable. |
| */ |
| static struct usb_serial *get_free_serial(int num_ports, int *minor) |
| { |
| struct usb_serial *serial = NULL; |
| int i, j; |
| int good_spot; |
| |
| mydbg("%s %d\n", __func__, num_ports); |
| |
| *minor = 0; |
| for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
| if (serial_table[i]) |
| continue; |
| |
| good_spot = 1; |
| /* |
| * find a spot in the array where you can fit consecutive |
| * positions to put the pointers to the usb_serail allocated |
| * structure for all the minor numbers (ie. ports) |
| */ |
| for (j = 1; j <= num_ports - 1; ++j) |
| if (serial_table[i + j]) |
| good_spot = 0; |
| if (good_spot == 0) |
| continue; |
| |
| serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL); |
| if (!serial) { |
| err("%s - Out of memory", __func__); |
| return NULL; |
| } |
| memset(serial, 0, sizeof(struct usb_serial)); |
| serial_table[i] = serial; |
| *minor = i; |
| mydbg("%s - minor base = %d\n", __func__, *minor); |
| |
| /* |
| * copy in the pointer into the array starting a the *minor |
| * position minor is the index into the array. |
| */ |
| for (i = *minor + 1; |
| (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) |
| serial_table[i] = serial; |
| return serial; |
| } |
| return NULL; |
| } |
| |
| static int flip_that(struct tty_struct *tty, __u16 UartNumber, |
| struct usb_serial *serial) |
| { |
| tty_flip_buffer_push(tty); |
| tty_schedule_flip(tty); |
| return 0; |
| } |
| |
| /* Handles processing and moving data to the tty layer */ |
| static void port_sofrint(void *private) |
| { |
| struct usb_serial_port *port = (struct usb_serial_port *)private; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| struct tty_struct *tty = port->tty; |
| unsigned char *data = port->read_urb->transfer_buffer; |
| unsigned int UartNumber; |
| struct urb *urb = port->read_urb; |
| unsigned int RxCount = urb->actual_length; |
| int i, result; |
| int flag, flag_data; |
| |
| /* UartNumber = MINOR(port->tty->device) - serial->minor; */ |
| UartNumber = tty->index - serial->minor; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| mydbg("%s - port->RxHolding = %d\n", __func__, port->RxHolding); |
| |
| if (port_paranoia_check(port, __func__) != 0) { |
| mydbg("%s - port_paranoia_check, exiting\n", __func__); |
| port->ReadBulkStopped = 1; |
| return; |
| } |
| |
| if (!serial) { |
| mydbg("%s - bad serial pointer, exiting\n", __func__); |
| return; |
| } |
| if (port->closePending == 1) { |
| /* Were closing , stop reading */ |
| mydbg("%s - (port->closepending == 1\n", __func__); |
| port->ReadBulkStopped = 1; |
| return; |
| } |
| |
| /* |
| * RxHolding is asserted by throttle, if we assert it, we're not |
| * receiving any more characters and let the box handle the flow |
| * control |
| */ |
| if (port->RxHolding == 1) { |
| port->ReadBulkStopped = 1; |
| return; |
| } |
| |
| if (urb->status) { |
| port->ReadBulkStopped = 1; |
| |
| mydbg("%s - nonzero read bulk status received: %d\n", |
| __func__, urb->status); |
| return; |
| } |
| |
| tty = port->tty; |
| mydbg("%s - port %d, tty =0x%p\n", __func__, port->number, tty); |
| |
| if (tty && RxCount) { |
| flag_data = 0; |
| for (i = 0; i < RxCount; ++i) { |
| /* Look ahead code here */ |
| if ((i <= (RxCount - 3)) && (THISCHAR == 0x1b) |
| && (NEXTCHAR == 0x1b)) { |
| flag = 0; |
| switch (THIRDCHAR) { |
| case 0x00: |
| /* Line status change 4th byte must follow */ |
| if (i > (RxCount - 4)) { |
| mydbg("Illegal escape sequences in received data\n"); |
| break; |
| } |
| ProcessLineStatus(port, FOURTHCHAR); |
| i += 3; |
| flag = 1; |
| break; |
| |
| case 0x01: |
| /* Modem status status change 4th byte must follow */ |
| mydbg("Modem status status. \n"); |
| if (i > (RxCount - 4)) { |
| mydbg |
| ("Illegal escape sequences in received data\n"); |
| break; |
| } |
| ProcessModemStatus(port, FOURTHCHAR); |
| i += 3; |
| flag = 1; |
| break; |
| case 0xff: |
| mydbg("No status sequence. \n"); |
| |
| ProcessRxChar(port, THISCHAR); |
| ProcessRxChar(port, NEXTCHAR); |
| i += 2; |
| break; |
| } |
| if (flag == 1) |
| continue; |
| } |
| |
| if (tty && urb->actual_length) { |
| tty_buffer_request_room(tty, 1); |
| tty_insert_flip_string(tty, (data + i), 1); |
| } |
| |
| } |
| tty_flip_buffer_push(tty); |
| } |
| |
| /* Continue trying to always read */ |
| usb_fill_bulk_urb(port->read_urb, serial->dev, |
| usb_rcvbulkpipe(serial->dev, |
| port->bulk_in_endpointAddress), |
| port->read_urb->transfer_buffer, |
| port->read_urb->transfer_buffer_length, |
| qt_read_bulk_callback, port); |
| result = usb_submit_urb(port->read_urb, GFP_ATOMIC); |
| if (result) |
| mydbg("%s - failed resubmitting read urb, error %d", |
| __func__, result); |
| else { |
| if (tty && RxCount) |
| flip_that(tty, UartNumber, serial); |
| } |
| |
| return; |
| |
| } |
| |
| static void qt_read_bulk_callback(struct urb *urb) |
| { |
| |
| struct usb_serial_port *port = (struct usb_serial_port *)urb->context; |
| |
| if (urb->status) { |
| port->ReadBulkStopped = 1; |
| mydbg("%s - nonzero write bulk status received: %d\n", |
| __func__, urb->status); |
| return; |
| } |
| |
| port_sofrint((void *)port); |
| schedule_work(&port->work); |
| } |
| |
| static void ProcessRxChar(struct usb_serial_port *port, unsigned char Data) |
| { |
| struct tty_struct *tty; |
| struct urb *urb = port->read_urb; |
| tty = port->tty; |
| /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ |
| |
| if (tty && urb->actual_length) { |
| tty_buffer_request_room(tty, 1); |
| tty_insert_flip_string(tty, &Data, 1); |
| /* tty_flip_buffer_push(tty); */ |
| } |
| |
| return; |
| } |
| |
| static void ProcessLineStatus(struct usb_serial_port *port, |
| unsigned char line_status) |
| { |
| |
| port->shadowLSR = |
| line_status & (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE | |
| SERIAL_LSR_BI); |
| return; |
| } |
| |
| static void ProcessModemStatus(struct usb_serial_port *port, |
| unsigned char modem_status) |
| { |
| |
| port->shadowMSR = modem_status; |
| wake_up_interruptible(&port->wait); |
| return; |
| } |
| |
| static void serqt_usb_disconnect(struct usb_interface *interface) |
| { |
| struct usb_serial *serial = usb_get_intfdata(interface); |
| /* struct device *dev = &interface->dev; */ |
| struct usb_serial_port *port; |
| int i; |
| |
| mydbg("%s\n", __func__); |
| if (serial) { |
| |
| serial->dev = NULL; |
| |
| for (i = 0; i < serial->num_ports; ++i) |
| serial->port[i].open_count = 0; |
| |
| for (i = 0; i < serial->num_bulk_in; ++i) { |
| port = &serial->port[i]; |
| usb_unlink_urb(port->read_urb); |
| usb_free_urb(port->read_urb); |
| kfree(port->bulk_in_buffer); |
| } |
| for (i = 0; i < serial->num_bulk_out; ++i) { |
| port = &serial->port[i]; |
| usb_unlink_urb(port->write_urb); |
| usb_free_urb(port->write_urb); |
| kfree(port->bulk_out_buffer); |
| } |
| for (i = 0; i < serial->num_interrupt_in; ++i) { |
| port = &serial->port[i]; |
| usb_unlink_urb(port->interrupt_in_urb); |
| usb_free_urb(port->interrupt_in_urb); |
| kfree(port->interrupt_in_buffer); |
| } |
| |
| /* return the minor range that this device had */ |
| return_serial(serial); |
| |
| /* free up any memory that we allocated */ |
| kfree(serial); |
| |
| } else { |
| dev_info(&interface->dev, "device disconnected"); |
| } |
| |
| } |
| |
| static struct usb_serial *get_serial_by_minor(unsigned int minor) |
| { |
| return serial_table[minor]; |
| } |
| |
| /***************************************************************************** |
| * Driver tty interface functions |
| *****************************************************************************/ |
| static int serial_open(struct tty_struct *tty, struct file *filp) |
| { |
| struct usb_serial *serial; |
| struct usb_serial_port *port; |
| unsigned int portNumber; |
| int retval = 0; |
| |
| mydbg("%s\n", __func__); |
| |
| /* initialize the pointer incase something fails */ |
| tty->driver_data = NULL; |
| |
| /* get the serial object associated with this tty pointer */ |
| /* serial = get_serial_by_minor (MINOR(tty->device)); */ |
| |
| /* get the serial object associated with this tty pointer */ |
| serial = get_serial_by_minor(tty->index); |
| |
| if (serial_paranoia_check(serial, __func__)) |
| return -ENODEV; |
| |
| /* set up our port structure making the tty driver remember our port object, and us it */ |
| portNumber = tty->index - serial->minor; |
| port = &serial->port[portNumber]; |
| tty->driver_data = port; |
| |
| down(&port->sem); |
| port->tty = tty; |
| |
| ++port->open_count; |
| if (port->open_count == 1) { |
| port->closePending = 0; |
| mydbg("%s port->closepending = 0\n", __func__); |
| |
| port->RxHolding = 0; |
| mydbg("%s port->RxHolding = 0\n", __func__); |
| |
| retval = qt_open(port, filp); |
| } |
| |
| if (retval) |
| port->open_count = 0; |
| mydbg("%s returning port->closePending = %d\n", __func__, |
| port->closePending); |
| |
| up(&port->sem); |
| return retval; |
| } |
| |
| /***************************************************************************** |
| *device's specific driver functions |
| *****************************************************************************/ |
| static int qt_open(struct usb_serial_port *port, struct file *filp) |
| { |
| struct usb_serial *serial = port->serial; |
| int result = 0; |
| unsigned int UartNumber; |
| struct qt_get_device_data DeviceData; |
| struct qt_open_channel_data ChannelData; |
| unsigned short default_divisor = 0x30; /* gives 9600 baud rate */ |
| unsigned char default_LCR = SERIAL_8_DATA; /* 8, none , 1 */ |
| int status = 0; |
| |
| if (port_paranoia_check(port, __func__)) |
| return -ENODEV; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| /* force low_latency on so that our tty_push actually forces the data through, |
| otherwise it is scheduled, and with high data rates (like with OHCI) data |
| can get lost. */ |
| if (port->tty) |
| port->tty->low_latency = 0; |
| |
| UartNumber = port->tty->index - serial->minor; |
| |
| status = box_get_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_get_device failed\n"); |
| return status; |
| } |
| serial->num_OpenCount++; |
| mydbg("%s serial->num_OpenCount = %d\n", __func__, |
| serial->num_OpenCount); |
| /* Open uart channel */ |
| |
| /* Port specific setups */ |
| status = BoxOPenCloseChannel(serial, UartNumber, 1, &ChannelData); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxOPenCloseChannel failed\n"); |
| return status; |
| } |
| mydbg(__FILE__ "BoxOPenCloseChannel completed.\n"); |
| |
| port->shadowLSR = ChannelData.line_status & |
| (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE | SERIAL_LSR_BI); |
| |
| port->shadowMSR = ChannelData.modem_status & |
| (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_CD); |
| |
| /* Set Baud rate to default and turn off (default)flow control here */ |
| status = BoxSetUart(serial, UartNumber, default_divisor, default_LCR); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetUart failed\n"); |
| return status; |
| } |
| mydbg(__FILE__ "BoxSetUart completed.\n"); |
| |
| /* Put this here to make it responsive to stty and defauls set by the tty layer */ |
| qt_set_termios(port, NULL); |
| |
| /* Initialize the wait que head */ |
| init_waitqueue_head(&(port->wait)); |
| |
| /* if we have a bulk endpoint, start reading from it */ |
| if (serial->num_bulk_in) { |
| /* Start reading from the device */ |
| usb_fill_bulk_urb(port->read_urb, serial->dev, |
| usb_rcvbulkpipe(serial->dev, |
| port-> |
| bulk_in_endpointAddress), |
| port->read_urb->transfer_buffer, |
| port->read_urb->transfer_buffer_length, |
| qt_read_bulk_callback, port); |
| |
| port->ReadBulkStopped = 0; |
| |
| result = usb_submit_urb(port->read_urb, GFP_ATOMIC); |
| |
| if (result) { |
| err("%s - failed resubmitting read urb, error %d\n", |
| __func__, result); |
| port->ReadBulkStopped = 1; |
| } |
| |
| } |
| |
| return result; |
| } |
| |
| static void serial_close(struct tty_struct *tty, struct file *filp) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| |
| if (!serial) |
| return; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| /* if disconnect beat us to the punch here, there's nothing to do */ |
| if (tty->driver_data) { |
| if (!port->open_count) { |
| mydbg("%s - port not opened\n", __func__); |
| goto exit; |
| } |
| |
| --port->open_count; |
| if (port->open_count <= 0) { |
| port->closePending = 1; |
| mydbg("%s - port->closePending = 1\n", __func__); |
| |
| if (serial->dev) { |
| qt_close(port, filp); |
| port->open_count = 0; |
| } |
| } |
| |
| } |
| |
| exit: |
| up(&port->sem); |
| |
| mydbg("%s - %d return\n", __func__, port->number); |
| |
| } |
| |
| static void qt_close(struct usb_serial_port *port, struct file *filp) |
| { |
| unsigned long jift = jiffies + 10 * HZ; |
| __u8 LSR_Value, MCR_Value; |
| struct usb_serial *serial = port->serial; |
| int status; |
| unsigned int UartNumber; |
| |
| struct qt_open_channel_data ChannelData; |
| status = 0; |
| LSR_Value = 0; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| UartNumber = port->tty->index - serial->minor; |
| |
| /* shutdown any bulk reads that might be going on */ |
| if (serial->num_bulk_out) |
| usb_unlink_urb(port->write_urb); |
| if (serial->num_bulk_in) |
| usb_unlink_urb(port->read_urb); |
| |
| /* wait up to 30 seconds for transmitter to empty */ |
| do { |
| status = BoxGetRegister(serial, UartNumber, LINE_STATUS_REGISTER, &LSR_Value); |
| if (status < 0) { |
| mydbg(__FILE__ "box_get_device failed\n"); |
| break; |
| } |
| |
| if ((LSR_Value & SERIAL_LSR_TEMT) |
| && (port->ReadBulkStopped == 1)) |
| break; |
| schedule(); |
| |
| } |
| while (jiffies <= jift) |
| ; |
| |
| if (jiffies > jift) |
| mydbg("%s - port %d timout of checking transmitter empty\n", |
| __func__, port->number); |
| else |
| mydbg("%s - port %d checking transmitter empty succeded\n", |
| __func__, port->number); |
| |
| status = |
| BoxGetRegister(serial, UartNumber, MODEM_CONTROL_REGISTER, |
| &MCR_Value); |
| mydbg(__FILE__ "BoxGetRegister MCR = 0x%x.\n", MCR_Value); |
| |
| if (status >= 0) { |
| MCR_Value &= ~(SERIAL_MCR_DTR | SERIAL_MCR_RTS); |
| /* status = BoxSetRegister(serial, UartNumber, MODEM_CONTROL_REGISTER, MCR_Value); */ |
| } |
| |
| /* Close uart channel */ |
| status = BoxOPenCloseChannel(serial, UartNumber, 0, &ChannelData); |
| if (status < 0) |
| mydbg("%s - port %d BoxOPenCloseChannel failed.\n", |
| __func__, port->number); |
| |
| serial->num_OpenCount--; |
| |
| } |
| |
| static int serial_write(struct tty_struct *tty, const unsigned char *buf, |
| int count) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial; |
| int retval = -EINVAL; |
| unsigned int UartNumber; |
| int from_user = 0; |
| |
| serial = get_usb_serial(port, __func__); |
| if (serial == NULL) |
| return -ENODEV; |
| /* This can happen if we get disconnected a */ |
| if (port->open_count == 0) |
| return -ENODEV; |
| UartNumber = port->tty->index - serial->minor; |
| |
| mydbg("%s - port %d, %d byte(s)\n", __func__, port->number, count); |
| mydbg("%s - port->RxHolding = %d\n", __func__, port->RxHolding); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not opened\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_write(port, from_user, buf, count); |
| |
| exit: |
| return retval; |
| } |
| |
| static int qt_write(struct usb_serial_port *port, int from_user, |
| const unsigned char *buf, int count) |
| { |
| int result; |
| unsigned int UartNumber; |
| |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| if (serial == NULL) |
| return -ENODEV; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (count == 0) { |
| mydbg("%s - write request of 0 bytes\n", __func__); |
| return 0; |
| } |
| |
| UartNumber = port->tty->index - serial->minor; |
| /* only do something if we have a bulk out endpoint */ |
| if (serial->num_bulk_out) { |
| if (port->write_urb->status == -EINPROGRESS) { |
| mydbg("%s - already writing\n", __func__); |
| return 0; |
| } |
| |
| count = |
| (count > port->bulk_out_size) ? port->bulk_out_size : count; |
| |
| if (from_user) { |
| if (copy_from_user |
| (port->write_urb->transfer_buffer, buf, count)) |
| return -EFAULT; |
| } else { |
| memcpy(port->write_urb->transfer_buffer, buf, count); |
| } |
| |
| /* usb_serial_debug_data(__FILE__, __func__, count, port->write_urb->transfer_buffer); */ |
| |
| /* set up our urb */ |
| |
| usb_fill_bulk_urb(port->write_urb, serial->dev, |
| usb_sndbulkpipe(serial->dev, |
| port-> |
| bulk_out_endpointAddress), |
| port->write_urb->transfer_buffer, count, |
| qt_write_bulk_callback, port); |
| |
| /* send the data out the bulk port */ |
| result = usb_submit_urb(port->write_urb, GFP_ATOMIC); |
| if (result) |
| mydbg("%s - failed submitting write urb, error %d\n", |
| __func__, result); |
| else |
| result = count; |
| |
| return result; |
| } |
| |
| /* no bulk out, so return 0 bytes written */ |
| return 0; |
| } |
| |
| static void qt_write_bulk_callback(struct urb *urb) |
| { |
| struct usb_serial_port *port = (struct usb_serial_port *)urb->context; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!serial) { |
| mydbg("%s - bad serial pointer, exiting\n", __func__); |
| return; |
| } |
| |
| if (urb->status) { |
| mydbg("%s - nonzero write bulk status received: %d\n", |
| __func__, urb->status); |
| return; |
| } |
| port_softint(&port->work); |
| schedule_work(&port->work); |
| |
| return; |
| } |
| |
| static void port_softint(struct work_struct *work) |
| { |
| struct usb_serial_port *port = |
| container_of(work, struct usb_serial_port, work); |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| struct tty_struct *tty; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!serial) |
| return; |
| |
| tty = port->tty; |
| if (!tty) |
| return; |
| #if 0 |
| if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) |
| && tty->ldisc.write_wakeup) { |
| mydbg("%s - write wakeup call.\n", __func__); |
| (tty->ldisc.write_wakeup) (tty); |
| } |
| #endif |
| |
| wake_up_interruptible(&tty->write_wait); |
| } |
| static int serial_write_room(struct tty_struct *tty) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| int retval = -EINVAL; |
| |
| if (!serial) |
| return -ENODEV; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_write_room(port); |
| |
| exit: |
| up(&port->sem); |
| return retval; |
| } |
| static int qt_write_room(struct usb_serial_port *port) |
| { |
| struct usb_serial *serial = port->serial; |
| int room = 0; |
| if (port->closePending == 1) { |
| mydbg("%s - port->closePending == 1\n", __func__); |
| return -ENODEV; |
| } |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (serial->num_bulk_out) { |
| if (port->write_urb->status != -EINPROGRESS) |
| room = port->bulk_out_size; |
| } |
| |
| mydbg("%s - returns %d\n", __func__, room); |
| return room; |
| } |
| static int serial_chars_in_buffer(struct tty_struct *tty) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| int retval = -EINVAL; |
| |
| if (!serial) |
| return -ENODEV; |
| |
| down(&port->sem); |
| |
| mydbg("%s = port %d\n", __func__, port->number); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_chars_in_buffer(port); |
| |
| exit: |
| up(&port->sem); |
| return retval; |
| } |
| |
| static int qt_chars_in_buffer(struct usb_serial_port *port) |
| { |
| struct usb_serial *serial = port->serial; |
| int chars = 0; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (serial->num_bulk_out) { |
| if (port->write_urb->status == -EINPROGRESS) |
| chars = port->write_urb->transfer_buffer_length; |
| } |
| |
| mydbg("%s - returns %d\n", __func__, chars); |
| return chars; |
| } |
| |
| static int serial_tiocmset(struct tty_struct *tty, struct file *file, |
| unsigned int set, unsigned int clear) |
| { |
| |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| int retval = -ENODEV; |
| unsigned int UartNumber; |
| mydbg("In %s \n", __func__); |
| |
| if (!serial) |
| return -ENODEV; |
| |
| UartNumber = port->tty->index - serial->minor; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d \n", __func__, port->number); |
| mydbg("%s - port->RxHolding = %d\n", __func__, port->RxHolding); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_tiocmset(port, file, set); |
| |
| exit: |
| up(&port->sem); |
| return retval; |
| } |
| |
| static int qt_tiocmset(struct usb_serial_port *port, struct file *file, |
| unsigned int value) |
| { |
| |
| __u8 MCR_Value; |
| int status; |
| unsigned int UartNumber; |
| |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| if (serial == NULL) |
| return -ENODEV; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| /**************************************************************************************/ |
| /** TIOCMGET |
| */ |
| UartNumber = port->tty->index - serial->minor; |
| status = |
| BoxGetRegister(port->serial, UartNumber, MODEM_CONTROL_REGISTER, |
| &MCR_Value); |
| if (status < 0) |
| return -ESPIPE; |
| |
| /* |
| * Turn off the RTS and DTR and loopbcck and then only turn on what was |
| * asked for |
| */ |
| MCR_Value &= ~(SERIAL_MCR_RTS | SERIAL_MCR_DTR | SERIAL_MCR_LOOP); |
| if (value & TIOCM_RTS) |
| MCR_Value |= SERIAL_MCR_RTS; |
| if (value & TIOCM_DTR) |
| MCR_Value |= SERIAL_MCR_DTR; |
| if (value & TIOCM_LOOP) |
| MCR_Value |= SERIAL_MCR_LOOP; |
| |
| status = |
| BoxSetRegister(port->serial, UartNumber, MODEM_CONTROL_REGISTER, |
| MCR_Value); |
| if (status < 0) |
| return -ESPIPE; |
| else |
| return 0; |
| } |
| |
| static int serial_tiocmget(struct tty_struct *tty, struct file *file) |
| { |
| |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| int retval = -ENODEV; |
| unsigned int UartNumber; |
| mydbg("In %s \n", __func__); |
| |
| if (!serial) |
| return -ENODEV; |
| |
| UartNumber = port->tty->index - serial->minor; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| mydbg("%s - port->RxHolding = %d\n", __func__, port->RxHolding); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_tiocmget(port, file); |
| |
| exit: |
| up(&port->sem); |
| return retval; |
| } |
| |
| static int qt_tiocmget(struct usb_serial_port *port, struct file *file) |
| { |
| |
| __u8 MCR_Value; |
| __u8 MSR_Value; |
| unsigned int result = 0; |
| int status; |
| unsigned int UartNumber; |
| struct tty_struct *tty; |
| |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| if (serial == NULL) |
| return -ENODEV; |
| tty = port->tty; |
| |
| mydbg("%s - port %d, tty =0x%p\n", __func__, port->number, tty); |
| |
| /**************************************************************************************/ |
| /** TIOCMGET |
| */ |
| UartNumber = port->tty->index - serial->minor; |
| status = |
| BoxGetRegister(port->serial, UartNumber, MODEM_CONTROL_REGISTER, |
| &MCR_Value); |
| if (status >= 0) { |
| status = |
| BoxGetRegister(port->serial, UartNumber, |
| MODEM_STATUS_REGISTER, &MSR_Value); |
| |
| } |
| |
| if (status >= 0) { |
| result = ((MCR_Value & SERIAL_MCR_DTR) ? TIOCM_DTR : 0) |
| /* DTR IS SET */ |
| | ((MCR_Value & SERIAL_MCR_RTS) ? TIOCM_RTS : 0) |
| /* RTS IS SET */ |
| | ((MSR_Value & SERIAL_MSR_CTS) ? TIOCM_CTS : 0) |
| /* CTS is set */ |
| | ((MSR_Value & SERIAL_MSR_CD) ? TIOCM_CAR : 0) |
| /* Carrier detect is set */ |
| | ((MSR_Value & SERIAL_MSR_RI) ? TIOCM_RI : 0) |
| /* Ring indicator set */ |
| | ((MSR_Value & SERIAL_MSR_DSR) ? TIOCM_DSR : 0); |
| /* DSR is set */ |
| return result; |
| |
| } else |
| return -ESPIPE; |
| } |
| |
| static int serial_ioctl(struct tty_struct *tty, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| int retval = -ENODEV; |
| unsigned int UartNumber; |
| mydbg("In %s \n", __func__); |
| |
| if (!serial) |
| return -ENODEV; |
| |
| UartNumber = port->tty->index - serial->minor; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d, cmd 0x%.4x\n", __func__, port->number, cmd); |
| mydbg("%s - port->RxHolding = %d\n", __func__, port->RxHolding); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| retval = qt_ioctl(port, file, cmd, arg); |
| |
| exit: |
| up(&port->sem); |
| return retval; |
| } |
| static int qt_ioctl(struct usb_serial_port *port, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| __u8 MCR_Value; |
| __u8 MSR_Value; |
| unsigned short Prev_MSR_Value; |
| unsigned int value, result = 0; |
| int status; |
| unsigned int UartNumber; |
| struct tty_struct *tty; |
| |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| if (serial == NULL) |
| return -ENODEV; |
| tty = port->tty; |
| |
| mydbg("%s - port %d, tty =0x%p\n", __func__, port->number, tty); |
| |
| /* TIOCMGET */ |
| UartNumber = port->tty->index - serial->minor; |
| |
| if (cmd == TIOCMGET) { |
| MCR_Value = port->shadowMCR; |
| MSR_Value = port->shadowMSR; |
| |
| { |
| result = ((MCR_Value & SERIAL_MCR_DTR) ? TIOCM_DTR : 0) |
| /* DTR IS SET */ |
| | ((MCR_Value & SERIAL_MCR_RTS) ? TIOCM_RTS : 0) |
| /* RTS IS SET */ |
| | ((MSR_Value & SERIAL_MSR_CTS) ? TIOCM_CTS : 0) |
| /* CTS is set */ |
| | ((MSR_Value & SERIAL_MSR_CD) ? TIOCM_CAR : 0) |
| /* Carrier detect is set */ |
| | ((MSR_Value & SERIAL_MSR_RI) ? TIOCM_RI : 0) |
| /* Ring indicator set */ |
| | ((MSR_Value & SERIAL_MSR_DSR) ? TIOCM_DSR : 0); |
| /* DSR is set */ |
| if (copy_to_user |
| ((unsigned int *)arg, &result, |
| sizeof(unsigned int))) |
| return -EFAULT; |
| return 0; |
| |
| } |
| } |
| |
| /* TIOCMBIS, TIOCMBIC, AND TIOCMSET */ |
| if (cmd == TIOCMBIS || cmd == TIOCMBIC || cmd == TIOCMSET) { |
| status = |
| BoxGetRegister(port->serial, UartNumber, |
| MODEM_CONTROL_REGISTER, &MCR_Value); |
| if (status < 0) |
| return -ESPIPE; |
| if (copy_from_user |
| (&value, (unsigned int *)arg, sizeof(unsigned int))) |
| return -EFAULT; |
| |
| switch (cmd) { |
| case TIOCMBIS: |
| if (value & TIOCM_RTS) |
| MCR_Value |= SERIAL_MCR_RTS; |
| if (value & TIOCM_DTR) |
| MCR_Value |= SERIAL_MCR_DTR; |
| if (value & TIOCM_LOOP) |
| MCR_Value |= SERIAL_MCR_LOOP; |
| break; |
| case TIOCMBIC: |
| if (value & TIOCM_RTS) |
| MCR_Value &= ~SERIAL_MCR_RTS; |
| if (value & TIOCM_DTR) |
| MCR_Value &= ~SERIAL_MCR_DTR; |
| if (value & TIOCM_LOOP) |
| MCR_Value &= ~SERIAL_MCR_LOOP; |
| break; |
| case TIOCMSET: |
| /* |
| * Turn off the RTS and DTR and loopbcck and then only |
| * turn on what was asked for |
| */ |
| MCR_Value &= |
| ~(SERIAL_MCR_RTS | SERIAL_MCR_DTR | |
| SERIAL_MCR_LOOP); |
| if (value & TIOCM_RTS) |
| MCR_Value |= SERIAL_MCR_RTS; |
| if (value & TIOCM_DTR) |
| MCR_Value |= SERIAL_MCR_DTR; |
| if (value & TIOCM_LOOP) |
| MCR_Value |= SERIAL_MCR_LOOP; |
| break; |
| default: |
| break; |
| |
| } |
| status = |
| BoxSetRegister(port->serial, UartNumber, |
| MODEM_CONTROL_REGISTER, MCR_Value); |
| if (status < 0) |
| return -ESPIPE; |
| else { |
| port->shadowMCR = MCR_Value; |
| return 0; |
| } |
| |
| } |
| /**************************************************************************************/ |
| /** TIOCMBIS, TIOCMBIC, AND TIOCMSET end |
| */ |
| /**************************************************************************************/ |
| |
| if (cmd == TIOCMIWAIT) { |
| DECLARE_WAITQUEUE(wait, current); |
| Prev_MSR_Value = port->shadowMSR & SERIAL_MSR_MASK; |
| while (1) { |
| add_wait_queue(&port->wait, &wait); |
| set_current_state(TASK_INTERRUPTIBLE); |
| schedule(); |
| remove_wait_queue(&port->wait, &wait); |
| /* see if a signal woke us up */ |
| if (signal_pending(current)) |
| return -ERESTARTSYS; |
| MSR_Value = port->shadowMSR & SERIAL_MSR_MASK; |
| if (MSR_Value == Prev_MSR_Value) |
| return -EIO; /* no change error */ |
| |
| if ((arg & TIOCM_RNG |
| && ((Prev_MSR_Value & SERIAL_MSR_RI) == |
| (MSR_Value & SERIAL_MSR_RI))) |
| || (arg & TIOCM_DSR |
| && ((Prev_MSR_Value & SERIAL_MSR_DSR) == |
| (MSR_Value & SERIAL_MSR_DSR))) |
| || (arg & TIOCM_CD |
| && ((Prev_MSR_Value & SERIAL_MSR_CD) == |
| (MSR_Value & SERIAL_MSR_CD))) |
| || (arg & TIOCM_CTS |
| && ((Prev_MSR_Value & SERIAL_MSR_CTS) == |
| (MSR_Value & SERIAL_MSR_CTS)))) { |
| return 0; |
| } |
| |
| } |
| |
| } |
| mydbg("%s -No ioctl for that one. port = %d\n", __func__, |
| port->number); |
| |
| return -ENOIOCTLCMD; |
| } |
| |
| static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| |
| if (!serial) |
| return; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| /* pass on to the driver specific version of this function if it is available */ |
| qt_set_termios(port, old); |
| |
| exit: |
| up(&port->sem); |
| } |
| |
| static void qt_set_termios(struct usb_serial_port *port, |
| struct ktermios *old_termios) |
| { |
| unsigned int cflag; |
| int baud, divisor, remainder; |
| unsigned char LCR_change_to = 0; |
| struct tty_struct *tty; |
| int status; |
| struct usb_serial *serial; |
| __u16 UartNumber; |
| __u16 tmp, tmp2; |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| tmp = port->tty->index; |
| mydbg("%s - MINOR(port->tty->index) = %d\n", __func__, tmp); |
| |
| serial = port->serial; |
| tmp2 = serial->minor; |
| mydbg("%s - serial->minor = %d\n", __func__, tmp2); |
| |
| UartNumber = port->tty->index - serial->minor; |
| |
| tty = port->tty; |
| |
| cflag = tty->termios->c_cflag; |
| |
| if (old_termios) { |
| if ((cflag == old_termios->c_cflag) |
| && (RELEVANT_IFLAG(tty->termios->c_iflag) == |
| RELEVANT_IFLAG(old_termios->c_iflag))) { |
| mydbg("%s - Nothing to change\n", __func__); |
| return; |
| } |
| |
| } |
| |
| mydbg("%s - 3\n", __func__); |
| |
| switch (cflag) { |
| case CS5: |
| LCR_change_to |= SERIAL_5_DATA; |
| break; |
| case CS6: |
| LCR_change_to |= SERIAL_6_DATA; |
| break; |
| case CS7: |
| LCR_change_to |= SERIAL_7_DATA; |
| break; |
| default: |
| case CS8: |
| LCR_change_to |= SERIAL_8_DATA; |
| break; |
| } |
| |
| /* Parity stuff */ |
| if (cflag & PARENB) { |
| if (cflag & PARODD) |
| LCR_change_to |= SERIAL_ODD_PARITY; |
| else |
| LCR_change_to |= SERIAL_EVEN_PARITY; |
| } |
| if (cflag & CSTOPB) |
| LCR_change_to |= SERIAL_TWO_STOPB; |
| else |
| LCR_change_to |= SERIAL_TWO_STOPB; |
| |
| mydbg("%s - 4\n", __func__); |
| /* Thats the LCR stuff, go ahead and set it */ |
| baud = tty_get_baud_rate(tty); |
| if (!baud) { |
| /* pick a default, any default... */ |
| baud = 9600; |
| } |
| |
| mydbg("%s - got baud = %d\n", __func__, baud); |
| |
| divisor = MAX_BAUD_RATE / baud; |
| remainder = MAX_BAUD_RATE % baud; |
| /* Round to nearest divisor */ |
| if (((remainder * 2) >= baud) && (baud != 110)) |
| divisor++; |
| |
| /* |
| * Set Baud rate to default and turn off (default)flow control here |
| */ |
| status = |
| BoxSetUart(serial, UartNumber, (unsigned short)divisor, |
| LCR_change_to); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetUart failed\n"); |
| return; |
| } |
| |
| /* Now determine flow control */ |
| if (cflag & CRTSCTS) { |
| mydbg("%s - Enabling HW flow control port %d\n", __func__, |
| port->number); |
| |
| /* Enable RTS/CTS flow control */ |
| status = BoxSetHW_FlowCtrl(serial, UartNumber, 1); |
| |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetHW_FlowCtrl failed\n"); |
| return; |
| } |
| } else { |
| /* Disable RTS/CTS flow control */ |
| mydbg("%s - disabling HW flow control port %d\n", __func__, |
| port->number); |
| |
| status = BoxSetHW_FlowCtrl(serial, UartNumber, 0); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetHW_FlowCtrl failed\n"); |
| return; |
| } |
| |
| } |
| |
| /* if we are implementing XON/XOFF, set the start and stop character in |
| * the device */ |
| if (I_IXOFF(tty) || I_IXON(tty)) { |
| unsigned char stop_char = STOP_CHAR(tty); |
| unsigned char start_char = START_CHAR(tty); |
| status = |
| BoxSetSW_FlowCtrl(serial, UartNumber, stop_char, |
| start_char); |
| if (status < 0) |
| mydbg(__FILE__ "BoxSetSW_FlowCtrl (enabled) failed\n"); |
| |
| } else { |
| /* disable SW flow control */ |
| status = BoxDisable_SW_FlowCtrl(serial, UartNumber); |
| if (status < 0) |
| mydbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n"); |
| |
| } |
| |
| } |
| |
| /**************************************************************************** |
| * BoxGetRegister |
| * issuse a GET_REGISTER vendor-spcific request on the default control pipe |
| * If successful, fills in the pValue with the register value asked for |
| ****************************************************************************/ |
| static int BoxGetRegister(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short Register_Num, __u8 *pValue) |
| { |
| int result; |
| __u16 current_length; |
| |
| current_length = sizeof(struct qt_get_device_data); |
| |
| result = |
| usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), |
| QT_GET_SET_REGISTER, 0xC0, Register_Num, |
| Uart_Number, (void *)pValue, sizeof(*pValue), 300); |
| |
| return result; |
| } |
| |
| /**************************************************************************** |
| * BoxSetRegister |
| * issuse a GET_REGISTER vendor-spcific request on the default control pipe |
| * If successful, fills in the pValue with the register value asked for |
| ****************************************************************************/ |
| static int BoxSetRegister(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short Register_Num, unsigned short Value) |
| { |
| int result; |
| unsigned short RegAndByte; |
| |
| RegAndByte = Value; |
| RegAndByte = RegAndByte << 8; |
| RegAndByte = RegAndByte + Register_Num; |
| |
| /* |
| result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_GET_SET_REGISTER, 0xC0, Register_Num, |
| Uart_Number, NULL, 0, 300); |
| */ |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_GET_SET_REGISTER, 0x40, RegAndByte, Uart_Number, |
| NULL, 0, 300); |
| |
| return result; |
| } |
| |
| /** |
| * box_get_device |
| * Issue a GET_DEVICE vendor-specific request on the default control pipe If |
| * successful, fills in the qt_get_device_data structure pointed to by |
| * device_data, otherwise return a negative error number of the problem. |
| */ |
| static int box_get_device(struct usb_serial *serial, |
| struct qt_get_device_data *device_data) |
| { |
| int result; |
| __u16 current_length; |
| unsigned char *transfer_buffer; |
| |
| current_length = sizeof(struct qt_get_device_data); |
| transfer_buffer = kmalloc(current_length, GFP_KERNEL); |
| if (!transfer_buffer) |
| return -ENOMEM; |
| |
| result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), |
| QT_SET_GET_DEVICE, 0xc0, 0, 0, |
| transfer_buffer, current_length, 300); |
| if (result > 0) |
| memcpy(device_data, transfer_buffer, current_length); |
| kfree(transfer_buffer); |
| |
| return result; |
| } |
| |
| /** |
| * box_set_device |
| * Issue a SET_DEVICE vendor-specific request on the default control pipe If |
| * successful returns the number of bytes written, otherwise it returns a |
| * negative error number of the problem. |
| */ |
| static int box_set_device(struct usb_serial *serial, |
| struct qt_get_device_data *device_data) |
| { |
| int result; |
| __u16 length; |
| __u16 PortSettings; |
| |
| PortSettings = ((__u16) (device_data->portb)); |
| PortSettings = (PortSettings << 8); |
| PortSettings += ((__u16) (device_data->porta)); |
| |
| length = sizeof(struct qt_get_device_data); |
| mydbg("%s - PortSettings = 0x%x\n", __func__, PortSettings); |
| |
| result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_SET_GET_DEVICE, 0x40, PortSettings, |
| 0, NULL, 0, 300); |
| return result; |
| } |
| |
| /**************************************************************************** |
| * BoxOPenCloseChannel |
| * This funciotn notifies the device that the device driver wishes to open a particular UART channel. its |
| * purpose is to allow the device driver and the device to synchronize state information. |
| * OpenClose = 1 for open , 0 for close |
| ****************************************************************************/ |
| static int BoxOPenCloseChannel(struct usb_serial *serial, __u16 Uart_Number, |
| __u16 OpenClose, |
| struct qt_open_channel_data *pDeviceData) |
| { |
| int result; |
| __u16 length; |
| __u8 Direcion; |
| unsigned int pipe; |
| length = sizeof(struct qt_open_channel_data); |
| |
| /* if opening... */ |
| if (OpenClose == 1) { |
| Direcion = USBD_TRANSFER_DIRECTION_IN; |
| pipe = usb_rcvctrlpipe(serial->dev, 0); |
| result = |
| usb_control_msg(serial->dev, pipe, QT_OPEN_CLOSE_CHANNEL, |
| Direcion, OpenClose, Uart_Number, |
| pDeviceData, length, 300); |
| |
| } else { |
| Direcion = USBD_TRANSFER_DIRECTION_OUT; |
| pipe = usb_sndctrlpipe(serial->dev, 0); |
| result = |
| usb_control_msg(serial->dev, pipe, QT_OPEN_CLOSE_CHANNEL, |
| Direcion, OpenClose, Uart_Number, NULL, 0, |
| 300); |
| |
| } |
| |
| return result; |
| } |
| |
| /**************************************************************************** |
| * BoxSetPrebufferLevel |
| TELLS BOX WHEN TO ASSERT FLOW CONTROL |
| ****************************************************************************/ |
| static int BoxSetPrebufferLevel(struct usb_serial *serial) |
| { |
| int result; |
| __u16 buffer_length; |
| |
| buffer_length = PREFUFF_LEVEL_CONSERVATIVE; |
| result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_GET_SET_PREBUF_TRIG_LVL, 0x40, |
| buffer_length, 0, NULL, 0, 300); |
| return result; |
| } |
| |
| /**************************************************************************** |
| * BoxSetATC |
| TELLS BOX WHEN TO ASSERT automatic transmitter control |
| ****************************************************************************/ |
| static int BoxSetATC(struct usb_serial *serial, __u16 n_Mode) |
| { |
| int result; |
| __u16 buffer_length; |
| |
| buffer_length = PREFUFF_LEVEL_CONSERVATIVE; |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_SET_ATF, 0x40, n_Mode, 0, NULL, 0, 300); |
| |
| return result; |
| } |
| |
| /**************************************************************************** |
| * BoxSetUart |
| * issuse a SET_UART vendor-spcific request on the default control pipe |
| * If successful sets baud rate divisor and LCR value |
| ****************************************************************************/ |
| static int BoxSetUart(struct usb_serial *serial, unsigned short Uart_Number, |
| unsigned short default_divisor, unsigned char default_LCR) |
| { |
| int result; |
| unsigned short UartNumandLCR; |
| |
| UartNumandLCR = (default_LCR << 8) + Uart_Number; |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_GET_SET_UART, 0x40, default_divisor, |
| UartNumandLCR, NULL, 0, 300); |
| |
| return result; |
| } |
| |
| static int BoxSetHW_FlowCtrl(struct usb_serial *serial, unsigned int UartNumber, |
| int bSet) |
| { |
| __u8 MCR_Value = 0; |
| __u8 MSR_Value = 0, MOUT_Value = 0; |
| struct usb_serial_port *port; |
| unsigned int status; |
| |
| port = serial->port; |
| |
| if (bSet == 1) { |
| /* flow control, box will clear RTS line to prevent remote */ |
| MCR_Value = SERIAL_MCR_RTS; |
| } /* device from xmitting more chars */ |
| else { |
| /* no flow control to remote device */ |
| MCR_Value = 0; |
| |
| } |
| MOUT_Value = MCR_Value << 8; |
| |
| if (bSet == 1) { |
| /* flow control, box will inhibit xmit data if CTS line is |
| * asserted */ |
| MSR_Value = SERIAL_MSR_CTS; |
| } else { |
| /* Box will not inhimbe xmit data due to CTS line */ |
| MSR_Value = 0; |
| } |
| MOUT_Value |= MSR_Value; |
| |
| status = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_HW_FLOW_CONTROL_MASK, 0x40, MOUT_Value, |
| UartNumber, NULL, 0, 300); |
| return status; |
| |
| } |
| |
| static int BoxSetSW_FlowCtrl(struct usb_serial *serial, __u16 UartNumber, |
| unsigned char stop_char, unsigned char start_char) |
| { |
| __u16 nSWflowout; |
| int result; |
| |
| nSWflowout = start_char << 8; |
| nSWflowout = (unsigned short)stop_char; |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_SW_FLOW_CONTROL_MASK, 0x40, nSWflowout, |
| UartNumber, NULL, 0, 300); |
| return result; |
| |
| } |
| static int BoxDisable_SW_FlowCtrl(struct usb_serial *serial, __u16 UartNumber) |
| { |
| int result; |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_SW_FLOW_CONTROL_DISABLE, 0x40, 0, UartNumber, |
| NULL, 0, 300); |
| return result; |
| |
| } |
| |
| static void serial_throttle(struct tty_struct *tty) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!serial) |
| return; |
| |
| down(&port->sem); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| /* shut down any bulk reads that may be going on */ |
| /* usb_unlink_urb (port->read_urb); */ |
| /* pass on to the driver specific version of this function */ |
| port->RxHolding = 1; |
| mydbg("%s - port->RxHolding = 1\n", __func__); |
| |
| exit: |
| up(&port->sem); |
| return; |
| } |
| |
| static void serial_unthrottle(struct tty_struct *tty) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| unsigned int result; |
| |
| if (!serial) |
| return; |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| if (port->RxHolding == 1) { |
| mydbg("%s -port->RxHolding == 1\n", __func__); |
| |
| port->RxHolding = 0; |
| mydbg("%s - port->RxHolding = 0\n", __func__); |
| |
| /* if we have a bulk endpoint, start it up */ |
| if ((serial->num_bulk_in) && (port->ReadBulkStopped == 1)) { |
| /* Start reading from the device */ |
| usb_fill_bulk_urb(port->read_urb, serial->dev, |
| usb_rcvbulkpipe(serial->dev, |
| port-> |
| bulk_in_endpointAddress), |
| port->read_urb->transfer_buffer, |
| port->read_urb-> |
| transfer_buffer_length, |
| qt_read_bulk_callback, port); |
| result = usb_submit_urb(port->read_urb, GFP_ATOMIC); |
| if (result) |
| err("%s - failed restarting read urb, error %d", |
| __func__, result); |
| } |
| } |
| exit: |
| up(&port->sem); |
| return; |
| |
| } |
| |
| static int serial_break(struct tty_struct *tty, int break_state) |
| { |
| struct usb_serial_port *port = |
| (struct usb_serial_port *)tty->driver_data; |
| struct usb_serial *serial = get_usb_serial(port, __func__); |
| __u16 UartNumber, Break_Value; |
| unsigned int result; |
| |
| UartNumber = port->tty->index - serial->minor; |
| if (!serial) |
| return -ENODEV; |
| |
| if (break_state == -1) |
| Break_Value = 1; |
| else |
| Break_Value = 0; |
| |
| down(&port->sem); |
| |
| mydbg("%s - port %d\n", __func__, port->number); |
| |
| if (!port->open_count) { |
| mydbg("%s - port not open\n", __func__); |
| goto exit; |
| } |
| |
| result = |
| usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| QT_BREAK_CONTROL, 0x40, Break_Value, UartNumber, |
| NULL, 0, 300); |
| |
| exit: |
| up(&port->sem); |
| return 0; |
| } |
| |
| static int ioctl_serial_usb(struct inode *innod, struct file *filp, unsigned int cmd, |
| unsigned long arg) |
| { |
| |
| unsigned err; |
| unsigned ucOPR_NewValue, uc_Value; |
| int *p_Num_of_adapters, counts, index, *p_QMCR_Value; |
| struct identity *p_Identity_of; |
| struct identity Identity_of; |
| struct usb_serial *lastserial, *serial; |
| |
| mydbg(KERN_DEBUG "ioctl_serial_usb cmd =\n"); |
| if (_IOC_TYPE(cmd) != SERIALQT_PCI_IOC_MAGIC) |
| return -ENOTTY; |
| if (_IOC_NR(cmd) > SERIALQT_IOC_MAXNR) |
| return -ENOTTY; |
| mydbg(KERN_DEBUG "ioctl_serial_usb cmd = 0x%x\n", cmd); |
| err = 0; |
| switch (cmd) { |
| |
| case SERIALQT_WRITE_QMCR: |
| err = -ENOTTY; |
| index = arg >> 16; |
| counts = 0; |
| |
| ucOPR_NewValue = arg; |
| |
| err = EmulateWriteQMCR_Reg(index, ucOPR_NewValue); |
| break; |
| |
| case SERIALQT_READ_QMCR: |
| err = -ENOTTY; |
| p_QMCR_Value = (int *)arg; |
| index = arg >> 16; |
| counts = 0; |
| |
| err = EmulateReadQMCR_Reg(index, &uc_Value); |
| if (err == 0) |
| err = put_user(uc_Value, p_QMCR_Value); |
| break; |
| |
| case SERIALQT_GET_NUMOF_UNITS: |
| p_Num_of_adapters = (int *)arg; |
| counts = 0; /* Initialize counts to zero */ |
| /* struct usb_serial *lastserial = serial_table[0], *serial; */ |
| lastserial = serial_table[0]; |
| |
| mydbg(KERN_DEBUG "SERIALQT_GET_NUMOF_UNITS \n"); |
| /* if first pointer is nonull, we at least have one box */ |
| if (lastserial) |
| counts = 1; /* we at least have one box */ |
| |
| for (index = 1; index < SERIAL_TTY_MINORS; index++) { |
| serial = serial_table[index]; |
| if (serial) { |
| if (serial != lastserial) { |
| /* we had a change in the array, hence |
| * another box is there */ |
| lastserial = serial; |
| counts++; |
| } |
| } else |
| break; |
| } |
| |
| mydbg(KERN_DEBUG "ioctl_serial_usb writting counts = %d", |
| counts); |
| |
| err = put_user(counts, p_Num_of_adapters); |
| |
| break; |
| case SERIALQT_GET_THIS_UNIT: |
| counts = 0; |
| p_Identity_of = (struct identity *)arg; |
| /* copy user structure to local variable */ |
| get_user(Identity_of.index, &p_Identity_of->index); |
| mydbg(KERN_DEBUG "SERIALQT_GET_THIS_UNIT Identity_of.index\n"); |
| mydbg(KERN_DEBUG |
| "SERIALQT_GET_THIS_UNIT Identity_of.index= 0x%x\n", |
| Identity_of.index); |
| |
| err = -ENOTTY; |
| serial = find_the_box(Identity_of.index); |
| if (serial) { |
| err = |
| put_user(serial->product, |
| &p_Identity_of->n_identity); |
| |
| } |
| break; |
| |
| case SERIALQT_IS422_EXTENDED: |
| err = -ENOTTY; |
| mydbg(KERN_DEBUG "SERIALQT_IS422_EXTENDED \n"); |
| index = arg >> 16; |
| |
| counts = 0; |
| |
| mydbg(KERN_DEBUG |
| "SERIALQT_IS422_EXTENDED, looking Identity_of.indext = 0x%x\n", |
| index); |
| serial = find_the_box(index); |
| if (serial) { |
| mydbg("%s index = 0x%x, serial = 0x%p\n", __func__, |
| index, serial); |
| for (counts = 0; serqt_422_table[counts] != 0; counts++) { |
| |
| mydbg |
| ("%s serial->product = = 0x%x, serqt_422_table[counts] = 0x%x\n", |
| __func__, serial->product, |
| serqt_422_table[counts]); |
| if (serial->product == serqt_422_table[counts]) { |
| err = 0; |
| |
| mydbg |
| ("%s found match for 422extended\n", |
| __func__); |
| break; |
| } |
| } |
| } |
| break; |
| |
| default: |
| err = -ENOTTY; |
| } |
| |
| mydbg("%s returning err = 0x%x\n", __func__, err); |
| return err; |
| } |
| |
| static struct usb_serial *find_the_box(unsigned int index) |
| { |
| struct usb_serial *lastserial, *foundserial, *serial; |
| int counts = 0, index2; |
| lastserial = serial_table[0]; |
| foundserial = NULL; |
| for (index2 = 0; index2 < SERIAL_TTY_MINORS; index2++) { |
| serial = serial_table[index2]; |
| |
| mydbg("%s index = 0x%x, index2 = 0x%x, serial = 0x%p\n", |
| __func__, index, index2, serial); |
| |
| if (serial) { |
| /* first see if this is the unit we'er looking for */ |
| mydbg |
| ("%s inside if(serial) counts = 0x%x , index = 0x%x\n", |
| __func__, counts, index); |
| if (counts == index) { |
| /* we found the one we're looking for, copythe |
| * product Id to user */ |
| mydbg("%s we found the one we're looking for serial = 0x%p\n", |
| __func__, serial); |
| foundserial = serial; |
| break; |
| } |
| |
| if (serial != lastserial) { |
| /* when we have a change in the pointer */ |
| lastserial = serial; |
| counts++; |
| } |
| } else |
| break; /* no matches */ |
| } |
| |
| mydbg("%s returning foundserial = 0x%p\n", __func__, foundserial); |
| return foundserial; |
| } |
| |
| static int EmulateWriteQMCR_Reg(int index, unsigned uc_value) |
| { |
| |
| __u16 ATC_Mode = 0; |
| struct usb_serial *serial; |
| int status; |
| struct qt_get_device_data DeviceData; |
| unsigned uc_temp = 0; |
| mydbg("Inside %s, uc_value = 0x%x\n", __func__, uc_value); |
| |
| DeviceData.porta = 0; |
| DeviceData.portb = 0; |
| serial = find_the_box(index); |
| /* Determine Duplex mode */ |
| if (!(serial)) |
| return -ENOTTY; |
| status = box_get_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_set_device failed\n"); |
| return status; |
| } |
| |
| uc_temp = uc_value & QMCR_HALF_DUPLEX_MASK; |
| switch (uc_temp) { |
| case QMCR_FULL_DUPLEX: |
| DeviceData.porta &= ~DUPMODE_BITS; |
| DeviceData.porta |= FULL_DUPLEX; |
| ATC_Mode = ATC_DISABLED; |
| break; |
| case QMCR_HALF_DUPLEX_RTS: |
| DeviceData.porta &= ~DUPMODE_BITS; |
| DeviceData.porta |= HALF_DUPLEX_RTS; |
| ATC_Mode = ATC_RTS_ENABLED; |
| break; |
| case QMCR_HALF_DUPLEX_DTR: |
| DeviceData.porta &= ~DUPMODE_BITS; |
| DeviceData.porta |= HALF_DUPLEX_DTR; |
| ATC_Mode = ATC_DTR_ENABLED; |
| break; |
| default: |
| break; |
| } |
| |
| uc_temp = uc_value & QMCR_CONNECTOR_MASK; |
| switch (uc_temp) { |
| case QMCR_MODEM_CONTROL: |
| DeviceData.portb &= ~LOOPMODE_BITS; /* reset connection bits */ |
| DeviceData.portb |= MODEM_CTRL; |
| break; |
| case QMCR_ALL_LOOPBACK: |
| DeviceData.portb &= ~LOOPMODE_BITS; /* reset connection bits */ |
| DeviceData.portb |= ALL_LOOPBACK; |
| break; |
| } |
| |
| mydbg(__FILE__ "Calling box_set_device with failed\n"); |
| status = box_set_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_set_device failed\n"); |
| return status; |
| } |
| |
| /* This bit (otherwise unused) i'll used to detect whether ATC is |
| * selected */ |
| if (uc_value & QMCR_RX_EN_MASK) { |
| |
| mydbg(__FILE__ |
| "calling BoxsetATC with DeviceData.porta = 0x%x and DeviceData.portb = 0x%x\n", |
| DeviceData.porta, DeviceData.portb); |
| status = BoxSetATC(serial, ATC_Mode); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetATC failed\n"); |
| return status; |
| } |
| } else { |
| |
| mydbg(__FILE__ |
| "calling BoxsetATC with DeviceData.porta = 0x%x and DeviceData.portb = 0x%x\n", |
| DeviceData.porta, DeviceData.portb); |
| status = BoxSetATC(serial, ATC_DISABLED); |
| if (status < 0) { |
| mydbg(__FILE__ "BoxSetATC failed\n"); |
| return status; |
| } |
| } |
| |
| return 0; |
| |
| } |
| |
| static int EmulateReadQMCR_Reg(int index, unsigned *uc_value) |
| { |
| struct usb_serial *serial; |
| int status; |
| struct qt_get_device_data DeviceData; |
| __u8 uc_temp; |
| |
| *uc_value = 0; |
| |
| serial = find_the_box(index); |
| if (!(serial)) |
| return -ENOTTY; |
| |
| status = box_get_device(serial, &DeviceData); |
| if (status < 0) { |
| mydbg(__FILE__ "box_get_device failed\n"); |
| return status; |
| } |
| uc_temp = DeviceData.porta & DUPMODE_BITS; |
| switch (uc_temp) { |
| case FULL_DUPLEX: |
| *uc_value |= QMCR_FULL_DUPLEX; |
| break; |
| case HALF_DUPLEX_RTS: |
| *uc_value |= QMCR_HALF_DUPLEX_RTS; |
| break; |
| case HALF_DUPLEX_DTR: |
| *uc_value |= QMCR_HALF_DUPLEX_DTR; |
| break; |
| default: |
| break; |
| } |
| |
| /* I use this for ATC control se */ |
| uc_temp = DeviceData.portb & LOOPMODE_BITS; |
| |
| switch (uc_temp) { |
| case ALL_LOOPBACK: |
| *uc_value |= QMCR_ALL_LOOPBACK; |
| break; |
| case MODEM_CTRL: |
| *uc_value |= QMCR_MODEM_CONTROL; |
| break; |
| default: |
| break; |
| |
| } |
| return 0; |
| |
| } |
| |
| static int __init serqt_usb_init(void) |
| { |
| int i, result; |
| int status = 0; |
| |
| mydbg("%s\n", __func__); |
| tty_set_operations(&serial_tty_driver, &serial_ops); |
| result = tty_register_driver(&serial_tty_driver); |
| if (result) { |
| mydbg("tty_register_driver failed error = 0x%x", result); |
| return result; |
| } |
| |
| /* Initalize our global data */ |
| for (i = 0; i < SERIAL_TTY_MINORS; ++i) |
| serial_table[i] = NULL; |
| |
| /* register this driver with the USB subsystem */ |
| result = usb_register(&serqt_usb_driver); |
| if (result < 0) { |
| err("usb_register failed for the " __FILE__ |
| " driver. Error number %d", result); |
| return result; |
| } |
| status = 0; /* Dynamic assignment of major number */ |
| major_number = |
| register_chrdev(status, "SerialQT_USB", &serialqt_usb_fops); |
| if (major_number < 0) { |
| mydbg(KERN_DEBUG "No devices found \n\n"); |
| return -EBUSY; |
| } else |
| mydbg(KERN_DEBUG "SerQT_USB major number assignment = %d \n\n", |
| major_number); |
| |
| printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION); |
| return 0; |
| } |
| |
| static void __exit serqt_usb_exit(void) |
| { |
| /* deregister this driver with the USB subsystem */ |
| usb_deregister(&serqt_usb_driver); |
| tty_unregister_driver(&serial_tty_driver); |
| unregister_chrdev(major_number, "SerialQT_USB"); |
| } |
| |
| module_init(serqt_usb_init); |
| module_exit(serqt_usb_exit); |
| |
| MODULE_AUTHOR(DRIVER_AUTHOR); |
| MODULE_DESCRIPTION(DRIVER_DESC); |
| MODULE_LICENSE("GPL"); |