#define __NO_VERSION__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #define PL2303_LOCK(port,flags) \ do { \ spin_lock_irqsave(&port->lock, flags); \ } while (0) #define PL2303_UNLOCK(port,flags) \ do { \ spin_unlock_irqrestore(&port->lock, flags); \ } while (0) #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG #else #undef DEBUG #endif #undef DEBUG #include #include "usb-serial.h" #include "pl2303.h" /* function prototypes for a PL2303 serial converter */ static int pl2303_startup (struct usb_serial *serial); static int pl2303_open (struct usb_serial_port *port, struct file *filp); static void pl2303_close (struct usb_serial_port *port, struct file *filp); static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old); static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); static void pl2303_throttle (struct usb_serial_port *port); static void pl2303_unthrottle (struct usb_serial_port *port); static void pl2303_read_int_callback (struct urb *urb); static void pl2303_read_bulk_callback (struct urb *urb); static void pl2303_write_bulk_callback (struct urb *urb); static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); static int pl2303_write_room(struct usb_serial_port *port); static int pl2303_chars_in_buffer(struct usb_serial_port *port); static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); static void start_xmit (struct usb_serial_port *port); /* All of the device info needed for the PL2303 SIO serial converter */ static __u16 pl2303_vendor_id = PL2303_VENDOR_ID; static __u16 pl2303_product_id = PL2303_PRODUCT_ID; struct usb_serial_device_type pl2303_device = { name:"PL-2303", idVendor:&pl2303_vendor_id, /* the PL2303 vendor ID */ idProduct:&pl2303_product_id, /* the PL2303 product id */ needs_interrupt_in:DONT_CARE, /* this device must have an interrupt in endpoint */ needs_bulk_in:MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out:MUST_HAVE, /* this device must have a bulk out endpoint */ num_interrupt_in:NUM_DONT_CARE, num_bulk_in:1, num_bulk_out:1, num_ports:1, open:pl2303_open, close:pl2303_close, throttle:pl2303_throttle, unthrottle:pl2303_unthrottle, write:pl2303_write, ioctl:pl2303_ioctl, write_room: pl2303_write_room, chars_in_buffer: pl2303_chars_in_buffer, break_ctl: pl2303_break_ctl, set_termios:pl2303_set_termios, read_bulk_callback:pl2303_read_bulk_callback, read_int_callback:pl2303_read_int_callback, write_bulk_callback:pl2303_write_bulk_callback, startup:pl2303_startup, }; #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ static unsigned char *tmp_buf; static struct semaphore tmp_buf_sem = MUTEX; 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; } static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { /* pl2303_write */ //struct usb_serial *serial = port->serial; unsigned long flags; int c,ret=0; struct tty_struct *tty=port->tty; dbg ("pl2303_write port %d, %d bytes", port->number, count); if (!tty || !port->xmit_buf || !tmp_buf){ return 0; } PL2303_LOCK(port,flags); if (from_user) { down(&tmp_buf_sem); while (1) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) { ret = -EFAULT; } break; } c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); port->xmit_head = ((port->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); port->xmit_cnt += c; buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { while (1) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) { break; } memcpy(port->xmit_buf + port->xmit_head, buf, c); port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); port->xmit_cnt += c; buf += c; count -= c; ret += c; } } PL2303_UNLOCK(port, flags); if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) { start_xmit(port); } return ret; } static int pl2303_write_room(struct usb_serial_port *port) { int ret; ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; if (ret < 0) ret = 0; return ret; } static int pl2303_chars_in_buffer(struct usb_serial_port *port) { return port->xmit_cnt; } static void pl2303_throttle(struct usb_serial_port *port) { //struct usb_serial *serial = port->serial; struct tty_struct *tty=port->tty; unsigned long flags; #if 0 char buf[64]; printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif #if 0 //FIXME FIXME FIXME if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); #endif PL2303_LOCK(port,flags); //Should remove read request if one is present PL2303_UNLOCK(fport,flags); } static void pl2303_unthrottle(struct usb_serial_port *port) { //struct usb_serial *serial = port->serial; struct tty_struct *tty=port->tty; unsigned long flags; #if 0 char buf[64]; printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif #if 0 //FIXME FIXME FIXME FIXME FIXME if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else rs_send_xchar(tty, START_CHAR(tty)); } #endif PL2303_LOCK(port,flags); //Should add read request if one is not present PL2303_UNLOCK(fport,flags); } static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) { /* pl2303_set_termios */ struct usb_serial *serial = port->serial; unsigned int cflag = port->tty->termios->c_cflag; unsigned char buf[7] = { 0, 0, 0, 0, 0, 0, 0 }; int baud; int i; dbg ("pl2303_set_termios port %d", port->number); i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), 0x21, 0xa1, 0, 0, buf, 7, 100); printk ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x\n", i, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), 1, 0x40, 0, 1, NULL, 0, 100); printk ("0x40:1:0:1 %d\n", i); if (cflag & CSIZE) { switch (cflag & CSIZE) { case CS6: buf[6] = 6; dbg ("Setting CS6"); break; case CS7: buf[6] = 7; dbg ("Setting CS7"); break; case CS8: buf[6] = 8; dbg ("Setting CS8"); break; default: err ("CSIZE was set but not CS6-CS8"); } } baud = 0; switch (cflag & CBAUD) { case B0: err ("Can't do B0 yet"); //FIXME break; case B300: baud = 300; break; case B600: baud = 600; break; case B1200: baud = 1200; break; case B2400: baud = 2400; break; case B4800: baud = 4800; break; case B9600: baud = 9600; break; case B19200: baud = 19200; break; case B38400: baud = 38400; break; case B57600: baud = 57600; break; case B115200: baud = 115200; break; default: dbg ("pl2303 driver does not support the baudrate requested (fix it)"); break; } if (baud) { buf[0] = baud & 0xff; buf[1] = (baud >> 8) & 0xff; buf[2] = (baud >> 16) & 0xff; buf[3] = (baud >> 24) & 0xff; } /* For reference buf[4]=1 is 1.5 stop bits */ if (cflag & CSTOPB) { buf[4] = 2; } if (cflag & PARENB) { /* For reference buf[5]=3 is mark parity */ /* For reference buf[5]=4 is space parity */ if (cflag & PARODD) { buf[5] = 1; } else { buf[5] = 2; } } i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), 0x20, 0x21, 0, 0, buf, 7, 100); printk ("0x21:0x20:0:0 %d \n", i); i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), 0x22, 0x21, 1, 0, NULL, 0, 100); printk ("0x21:0x22:1:0 %d\n", i); i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), 0x22, 0x21, 3, 0, NULL, 0, 100); printk ("0x21:0x22:3:0 %d\n", i); buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), 0x21, 0xa1, 0, 0, buf, 7, 100); printk ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x\n", i, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CRTSCTS) { i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), 0x01, 0x40, 0x0, 0x41, NULL, 0, 100); printk ("0x40:0x1:0x0:0x41 %d \n", i); } return; } static int pl2303_open (struct usb_serial_port *port, struct file *filp) { /* pl2303_open */ struct termios tmp_termios; struct usb_serial *serial = port->serial; unsigned char buf[10]; int i; dbg ("pl2303_open port %d", port->number); port->active++; #define FISH(a,b,c,d) \ i=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ b, a,c , d, buf, 1, 100); \ printk("0x%x:0x%x:0x%x:0x%x %d - %x\n",a,b,c,d,i,buf[0]); #define SOUP(a,b,c,d) \ i=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ b, a,c , d, NULL, 0, 100); \ printk("0x%x:0x%x:0x%x:0x%x %d\n",a,b,c,d,i); FISH (0xc0, 1, 0x8484, 0); SOUP (0x40, 1, 0x0404, 0); FISH (0xc0, 1, 0x8484, 0); FISH (0xc0, 1, 0x8383, 0); FISH (0xc0, 1, 0x8484, 0); SOUP (0x40, 1, 0x0404, 1); FISH (0xc0, 1, 0x8484, 0); FISH (0xc0, 1, 0x8383, 0); SOUP (0x40, 1, 0, 1); SOUP (0x40, 1, 1, 0xc0); SOUP (0x40, 1, 2, 4); /* Setup termios */ if (port->active == 1) { *(port->tty->termios) = tty_std_termios; port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; } pl2303_set_termios (port, &tmp_termios); //FIXME: need to assert RTS and DTR if CRTSCTS off if (port->active == 1) { unsigned long flags,page; int i; page = get_free_page(GFP_KERNEL); if (!page) { pl2303_close (port, NULL); return -ENOMEM; } PL2303_LOCK(port,flags); port->xmit_cnt=port->xmit_head=port->xmit_tail=0; if (tmp_buf) free_page(page); else tmp_buf = (unsigned char *) page; PL2303_UNLOCK(port,flags); page = get_free_page(GFP_KERNEL); if (!page) { pl2303_close (port, NULL); return -ENOMEM; } PL2303_LOCK(port,flags); if (port->xmit_buf) free_page(page); else port->xmit_buf=(unsigned char *) page; PL2303_UNLOCK(port,flags); if ((i = usb_submit_urb (port->read_urb))) { err ("usb_submit_urb(read bulk 1) failed"); printk ("i=%d\n", i); pl2303_close (port, NULL); return -EPROTO; } if ((i = usb_submit_urb (port->interrupt_in_urb))) { err ("usb_submit_urb(interrupt ink) failed"); printk ("i=%d\n", i); pl2303_close (port, NULL); return -EPROTO; } } return (0); } /* pl2303_open */ static void pl2303_close (struct usb_serial_port *port, struct file *filp) { /* pl2303_close */ unsigned int c_cflag = port->tty->termios->c_cflag; unsigned long flags; dbg ("pl2303_close port %d", port->number); /* shutdown our bulk reads and writes */ if (port->active == 1) { if (c_cflag & HUPCL) { //FIXME: Do drop DTR //FIXME: Do drop RTS } usb_unlink_urb (port->write_urb); usb_unlink_urb (port->read_urb); usb_unlink_urb (port->interrupt_in_urb); PL2303_LOCK(port,flags); if (port->xmit_buf){ unsigned char * temp; temp = port->xmit_buf; port->xmit_buf = 0; free_page((unsigned long) temp); } PL2303_UNLOCK(port,flags); //FIXME: tmp_buf memory leak } port->active--; } /* pl2303_close */ /* do some startup allocations not currently performed by usb_serial_probe() */ static int pl2303_startup (struct usb_serial *serial) { spin_lock_init(&serial->ports[0].lock); return (0); } static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { // struct usb_serial *serial = port->serial; // __u16 urb_value=0; /* Will hold the new flags */ // char buf[1]; // int ret, mask; printk ("pl2303_sio ioctl 0x%04x\n", cmd); /* Based on code from acm.c and others */ switch (cmd) { case TIOCMGET: dbg ("TIOCMGET"); return put_user (0, (unsigned long *) arg); break; case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return 0; default: /* This is not an error - turns out the higher layers will do * some ioctls itself (see comment above) */ dbg ("pl2303_sio ioctl arg not supported - it was 0x%04x", cmd); return (-ENOIOCTLCMD); break; } dbg ("pl2303_ioctl returning 0"); return 0; } /* pl2303_ioctl */ static void pl2303_break_ctl(struct usb_serial_port *port,int break_state) { //FIXME } static void pl2303_read_int_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial = get_usb_serial (port, "pl2303_read_int_callback"); //unsigned char *data = urb->transfer_buffer; //int i; //ints auto restart... if (!serial) { return; } if (urb->status) { urb->status = 0; return; } #if 0 //FIXME need to update state of terminal lines variable if (urb->actual_length) { printk (KERN_DEBUG __FILE__ ": INT data read - length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } printk ("\n"); } #endif return; } static void pl2303_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial = get_usb_serial (port, "pl2303_read_bulk_callback"); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int i; if (!serial) { return; } // PL2303 mysteriously fails with -EPROTO reschedule the read if (urb->status) { urb->status = 0; if (usb_submit_urb (urb)) dbg ("failed resubmitting read bulk urb"); return; } #ifdef DEBUG if (urb->actual_length) { printk (KERN_DEBUG __FILE__ ": BULK data read - length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } printk ("\n"); } #endif tty = port->tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length; ++i) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk ("ARGH ------------ Flip buffer overrun...\n"); break; } tty_insert_flip_char (tty, data[i], 0); } tty_flip_buffer_push (tty); } /* Schedule the next read*/ if (usb_submit_urb (urb)) dbg ("failed submitting read bulk urb"); return; } static void pl2303_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial; struct tty_struct *tty = port->tty; dbg ("pl2303_write_bulk_callback"); if (port_paranoia_check (port, "pl2303_write_bulk_callback")) { return; } serial = port->serial; if (serial_paranoia_check (serial, "pl2303_write_bulk_callback")) { return; } if (urb->status) { printk ("Overflow in write\n"); dbg ("nonzero write bulk status received: %d", urb->status); //need to resubmit frame; port->write_urb->transfer_buffer_length = 1; //Resubmit ourselves if (usb_submit_urb (port->write_urb)) err ("usb_submit_urb(write bulk) failed"); return; } wake_up_interruptible (&port->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty); wake_up_interruptible (&tty->write_wait); start_xmit(port); return; } static void start_xmit (struct usb_serial_port *port) { struct usb_serial *serial; unsigned long flags; serial = port->serial; PL2303_LOCK(port,flags); if (port->write_urb->status != -EINPROGRESS) { if (port->xmit_tail!=port->xmit_head) { memcpy (port->write_urb->transfer_buffer, &port->xmit_buf[port->xmit_tail],1); port->xmit_cnt--; port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->write_urb->transfer_buffer_length = 1; if (usb_submit_urb (port->write_urb)) err ("usb_submit_urb(write bulk) failed"); } } PL2303_UNLOCK(port,flags); }