X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fvisor.c;h=0bb6262ad89c702524753938fb9d2618f0ff4a48;hb=8e8ece46a861c84343256819eaec77e608ff9217;hp=377a18479dead7a27d4a200362d8e91005862f0f;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 377a18479..0bb6262ad 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -155,13 +155,6 @@ #include #include #include - -#ifdef CONFIG_USB_SERIAL_DEBUG - static int debug = 1; -#else - static int debug; -#endif - #include "usb-serial.h" #include "visor.h" @@ -175,7 +168,7 @@ /* function prototypes for a handspring visor */ static int visor_open (struct usb_serial_port *port, struct file *filp); static void visor_close (struct usb_serial_port *port, struct file *filp); -static int visor_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count); static int visor_write_room (struct usb_serial_port *port); static int visor_chars_in_buffer (struct usb_serial_port *port); static void visor_throttle (struct usb_serial_port *port); @@ -195,6 +188,7 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id); /* Parameters that may be passed into the module. */ +static int debug; static __u16 vendor; static __u16 product; @@ -247,6 +241,8 @@ static struct usb_device_id id_table [] = { .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { }, /* optional parameter entry */ { } /* Terminating entry */ }; @@ -290,6 +286,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) }, { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) }, { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) }, + { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) }, { }, /* optional parameter entry */ { } /* Terminating entry */ }; @@ -384,10 +381,18 @@ static struct usb_serial_device_type clie_3_5_device = { .read_bulk_callback = visor_read_bulk_callback, }; +struct visor_private { + spinlock_t lock; + int bytes_in; + int bytes_out; + int outstanding_urbs; + int throttled; +}; -static int bytes_in; -static int bytes_out; +/* number of outstanding urbs to prevent userspace DoS from happening */ +#define URB_UPPER_LIMIT 42 +static int stats; /****************************************************************************** * Handspring Visor specific driver functions @@ -395,6 +400,8 @@ static int bytes_out; static int visor_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; + struct visor_private *priv = usb_get_serial_port_data(port); + unsigned long flags; int result = 0; dbg("%s - port %d", __FUNCTION__, port->number); @@ -405,8 +412,12 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) return -ENODEV; } - bytes_in = 0; - bytes_out = 0; + spin_lock_irqsave(&priv->lock, flags); + priv->bytes_in = 0; + priv->bytes_out = 0; + priv->outstanding_urbs = 0; + priv->throttled = 0; + spin_unlock_irqrestore(&priv->lock, flags); /* * Force low_latency on so that our tty_push actually forces the data @@ -444,14 +455,15 @@ exit: static void visor_close (struct usb_serial_port *port, struct file * filp) { + struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our urbs */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); if (port->interrupt_in_urb) - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); /* Try to send shutdown message, if the device is gone, this will just fail. */ transfer_buffer = kmalloc (0x12, GFP_KERNEL); @@ -464,20 +476,31 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) kfree (transfer_buffer); } - /* Uncomment the following line if you want to see some statistics in your syslog */ - /* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */ + if (stats) + dev_info(&port->dev, "Bytes In = %d Bytes Out = %d\n", + priv->bytes_in, priv->bytes_out); } -static int visor_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count) { + struct visor_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; struct urb *urb; unsigned char *buffer; + unsigned long flags; int status; dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > URB_UPPER_LIMIT) { + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + buffer = kmalloc (count, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); @@ -491,17 +514,9 @@ static int visor_write (struct usb_serial_port *port, int from_user, const unsig return -ENOMEM; } - if (from_user) { - if (copy_from_user (buffer, buf, count)) { - kfree (buffer); - usb_free_urb (urb); - return -EFAULT; - } - } else { - memcpy (buffer, buf, count); - } + memcpy (buffer, buf, count); - usb_serial_debug_data (__FILE__, __FUNCTION__, count, buffer); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer); usb_fill_bulk_urb (urb, serial->dev, usb_sndbulkpipe (serial->dev, @@ -515,8 +530,12 @@ static int visor_write (struct usb_serial_port *port, int from_user, const unsig dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __FUNCTION__, status); count = status; + kfree (buffer); } else { - bytes_out += count; + spin_lock_irqsave(&priv->lock, flags); + ++priv->outstanding_urbs; + priv->bytes_out += count; + spin_unlock_irqrestore(&priv->lock, flags); } /* we are done with this urb, so let the host driver @@ -557,6 +576,8 @@ static int visor_chars_in_buffer (struct usb_serial_port *port) static void visor_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct visor_private *priv = usb_get_serial_port_data(port); + unsigned long flags; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); @@ -567,6 +588,10 @@ static void visor_write_bulk_callback (struct urb *urb, struct pt_regs *regs) dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + schedule_work(&port->work); } @@ -574,9 +599,12 @@ static void visor_write_bulk_callback (struct urb *urb, struct pt_regs *regs) static void visor_read_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct tty_struct *tty; + struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + unsigned long flags; int i; + int throttled; int result; dbg("%s - port %d", __FUNCTION__, port->number); @@ -586,7 +614,7 @@ static void visor_read_bulk_callback (struct urb *urb, struct pt_regs *regs) return; } - usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); tty = port->tty; if (tty && urb->actual_length) { @@ -600,23 +628,29 @@ static void visor_read_bulk_callback (struct urb *urb, struct pt_regs *regs) } tty_flip_buffer_push(tty); } - bytes_in += urb->actual_length; - - /* Continue trying to always read */ - usb_fill_bulk_urb (port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - visor_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + spin_lock_irqsave(&priv->lock, flags); + priv->bytes_in += urb->actual_length; + throttled = priv->throttled; + spin_unlock_irqrestore(&priv->lock, flags); + + /* Continue trying to always read if we should */ + if (!throttled) { + usb_fill_bulk_urb (port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + visor_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + } return; } static void visor_read_int_callback (struct urb *urb, struct pt_regs *regs) { + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; int result; switch (urb->status) { @@ -643,8 +677,8 @@ static void visor_read_int_callback (struct urb *urb, struct pt_regs *regs) * Rumor has it this endpoint is used to notify when data * is ready to be read from the bulk ones. */ - usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, - urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + urb->actual_length, urb->transfer_buffer); exit: result = usb_submit_urb (urb, GFP_ATOMIC); @@ -655,16 +689,26 @@ exit: static void visor_throttle (struct usb_serial_port *port) { + struct visor_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = 1; + spin_unlock_irqrestore(&priv->lock, flags); } static void visor_unthrottle (struct usb_serial_port *port) { + struct visor_private *priv = usb_get_serial_port_data(port); + unsigned long flags; int result; dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = 0; + spin_unlock_irqrestore(&priv->lock, flags); port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_ATOMIC); @@ -706,9 +750,7 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i if (retval == sizeof(*connection_info)) { connection_info = (struct visor_connection_info *)transfer_buffer; - le16_to_cpus(&connection_info->num_ports); - num_ports = connection_info->num_ports; - + num_ports = le16_to_cpu(connection_info->num_ports); for (i = 0; i < num_ports; ++i) { switch (connection_info->connections[i].port_function_id) { case VISOR_FUNCTION_GENERIC: @@ -795,7 +837,8 @@ static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_i dev_err(dev, "%s - error %d getting connection info\n", __FUNCTION__, retval); else - usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14, transfer_buffer); + usb_serial_debug_data(debug, &serial->dev->dev, __FUNCTION__, + retval, transfer_buffer); kfree (transfer_buffer); return 0; @@ -833,6 +876,22 @@ static int visor_calc_num_ports (struct usb_serial *serial) return num_ports; } +static int generic_startup(struct usb_serial *serial) +{ + struct visor_private *priv; + int i; + + for (i = 0; i < serial->num_ports; ++i) { + priv = kmalloc (sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + memset (priv, 0x00, sizeof(*priv)); + spin_lock_init(&priv->lock); + usb_set_serial_port_data(serial->port[i], priv); + } + return 0; +} + static int clie_3_5_startup (struct usb_serial *serial) { struct device *dev = &serial->dev->dev; @@ -872,7 +931,7 @@ static int clie_3_5_startup (struct usb_serial *serial) return -EIO; } - return 0; + return generic_startup(serial); } static int treo_attach (struct usb_serial *serial) @@ -881,18 +940,19 @@ static int treo_attach (struct usb_serial *serial) /* Only do this endpoint hack for the Handspring devices with * interrupt in endpoints, which for now are the Treo devices. */ - if ((serial->dev->descriptor.idVendor != HANDSPRING_VENDOR_ID) || + if (!((le16_to_cpu(serial->dev->descriptor.idVendor) == HANDSPRING_VENDOR_ID) || + (le16_to_cpu(serial->dev->descriptor.idVendor) == KYOCERA_VENDOR_ID)) || (serial->num_interrupt_in == 0)) - return 0; + goto generic_startup; dbg("%s", __FUNCTION__); /* - * It appears that Treos want to use the 1st interrupt endpoint to - * communicate with the 2nd bulk out endpoint, so let's swap the 1st - * and 2nd bulk in and interrupt endpoints. Note that swapping the - * bulk out endpoints would break lots of apps that want to communicate - * on the second port. + * It appears that Treos and Kyoceras want to use the + * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint, + * so let's swap the 1st and 2nd bulk in and interrupt endpoints. + * Note that swapping the bulk out endpoints would break lots of + * apps that want to communicate on the second port. */ #define COPY_PORT(dest, src) \ dest->read_urb = src->read_urb; \ @@ -910,7 +970,8 @@ static int treo_attach (struct usb_serial *serial) COPY_PORT(serial->port[1], swap_port); kfree(swap_port); - return 0; +generic_startup: + return generic_startup(serial); } static int clie_5_attach (struct usb_serial *serial) @@ -931,7 +992,7 @@ static int clie_5_attach (struct usb_serial *serial) /* port 0 now uses the modified endpoint Address */ serial->port[0]->bulk_out_endpointAddress = serial->port[1]->bulk_out_endpointAddress; - return 0; + return generic_startup(serial); } static void visor_shutdown (struct usb_serial *serial) @@ -1087,8 +1148,11 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); +module_param(stats, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(stats, "Enables statistics or not"); module_param(vendor, ushort, 0); MODULE_PARM_DESC(vendor, "User specified vendor ID"); module_param(product, ushort, 0); MODULE_PARM_DESC(product, "User specified product ID"); +