/shark/trunk/drivers/usb/serial/pl2303.c |
---|
1,818 → 1,816 |
/* |
* Prolific PL2303 USB to serial adaptor driver |
* |
* Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com) |
* Copyright (C) 2003 IBM Corp. |
* |
* Original driver for 2.2.x by anonymous |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* See Documentation/usb/usb-serial.txt for more information on using this driver |
* |
* 2002_Mar_26 gkh |
* allowed driver to work properly if there is no tty assigned to a port |
* (this happens for serial console devices.) |
* |
* 2001_Oct_06 gkh |
* Added RTS and DTR line control. Thanks to joe@bndlg.de for parts of it. |
* |
* 2001_Sep_19 gkh |
* Added break support. |
* |
* 2001_Aug_30 gkh |
* fixed oops in write_bulk_callback. |
* |
* 2001_Aug_28 gkh |
* reworked buffer logic to be like other usb-serial drivers. Hopefully |
* removing some reported problems. |
* |
* 2001_Jun_06 gkh |
* finished porting to 2.4 format. |
* |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/slab.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/serial.h> |
#include <linux/module.h> |
#include <linux/spinlock.h> |
#include <asm/uaccess.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
#include "pl2303.h" |
/* |
* Version Information |
*/ |
#define DRIVER_VERSION "v0.10" |
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" |
static struct usb_device_id id_table [] = { |
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, |
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, |
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, |
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, |
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, |
{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, |
{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, |
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, |
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, |
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, |
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, |
{ } /* Terminating entry */ |
}; |
MODULE_DEVICE_TABLE (usb, id_table); |
static struct usb_driver pl2303_driver = { |
.owner = THIS_MODULE, |
.name = "pl2303", |
.probe = usb_serial_probe, |
.disconnect = usb_serial_disconnect, |
.id_table = id_table, |
}; |
#define SET_LINE_REQUEST_TYPE 0x21 |
#define SET_LINE_REQUEST 0x20 |
#define SET_CONTROL_REQUEST_TYPE 0x21 |
#define SET_CONTROL_REQUEST 0x22 |
#define CONTROL_DTR 0x01 |
#define CONTROL_RTS 0x02 |
#define BREAK_REQUEST_TYPE 0x21 |
#define BREAK_REQUEST 0x23 |
#define BREAK_ON 0xffff |
#define BREAK_OFF 0x0000 |
#define GET_LINE_REQUEST_TYPE 0xa1 |
#define GET_LINE_REQUEST 0x21 |
#define VENDOR_WRITE_REQUEST_TYPE 0x40 |
#define VENDOR_WRITE_REQUEST 0x01 |
#define VENDOR_READ_REQUEST_TYPE 0xc0 |
#define VENDOR_READ_REQUEST 0x01 |
#define UART_STATE 0x08 |
#define UART_DCD 0x01 |
#define UART_DSR 0x02 |
#define UART_BREAK_ERROR 0x04 |
#define UART_RING 0x08 |
#define UART_FRAME_ERROR 0x10 |
#define UART_PARITY_ERROR 0x20 |
#define UART_OVERRUN_ERROR 0x40 |
#define UART_CTS 0x80 |
/* function prototypes for a PL2303 serial converter */ |
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_read_int_callback (struct urb *urb, struct pt_regs *regs); |
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); |
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); |
static int pl2303_write (struct usb_serial_port *port, int from_user, |
const unsigned char *buf, int count); |
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); |
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); |
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, |
unsigned int set, unsigned int clear); |
static int pl2303_startup (struct usb_serial *serial); |
static void pl2303_shutdown (struct usb_serial *serial); |
/* All of the device info needed for the PL2303 SIO serial converter */ |
static struct usb_serial_device_type pl2303_device = { |
.owner = THIS_MODULE, |
.name = "PL-2303", |
.id_table = id_table, |
.num_interrupt_in = NUM_DONT_CARE, |
.num_bulk_in = 1, |
.num_bulk_out = 1, |
.num_ports = 1, |
.open = pl2303_open, |
.close = pl2303_close, |
.write = pl2303_write, |
.ioctl = pl2303_ioctl, |
.break_ctl = pl2303_break_ctl, |
.set_termios = pl2303_set_termios, |
.tiocmget = pl2303_tiocmget, |
.tiocmset = pl2303_tiocmset, |
.read_bulk_callback = pl2303_read_bulk_callback, |
.read_int_callback = pl2303_read_int_callback, |
.write_bulk_callback = pl2303_write_bulk_callback, |
.attach = pl2303_startup, |
.shutdown = pl2303_shutdown, |
}; |
struct pl2303_private { |
spinlock_t lock; |
u8 line_control; |
u8 line_status; |
u8 termios_initialized; |
}; |
static int pl2303_startup (struct usb_serial *serial) |
{ |
struct pl2303_private *priv; |
int i; |
for (i = 0; i < serial->num_ports; ++i) { |
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); |
if (!priv) |
return -ENOMEM; |
memset (priv, 0x00, sizeof (struct pl2303_private)); |
spin_lock_init(&priv->lock); |
usb_set_serial_port_data(serial->port[i], priv); |
} |
return 0; |
} |
static int set_control_lines (struct usb_device *dev, u8 value) |
{ |
int retval; |
retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0), |
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, |
value, 0, NULL, 0, 100); |
dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval); |
return retval; |
} |
static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) |
{ |
int result; |
dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); |
if (port->write_urb->status == -EINPROGRESS) { |
dbg("%s - already writing", __FUNCTION__); |
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__, __FUNCTION__, count, port->write_urb->transfer_buffer); |
port->write_urb->transfer_buffer_length = count; |
port->write_urb->dev = port->serial->dev; |
result = usb_submit_urb (port->write_urb, GFP_ATOMIC); |
if (result) |
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); |
else |
result = count; |
return result; |
} |
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) |
{ |
struct usb_serial *serial = port->serial; |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
unsigned int cflag; |
unsigned char *buf; |
int baud; |
int i; |
u8 control; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if ((!port->tty) || (!port->tty->termios)) { |
dbg("%s - no tty structures", __FUNCTION__); |
return; |
} |
spin_lock_irqsave(&priv->lock, flags); |
if (!priv->termios_initialized) { |
printk(KERN_INFO "@####### termios initializated\n"); |
*(port->tty->termios) = tty_std_termios; |
port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; |
priv->termios_initialized = 1; |
} |
spin_unlock_irqrestore(&priv->lock, flags); |
cflag = port->tty->termios->c_cflag; |
/* check that they really want us to change something */ |
if (old_termios) { |
if ((cflag == old_termios->c_cflag) && |
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { |
dbg("%s - nothing to change...", __FUNCTION__); |
return; |
} |
} |
buf = kmalloc (7, GFP_KERNEL); |
if (!buf) { |
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); |
return; |
} |
memset (buf, 0x00, 0x07); |
i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), |
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, |
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); |
if (cflag & CSIZE) { |
switch (cflag & CSIZE) { |
case CS5: buf[6] = 5; break; |
case CS6: buf[6] = 6; break; |
case CS7: buf[6] = 7; break; |
default: |
case CS8: buf[6] = 8; break; |
} |
dbg("%s - data bits = %d", __FUNCTION__, buf[6]); |
} |
baud = 0; |
switch (cflag & CBAUD) { |
case B0: baud = 0; break; |
case B75: baud = 75; break; |
case B150: baud = 150; break; |
case B300: baud = 300; break; |
case B600: baud = 600; break; |
case B1200: baud = 1200; break; |
case B1800: baud = 1800; 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; |
case B230400: baud = 230400; break; |
case B460800: baud = 460800; break; |
default: |
dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n"); |
break; |
} |
dbg("%s - baud = %d", __FUNCTION__, baud); |
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]=0 is 1 stop bits */ |
/* For reference buf[4]=1 is 1.5 stop bits */ |
/* For reference buf[4]=2 is 2 stop bits */ |
if (cflag & CSTOPB) { |
buf[4] = 2; |
dbg("%s - stop bits = 2", __FUNCTION__); |
} else { |
buf[4] = 0; |
dbg("%s - stop bits = 1", __FUNCTION__); |
} |
if (cflag & PARENB) { |
/* For reference buf[5]=0 is none parity */ |
/* For reference buf[5]=1 is odd parity */ |
/* For reference buf[5]=2 is even parity */ |
/* For reference buf[5]=3 is mark parity */ |
/* For reference buf[5]=4 is space parity */ |
if (cflag & PARODD) { |
buf[5] = 1; |
dbg("%s - parity = odd", __FUNCTION__); |
} else { |
buf[5] = 2; |
dbg("%s - parity = even", __FUNCTION__); |
} |
} else { |
buf[5] = 0; |
dbg("%s - parity = none", __FUNCTION__); |
} |
i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), |
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0x21:0x20:0:0 %d", i); |
/* change control lines if we are switching to or from B0 */ |
spin_lock_irqsave(&priv->lock, flags); |
control = priv->line_control; |
if ((cflag & CBAUD) == B0) |
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); |
else |
priv->line_control |= (CONTROL_DTR | CONTROL_RTS); |
if (control != priv->line_control) { |
control = priv->line_control; |
spin_unlock_irqrestore(&priv->lock, flags); |
set_control_lines(serial->dev, control); |
} else { |
spin_unlock_irqrestore(&priv->lock, flags); |
} |
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), |
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", 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), |
VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, |
0x0, 0x41, NULL, 0, 100); |
dbg ("0x40:0x1:0x0:0x41 %d", i); |
} |
kfree (buf); |
} |
static int pl2303_open (struct usb_serial_port *port, struct file *filp) |
{ |
struct termios tmp_termios; |
struct usb_serial *serial = port->serial; |
unsigned char buf[10]; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return -ENODEV; |
dbg("%s - port %d", __FUNCTION__, port->number); |
usb_clear_halt(serial->dev, port->write_urb->pipe); |
usb_clear_halt(serial->dev, port->read_urb->pipe); |
#define FISH(a,b,c,d) \ |
result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ |
b, a, c, d, buf, 1, 100); \ |
dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]); |
#define SOUP(a,b,c,d) \ |
result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ |
b, a, c, d, NULL, 0, 100); \ |
dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); |
/* Setup termios */ |
if (port->tty) { |
printk(KERN_INFO "#####@ set termios"); |
pl2303_set_termios (port, &tmp_termios); |
} |
//FIXME: need to assert RTS and DTR if CRTSCTS off |
dbg("%s - submitting read urb", __FUNCTION__); |
port->read_urb->dev = serial->dev; |
result = usb_submit_urb (port->read_urb, GFP_KERNEL); |
if (result) { |
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); |
pl2303_close (port, NULL); |
return -EPROTO; |
} |
dbg("%s - submitting interrupt urb", __FUNCTION__); |
port->interrupt_in_urb->dev = serial->dev; |
result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); |
if (result) { |
dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result); |
pl2303_close (port, NULL); |
return -EPROTO; |
} |
return 0; |
} |
static void pl2303_close (struct usb_serial_port *port, struct file *filp) |
{ |
struct usb_serial *serial; |
struct pl2303_private *priv; |
unsigned long flags; |
unsigned int c_cflag; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
/* shutdown our urbs */ |
dbg("%s - shutting down urbs", __FUNCTION__); |
result = usb_unlink_urb (port->write_urb); |
if (result) |
dbg("%s - usb_unlink_urb (write_urb)" |
" failed with reason: %d", __FUNCTION__, |
result); |
result = usb_unlink_urb (port->read_urb); |
if (result) |
dbg("%s - usb_unlink_urb (read_urb) " |
"failed with reason: %d", __FUNCTION__, |
result); |
result = usb_unlink_urb (port->interrupt_in_urb); |
if (result) |
dbg("%s - usb_unlink_urb (interrupt_in_urb)" |
" failed with reason: %d", __FUNCTION__, |
result); |
if (port->tty) { |
c_cflag = port->tty->termios->c_cflag; |
if (c_cflag & HUPCL) { |
/* drop DTR and RTS */ |
priv = usb_get_serial_port_data(port); |
spin_lock_irqsave(&priv->lock, flags); |
priv->line_control = 0; |
spin_unlock_irqrestore (&priv->lock, flags); |
set_control_lines (port->serial->dev, 0); |
} |
} |
} |
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, |
unsigned int set, unsigned int clear) |
{ |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
u8 control; |
spin_lock_irqsave (&priv->lock, flags); |
if (set & TIOCM_RTS) |
priv->line_control |= CONTROL_RTS; |
if (set & TIOCM_DTR) |
priv->line_control |= CONTROL_DTR; |
if (clear & TIOCM_RTS) |
priv->line_control &= ~CONTROL_RTS; |
if (clear & TIOCM_DTR) |
priv->line_control &= ~CONTROL_DTR; |
control = priv->line_control; |
spin_unlock_irqrestore (&priv->lock, flags); |
return set_control_lines (port->serial->dev, control); |
} |
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file) |
{ |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
unsigned int mcr; |
unsigned int status; |
unsigned int result; |
dbg("%s (%d)", __FUNCTION__, port->number); |
spin_lock_irqsave (&priv->lock, flags); |
mcr = priv->line_control; |
status = priv->line_status; |
spin_unlock_irqrestore (&priv->lock, flags); |
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) |
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) |
| ((status & UART_CTS) ? TIOCM_CTS : 0) |
| ((status & UART_DSR) ? TIOCM_DSR : 0) |
| ((status & UART_RING) ? TIOCM_RI : 0) |
| ((status & UART_DCD) ? TIOCM_CD : 0); |
dbg("%s - result = %x", __FUNCTION__, result); |
return result; |
} |
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) |
{ |
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); |
switch (cmd) { |
default: |
dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); |
break; |
} |
return -ENOIOCTLCMD; |
} |
static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) |
{ |
struct usb_serial *serial = port->serial; |
u16 state; |
int result; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (break_state == 0) |
state = BREAK_OFF; |
else |
state = BREAK_ON; |
dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__); |
result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), |
BREAK_REQUEST, BREAK_REQUEST_TYPE, state, |
0, NULL, 0, 100); |
if (result) |
dbg("%s - error sending break = %d", __FUNCTION__, result); |
} |
static void pl2303_shutdown (struct usb_serial *serial) |
{ |
int i; |
dbg("%s", __FUNCTION__); |
for (i = 0; i < serial->num_ports; ++i) { |
kfree (usb_get_serial_port_data(serial->port[i])); |
usb_set_serial_port_data(serial->port[i], NULL); |
} |
} |
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned char *data = urb->transfer_buffer; |
unsigned long flags; |
int status; |
dbg("%s (%d)", __FUNCTION__, port->number); |
switch (urb->status) { |
case 0: |
/* success */ |
break; |
case -ECONNRESET: |
case -ENOENT: |
case -ESHUTDOWN: |
/* this urb is terminated, clean up */ |
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); |
return; |
default: |
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); |
goto exit; |
} |
if (!serial) { |
return; |
} |
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer); |
if (urb->actual_length < UART_STATE) |
goto exit; |
/* Save off the uart status for others to look at */ |
spin_lock_irqsave(&priv->lock, flags); |
priv->line_status = data[UART_STATE]; |
spin_unlock_irqrestore(&priv->lock, flags); |
exit: |
status = usb_submit_urb (urb, GFP_ATOMIC); |
if (status) |
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", |
__FUNCTION__, status); |
} |
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
struct tty_struct *tty; |
unsigned char *data = urb->transfer_buffer; |
unsigned long flags; |
int i; |
int result; |
u8 status; |
char tty_flag; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!serial) { |
dbg("%s - bad serial pointer, exiting", __FUNCTION__); |
return; |
} |
if (urb->status) { |
dbg("%s - urb->status = %d", __FUNCTION__, urb->status); |
if (!port->open_count) { |
dbg("%s - port is closed, exiting.", __FUNCTION__); |
return; |
} |
if (urb->status == -EPROTO) { |
/* PL2303 mysteriously fails with -EPROTO reschedule the read */ |
dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); |
urb->status = 0; |
urb->dev = serial->dev; |
result = usb_submit_urb(urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); |
return; |
} |
dbg("%s - unable to handle the error, exiting.", __FUNCTION__); |
return; |
} |
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); |
/* get tty_flag from status */ |
tty_flag = TTY_NORMAL; |
spin_lock_irqsave(&priv->lock, flags); |
status = priv->line_status; |
spin_unlock_irqrestore(&priv->lock, flags); |
/* break takes precedence over parity, */ |
/* which takes precedence over framing errors */ |
if (status & UART_BREAK_ERROR ) |
tty_flag = TTY_BREAK; |
else if (status & UART_PARITY_ERROR) |
tty_flag = TTY_PARITY; |
else if (status & UART_FRAME_ERROR) |
tty_flag = TTY_FRAME; |
dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); |
tty = port->tty; |
if (tty && urb->actual_length) { |
/* overrun is special, not associated with a char */ |
if (status & UART_OVERRUN_ERROR) |
tty_insert_flip_char(tty, 0, TTY_OVERRUN); |
for (i = 0; i < urb->actual_length; ++i) { |
if (tty->flip.count >= TTY_FLIPBUF_SIZE) { |
tty_flip_buffer_push(tty); |
} |
//**#ifdef DEBUG_ME |
tty_insert_flip_char (tty, data[i], tty_flag); |
//**#endif /* DEBUG_ME */ |
} |
tty_flip_buffer_push (tty); |
} |
/* Schedule the next read _if_ we are still open */ |
if (port->open_count) { |
urb->dev = serial->dev; |
result = usb_submit_urb(urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); |
} |
return; |
} |
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (urb->status) { |
/* error in the urb, so we have to resubmit it */ |
if (serial_paranoia_check (port->serial, __FUNCTION__)) { |
return; |
} |
dbg("%s - Overflow in write", __FUNCTION__); |
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); |
port->write_urb->transfer_buffer_length = 1; |
port->write_urb->dev = port->serial->dev; |
result = usb_submit_urb (port->write_urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); |
return; |
} |
schedule_work(&port->work); |
} |
/*static*/ int __init pl2303_init (void) |
{ |
int retval; |
retval = usb_serial_register(&pl2303_device); |
if (retval) |
goto failed_usb_serial_register; |
retval = usb_register(&pl2303_driver); |
if (retval) |
goto failed_usb_register; |
info(DRIVER_DESC " " DRIVER_VERSION); |
return 0; |
failed_usb_register: |
usb_serial_deregister(&pl2303_device); |
failed_usb_serial_register: |
return retval; |
} |
/*static*/ void __exit pl2303_exit (void) |
{ |
usb_deregister (&pl2303_driver); |
usb_serial_deregister (&pl2303_device); |
} |
module_init(pl2303_init); |
module_exit(pl2303_exit); |
MODULE_DESCRIPTION(DRIVER_DESC); |
MODULE_LICENSE("GPL"); |
MODULE_PARM(debug, "i"); |
MODULE_PARM_DESC(debug, "Debug enabled or not"); |
/* |
* Prolific PL2303 USB to serial adaptor driver |
* |
* Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com) |
* Copyright (C) 2003 IBM Corp. |
* |
* Original driver for 2.2.x by anonymous |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* See Documentation/usb/usb-serial.txt for more information on using this driver |
* |
* 2002_Mar_26 gkh |
* allowed driver to work properly if there is no tty assigned to a port |
* (this happens for serial console devices.) |
* |
* 2001_Oct_06 gkh |
* Added RTS and DTR line control. Thanks to joe@bndlg.de for parts of it. |
* |
* 2001_Sep_19 gkh |
* Added break support. |
* |
* 2001_Aug_30 gkh |
* fixed oops in write_bulk_callback. |
* |
* 2001_Aug_28 gkh |
* reworked buffer logic to be like other usb-serial drivers. Hopefully |
* removing some reported problems. |
* |
* 2001_Jun_06 gkh |
* finished porting to 2.4 format. |
* |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/slab.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/serial.h> |
#include <linux/module.h> |
#include <linux/spinlock.h> |
#include <asm/uaccess.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
#include "pl2303.h" |
/* |
* Version Information |
*/ |
#define DRIVER_VERSION "v0.10" |
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" |
static struct usb_device_id id_table [] = { |
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, |
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, |
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, |
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, |
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, |
{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, |
{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, |
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, |
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, |
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, |
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, |
{ } /* Terminating entry */ |
}; |
MODULE_DEVICE_TABLE (usb, id_table); |
static struct usb_driver pl2303_driver = { |
.owner = THIS_MODULE, |
.name = "pl2303", |
.probe = usb_serial_probe, |
.disconnect = usb_serial_disconnect, |
.id_table = id_table, |
}; |
#define SET_LINE_REQUEST_TYPE 0x21 |
#define SET_LINE_REQUEST 0x20 |
#define SET_CONTROL_REQUEST_TYPE 0x21 |
#define SET_CONTROL_REQUEST 0x22 |
#define CONTROL_DTR 0x01 |
#define CONTROL_RTS 0x02 |
#define BREAK_REQUEST_TYPE 0x21 |
#define BREAK_REQUEST 0x23 |
#define BREAK_ON 0xffff |
#define BREAK_OFF 0x0000 |
#define GET_LINE_REQUEST_TYPE 0xa1 |
#define GET_LINE_REQUEST 0x21 |
#define VENDOR_WRITE_REQUEST_TYPE 0x40 |
#define VENDOR_WRITE_REQUEST 0x01 |
#define VENDOR_READ_REQUEST_TYPE 0xc0 |
#define VENDOR_READ_REQUEST 0x01 |
#define UART_STATE 0x08 |
#define UART_DCD 0x01 |
#define UART_DSR 0x02 |
#define UART_BREAK_ERROR 0x04 |
#define UART_RING 0x08 |
#define UART_FRAME_ERROR 0x10 |
#define UART_PARITY_ERROR 0x20 |
#define UART_OVERRUN_ERROR 0x40 |
#define UART_CTS 0x80 |
/* function prototypes for a PL2303 serial converter */ |
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_read_int_callback (struct urb *urb, struct pt_regs *regs); |
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); |
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); |
static int pl2303_write (struct usb_serial_port *port, int from_user, |
const unsigned char *buf, int count); |
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); |
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); |
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, |
unsigned int set, unsigned int clear); |
static int pl2303_startup (struct usb_serial *serial); |
static void pl2303_shutdown (struct usb_serial *serial); |
/* All of the device info needed for the PL2303 SIO serial converter */ |
static struct usb_serial_device_type pl2303_device = { |
.owner = THIS_MODULE, |
.name = "PL-2303", |
.id_table = id_table, |
.num_interrupt_in = NUM_DONT_CARE, |
.num_bulk_in = 1, |
.num_bulk_out = 1, |
.num_ports = 1, |
.open = pl2303_open, |
.close = pl2303_close, |
.write = pl2303_write, |
.ioctl = pl2303_ioctl, |
.break_ctl = pl2303_break_ctl, |
.set_termios = pl2303_set_termios, |
.tiocmget = pl2303_tiocmget, |
.tiocmset = pl2303_tiocmset, |
.read_bulk_callback = pl2303_read_bulk_callback, |
.read_int_callback = pl2303_read_int_callback, |
.write_bulk_callback = pl2303_write_bulk_callback, |
.attach = pl2303_startup, |
.shutdown = pl2303_shutdown, |
}; |
struct pl2303_private { |
spinlock_t lock; |
u8 line_control; |
u8 line_status; |
u8 termios_initialized; |
}; |
static int pl2303_startup (struct usb_serial *serial) |
{ |
struct pl2303_private *priv; |
int i; |
for (i = 0; i < serial->num_ports; ++i) { |
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); |
if (!priv) |
return -ENOMEM; |
memset (priv, 0x00, sizeof (struct pl2303_private)); |
spin_lock_init(&priv->lock); |
usb_set_serial_port_data(serial->port[i], priv); |
} |
return 0; |
} |
static int set_control_lines (struct usb_device *dev, u8 value) |
{ |
int retval; |
retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0), |
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, |
value, 0, NULL, 0, 100); |
dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval); |
return retval; |
} |
static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) |
{ |
int result; |
dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); |
if (port->write_urb->status == -EINPROGRESS) { |
dbg("%s - already writing", __FUNCTION__); |
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__, __FUNCTION__, count, port->write_urb->transfer_buffer); |
port->write_urb->transfer_buffer_length = count; |
port->write_urb->dev = port->serial->dev; |
result = usb_submit_urb (port->write_urb, GFP_ATOMIC); |
if (result) |
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); |
else |
result = count; |
return result; |
} |
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) |
{ |
struct usb_serial *serial = port->serial; |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
unsigned int cflag; |
unsigned char *buf; |
int baud; |
int i; |
u8 control; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if ((!port->tty) || (!port->tty->termios)) { |
dbg("%s - no tty structures", __FUNCTION__); |
return; |
} |
spin_lock_irqsave(&priv->lock, flags); |
if (!priv->termios_initialized) { |
*(port->tty->termios) = tty_std_termios; |
port->tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; |
priv->termios_initialized = 1; |
} |
spin_unlock_irqrestore(&priv->lock, flags); |
cflag = port->tty->termios->c_cflag; |
/* check that they really want us to change something */ |
if (old_termios) { |
if ((cflag == old_termios->c_cflag) && |
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { |
dbg("%s - nothing to change...", __FUNCTION__); |
return; |
} |
} |
buf = kmalloc (7, GFP_KERNEL); |
if (!buf) { |
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); |
return; |
} |
memset (buf, 0x00, 0x07); |
i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), |
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, |
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); |
if (cflag & CSIZE) { |
switch (cflag & CSIZE) { |
case CS5: buf[6] = 5; break; |
case CS6: buf[6] = 6; break; |
case CS7: buf[6] = 7; break; |
default: |
case CS8: buf[6] = 8; break; |
} |
dbg("%s - data bits = %d", __FUNCTION__, buf[6]); |
} |
baud = 0; |
switch (cflag & CBAUD) { |
case B0: baud = 0; break; |
case B75: baud = 75; break; |
case B150: baud = 150; break; |
case B300: baud = 300; break; |
case B600: baud = 600; break; |
case B1200: baud = 1200; break; |
case B1800: baud = 1800; 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; |
case B230400: baud = 230400; break; |
case B460800: baud = 460800; break; |
default: |
dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n"); |
break; |
} |
dbg("%s - baud = %d", __FUNCTION__, baud); |
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]=0 is 1 stop bits */ |
/* For reference buf[4]=1 is 1.5 stop bits */ |
/* For reference buf[4]=2 is 2 stop bits */ |
if (cflag & CSTOPB) { |
buf[4] = 2; |
dbg("%s - stop bits = 2", __FUNCTION__); |
} else { |
buf[4] = 0; |
dbg("%s - stop bits = 1", __FUNCTION__); |
} |
if (cflag & PARENB) { |
/* For reference buf[5]=0 is none parity */ |
/* For reference buf[5]=1 is odd parity */ |
/* For reference buf[5]=2 is even parity */ |
/* For reference buf[5]=3 is mark parity */ |
/* For reference buf[5]=4 is space parity */ |
if (cflag & PARODD) { |
buf[5] = 1; |
dbg("%s - parity = odd", __FUNCTION__); |
} else { |
buf[5] = 2; |
dbg("%s - parity = even", __FUNCTION__); |
} |
} else { |
buf[5] = 0; |
dbg("%s - parity = none", __FUNCTION__); |
} |
i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), |
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0x21:0x20:0:0 %d", i); |
/* change control lines if we are switching to or from B0 */ |
spin_lock_irqsave(&priv->lock, flags); |
control = priv->line_control; |
if ((cflag & CBAUD) == B0) |
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); |
else |
priv->line_control |= (CONTROL_DTR | CONTROL_RTS); |
if (control != priv->line_control) { |
control = priv->line_control; |
spin_unlock_irqrestore(&priv->lock, flags); |
set_control_lines(serial->dev, control); |
} else { |
spin_unlock_irqrestore(&priv->lock, flags); |
} |
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), |
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, |
0, 0, buf, 7, 100); |
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", 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), |
VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, |
0x0, 0x41, NULL, 0, 100); |
dbg ("0x40:0x1:0x0:0x41 %d", i); |
} |
kfree (buf); |
} |
static int pl2303_open (struct usb_serial_port *port, struct file *filp) |
{ |
struct termios tmp_termios; |
struct usb_serial *serial = port->serial; |
unsigned char buf[10]; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return -ENODEV; |
dbg("%s - port %d", __FUNCTION__, port->number); |
usb_clear_halt(serial->dev, port->write_urb->pipe); |
usb_clear_halt(serial->dev, port->read_urb->pipe); |
#define FISH(a,b,c,d) \ |
result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ |
b, a, c, d, buf, 1, 100); \ |
dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]); |
#define SOUP(a,b,c,d) \ |
result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ |
b, a, c, d, NULL, 0, 100); \ |
dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); |
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); |
/* Setup termios */ |
if (port->tty) { |
pl2303_set_termios (port, &tmp_termios); |
} |
//FIXME: need to assert RTS and DTR if CRTSCTS off |
dbg("%s - submitting read urb", __FUNCTION__); |
port->read_urb->dev = serial->dev; |
result = usb_submit_urb (port->read_urb, GFP_KERNEL); |
if (result) { |
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); |
pl2303_close (port, NULL); |
return -EPROTO; |
} |
dbg("%s - submitting interrupt urb", __FUNCTION__); |
port->interrupt_in_urb->dev = serial->dev; |
result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); |
if (result) { |
dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result); |
pl2303_close (port, NULL); |
return -EPROTO; |
} |
return 0; |
} |
static void pl2303_close (struct usb_serial_port *port, struct file *filp) |
{ |
struct usb_serial *serial; |
struct pl2303_private *priv; |
unsigned long flags; |
unsigned int c_cflag; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
/* shutdown our urbs */ |
dbg("%s - shutting down urbs", __FUNCTION__); |
result = usb_unlink_urb (port->write_urb); |
if (result) |
dbg("%s - usb_unlink_urb (write_urb)" |
" failed with reason: %d", __FUNCTION__, |
result); |
result = usb_unlink_urb (port->read_urb); |
if (result) |
dbg("%s - usb_unlink_urb (read_urb) " |
"failed with reason: %d", __FUNCTION__, |
result); |
result = usb_unlink_urb (port->interrupt_in_urb); |
if (result) |
dbg("%s - usb_unlink_urb (interrupt_in_urb)" |
" failed with reason: %d", __FUNCTION__, |
result); |
if (port->tty) { |
c_cflag = port->tty->termios->c_cflag; |
if (c_cflag & HUPCL) { |
/* drop DTR and RTS */ |
priv = usb_get_serial_port_data(port); |
spin_lock_irqsave(&priv->lock, flags); |
priv->line_control = 0; |
spin_unlock_irqrestore (&priv->lock, flags); |
set_control_lines (port->serial->dev, 0); |
} |
} |
} |
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, |
unsigned int set, unsigned int clear) |
{ |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
u8 control; |
spin_lock_irqsave (&priv->lock, flags); |
if (set & TIOCM_RTS) |
priv->line_control |= CONTROL_RTS; |
if (set & TIOCM_DTR) |
priv->line_control |= CONTROL_DTR; |
if (clear & TIOCM_RTS) |
priv->line_control &= ~CONTROL_RTS; |
if (clear & TIOCM_DTR) |
priv->line_control &= ~CONTROL_DTR; |
control = priv->line_control; |
spin_unlock_irqrestore (&priv->lock, flags); |
return set_control_lines (port->serial->dev, control); |
} |
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file) |
{ |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned long flags; |
unsigned int mcr; |
unsigned int status; |
unsigned int result; |
dbg("%s (%d)", __FUNCTION__, port->number); |
spin_lock_irqsave (&priv->lock, flags); |
mcr = priv->line_control; |
status = priv->line_status; |
spin_unlock_irqrestore (&priv->lock, flags); |
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) |
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) |
| ((status & UART_CTS) ? TIOCM_CTS : 0) |
| ((status & UART_DSR) ? TIOCM_DSR : 0) |
| ((status & UART_RING) ? TIOCM_RI : 0) |
| ((status & UART_DCD) ? TIOCM_CD : 0); |
dbg("%s - result = %x", __FUNCTION__, result); |
return result; |
} |
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) |
{ |
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); |
switch (cmd) { |
default: |
dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); |
break; |
} |
return -ENOIOCTLCMD; |
} |
static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) |
{ |
struct usb_serial *serial = port->serial; |
u16 state; |
int result; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (break_state == 0) |
state = BREAK_OFF; |
else |
state = BREAK_ON; |
dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__); |
result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), |
BREAK_REQUEST, BREAK_REQUEST_TYPE, state, |
0, NULL, 0, 100); |
if (result) |
dbg("%s - error sending break = %d", __FUNCTION__, result); |
} |
static void pl2303_shutdown (struct usb_serial *serial) |
{ |
int i; |
dbg("%s", __FUNCTION__); |
for (i = 0; i < serial->num_ports; ++i) { |
kfree (usb_get_serial_port_data(serial->port[i])); |
usb_set_serial_port_data(serial->port[i], NULL); |
} |
} |
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
unsigned char *data = urb->transfer_buffer; |
unsigned long flags; |
int status; |
dbg("%s (%d)", __FUNCTION__, port->number); |
switch (urb->status) { |
case 0: |
/* success */ |
break; |
case -ECONNRESET: |
case -ENOENT: |
case -ESHUTDOWN: |
/* this urb is terminated, clean up */ |
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); |
return; |
default: |
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); |
goto exit; |
} |
if (!serial) { |
return; |
} |
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer); |
if (urb->actual_length < UART_STATE) |
goto exit; |
/* Save off the uart status for others to look at */ |
spin_lock_irqsave(&priv->lock, flags); |
priv->line_status = data[UART_STATE]; |
spin_unlock_irqrestore(&priv->lock, flags); |
exit: |
status = usb_submit_urb (urb, GFP_ATOMIC); |
if (status) |
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", |
__FUNCTION__, status); |
} |
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
struct pl2303_private *priv = usb_get_serial_port_data(port); |
struct tty_struct *tty; |
unsigned char *data = urb->transfer_buffer; |
unsigned long flags; |
int i; |
int result; |
u8 status; |
char tty_flag; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!serial) { |
dbg("%s - bad serial pointer, exiting", __FUNCTION__); |
return; |
} |
if (urb->status) { |
dbg("%s - urb->status = %d", __FUNCTION__, urb->status); |
if (!port->open_count) { |
dbg("%s - port is closed, exiting.", __FUNCTION__); |
return; |
} |
if (urb->status == -EPROTO) { |
/* PL2303 mysteriously fails with -EPROTO reschedule the read */ |
dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); |
urb->status = 0; |
urb->dev = serial->dev; |
result = usb_submit_urb(urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); |
return; |
} |
dbg("%s - unable to handle the error, exiting.", __FUNCTION__); |
return; |
} |
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); |
/* get tty_flag from status */ |
tty_flag = TTY_NORMAL; |
spin_lock_irqsave(&priv->lock, flags); |
status = priv->line_status; |
spin_unlock_irqrestore(&priv->lock, flags); |
/* break takes precedence over parity, */ |
/* which takes precedence over framing errors */ |
if (status & UART_BREAK_ERROR ) |
tty_flag = TTY_BREAK; |
else if (status & UART_PARITY_ERROR) |
tty_flag = TTY_PARITY; |
else if (status & UART_FRAME_ERROR) |
tty_flag = TTY_FRAME; |
dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); |
tty = port->tty; |
if (tty && urb->actual_length) { |
/* overrun is special, not associated with a char */ |
if (status & UART_OVERRUN_ERROR) |
tty_insert_flip_char(tty, 0, TTY_OVERRUN); |
for (i = 0; i < urb->actual_length; ++i) { |
if (tty->flip.count >= TTY_FLIPBUF_SIZE) { |
tty_flip_buffer_push(tty); |
} |
//**#ifdef DEBUG_ME |
tty_insert_flip_char (tty, data[i], tty_flag); |
//**#endif /* DEBUG_ME */ |
} |
tty_flip_buffer_push (tty); |
} |
/* Schedule the next read _if_ we are still open */ |
if (port->open_count) { |
urb->dev = serial->dev; |
result = usb_submit_urb(urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); |
} |
return; |
} |
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
int result; |
if (port_paranoia_check (port, __FUNCTION__)) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (urb->status) { |
/* error in the urb, so we have to resubmit it */ |
if (serial_paranoia_check (port->serial, __FUNCTION__)) { |
return; |
} |
dbg("%s - Overflow in write", __FUNCTION__); |
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); |
port->write_urb->transfer_buffer_length = 1; |
port->write_urb->dev = port->serial->dev; |
result = usb_submit_urb (port->write_urb, GFP_ATOMIC); |
if (result) |
dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); |
return; |
} |
schedule_work(&port->work); |
} |
/*static*/ int __init pl2303_init (void) |
{ |
int retval; |
retval = usb_serial_register(&pl2303_device); |
if (retval) |
goto failed_usb_serial_register; |
retval = usb_register(&pl2303_driver); |
if (retval) |
goto failed_usb_register; |
info(DRIVER_DESC " " DRIVER_VERSION); |
return 0; |
failed_usb_register: |
usb_serial_deregister(&pl2303_device); |
failed_usb_serial_register: |
return retval; |
} |
/*static*/ void __exit pl2303_exit (void) |
{ |
usb_deregister (&pl2303_driver); |
usb_serial_deregister (&pl2303_device); |
} |
module_init(pl2303_init); |
module_exit(pl2303_exit); |
MODULE_DESCRIPTION(DRIVER_DESC); |
MODULE_LICENSE("GPL"); |
MODULE_PARM(debug, "i"); |
MODULE_PARM_DESC(debug, "Debug enabled or not"); |
/shark/trunk/drivers/usb/serial/usb-serial.c |
---|
1,1476 → 1,1477 |
/* |
* USB Serial Converter driver |
* |
* Copyright (C) 1999 - 2003 Greg Kroah-Hartman (greg@kroah.com) |
* Copyright (C) 2000 Peter Berger (pberger@brimson.com) |
* Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com) |
* |
* This program is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License version |
* 2 as published by the Free Software Foundation. |
* |
* This driver was originally based on the ACM driver by Armin Fuerst (which was |
* based on a driver by Brad Keryan) |
* |
* See Documentation/usb/usb-serial.txt for more information on using this driver |
* |
* (12/10/2002) gkh |
* Split the ports off into their own struct device, and added a |
* usb-serial bus driver. |
* |
* (11/19/2002) gkh |
* removed a few #ifdefs for the generic code and cleaned up the failure |
* logic in initialization. |
* |
* (10/02/2002) gkh |
* moved the console code to console.c and out of this file. |
* |
* (06/05/2002) gkh |
* moved location of startup() call in serial_probe() until after all |
* of the port information and endpoints are initialized. This makes |
* things easier for some drivers. |
* |
* (04/10/2002) gkh |
* added serial_read_proc function which creates a |
* /proc/tty/driver/usb-serial file. |
* |
* (03/27/2002) gkh |
* Got USB serial console code working properly and merged into the main |
* version of the tree. Thanks to Randy Dunlap for the initial version |
* of this code, and for pushing me to finish it up. |
* The USB serial console works with any usb serial driver device. |
* |
* (03/21/2002) gkh |
* Moved all manipulation of port->open_count into the core. Now the |
* individual driver's open and close functions are called only when the |
* first open() and last close() is called. Making the drivers a bit |
* smaller and simpler. |
* Fixed a bug if a driver didn't have the owner field set. |
* |
* (02/26/2002) gkh |
* Moved all locking into the main serial_* functions, instead of having |
* the individual drivers have to grab the port semaphore. This should |
* reduce races. |
* Reworked the MOD_INC logic a bit to always increment and decrement, even |
* if the generic driver is being used. |
* |
* (10/10/2001) gkh |
* usb_serial_disconnect() now sets the serial->dev pointer is to NULL to |
* help prevent child drivers from accessing the device since it is now |
* gone. |
* |
* (09/13/2001) gkh |
* Moved generic driver initialize after we have registered with the USB |
* core. Thanks to Randy Dunlap for pointing this problem out. |
* |
* (07/03/2001) gkh |
* Fixed module paramater size. Thanks to John Brockmeyer for the pointer. |
* Fixed vendor and product getting defined through the MODULE_PARM macro |
* if the Generic driver wasn't compiled in. |
* Fixed problem with generic_shutdown() not being called for drivers that |
* don't have a shutdown() function. |
* |
* (06/06/2001) gkh |
* added evil hack that is needed for the prolific pl2303 device due to the |
* crazy way its endpoints are set up. |
* |
* (05/30/2001) gkh |
* switched from using spinlock to a semaphore, which fixes lots of problems. |
* |
* (04/08/2001) gb |
* Identify version on module load. |
* |
* 2001_02_05 gkh |
* Fixed buffer overflows bug with the generic serial driver. Thanks to |
* Todd Squires <squirest@ct0.com> for fixing this. |
* |
* (01/10/2001) gkh |
* Fixed bug where the generic serial adaptor grabbed _any_ device that was |
* offered to it. |
* |
* (12/12/2000) gkh |
* Removed MOD_INC and MOD_DEC from poll and disconnect functions, and |
* moved them to the serial_open and serial_close functions. |
* Also fixed bug with there not being a MOD_DEC for the generic driver |
* (thanks to Gary Brubaker for finding this.) |
* |
* (11/29/2000) gkh |
* Small NULL pointer initialization cleanup which saves a bit of disk image |
* |
* (11/01/2000) Adam J. Richter |
* instead of using idVendor/idProduct pairs, usb serial drivers |
* now identify their hardware interest with usb_device_id tables, |
* which they usually have anyhow for use with MODULE_DEVICE_TABLE. |
* |
* (10/05/2000) gkh |
* Fixed bug with urb->dev not being set properly, now that the usb |
* core needs it. |
* |
* (09/11/2000) gkh |
* Removed DEBUG #ifdefs with call to usb_serial_debug_data |
* |
* (08/28/2000) gkh |
* Added port_lock to port structure. |
* Added locks for SMP safeness to generic driver |
* Fixed the ability to open a generic device's port more than once. |
* |
* (07/23/2000) gkh |
* Added bulk_out_endpointAddress to port structure. |
* |
* (07/19/2000) gkh, pberger, and borchers |
* Modifications to allow usb-serial drivers to be modules. |
* |
* (07/03/2000) gkh |
* Added more debugging to serial_ioctl call |
* |
* (06/25/2000) gkh |
* Changed generic_write_bulk_callback to not call wake_up_interruptible |
* directly, but to have port_softint do it at a safer time. |
* |
* (06/23/2000) gkh |
* Cleaned up debugging statements in a quest to find UHCI timeout bug. |
* |
* (05/22/2000) gkh |
* Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be |
* removed from the individual device source files. |
* |
* (05/03/2000) gkh |
* Added the Digi Acceleport driver from Al Borchers and Peter Berger. |
* |
* (05/02/2000) gkh |
* Changed devfs and tty register code to work properly now. This was based on |
* the ACM driver changes by Vojtech Pavlik. |
* |
* (04/27/2000) Ryan VanderBijl |
* Put calls to *_paranoia_checks into one function. |
* |
* (04/23/2000) gkh |
* Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports. |
* Moved when the startup code printed out the devices that are supported. |
* |
* (04/19/2000) gkh |
* Added driver for ZyXEL omni.net lcd plus ISDN TA |
* Made startup info message specify which drivers were compiled in. |
* |
* (04/03/2000) gkh |
* Changed the probe process to remove the module unload races. |
* Changed where the tty layer gets initialized to have devfs work nicer. |
* Added initial devfs support. |
* |
* (03/26/2000) gkh |
* Split driver up into device specific pieces. |
* |
* (03/19/2000) gkh |
* Fixed oops that could happen when device was removed while a program |
* was talking to the device. |
* Removed the static urbs and now all urbs are created and destroyed |
* dynamically. |
* Reworked the internal interface. Now everything is based on the |
* usb_serial_port structure instead of the larger usb_serial structure. |
* This fixes the bug that a multiport device could not have more than |
* one port open at one time. |
* |
* (03/17/2000) gkh |
* Added config option for debugging messages. |
* Added patch for keyspan pda from Brian Warner. |
* |
* (03/06/2000) gkh |
* Added the keyspan pda code from Brian Warner <warner@lothar.com> |
* Moved a bunch of the port specific stuff into its own structure. This |
* is in anticipation of the true multiport devices (there's a bug if you |
* try to access more than one port of any multiport device right now) |
* |
* (02/21/2000) gkh |
* Made it so that any serial devices only have to specify which functions |
* they want to overload from the generic function calls (great, |
* inheritance in C, in a driver, just what I wanted...) |
* Added support for set_termios and ioctl function calls. No drivers take |
* advantage of this yet. |
* Removed the #ifdef MODULE, now there is no module specific code. |
* Cleaned up a few comments in usb-serial.h that were wrong (thanks again |
* to Miles Lott). |
* Small fix to get_free_serial. |
* |
* (02/14/2000) gkh |
* Removed the Belkin and Peracom functionality from the driver due to |
* the lack of support from the vendor, and me not wanting people to |
* accidenatly buy the device, expecting it to work with Linux. |
* Added read_bulk_callback and write_bulk_callback to the type structure |
* for the needs of the FTDI and WhiteHEAT driver. |
* Changed all reverences to FTDI to FTDI_SIO at the request of Bill |
* Ryder. |
* Changed the output urb size back to the max endpoint size to make |
* the ftdi_sio driver have it easier, and due to the fact that it didn't |
* really increase the speed any. |
* |
* (02/11/2000) gkh |
* Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a |
* patch from Miles Lott (milos@insync.net). |
* Fixed bug with not restoring the minor range that a device grabs, if |
* the startup function fails (thanks Miles for finding this). |
* |
* (02/05/2000) gkh |
* Added initial framework for the Keyspan PDA serial converter so that |
* Brian Warner has a place to put his code. |
* Made the ezusb specific functions generic enough that different |
* devices can use them (whiteheat and keyspan_pda both need them). |
* Split out a whole bunch of structure and other stuff to a separate |
* usb-serial.h file. |
* Made the Visor connection messages a little more understandable, now |
* that Miles Lott (milos@insync.net) has gotten the Generic channel to |
* work. Also made them always show up in the log file. |
* |
* (01/25/2000) gkh |
* Added initial framework for FTDI serial converter so that Bill Ryder |
* has a place to put his code. |
* Added the vendor specific info from Handspring. Now we can print out |
* informational debug messages as well as understand what is happening. |
* |
* (01/23/2000) gkh |
* Fixed problem of crash when trying to open a port that didn't have a |
* device assigned to it. Made the minor node finding a little smarter, |
* now it looks to find a continuous space for the new device. |
* |
* (01/21/2000) gkh |
* Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) |
* Fixed get_serial_by_minor which was all messed up for multi port |
* devices. Fixed multi port problem for generic devices. Now the number |
* of ports is determined by the number of bulk out endpoints for the |
* generic device. |
* |
* (01/19/2000) gkh |
* Removed lots of cruft that was around from the old (pre urb) driver |
* interface. |
* Made the serial_table dynamic. This should save lots of memory when |
* the number of minor nodes goes up to 256. |
* Added initial support for devices that have more than one port. |
* Added more debugging comments for the Visor, and added a needed |
* set_configuration call. |
* |
* (01/17/2000) gkh |
* Fixed the WhiteHEAT firmware (my processing tool had a bug) |
* and added new debug loader firmware for it. |
* Removed the put_char function as it isn't really needed. |
* Added visor startup commands as found by the Win98 dump. |
* |
* (01/13/2000) gkh |
* Fixed the vendor id for the generic driver to the one I meant it to be. |
* |
* (01/12/2000) gkh |
* Forget the version numbering...that's pretty useless... |
* Made the driver able to be compiled so that the user can select which |
* converter they want to use. This allows people who only want the Visor |
* support to not pay the memory size price of the WhiteHEAT. |
* Fixed bug where the generic driver (idVendor=0000 and idProduct=0000) |
* grabbed the root hub. Not good. |
* |
* version 0.4.0 (01/10/2000) gkh |
* Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT |
* device. Added startup function to allow firmware to be downloaded to |
* a device if it needs to be. |
* Added firmware download logic to the WhiteHEAT device. |
* Started to add #defines to split up the different drivers for potential |
* configuration option. |
* |
* version 0.3.1 (12/30/99) gkh |
* Fixed problems with urb for bulk out. |
* Added initial support for multiple sets of endpoints. This enables |
* the Handspring Visor to be attached successfully. Only the first |
* bulk in / bulk out endpoint pair is being used right now. |
* |
* version 0.3.0 (12/27/99) gkh |
* Added initial support for the Handspring Visor based on a patch from |
* Miles Lott (milos@sneety.insync.net) |
* Cleaned up the code a bunch and converted over to using urbs only. |
* |
* version 0.2.3 (12/21/99) gkh |
* Added initial support for the Connect Tech WhiteHEAT converter. |
* Incremented the number of ports in expectation of getting the |
* WhiteHEAT to work properly (4 ports per connection). |
* Added notification on insertion and removal of what port the |
* device is/was connected to (and what kind of device it was). |
* |
* version 0.2.2 (12/16/99) gkh |
* Changed major number to the new allocated number. We're legal now! |
* |
* version 0.2.1 (12/14/99) gkh |
* Fixed bug that happens when device node is opened when there isn't a |
* device attached to it. Thanks to marek@webdesign.no for noticing this. |
* |
* version 0.2.0 (11/10/99) gkh |
* Split up internals to make it easier to add different types of serial |
* converters to the code. |
* Added a "generic" driver that gets it's vendor and product id |
* from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net) |
* for the idea and sample code (from the usb scanner driver.) |
* Cleared up any licensing questions by releasing it under the GNU GPL. |
* |
* version 0.1.2 (10/25/99) gkh |
* Fixed bug in detecting device. |
* |
* version 0.1.1 (10/05/99) gkh |
* Changed the major number to not conflict with anything else. |
* |
* version 0.1 (09/28/99) gkh |
* Can recognize the two different devices and start up a read from |
* device when asked to. Writes also work. No control signals yet, this |
* all is vendor specific data (i.e. no spec), also no control for |
* different baud rates or other bit settings. |
* Currently we are using the same devid as the acm driver. This needs |
* to change. |
* |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/slab.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/module.h> |
#include <linux/spinlock.h> |
#include <linux/list.h> |
#include <linux/smp_lock.h> |
#include <asm/uaccess.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
#include "pl2303.h" |
/* |
* Version Information |
*/ |
#define DRIVER_VERSION "v2.0" |
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/" |
#define DRIVER_DESC "USB Serial Driver core" |
#ifdef CONFIG_USB_SERIAL_GENERIC |
/* we want to look at all devices, as the vendor/product id can change |
* depending on the command line argument */ |
static struct usb_device_id generic_serial_ids[] = { |
{.driver_info = 42}, |
{} |
}; |
#endif /* CONFIG_USB_SERIAL_GENERIC */ |
/* Driver structure we register with the USB core */ |
static struct usb_driver usb_serial_driver = { |
.owner = THIS_MODULE, |
.name = "usbserial", |
.probe = usb_serial_probe, |
.disconnect = usb_serial_disconnect, |
#ifdef CONFIG_USB_SERIAL_GENERIC |
.id_table = generic_serial_ids, |
#endif |
}; |
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead |
the MODULE_DEVICE_TABLE declarations in each serial driver |
cause the "hotplug" program to pull in whatever module is necessary |
via modprobe, and modprobe will load usbserial because the serial |
drivers depend on it. |
*/ |
static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ |
static LIST_HEAD(usb_serial_driver_list); |
struct usb_serial *usb_serial_get_by_index(unsigned index) |
{ |
struct usb_serial *serial = serial_table[index]; |
if (serial) |
kobject_get (&serial->kobj); |
return serial; |
} |
static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor) |
{ |
unsigned int i, j; |
int good_spot; |
dbg("%s %d", __FUNCTION__, num_ports); |
*minor = 0; |
for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
if (serial_table[i]) |
continue; |
good_spot = 1; |
for (j = 1; j <= num_ports-1; ++j) |
if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { |
good_spot = 0; |
i += j; |
break; |
} |
if (good_spot == 0) |
continue; |
serial->magic = USB_SERIAL_MAGIC; |
*minor = i; |
dbg("%s - minor base = %d", __FUNCTION__, *minor); |
for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) |
serial_table[i] = serial; |
return serial; |
} |
return NULL; |
} |
static void return_serial (struct usb_serial *serial) |
{ |
int i; |
dbg("%s", __FUNCTION__); |
if (serial == NULL) |
return; |
for (i = 0; i < serial->num_ports; ++i) { |
serial_table[serial->minor + i] = NULL; |
} |
return; |
} |
/***************************************************************************** |
* 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; |
dbg("%s", __FUNCTION__); |
/* initialize the pointer incase something fails */ |
tty->driver_data = NULL; |
/* get the serial object associated with this tty pointer */ |
serial = usb_serial_get_by_index(tty->index); |
if (serial_paranoia_check (serial, __FUNCTION__)) |
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; |
port->tty = tty; |
/* lock this module before we call it, |
this may, which means we must bail out, safe because we are called with BKL held */ |
if (!try_module_get(serial->type->owner)) { |
retval = -ENODEV; |
goto bailout; |
} |
++port->open_count; |
if (port->open_count == 1) { |
/* only call the device specific open if this |
* is the first time the port is opened */ |
retval = serial->type->open(port, filp); |
if (retval) { |
port->open_count = 0; |
module_put(serial->type->owner); |
kobject_put(&serial->kobj); |
} |
} |
bailout: |
return retval; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
--port->open_count; |
if (port->open_count <= 0) { |
/* only call the device specific close if this |
* port is being closed by the last owner */ |
port->serial->type->close(port, filp); |
port->open_count = 0; |
if (port->tty) { |
if (port->tty->driver_data) |
port->tty->driver_data = NULL; |
port->tty = NULL; |
} |
} |
module_put(port->serial->type->owner); |
kobject_put(&port->serial->kobj); |
} |
/*static*/ int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); |
if (!port->open_count) { |
dbg("%s - port not opened", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->write(port, from_user, buf, count); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->write_room(port); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s = port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->chars_in_buffer(port); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg ("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
if (serial->type->throttle) |
serial->type->throttle(port); |
exit: |
; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
if (serial->type->unthrottle) |
serial->type->unthrottle(port); |
exit: |
; |
} |
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, __FUNCTION__); |
int retval = -ENODEV; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); |
if (!port->open_count) { |
dbg ("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->ioctl) |
retval = serial->type->ioctl(port, file, cmd, arg); |
else |
retval = -ENOIOCTLCMD; |
exit: |
return retval; |
} |
static void serial_set_termios (struct tty_struct *tty, struct termios * old) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->set_termios) |
serial->type->set_termios(port, old); |
exit: |
; |
} |
static void 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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->break_ctl) |
serial->type->break_ctl(port, break_state); |
exit: |
; |
} |
static void serial_shutdown (struct usb_serial *serial) |
{ |
dbg ("%s", __FUNCTION__); |
serial->type->shutdown(serial); |
} |
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) |
{ |
struct usb_serial *serial; |
int length = 0; |
int i; |
off_t begin = 0; |
char tmp[40]; |
dbg("%s", __FUNCTION__); |
length += sprintf26 (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION); |
for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { |
serial = usb_serial_get_by_index(i); |
if (serial == NULL) |
continue; |
length += sprintf26 (page+length, "%d:", i); |
if (serial->type->owner) |
length += sprintf26 (page+length, " module:%s", module_name(serial->type->owner)); |
length += sprintf26 (page+length, " name:\"%s\"", serial->type->name); |
length += sprintf26 (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product); |
length += sprintf26 (page+length, " num_ports:%d", serial->num_ports); |
length += sprintf26 (page+length, " port:%d", i - serial->minor + 1); |
usb_make_path(serial->dev, tmp, sizeof(tmp)); |
length += sprintf26 (page+length, " path:%s", tmp); |
length += sprintf26 (page+length, "\n"); |
if ((length + begin) > (off + count)) |
goto done; |
if ((length + begin) < off) { |
begin += length; |
length = 0; |
} |
kobject_put(&serial->kobj); |
} |
*eof = 1; |
done: |
if (off >= (length + begin)) |
return 0; |
*start = page + (off-begin); |
return ((count < begin+length-off) ? count : begin+length-off); |
} |
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, __FUNCTION__); |
if (!serial) |
goto exit; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
if (serial->type->tiocmget) |
return serial->type->tiocmget(port, file); |
exit: |
return -EINVAL; |
} |
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, __FUNCTION__); |
if (!serial) |
goto exit; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
if (serial->type->tiocmset) |
return serial->type->tiocmset(port, file, set, clear); |
exit: |
return -EINVAL; |
} |
void usb_serial_port_softint(void *private) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *)private; |
struct usb_serial *serial; |
struct tty_struct *tty; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port) |
return; |
serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
tty = port->tty; |
if (!tty) |
return; |
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { |
dbg("%s - write wakeup call.", __FUNCTION__); |
(tty->ldisc.write_wakeup)(tty); |
} |
wake_up_interruptible(&tty->write_wait); |
} |
static void destroy_serial (struct kobject *kobj) |
{ |
struct usb_serial *serial; |
struct usb_serial_port *port; |
int i; |
dbg ("%s - %s", __FUNCTION__, kobj->name); |
serial = to_usb_serial(kobj); |
serial_shutdown (serial); |
/* return the minor range that this device had */ |
return_serial(serial); |
for (i = 0; i < serial->num_ports; ++i) |
serial->port[i]->open_count = 0; |
/* the ports are cleaned up and released in port_release() */ |
for (i = 0; i < serial->num_ports; ++i) |
if (serial->port[i]->dev.parent != NULL) { |
device_unregister(&serial->port[i]->dev); |
serial->port[i] = NULL; |
} |
/* If this is a "fake" port, we have to clean it up here, as it will |
* not get cleaned up in port_release() as it was never registered with |
* the driver core */ |
if (serial->num_ports < serial->num_port_pointers) { |
for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->read_urb) { |
usb_unlink_urb(port->read_urb); |
usb_free_urb(port->read_urb); |
} |
if (port->write_urb) { |
usb_unlink_urb(port->write_urb); |
usb_free_urb(port->write_urb); |
} |
if (port->interrupt_in_urb) { |
usb_unlink_urb(port->interrupt_in_urb); |
usb_free_urb(port->interrupt_in_urb); |
} |
kfree(port->bulk_in_buffer); |
kfree(port->bulk_out_buffer); |
kfree(port->interrupt_in_buffer); |
} |
} |
usb_put_dev(serial->dev); |
/* free up any memory that we allocated */ |
kfree (serial); |
} |
static struct kobj_type usb_serial_kobj_type = { |
.release = destroy_serial, |
}; |
static void port_release(struct device *dev) |
{ |
struct usb_serial_port *port = to_usb_serial_port(dev); |
dbg ("%s - %s", __FUNCTION__, dev->bus_id); |
if (port->read_urb) { |
usb_unlink_urb(port->read_urb); |
usb_free_urb(port->read_urb); |
} |
if (port->write_urb) { |
usb_unlink_urb(port->write_urb); |
usb_free_urb(port->write_urb); |
} |
if (port->interrupt_in_urb) { |
usb_unlink_urb(port->interrupt_in_urb); |
usb_free_urb(port->interrupt_in_urb); |
} |
kfree(port->bulk_in_buffer); |
kfree(port->bulk_out_buffer); |
kfree(port->interrupt_in_buffer); |
kfree(port); |
} |
static struct usb_serial * create_serial (struct usb_device *dev, |
struct usb_interface *interface, |
struct usb_serial_device_type *type) |
{ |
struct usb_serial *serial; |
serial = kmalloc (sizeof (*serial), GFP_KERNEL); |
if (!serial) { |
dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); |
return NULL; |
} |
memset (serial, 0, sizeof(*serial)); |
serial->dev = usb_get_dev(dev); |
serial->type = type; |
serial->interface = interface; |
serial->vendor = dev->descriptor.idVendor; |
serial->product = dev->descriptor.idProduct; |
/* initialize the kobject portion of the usb_device */ |
kobject_init(&serial->kobj); |
serial->kobj.ktype = &usb_serial_kobj_type; |
return serial; |
} |
int usb_serial_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_host_interface *iface_desc; |
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]; |
struct usb_serial_device_type *type = NULL; |
struct list_head *tmp; |
int retval; |
int found; |
int minor; |
int buffer_size; |
int i; |
int num_interrupt_in = 0; |
int num_bulk_in = 0; |
int num_bulk_out = 0; |
int num_ports = 0; |
int max_endpoints; |
const struct usb_device_id *id_pattern = NULL; |
/* loop through our list of known serial converters, and see if this |
device matches. */ |
found = 0; |
list_for_each (tmp, &usb_serial_driver_list) { |
type = list_entry(tmp, struct usb_serial_device_type, driver_list); |
id_pattern = usb_match_id(interface, type->id_table); |
if (id_pattern != NULL) { |
dbg("descriptor matches"); |
found = 1; |
break; |
} |
} |
if (!found) { |
/* no match */ |
dbg("none matched"); |
return -ENODEV; |
} |
serial = create_serial (dev, interface, type); |
if (!serial) { |
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); |
return -ENODEV; |
} |
/* if this device type has a probe function, call it */ |
if (type->probe) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
kfree (serial); |
return -EIO; |
} |
retval = type->probe (serial, id_pattern); |
module_put(type->owner); |
if (retval) { |
dbg ("sub driver rejected device"); |
kfree (serial); |
return retval; |
} |
} |
/* descriptor matches, let's find the endpoints needed */ |
/* check out the endpoints */ |
iface_desc = &interface->altsetting[0]; |
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 */ |
dbg("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 */ |
dbg("found bulk out"); |
bulk_out_endpoint[num_bulk_out] = endpoint; |
++num_bulk_out; |
} |
if ((endpoint->bEndpointAddress & 0x80) && |
((endpoint->bmAttributes & 3) == 0x03)) { |
/* we found a interrupt in endpoint */ |
dbg("found interrupt in"); |
interrupt_in_endpoint[num_interrupt_in] = endpoint; |
++num_interrupt_in; |
} |
} |
#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE) |
/* BEGIN HORRIBLE HACK FOR PL2303 */ |
/* this is needed due to the looney way its endpoints are set up */ |
if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) && |
(dev->descriptor.idProduct == PL2303_PRODUCT_ID)) || |
((dev->descriptor.idVendor == ATEN_VENDOR_ID) && |
(dev->descriptor.idProduct == ATEN_PRODUCT_ID))) { |
if (interface != dev->actconfig->interface[0]) { |
/* check out the endpoints of the other interface*/ |
iface_desc = &dev->actconfig->interface[0]->altsetting[0]; |
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
endpoint = &iface_desc->endpoint[i].desc; |
if ((endpoint->bEndpointAddress & 0x80) && |
((endpoint->bmAttributes & 3) == 0x03)) { |
/* we found a interrupt in endpoint */ |
dbg("found interrupt in for Prolific device on separate interface"); |
interrupt_in_endpoint[num_interrupt_in] = endpoint; |
++num_interrupt_in; |
} |
} |
} |
/* Now make sure the PL-2303 is configured correctly. |
* If not, give up now and hope this hack will work |
* properly during a later invocation of usb_serial_probe |
*/ |
if (num_bulk_in == 0 || num_bulk_out == 0) { |
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n"); |
kfree (serial); |
return -ENODEV; |
} |
} |
/* END HORRIBLE HACK FOR PL2303 */ |
#endif |
/* found all that we need */ |
dev_info(&interface->dev, "%s converter detected\n", type->name); |
#ifdef CONFIG_USB_SERIAL_GENERIC |
if (type == &usb_serial_generic_device) { |
num_ports = num_bulk_out; |
if (num_ports == 0) { |
dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n"); |
kfree (serial); |
return -EIO; |
} |
} |
#endif |
if (!num_ports) { |
/* if this device type has a calc_num_ports function, call it */ |
if (type->calc_num_ports) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
kfree (serial); |
return -EIO; |
} |
num_ports = type->calc_num_ports (serial); |
module_put(type->owner); |
} |
if (!num_ports) |
num_ports = type->num_ports; |
} |
if (get_free_serial (serial, num_ports, &minor) == NULL) { |
dev_err(&interface->dev, "No more free serial devices\n"); |
kfree (serial); |
return -ENOMEM; |
} |
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; |
/* create our ports, we need as many as the max endpoints */ |
/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */ |
max_endpoints = max(num_bulk_in, num_bulk_out); |
max_endpoints = max(max_endpoints, num_interrupt_in); |
max_endpoints = max(max_endpoints, (int)serial->num_ports); |
serial->num_port_pointers = max_endpoints; |
dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints); |
for (i = 0; i < max_endpoints; ++i) { |
port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL); |
if (!port) |
goto probe_error; |
memset(port, 0x00, sizeof(struct usb_serial_port)); |
port->number = i + serial->minor; |
port->serial = serial; |
port->magic = USB_SERIAL_PORT_MAGIC; |
INIT_WORK(&port->work, usb_serial_port_softint, port); |
serial->port[i] = port; |
} |
/* 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) { |
dev_err(&interface->dev, "No free urbs available\n"); |
goto probe_error; |
} |
buffer_size = endpoint->wMaxPacketSize; |
port->bulk_in_endpointAddress = endpoint->bEndpointAddress; |
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); |
if (!port->bulk_in_buffer) { |
dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n"); |
goto probe_error; |
} |
usb_fill_bulk_urb (port->read_urb, dev, |
usb_rcvbulkpipe (dev, |
endpoint->bEndpointAddress), |
port->bulk_in_buffer, buffer_size, |
serial->type->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) { |
dev_err(&interface->dev, "No free urbs available\n"); |
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) { |
dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n"); |
goto probe_error; |
} |
usb_fill_bulk_urb (port->write_urb, dev, |
usb_sndbulkpipe (dev, |
endpoint->bEndpointAddress), |
port->bulk_out_buffer, buffer_size, |
serial->type->write_bulk_callback, |
port); |
} |
for (i = 0; i < num_interrupt_in; ++i) { |
endpoint = interrupt_in_endpoint[i]; |
port = serial->port[i]; |
port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); |
if (!port->interrupt_in_urb) { |
dev_err(&interface->dev, "No free urbs available\n"); |
goto probe_error; |
} |
buffer_size = endpoint->wMaxPacketSize; |
port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; |
port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); |
if (!port->interrupt_in_buffer) { |
dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); |
goto probe_error; |
} |
usb_fill_int_urb (port->interrupt_in_urb, dev, |
usb_rcvintpipe (dev, |
endpoint->bEndpointAddress), |
port->interrupt_in_buffer, buffer_size, |
serial->type->read_int_callback, port, |
endpoint->bInterval); |
} |
/* if this device type has an attach function, call it */ |
if (type->attach) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
goto probe_error; |
} |
retval = type->attach (serial); |
module_put(type->owner); |
if (retval < 0) |
goto probe_error; |
if (retval > 0) { |
/* quietly accept this device, but don't bind to a serial port |
* as it's about to disappear */ |
goto exit; |
} |
} |
/* register all of the individual ports with the driver core */ |
for (i = 0; i < num_ports; ++i) { |
port = serial->port[i]; |
port->dev.parent = &interface->dev; |
port->dev.driver = NULL; |
port->dev.bus = &usb_serial_bus_type; |
port->dev.release = &port_release; |
snprintf26(&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number); |
dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id); |
device_register (&port->dev); |
} |
usb_serial_console_init (debug, minor); |
exit: |
/* success */ |
usb_set_intfdata (interface, serial); |
return 0; |
probe_error: |
for (i = 0; i < num_bulk_in; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->read_urb) |
usb_free_urb (port->read_urb); |
kfree(port->bulk_in_buffer); |
} |
for (i = 0; i < num_bulk_out; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->write_urb) |
usb_free_urb (port->write_urb); |
kfree(port->bulk_out_buffer); |
} |
for (i = 0; i < num_interrupt_in; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (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 */ |
for (i = 0; i < serial->num_port_pointers; ++i) |
kfree(serial->port[i]); |
kfree (serial); |
return -EIO; |
} |
void usb_serial_disconnect(struct usb_interface *interface) |
{ |
struct usb_serial *serial = usb_get_intfdata (interface); |
struct device *dev = &interface->dev; |
dbg ("%s", __FUNCTION__); |
usb_set_intfdata (interface, NULL); |
if (serial) { |
/* let the last holder of this object |
* cause it to be cleaned up */ |
kobject_put (&serial->kobj); |
} |
dev_info(dev, "device disconnected\n"); |
} |
static 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, |
.read_proc = serial_read_proc, |
.tiocmget = serial_tiocmget, |
.tiocmset = serial_tiocmset, |
}; |
struct tty_driver *usb_serial_tty_driver; |
/*static*/ int __init usb_serial_init(void) |
{ |
int i; |
int result = 0; |
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); |
if (!usb_serial_tty_driver) |
return -ENOMEM; |
/* Initialize our global data */ |
for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
serial_table[i] = NULL; |
} |
bus_register(&usb_serial_bus_type); |
/* register the generic driver, if we should */ |
result = usb_serial_generic_register(debug); |
if (result < 0) { |
err("%s - registering generic driver failed", __FUNCTION__); |
goto exit; |
} |
usb_serial_tty_driver->owner = THIS_MODULE; |
usb_serial_tty_driver->driver_name = "usbserial"; |
usb_serial_tty_driver->devfs_name = "usb/tts/"; |
usb_serial_tty_driver->name = "ttyUSB"; |
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; |
usb_serial_tty_driver->minor_start = 0; |
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; |
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; |
usb_serial_tty_driver->init_termios = tty_std_termios; |
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; |
tty_set_operations(usb_serial_tty_driver, &serial_ops); |
result = tty_register_driver(usb_serial_tty_driver); |
if (result) { |
err("%s - tty_register_driver failed", __FUNCTION__); |
goto exit_generic; |
} |
/* register the USB driver */ |
result = usb_register(&usb_serial_driver); |
if (result < 0) { |
err("%s - usb_register failed", __FUNCTION__); |
goto exit_tty; |
} |
info(DRIVER_DESC " " DRIVER_VERSION); |
return result; |
exit_tty: |
tty_unregister_driver(usb_serial_tty_driver); |
exit_generic: |
usb_serial_generic_deregister(); |
exit: |
err ("%s - returning with error %d", __FUNCTION__, result); |
put_tty_driver(usb_serial_tty_driver); |
return result; |
} |
/*static*/ void __exit usb_serial_exit(void) |
{ |
usb_serial_console_exit(); |
usb_serial_generic_deregister(); |
usb_deregister(&usb_serial_driver); |
tty_unregister_driver(usb_serial_tty_driver); |
put_tty_driver(usb_serial_tty_driver); |
bus_unregister(&usb_serial_bus_type); |
} |
module_init(usb_serial_init); |
module_exit(usb_serial_exit); |
#define set_to_generic_if_null(type, function) \ |
do { \ |
if (!type->function) { \ |
type->function = usb_serial_generic_##function; \ |
dbg("Had to override the " #function \ |
" usb serial operation with the generic one.");\ |
} \ |
} while (0) |
static void fixup_generic(struct usb_serial_device_type *device) |
{ |
set_to_generic_if_null(device, open); |
set_to_generic_if_null(device, write); |
set_to_generic_if_null(device, close); |
set_to_generic_if_null(device, write_room); |
set_to_generic_if_null(device, chars_in_buffer); |
set_to_generic_if_null(device, read_bulk_callback); |
set_to_generic_if_null(device, write_bulk_callback); |
set_to_generic_if_null(device, shutdown); |
} |
int usb_serial_register(struct usb_serial_device_type *new_device) |
{ |
int retval; |
fixup_generic(new_device); |
/* Add this device to our list of devices */ |
list_add(&new_device->driver_list, &usb_serial_driver_list); |
retval = usb_serial_bus_register (new_device); |
if (retval) |
goto error; |
info("USB Serial support registered for %s", new_device->name); |
return retval; |
error: |
err("problem %d when registering driver %s", retval, new_device->name); |
list_del(&new_device->driver_list); |
return retval; |
} |
void usb_serial_deregister(struct usb_serial_device_type *device) |
{ |
struct usb_serial *serial; |
int i; |
info("USB Serial deregistering driver %s", device->name); |
/* clear out the serial_table if the device is attached to a port */ |
for(i = 0; i < SERIAL_TTY_MINORS; ++i) { |
serial = serial_table[i]; |
if ((serial != NULL) && (serial->type == device)) { |
usb_driver_release_interface (&usb_serial_driver, serial->interface); |
usb_serial_disconnect (serial->interface); |
} |
} |
list_del(&device->driver_list); |
usb_serial_bus_deregister (device); |
} |
/* If the usb-serial core is built into the core, the usb-serial drivers |
need these symbols to load properly as modules. */ |
EXPORT_SYMBOL(usb_serial_register); |
EXPORT_SYMBOL(usb_serial_deregister); |
EXPORT_SYMBOL(usb_serial_probe); |
EXPORT_SYMBOL(usb_serial_disconnect); |
EXPORT_SYMBOL(usb_serial_port_softint); |
/* Module information */ |
MODULE_AUTHOR( DRIVER_AUTHOR ); |
MODULE_DESCRIPTION( DRIVER_DESC ); |
MODULE_LICENSE("GPL"); |
MODULE_PARM(debug, "i"); |
MODULE_PARM_DESC(debug, "Debug enabled or not"); |
/* |
* USB Serial Converter driver |
* |
* Copyright (C) 1999 - 2003 Greg Kroah-Hartman (greg@kroah.com) |
* Copyright (C) 2000 Peter Berger (pberger@brimson.com) |
* Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com) |
* |
* This program is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License version |
* 2 as published by the Free Software Foundation. |
* |
* This driver was originally based on the ACM driver by Armin Fuerst (which was |
* based on a driver by Brad Keryan) |
* |
* See Documentation/usb/usb-serial.txt for more information on using this driver |
* |
* (12/10/2002) gkh |
* Split the ports off into their own struct device, and added a |
* usb-serial bus driver. |
* |
* (11/19/2002) gkh |
* removed a few #ifdefs for the generic code and cleaned up the failure |
* logic in initialization. |
* |
* (10/02/2002) gkh |
* moved the console code to console.c and out of this file. |
* |
* (06/05/2002) gkh |
* moved location of startup() call in serial_probe() until after all |
* of the port information and endpoints are initialized. This makes |
* things easier for some drivers. |
* |
* (04/10/2002) gkh |
* added serial_read_proc function which creates a |
* /proc/tty/driver/usb-serial file. |
* |
* (03/27/2002) gkh |
* Got USB serial console code working properly and merged into the main |
* version of the tree. Thanks to Randy Dunlap for the initial version |
* of this code, and for pushing me to finish it up. |
* The USB serial console works with any usb serial driver device. |
* |
* (03/21/2002) gkh |
* Moved all manipulation of port->open_count into the core. Now the |
* individual driver's open and close functions are called only when the |
* first open() and last close() is called. Making the drivers a bit |
* smaller and simpler. |
* Fixed a bug if a driver didn't have the owner field set. |
* |
* (02/26/2002) gkh |
* Moved all locking into the main serial_* functions, instead of having |
* the individual drivers have to grab the port semaphore. This should |
* reduce races. |
* Reworked the MOD_INC logic a bit to always increment and decrement, even |
* if the generic driver is being used. |
* |
* (10/10/2001) gkh |
* usb_serial_disconnect() now sets the serial->dev pointer is to NULL to |
* help prevent child drivers from accessing the device since it is now |
* gone. |
* |
* (09/13/2001) gkh |
* Moved generic driver initialize after we have registered with the USB |
* core. Thanks to Randy Dunlap for pointing this problem out. |
* |
* (07/03/2001) gkh |
* Fixed module paramater size. Thanks to John Brockmeyer for the pointer. |
* Fixed vendor and product getting defined through the MODULE_PARM macro |
* if the Generic driver wasn't compiled in. |
* Fixed problem with generic_shutdown() not being called for drivers that |
* don't have a shutdown() function. |
* |
* (06/06/2001) gkh |
* added evil hack that is needed for the prolific pl2303 device due to the |
* crazy way its endpoints are set up. |
* |
* (05/30/2001) gkh |
* switched from using spinlock to a semaphore, which fixes lots of problems. |
* |
* (04/08/2001) gb |
* Identify version on module load. |
* |
* 2001_02_05 gkh |
* Fixed buffer overflows bug with the generic serial driver. Thanks to |
* Todd Squires <squirest@ct0.com> for fixing this. |
* |
* (01/10/2001) gkh |
* Fixed bug where the generic serial adaptor grabbed _any_ device that was |
* offered to it. |
* |
* (12/12/2000) gkh |
* Removed MOD_INC and MOD_DEC from poll and disconnect functions, and |
* moved them to the serial_open and serial_close functions. |
* Also fixed bug with there not being a MOD_DEC for the generic driver |
* (thanks to Gary Brubaker for finding this.) |
* |
* (11/29/2000) gkh |
* Small NULL pointer initialization cleanup which saves a bit of disk image |
* |
* (11/01/2000) Adam J. Richter |
* instead of using idVendor/idProduct pairs, usb serial drivers |
* now identify their hardware interest with usb_device_id tables, |
* which they usually have anyhow for use with MODULE_DEVICE_TABLE. |
* |
* (10/05/2000) gkh |
* Fixed bug with urb->dev not being set properly, now that the usb |
* core needs it. |
* |
* (09/11/2000) gkh |
* Removed DEBUG #ifdefs with call to usb_serial_debug_data |
* |
* (08/28/2000) gkh |
* Added port_lock to port structure. |
* Added locks for SMP safeness to generic driver |
* Fixed the ability to open a generic device's port more than once. |
* |
* (07/23/2000) gkh |
* Added bulk_out_endpointAddress to port structure. |
* |
* (07/19/2000) gkh, pberger, and borchers |
* Modifications to allow usb-serial drivers to be modules. |
* |
* (07/03/2000) gkh |
* Added more debugging to serial_ioctl call |
* |
* (06/25/2000) gkh |
* Changed generic_write_bulk_callback to not call wake_up_interruptible |
* directly, but to have port_softint do it at a safer time. |
* |
* (06/23/2000) gkh |
* Cleaned up debugging statements in a quest to find UHCI timeout bug. |
* |
* (05/22/2000) gkh |
* Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be |
* removed from the individual device source files. |
* |
* (05/03/2000) gkh |
* Added the Digi Acceleport driver from Al Borchers and Peter Berger. |
* |
* (05/02/2000) gkh |
* Changed devfs and tty register code to work properly now. This was based on |
* the ACM driver changes by Vojtech Pavlik. |
* |
* (04/27/2000) Ryan VanderBijl |
* Put calls to *_paranoia_checks into one function. |
* |
* (04/23/2000) gkh |
* Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports. |
* Moved when the startup code printed out the devices that are supported. |
* |
* (04/19/2000) gkh |
* Added driver for ZyXEL omni.net lcd plus ISDN TA |
* Made startup info message specify which drivers were compiled in. |
* |
* (04/03/2000) gkh |
* Changed the probe process to remove the module unload races. |
* Changed where the tty layer gets initialized to have devfs work nicer. |
* Added initial devfs support. |
* |
* (03/26/2000) gkh |
* Split driver up into device specific pieces. |
* |
* (03/19/2000) gkh |
* Fixed oops that could happen when device was removed while a program |
* was talking to the device. |
* Removed the static urbs and now all urbs are created and destroyed |
* dynamically. |
* Reworked the internal interface. Now everything is based on the |
* usb_serial_port structure instead of the larger usb_serial structure. |
* This fixes the bug that a multiport device could not have more than |
* one port open at one time. |
* |
* (03/17/2000) gkh |
* Added config option for debugging messages. |
* Added patch for keyspan pda from Brian Warner. |
* |
* (03/06/2000) gkh |
* Added the keyspan pda code from Brian Warner <warner@lothar.com> |
* Moved a bunch of the port specific stuff into its own structure. This |
* is in anticipation of the true multiport devices (there's a bug if you |
* try to access more than one port of any multiport device right now) |
* |
* (02/21/2000) gkh |
* Made it so that any serial devices only have to specify which functions |
* they want to overload from the generic function calls (great, |
* inheritance in C, in a driver, just what I wanted...) |
* Added support for set_termios and ioctl function calls. No drivers take |
* advantage of this yet. |
* Removed the #ifdef MODULE, now there is no module specific code. |
* Cleaned up a few comments in usb-serial.h that were wrong (thanks again |
* to Miles Lott). |
* Small fix to get_free_serial. |
* |
* (02/14/2000) gkh |
* Removed the Belkin and Peracom functionality from the driver due to |
* the lack of support from the vendor, and me not wanting people to |
* accidenatly buy the device, expecting it to work with Linux. |
* Added read_bulk_callback and write_bulk_callback to the type structure |
* for the needs of the FTDI and WhiteHEAT driver. |
* Changed all reverences to FTDI to FTDI_SIO at the request of Bill |
* Ryder. |
* Changed the output urb size back to the max endpoint size to make |
* the ftdi_sio driver have it easier, and due to the fact that it didn't |
* really increase the speed any. |
* |
* (02/11/2000) gkh |
* Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a |
* patch from Miles Lott (milos@insync.net). |
* Fixed bug with not restoring the minor range that a device grabs, if |
* the startup function fails (thanks Miles for finding this). |
* |
* (02/05/2000) gkh |
* Added initial framework for the Keyspan PDA serial converter so that |
* Brian Warner has a place to put his code. |
* Made the ezusb specific functions generic enough that different |
* devices can use them (whiteheat and keyspan_pda both need them). |
* Split out a whole bunch of structure and other stuff to a separate |
* usb-serial.h file. |
* Made the Visor connection messages a little more understandable, now |
* that Miles Lott (milos@insync.net) has gotten the Generic channel to |
* work. Also made them always show up in the log file. |
* |
* (01/25/2000) gkh |
* Added initial framework for FTDI serial converter so that Bill Ryder |
* has a place to put his code. |
* Added the vendor specific info from Handspring. Now we can print out |
* informational debug messages as well as understand what is happening. |
* |
* (01/23/2000) gkh |
* Fixed problem of crash when trying to open a port that didn't have a |
* device assigned to it. Made the minor node finding a little smarter, |
* now it looks to find a continuous space for the new device. |
* |
* (01/21/2000) gkh |
* Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) |
* Fixed get_serial_by_minor which was all messed up for multi port |
* devices. Fixed multi port problem for generic devices. Now the number |
* of ports is determined by the number of bulk out endpoints for the |
* generic device. |
* |
* (01/19/2000) gkh |
* Removed lots of cruft that was around from the old (pre urb) driver |
* interface. |
* Made the serial_table dynamic. This should save lots of memory when |
* the number of minor nodes goes up to 256. |
* Added initial support for devices that have more than one port. |
* Added more debugging comments for the Visor, and added a needed |
* set_configuration call. |
* |
* (01/17/2000) gkh |
* Fixed the WhiteHEAT firmware (my processing tool had a bug) |
* and added new debug loader firmware for it. |
* Removed the put_char function as it isn't really needed. |
* Added visor startup commands as found by the Win98 dump. |
* |
* (01/13/2000) gkh |
* Fixed the vendor id for the generic driver to the one I meant it to be. |
* |
* (01/12/2000) gkh |
* Forget the version numbering...that's pretty useless... |
* Made the driver able to be compiled so that the user can select which |
* converter they want to use. This allows people who only want the Visor |
* support to not pay the memory size price of the WhiteHEAT. |
* Fixed bug where the generic driver (idVendor=0000 and idProduct=0000) |
* grabbed the root hub. Not good. |
* |
* version 0.4.0 (01/10/2000) gkh |
* Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT |
* device. Added startup function to allow firmware to be downloaded to |
* a device if it needs to be. |
* Added firmware download logic to the WhiteHEAT device. |
* Started to add #defines to split up the different drivers for potential |
* configuration option. |
* |
* version 0.3.1 (12/30/99) gkh |
* Fixed problems with urb for bulk out. |
* Added initial support for multiple sets of endpoints. This enables |
* the Handspring Visor to be attached successfully. Only the first |
* bulk in / bulk out endpoint pair is being used right now. |
* |
* version 0.3.0 (12/27/99) gkh |
* Added initial support for the Handspring Visor based on a patch from |
* Miles Lott (milos@sneety.insync.net) |
* Cleaned up the code a bunch and converted over to using urbs only. |
* |
* version 0.2.3 (12/21/99) gkh |
* Added initial support for the Connect Tech WhiteHEAT converter. |
* Incremented the number of ports in expectation of getting the |
* WhiteHEAT to work properly (4 ports per connection). |
* Added notification on insertion and removal of what port the |
* device is/was connected to (and what kind of device it was). |
* |
* version 0.2.2 (12/16/99) gkh |
* Changed major number to the new allocated number. We're legal now! |
* |
* version 0.2.1 (12/14/99) gkh |
* Fixed bug that happens when device node is opened when there isn't a |
* device attached to it. Thanks to marek@webdesign.no for noticing this. |
* |
* version 0.2.0 (11/10/99) gkh |
* Split up internals to make it easier to add different types of serial |
* converters to the code. |
* Added a "generic" driver that gets it's vendor and product id |
* from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net) |
* for the idea and sample code (from the usb scanner driver.) |
* Cleared up any licensing questions by releasing it under the GNU GPL. |
* |
* version 0.1.2 (10/25/99) gkh |
* Fixed bug in detecting device. |
* |
* version 0.1.1 (10/05/99) gkh |
* Changed the major number to not conflict with anything else. |
* |
* version 0.1 (09/28/99) gkh |
* Can recognize the two different devices and start up a read from |
* device when asked to. Writes also work. No control signals yet, this |
* all is vendor specific data (i.e. no spec), also no control for |
* different baud rates or other bit settings. |
* Currently we are using the same devid as the acm driver. This needs |
* to change. |
* |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/slab.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/module.h> |
#include <linux/spinlock.h> |
#include <linux/list.h> |
#include <linux/smp_lock.h> |
#include <asm/uaccess.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
#include "pl2303.h" |
/* |
* Version Information |
*/ |
#define DRIVER_VERSION "v2.0" |
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/" |
#define DRIVER_DESC "USB Serial Driver core" |
#ifdef CONFIG_USB_SERIAL_GENERIC |
/* we want to look at all devices, as the vendor/product id can change |
* depending on the command line argument */ |
static struct usb_device_id generic_serial_ids[] = { |
{.driver_info = 42}, |
{} |
}; |
#endif /* CONFIG_USB_SERIAL_GENERIC */ |
/* Driver structure we register with the USB core */ |
static struct usb_driver usb_serial_driver = { |
.owner = THIS_MODULE, |
.name = "usbserial", |
.probe = usb_serial_probe, |
.disconnect = usb_serial_disconnect, |
#ifdef CONFIG_USB_SERIAL_GENERIC |
.id_table = generic_serial_ids, |
#endif |
}; |
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead |
the MODULE_DEVICE_TABLE declarations in each serial driver |
cause the "hotplug" program to pull in whatever module is necessary |
via modprobe, and modprobe will load usbserial because the serial |
drivers depend on it. |
*/ |
static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ |
static LIST_HEAD(usb_serial_driver_list); |
struct usb_serial *usb_serial_get_by_index(unsigned index) |
{ |
struct usb_serial *serial = serial_table[index]; |
if (serial) |
kobject_get (&serial->kobj); |
return serial; |
} |
static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor) |
{ |
unsigned int i, j; |
int good_spot; |
dbg("%s %d", __FUNCTION__, num_ports); |
*minor = 0; |
for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
if (serial_table[i]) |
continue; |
good_spot = 1; |
for (j = 1; j <= num_ports-1; ++j) |
if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { |
good_spot = 0; |
i += j; |
break; |
} |
if (good_spot == 0) |
continue; |
serial->magic = USB_SERIAL_MAGIC; |
*minor = i; |
dbg("%s - minor base = %d", __FUNCTION__, *minor); |
for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) |
serial_table[i] = serial; |
return serial; |
} |
return NULL; |
} |
static void return_serial (struct usb_serial *serial) |
{ |
int i; |
dbg("%s", __FUNCTION__); |
printk("Returning serial %d ports %d\n", serial->minor, serial->num_ports); |
if (serial == NULL) |
return; |
for (i = 0; i < serial->num_ports; ++i) { |
serial_table[serial->minor + i] = NULL; |
} |
return; |
} |
/***************************************************************************** |
* 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; |
dbg("%s", __FUNCTION__); |
/* initialize the pointer incase something fails */ |
tty->driver_data = NULL; |
/* get the serial object associated with this tty pointer */ |
serial = usb_serial_get_by_index(tty->index); |
if (serial_paranoia_check (serial, __FUNCTION__)) |
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; |
port->tty = tty; |
/* lock this module before we call it, |
this may, which means we must bail out, safe because we are called with BKL held */ |
if (!try_module_get(serial->type->owner)) { |
retval = -ENODEV; |
goto bailout; |
} |
++port->open_count; |
if (port->open_count == 1) { |
/* only call the device specific open if this |
* is the first time the port is opened */ |
retval = serial->type->open(port, filp); |
if (retval) { |
port->open_count = 0; |
module_put(serial->type->owner); |
kobject_put(&serial->kobj); |
} |
} |
bailout: |
return retval; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
--port->open_count; |
if (port->open_count <= 0) { |
/* only call the device specific close if this |
* port is being closed by the last owner */ |
port->serial->type->close(port, filp); |
port->open_count = 0; |
if (port->tty) { |
if (port->tty->driver_data) |
port->tty->driver_data = NULL; |
port->tty = NULL; |
} |
} |
module_put(port->serial->type->owner); |
kobject_put(&port->serial->kobj); |
} |
/*static*/ int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); |
if (!port->open_count) { |
dbg("%s - port not opened", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->write(port, from_user, buf, count); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->write_room(port); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
int retval = -EINVAL; |
if (!serial) |
return -ENODEV; |
dbg("%s = port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
retval = serial->type->chars_in_buffer(port); |
exit: |
return retval; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg ("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
if (serial->type->throttle) |
serial->type->throttle(port); |
exit: |
; |
} |
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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function */ |
if (serial->type->unthrottle) |
serial->type->unthrottle(port); |
exit: |
; |
} |
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, __FUNCTION__); |
int retval = -ENODEV; |
if (!serial) |
return -ENODEV; |
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); |
if (!port->open_count) { |
dbg ("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->ioctl) |
retval = serial->type->ioctl(port, file, cmd, arg); |
else |
retval = -ENOIOCTLCMD; |
exit: |
return retval; |
} |
static void serial_set_termios (struct tty_struct *tty, struct termios * old) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; |
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->set_termios) |
serial->type->set_termios(port, old); |
exit: |
; |
} |
static void 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, __FUNCTION__); |
if (!serial) |
return; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
/* pass on to the driver specific version of this function if it is available */ |
if (serial->type->break_ctl) |
serial->type->break_ctl(port, break_state); |
exit: |
; |
} |
static void serial_shutdown (struct usb_serial *serial) |
{ |
dbg ("%s", __FUNCTION__); |
serial->type->shutdown(serial); |
} |
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) |
{ |
struct usb_serial *serial; |
int length = 0; |
int i; |
off_t begin = 0; |
char tmp[40]; |
dbg("%s", __FUNCTION__); |
length += sprintf26 (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION); |
for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { |
serial = usb_serial_get_by_index(i); |
if (serial == NULL) |
continue; |
length += sprintf26 (page+length, "%d:", i); |
if (serial->type->owner) |
length += sprintf26 (page+length, " module:%s", module_name(serial->type->owner)); |
length += sprintf26 (page+length, " name:\"%s\"", serial->type->name); |
length += sprintf26 (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product); |
length += sprintf26 (page+length, " num_ports:%d", serial->num_ports); |
length += sprintf26 (page+length, " port:%d", i - serial->minor + 1); |
usb_make_path(serial->dev, tmp, sizeof(tmp)); |
length += sprintf26 (page+length, " path:%s", tmp); |
length += sprintf26 (page+length, "\n"); |
if ((length + begin) > (off + count)) |
goto done; |
if ((length + begin) < off) { |
begin += length; |
length = 0; |
} |
kobject_put(&serial->kobj); |
} |
*eof = 1; |
done: |
if (off >= (length + begin)) |
return 0; |
*start = page + (off-begin); |
return ((count < begin+length-off) ? count : begin+length-off); |
} |
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, __FUNCTION__); |
if (!serial) |
goto exit; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
if (serial->type->tiocmget) |
return serial->type->tiocmget(port, file); |
exit: |
return -EINVAL; |
} |
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, __FUNCTION__); |
if (!serial) |
goto exit; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port->open_count) { |
dbg("%s - port not open", __FUNCTION__); |
goto exit; |
} |
if (serial->type->tiocmset) |
return serial->type->tiocmset(port, file, set, clear); |
exit: |
return -EINVAL; |
} |
void usb_serial_port_softint(void *private) |
{ |
struct usb_serial_port *port = (struct usb_serial_port *)private; |
struct usb_serial *serial; |
struct tty_struct *tty; |
dbg("%s - port %d", __FUNCTION__, port->number); |
if (!port) |
return; |
serial = get_usb_serial (port, __FUNCTION__); |
if (!serial) |
return; |
tty = port->tty; |
if (!tty) |
return; |
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { |
dbg("%s - write wakeup call.", __FUNCTION__); |
(tty->ldisc.write_wakeup)(tty); |
} |
wake_up_interruptible(&tty->write_wait); |
} |
static void destroy_serial (struct kobject *kobj) |
{ |
struct usb_serial *serial; |
struct usb_serial_port *port; |
int i; |
dbg ("%s - %s", __FUNCTION__, kobj->name); |
serial = to_usb_serial(kobj); |
serial_shutdown (serial); |
/* return the minor range that this device had */ |
return_serial(serial); |
for (i = 0; i < serial->num_ports; ++i) |
serial->port[i]->open_count = 0; |
/* the ports are cleaned up and released in port_release() */ |
for (i = 0; i < serial->num_ports; ++i) |
if (serial->port[i]->dev.parent != NULL) { |
device_unregister(&serial->port[i]->dev); |
serial->port[i] = NULL; |
} |
/* If this is a "fake" port, we have to clean it up here, as it will |
* not get cleaned up in port_release() as it was never registered with |
* the driver core */ |
if (serial->num_ports < serial->num_port_pointers) { |
for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->read_urb) { |
usb_unlink_urb(port->read_urb); |
usb_free_urb(port->read_urb); |
} |
if (port->write_urb) { |
usb_unlink_urb(port->write_urb); |
usb_free_urb(port->write_urb); |
} |
if (port->interrupt_in_urb) { |
usb_unlink_urb(port->interrupt_in_urb); |
usb_free_urb(port->interrupt_in_urb); |
} |
kfree(port->bulk_in_buffer); |
kfree(port->bulk_out_buffer); |
kfree(port->interrupt_in_buffer); |
} |
} |
usb_put_dev(serial->dev); |
/* free up any memory that we allocated */ |
kfree (serial); |
} |
static struct kobj_type usb_serial_kobj_type = { |
.release = destroy_serial, |
}; |
static void port_release(struct device *dev) |
{ |
struct usb_serial_port *port = to_usb_serial_port(dev); |
dbg ("%s - %s", __FUNCTION__, dev->bus_id); |
if (port->read_urb) { |
usb_unlink_urb(port->read_urb); |
usb_free_urb(port->read_urb); |
} |
if (port->write_urb) { |
usb_unlink_urb(port->write_urb); |
usb_free_urb(port->write_urb); |
} |
if (port->interrupt_in_urb) { |
usb_unlink_urb(port->interrupt_in_urb); |
usb_free_urb(port->interrupt_in_urb); |
} |
kfree(port->bulk_in_buffer); |
kfree(port->bulk_out_buffer); |
kfree(port->interrupt_in_buffer); |
kfree(port); |
} |
static struct usb_serial * create_serial (struct usb_device *dev, |
struct usb_interface *interface, |
struct usb_serial_device_type *type) |
{ |
struct usb_serial *serial; |
serial = kmalloc (sizeof (*serial), GFP_KERNEL); |
if (!serial) { |
dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); |
return NULL; |
} |
memset (serial, 0, sizeof(*serial)); |
serial->dev = usb_get_dev(dev); |
serial->type = type; |
serial->interface = interface; |
serial->vendor = dev->descriptor.idVendor; |
serial->product = dev->descriptor.idProduct; |
/* initialize the kobject portion of the usb_device */ |
kobject_init(&serial->kobj); |
serial->kobj.ktype = &usb_serial_kobj_type; |
return serial; |
} |
int usb_serial_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_host_interface *iface_desc; |
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]; |
struct usb_serial_device_type *type = NULL; |
struct list_head *tmp; |
int retval; |
int found; |
int minor; |
int buffer_size; |
int i; |
int num_interrupt_in = 0; |
int num_bulk_in = 0; |
int num_bulk_out = 0; |
int num_ports = 0; |
int max_endpoints; |
const struct usb_device_id *id_pattern = NULL; |
/* loop through our list of known serial converters, and see if this |
device matches. */ |
found = 0; |
list_for_each (tmp, &usb_serial_driver_list) { |
type = list_entry(tmp, struct usb_serial_device_type, driver_list); |
id_pattern = usb_match_id(interface, type->id_table); |
if (id_pattern != NULL) { |
dbg("descriptor matches"); |
found = 1; |
break; |
} |
} |
if (!found) { |
/* no match */ |
dbg("none matched"); |
return -ENODEV; |
} |
serial = create_serial (dev, interface, type); |
if (!serial) { |
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); |
return -ENODEV; |
} |
/* if this device type has a probe function, call it */ |
if (type->probe) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
kfree (serial); |
return -EIO; |
} |
retval = type->probe (serial, id_pattern); |
module_put(type->owner); |
if (retval) { |
dbg ("sub driver rejected device"); |
kfree (serial); |
return retval; |
} |
} |
/* descriptor matches, let's find the endpoints needed */ |
/* check out the endpoints */ |
iface_desc = &interface->altsetting[0]; |
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 */ |
dbg("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 */ |
dbg("found bulk out"); |
bulk_out_endpoint[num_bulk_out] = endpoint; |
++num_bulk_out; |
} |
if ((endpoint->bEndpointAddress & 0x80) && |
((endpoint->bmAttributes & 3) == 0x03)) { |
/* we found a interrupt in endpoint */ |
dbg("found interrupt in"); |
interrupt_in_endpoint[num_interrupt_in] = endpoint; |
++num_interrupt_in; |
} |
} |
#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE) |
/* BEGIN HORRIBLE HACK FOR PL2303 */ |
/* this is needed due to the looney way its endpoints are set up */ |
if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) && |
(dev->descriptor.idProduct == PL2303_PRODUCT_ID)) || |
((dev->descriptor.idVendor == ATEN_VENDOR_ID) && |
(dev->descriptor.idProduct == ATEN_PRODUCT_ID))) { |
if (interface != dev->actconfig->interface[0]) { |
/* check out the endpoints of the other interface*/ |
iface_desc = &dev->actconfig->interface[0]->altsetting[0]; |
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
endpoint = &iface_desc->endpoint[i].desc; |
if ((endpoint->bEndpointAddress & 0x80) && |
((endpoint->bmAttributes & 3) == 0x03)) { |
/* we found a interrupt in endpoint */ |
dbg("found interrupt in for Prolific device on separate interface"); |
interrupt_in_endpoint[num_interrupt_in] = endpoint; |
++num_interrupt_in; |
} |
} |
} |
/* Now make sure the PL-2303 is configured correctly. |
* If not, give up now and hope this hack will work |
* properly during a later invocation of usb_serial_probe |
*/ |
if (num_bulk_in == 0 || num_bulk_out == 0) { |
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n"); |
kfree (serial); |
return -ENODEV; |
} |
} |
/* END HORRIBLE HACK FOR PL2303 */ |
#endif |
/* found all that we need */ |
dev_info(&interface->dev, "%s converter detected\n", type->name); |
#ifdef CONFIG_USB_SERIAL_GENERIC |
if (type == &usb_serial_generic_device) { |
num_ports = num_bulk_out; |
if (num_ports == 0) { |
dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n"); |
kfree (serial); |
return -EIO; |
} |
} |
#endif |
if (!num_ports) { |
/* if this device type has a calc_num_ports function, call it */ |
if (type->calc_num_ports) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
kfree (serial); |
return -EIO; |
} |
num_ports = type->calc_num_ports (serial); |
module_put(type->owner); |
} |
if (!num_ports) |
num_ports = type->num_ports; |
} |
if (get_free_serial (serial, num_ports, &minor) == NULL) { |
dev_err(&interface->dev, "No more free serial devices\n"); |
kfree (serial); |
return -ENOMEM; |
} |
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; |
/* create our ports, we need as many as the max endpoints */ |
/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */ |
max_endpoints = max(num_bulk_in, num_bulk_out); |
max_endpoints = max(max_endpoints, num_interrupt_in); |
max_endpoints = max(max_endpoints, (int)serial->num_ports); |
serial->num_port_pointers = max_endpoints; |
dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints); |
for (i = 0; i < max_endpoints; ++i) { |
port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL); |
if (!port) |
goto probe_error; |
memset(port, 0x00, sizeof(struct usb_serial_port)); |
port->number = i + serial->minor; |
port->serial = serial; |
port->magic = USB_SERIAL_PORT_MAGIC; |
INIT_WORK(&port->work, usb_serial_port_softint, port); |
serial->port[i] = port; |
} |
/* 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) { |
dev_err(&interface->dev, "No free urbs available\n"); |
goto probe_error; |
} |
buffer_size = endpoint->wMaxPacketSize; |
port->bulk_in_endpointAddress = endpoint->bEndpointAddress; |
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); |
if (!port->bulk_in_buffer) { |
dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n"); |
goto probe_error; |
} |
usb_fill_bulk_urb (port->read_urb, dev, |
usb_rcvbulkpipe (dev, |
endpoint->bEndpointAddress), |
port->bulk_in_buffer, buffer_size, |
serial->type->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) { |
dev_err(&interface->dev, "No free urbs available\n"); |
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) { |
dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n"); |
goto probe_error; |
} |
usb_fill_bulk_urb (port->write_urb, dev, |
usb_sndbulkpipe (dev, |
endpoint->bEndpointAddress), |
port->bulk_out_buffer, buffer_size, |
serial->type->write_bulk_callback, |
port); |
} |
for (i = 0; i < num_interrupt_in; ++i) { |
endpoint = interrupt_in_endpoint[i]; |
port = serial->port[i]; |
port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); |
if (!port->interrupt_in_urb) { |
dev_err(&interface->dev, "No free urbs available\n"); |
goto probe_error; |
} |
buffer_size = endpoint->wMaxPacketSize; |
port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; |
port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); |
if (!port->interrupt_in_buffer) { |
dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); |
goto probe_error; |
} |
usb_fill_int_urb (port->interrupt_in_urb, dev, |
usb_rcvintpipe (dev, |
endpoint->bEndpointAddress), |
port->interrupt_in_buffer, buffer_size, |
serial->type->read_int_callback, port, |
endpoint->bInterval); |
} |
/* if this device type has an attach function, call it */ |
if (type->attach) { |
if (!try_module_get(type->owner)) { |
dev_err(&interface->dev, "module get failed, exiting\n"); |
goto probe_error; |
} |
retval = type->attach (serial); |
module_put(type->owner); |
if (retval < 0) |
goto probe_error; |
if (retval > 0) { |
/* quietly accept this device, but don't bind to a serial port |
* as it's about to disappear */ |
goto exit; |
} |
} |
/* register all of the individual ports with the driver core */ |
for (i = 0; i < num_ports; ++i) { |
port = serial->port[i]; |
port->dev.parent = &interface->dev; |
port->dev.driver = NULL; |
port->dev.bus = &usb_serial_bus_type; |
port->dev.release = &port_release; |
snprintf26(&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number); |
/*dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);*/ |
device_register (&port->dev); |
} |
usb_serial_console_init (debug, minor); |
exit: |
/* success */ |
usb_set_intfdata (interface, serial); |
return 0; |
probe_error: |
for (i = 0; i < num_bulk_in; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->read_urb) |
usb_free_urb (port->read_urb); |
kfree(port->bulk_in_buffer); |
} |
for (i = 0; i < num_bulk_out; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (port->write_urb) |
usb_free_urb (port->write_urb); |
kfree(port->bulk_out_buffer); |
} |
for (i = 0; i < num_interrupt_in; ++i) { |
port = serial->port[i]; |
if (!port) |
continue; |
if (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 */ |
for (i = 0; i < serial->num_port_pointers; ++i) |
kfree(serial->port[i]); |
kfree (serial); |
return -EIO; |
} |
void usb_serial_disconnect(struct usb_interface *interface) |
{ |
struct usb_serial *serial = usb_get_intfdata (interface); |
struct device *dev = &interface->dev; |
dbg ("%s", __FUNCTION__); |
usb_set_intfdata (interface, NULL); |
if (serial) { |
/* let the last holder of this object |
* cause it to be cleaned up */ |
kobject_put (&serial->kobj); |
} |
dev_info(dev, "device disconnected\n"); |
} |
static 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, |
.read_proc = serial_read_proc, |
.tiocmget = serial_tiocmget, |
.tiocmset = serial_tiocmset, |
}; |
struct tty_driver *usb_serial_tty_driver; |
/*static*/ int __init usb_serial_init(void) |
{ |
int i; |
int result = 0; |
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); |
if (!usb_serial_tty_driver) |
return -ENOMEM; |
/* Initialize our global data */ |
for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
serial_table[i] = NULL; |
} |
bus_register(&usb_serial_bus_type); |
/* register the generic driver, if we should */ |
result = usb_serial_generic_register(debug); |
if (result < 0) { |
err("%s - registering generic driver failed", __FUNCTION__); |
goto exit; |
} |
usb_serial_tty_driver->owner = THIS_MODULE; |
usb_serial_tty_driver->driver_name = "usbserial"; |
usb_serial_tty_driver->devfs_name = "usb/tts/"; |
usb_serial_tty_driver->name = "ttyUSB"; |
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; |
usb_serial_tty_driver->minor_start = 0; |
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; |
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; |
usb_serial_tty_driver->init_termios = tty_std_termios; |
usb_serial_tty_driver->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; |
tty_set_operations(usb_serial_tty_driver, &serial_ops); |
result = tty_register_driver(usb_serial_tty_driver); |
if (result) { |
err("%s - tty_register_driver failed", __FUNCTION__); |
goto exit_generic; |
} |
/* register the USB driver */ |
result = usb_register(&usb_serial_driver); |
if (result < 0) { |
err("%s - usb_register failed", __FUNCTION__); |
goto exit_tty; |
} |
info(DRIVER_DESC " " DRIVER_VERSION); |
return result; |
exit_tty: |
tty_unregister_driver(usb_serial_tty_driver); |
exit_generic: |
usb_serial_generic_deregister(); |
exit: |
err ("%s - returning with error %d", __FUNCTION__, result); |
put_tty_driver(usb_serial_tty_driver); |
return result; |
} |
/*static*/ void __exit usb_serial_exit(void) |
{ |
usb_serial_console_exit(); |
usb_serial_generic_deregister(); |
usb_deregister(&usb_serial_driver); |
tty_unregister_driver(usb_serial_tty_driver); |
put_tty_driver(usb_serial_tty_driver); |
bus_unregister(&usb_serial_bus_type); |
} |
module_init(usb_serial_init); |
module_exit(usb_serial_exit); |
#define set_to_generic_if_null(type, function) \ |
do { \ |
if (!type->function) { \ |
type->function = usb_serial_generic_##function; \ |
dbg("Had to override the " #function \ |
" usb serial operation with the generic one.");\ |
} \ |
} while (0) |
static void fixup_generic(struct usb_serial_device_type *device) |
{ |
set_to_generic_if_null(device, open); |
set_to_generic_if_null(device, write); |
set_to_generic_if_null(device, close); |
set_to_generic_if_null(device, write_room); |
set_to_generic_if_null(device, chars_in_buffer); |
set_to_generic_if_null(device, read_bulk_callback); |
set_to_generic_if_null(device, write_bulk_callback); |
set_to_generic_if_null(device, shutdown); |
} |
int usb_serial_register(struct usb_serial_device_type *new_device) |
{ |
int retval; |
fixup_generic(new_device); |
/* Add this device to our list of devices */ |
list_add(&new_device->driver_list, &usb_serial_driver_list); |
retval = usb_serial_bus_register (new_device); |
if (retval) |
goto error; |
info("USB Serial support registered for %s", new_device->name); |
return retval; |
error: |
err("problem %d when registering driver %s", retval, new_device->name); |
list_del(&new_device->driver_list); |
return retval; |
} |
void usb_serial_deregister(struct usb_serial_device_type *device) |
{ |
struct usb_serial *serial; |
int i; |
info("USB Serial deregistering driver %s", device->name); |
/* clear out the serial_table if the device is attached to a port */ |
for(i = 0; i < SERIAL_TTY_MINORS; ++i) { |
serial = serial_table[i]; |
if ((serial != NULL) && (serial->type == device)) { |
usb_driver_release_interface (&usb_serial_driver, serial->interface); |
usb_serial_disconnect (serial->interface); |
} |
} |
list_del(&device->driver_list); |
usb_serial_bus_deregister (device); |
} |
/* If the usb-serial core is built into the core, the usb-serial drivers |
need these symbols to load properly as modules. */ |
EXPORT_SYMBOL(usb_serial_register); |
EXPORT_SYMBOL(usb_serial_deregister); |
EXPORT_SYMBOL(usb_serial_probe); |
EXPORT_SYMBOL(usb_serial_disconnect); |
EXPORT_SYMBOL(usb_serial_port_softint); |
/* Module information */ |
MODULE_AUTHOR( DRIVER_AUTHOR ); |
MODULE_DESCRIPTION( DRIVER_DESC ); |
MODULE_LICENSE("GPL"); |
MODULE_PARM(debug, "i"); |
MODULE_PARM_DESC(debug, "Debug enabled or not"); |
/shark/trunk/drivers/usb/serial/tty_io.c |
---|
1,210 → 1,319 |
/* |
* linux/drivers/char/tty_io.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/types.h> |
#include <linux/major.h> |
#include <linux/errno.h> |
#include <linux/signal.h> |
#include <linux/fcntl.h> |
#include <linux/sched.h> |
#include <linux/interrupt.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/devpts_fs.h> |
#include <linux/file.h> |
#include <linux/console.h> |
#include <linux/timer.h> |
#include <linux/ctype.h> |
#include <linux/kd.h> |
#include <linux/mm.h> |
#include <linux/string.h> |
#include <linux/slab.h> |
#include <linux/poll.h> |
#include <linux/proc_fs.h> |
#include <linux/init.h> |
#include <linux/module.h> |
#include <linux/smp_lock.h> |
#include <linux/device.h> |
#include <asm/uaccess.h> |
#include <asm/system.h> |
#include <asm/bitops.h> |
#include <linux/kbd_kern.h> |
#include <linux/vt_kern.h> |
#include <linux/selection.h> |
#include <linux/devfs_fs_kernel.h> |
#include <linux/kmod.h> |
#undef TTY_DEBUG_HANGUP |
#define TTY_PARANOIA_CHECK 1 |
#define CHECK_TTY_COUNT 1 |
struct tty_ldisc ldiscs[NR_LDISCS]; |
struct termios tty_std_termios = { /* for the benefit of tty drivers */ |
.c_iflag = ICRNL | IXON, |
.c_oflag = OPOST | ONLCR, |
.c_cflag = B38400 | CS8 | CREAD | HUPCL, |
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | |
ECHOCTL | ECHOKE | IEXTEN, |
.c_cc = INIT_C_CC |
}; |
struct tty_driver *alloc_tty_driver(int lines) |
{ |
struct tty_driver *driver; |
driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL); |
if (driver) { |
memset(driver, 0, sizeof(struct tty_driver)); |
driver->magic = TTY_DRIVER_MAGIC; |
driver->num = lines; |
/* later we'll move allocation of tables here */ |
} |
return driver; |
} |
void put_tty_driver(struct tty_driver *driver) |
{ |
kfree(driver); |
} |
void tty_set_operations(struct tty_driver *driver, struct tty_operations *op) |
{ |
driver->open = op->open; |
driver->close = op->close; |
driver->write = op->write; |
driver->put_char = op->put_char; |
driver->flush_chars = op->flush_chars; |
driver->write_room = op->write_room; |
driver->chars_in_buffer = op->chars_in_buffer; |
driver->ioctl = op->ioctl; |
driver->set_termios = op->set_termios; |
driver->throttle = op->throttle; |
driver->unthrottle = op->unthrottle; |
driver->stop = op->stop; |
driver->start = op->start; |
driver->hangup = op->hangup; |
driver->break_ctl = op->break_ctl; |
driver->flush_buffer = op->flush_buffer; |
driver->set_ldisc = op->set_ldisc; |
driver->wait_until_sent = op->wait_until_sent; |
driver->send_xchar = op->send_xchar; |
driver->read_proc = op->read_proc; |
driver->write_proc = op->write_proc; |
driver->tiocmget = op->tiocmget; |
driver->tiocmset = op->tiocmset; |
} |
int tty_register_driver(struct tty_driver *driver) |
{ |
return 0; |
} |
int tty_unregister_driver(struct tty_driver *driver) |
{ |
return 0; |
} |
void tty_register_device(struct tty_driver *driver, unsigned index, |
struct device *device) |
{ |
} |
void tty_unregister_device(struct tty_driver *driver, unsigned index) |
{ |
} |
void tty_flip_buffer_push(struct tty_struct *tty) |
{ |
printk(KERN_INFO "%c\n", *(tty->flip.char_buf_ptr -1)); |
} |
static void flush_to_ldisc(void *private_) |
{ |
} |
static struct tty_struct *alloc_tty_struct(void) |
{ |
struct tty_struct *tty; |
tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); |
// if (tty) |
// memset(tty, 0, sizeof(struct tty_struct)); |
return tty; |
} |
void do_tty_hangup(void *data) |
{ |
} |
/* |
* This subroutine initializes a tty structure. |
*/ |
static void initialize_tty_struct(struct tty_struct *tty) |
{ |
memset(tty, 0, sizeof(struct tty_struct)); |
tty->magic = TTY_MAGIC; |
tty->ldisc = ldiscs[N_TTY]; |
tty->pgrp = -1; |
tty->flip.char_buf_ptr = tty->flip.char_buf; |
tty->flip.flag_buf_ptr = tty->flip.flag_buf; |
INIT_WORK(&tty->flip.work, flush_to_ldisc, tty); |
init_MUTEX(&tty->flip.pty_sem); |
init_waitqueue_head(&tty->write_wait); |
init_waitqueue_head(&tty->read_wait); |
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty); |
//*sema_init(&tty->atomic_read, 1); |
//*sema_init(&tty->atomic_write, 1); |
spin_lock_init(&tty->read_lock); |
INIT_LIST_HEAD(&tty->tty_files); |
INIT_WORK(&tty->SAK_work, NULL, NULL); |
} |
extern int serial_open (struct tty_struct *tty, struct file * filp); |
extern int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count); |
struct tty_struct *tty; |
int serial_usbport_open(int port_number) |
{ |
int retval; |
tty = alloc_tty_struct(); |
if(!tty) |
goto fail_no_mem; |
initialize_tty_struct (tty); |
tty->termios = malloc (sizeof(struct termios)); |
*(tty->termios) = tty_std_termios; |
retval = serial_open(tty, NULL); |
return retval; |
fail_no_mem: |
return -ENOMEM; |
} |
int serial_usbport_write(const unsigned char *buf, int count) |
{ |
int retval; |
retval = serial_write(tty, 0, buf, count); |
return retval; |
// printk(KERN_DEBUG "######### retval=%d\n", retval); |
} |
/* |
* linux/drivers/char/tty_io.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/types.h> |
#include <linux/major.h> |
#include <linux/errno.h> |
#include <linux/signal.h> |
#include <linux/fcntl.h> |
#include <linux/sched.h> |
#include <linux/interrupt.h> |
#include <linux/tty.h> |
#include <linux/tty_driver.h> |
#include <linux/tty_flip.h> |
#include <linux/devpts_fs.h> |
#include <linux/file.h> |
#include <linux/console.h> |
#include <linux/timer.h> |
#include <linux/ctype.h> |
#include <linux/kd.h> |
#include <linux/mm.h> |
#include <linux/string.h> |
#include <linux/slab.h> |
#include <linux/poll.h> |
#include <linux/proc_fs.h> |
#include <linux/init.h> |
#include <linux/module.h> |
#include <linux/smp_lock.h> |
#include <linux/device.h> |
#include <asm/uaccess.h> |
#include <asm/system.h> |
#include <asm/bitops.h> |
#include <linux/kbd_kern.h> |
#include <linux/vt_kern.h> |
#include <linux/selection.h> |
#include <linux/devfs_fs_kernel.h> |
#include <linux/kmod.h> |
#undef TTY_DEBUG_HANGUP |
#define TTY_PARANOIA_CHECK 1 |
#define CHECK_TTY_COUNT 1 |
struct tty_ldisc ldiscs[NR_LDISCS]; |
struct termios tty_std_termios = { /* for the benefit of tty drivers */ |
.c_iflag = ICRNL | IXON, |
.c_oflag = OPOST | ONLCR, |
.c_cflag = B4800 | CS8 | CREAD | HUPCL, |
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | |
ECHOCTL | ECHOKE | IEXTEN, |
.c_cc = INIT_C_CC |
}; |
struct tty_driver *alloc_tty_driver(int lines) |
{ |
struct tty_driver *driver; |
driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL); |
if (driver) { |
memset(driver, 0, sizeof(struct tty_driver)); |
driver->magic = TTY_DRIVER_MAGIC; |
driver->num = lines; |
/* later we'll move allocation of tables here */ |
} |
return driver; |
} |
void put_tty_driver(struct tty_driver *driver) |
{ |
kfree(driver); |
} |
void tty_set_operations(struct tty_driver *driver, struct tty_operations *op) |
{ |
driver->open = op->open; |
driver->close = op->close; |
driver->write = op->write; |
driver->put_char = op->put_char; |
driver->flush_chars = op->flush_chars; |
driver->write_room = op->write_room; |
driver->chars_in_buffer = op->chars_in_buffer; |
driver->ioctl = op->ioctl; |
driver->set_termios = op->set_termios; |
driver->throttle = op->throttle; |
driver->unthrottle = op->unthrottle; |
driver->stop = op->stop; |
driver->start = op->start; |
driver->hangup = op->hangup; |
driver->break_ctl = op->break_ctl; |
driver->flush_buffer = op->flush_buffer; |
driver->set_ldisc = op->set_ldisc; |
driver->wait_until_sent = op->wait_until_sent; |
driver->send_xchar = op->send_xchar; |
driver->read_proc = op->read_proc; |
driver->write_proc = op->write_proc; |
driver->tiocmget = op->tiocmget; |
driver->tiocmset = op->tiocmset; |
} |
int tty_register_driver(struct tty_driver *driver) |
{ |
return 0; |
} |
int tty_unregister_driver(struct tty_driver *driver) |
{ |
return 0; |
} |
void tty_register_device(struct tty_driver *driver, unsigned index, |
struct device *device) |
{ |
} |
void tty_unregister_device(struct tty_driver *driver, unsigned index) |
{ |
} |
/* |
* This routine is called out of the software interrupt to flush data |
* from the flip buffer to the line discipline. |
*/ |
static void flush_to_ldisc(void *private_) |
{ |
struct tty_struct *tty = (struct tty_struct *) private_; |
unsigned char *cp; |
char *fp; |
int count; |
if (tty->flip.buf_num) { |
cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE; |
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; |
tty->flip.buf_num = 0; |
tty->flip.char_buf_ptr = tty->flip.char_buf; |
tty->flip.flag_buf_ptr = tty->flip.flag_buf; |
} else { |
cp = tty->flip.char_buf; |
fp = tty->flip.flag_buf; |
tty->flip.buf_num = 1; |
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE; |
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; |
} |
count = tty->flip.count; |
tty->flip.count = 0; |
tty->ldisc.receive_buf(tty, cp, fp, count); |
} |
void tty_flip_buffer_push(struct tty_struct *tty) |
{ |
flush_to_ldisc((void *) tty); |
} |
static struct tty_struct *alloc_tty_struct(void) |
{ |
struct tty_struct *tty; |
tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); |
if (tty) |
memset(tty, 0, sizeof(struct tty_struct)); |
return tty; |
} |
void do_tty_hangup(void *data) |
{ |
} |
/* |
* This subroutine initializes a tty structure. |
*/ |
static void initialize_tty_struct(struct tty_struct *tty) |
{ |
memset(tty, 0, sizeof(struct tty_struct)); |
tty->magic = TTY_MAGIC; |
tty->ldisc = ldiscs[N_TTY]; |
tty->pgrp = -1; |
tty->flip.char_buf_ptr = tty->flip.char_buf; |
tty->flip.flag_buf_ptr = tty->flip.flag_buf; |
INIT_WORK(&tty->flip.work, flush_to_ldisc, tty); |
init_MUTEX(&tty->flip.pty_sem); |
init_waitqueue_head(&tty->write_wait); |
init_waitqueue_head(&tty->read_wait); |
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty); |
//*sema_init(&tty->atomic_read, 1); |
//*sema_init(&tty->atomic_write, 1); |
spin_lock_init(&tty->read_lock); |
INIT_LIST_HEAD(&tty->tty_files); |
INIT_WORK(&tty->SAK_work, NULL, NULL); |
} |
extern int serial_open (struct tty_struct *tty, struct file * filp); |
extern int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count); |
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) |
{ |
const unsigned char *p; |
char *f, flags = TTY_NORMAL; |
int i; |
char buf[64]; |
unsigned long cpuflags; |
if (!tty->read_buf) |
return; |
spin_lock_irqsave(&tty->read_lock, cpuflags); |
i = min(N_TTY_BUF_SIZE - tty->read_cnt, \ |
N_TTY_BUF_SIZE - tty->read_head); |
i = min(count, i); |
memcpy(tty->read_buf + tty->read_head, cp, i); |
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); |
tty->read_cnt += i; |
spin_unlock_irqrestore(&tty->read_lock, cpuflags); |
//** for (i=0; i<count; i++) |
//** printk("%c", *(cp+i) ); |
} |
/* |
* Check whether to call the driver.unthrottle function. |
* We test the TTY_THROTTLED bit first so that it always |
* indicates the current state. |
*/ |
static void check_unthrottle(struct tty_struct * tty) |
{ |
if (tty->count && |
test_and_clear_bit(TTY_THROTTLED, &tty->flags) && |
tty->driver->unthrottle) |
tty->driver->unthrottle(tty); |
} |
/* |
* Reset the read buffer counters, clear the flags, |
* and make sure the driver is unthrottled. Called |
* from n_tty_open() and n_tty_flush_buffer(). |
*/ |
static void reset_buffer_flags(struct tty_struct *tty) |
{ |
unsigned long flags; |
spin_lock_irqsave(&tty->read_lock, flags); |
tty->read_head = tty->read_tail = tty->read_cnt = 0; |
spin_unlock_irqrestore(&tty->read_lock, flags); |
tty->canon_head = tty->canon_data = tty->erasing = 0; |
memset(&tty->read_flags, 0, sizeof tty->read_flags); |
check_unthrottle(tty); |
} |
int serial_usbport_open(void **private, int port_number) |
{ |
int retval; |
struct tty_struct *tty; |
tty = alloc_tty_struct(); |
if(!tty) |
goto fail_no_mem; |
initialize_tty_struct (tty); |
tty->termios = kmalloc (sizeof(struct termios), GFP_KERNEL); |
*(tty->termios) = tty_std_termios; |
tty->index = port_number; |
tty->ldisc.receive_buf = n_tty_receive_buf; |
if (!tty->read_buf) { |
tty->read_buf = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); |
if (!tty->read_buf) |
return -ENOMEM; |
} |
memset(tty->read_buf, 0, N_TTY_BUF_SIZE); |
reset_buffer_flags(tty); |
retval = serial_open(tty, NULL); |
*private = (void*)tty; |
return retval; |
fail_no_mem: |
return -ENOMEM; |
} |
int serial_usbport_write(void *private, const unsigned char *buf, int count) |
{ |
int retval; |
struct tty_struct *tty = (struct tty_struct*) private; |
retval = serial_write(tty, 0, buf, count); |
return retval; |
} |
int serial_usbport_read(void *private, char* data_in) |
{ |
char c; |
struct tty_struct *tty = (struct tty_struct*) private; |
unsigned long flags; |
if (tty->read_cnt) |
{ |
c = tty->read_buf[tty->read_tail]; |
tty->read_tail = ((tty->read_tail+1) & \ |
(N_TTY_BUF_SIZE-1)); |
tty->read_cnt--; |
*data_in = c; |
return 1; |
} |
*data_in = 0; |
return 0; |
} |
/shark/trunk/drivers/usb/serial/bus.c |
---|
1,142 → 1,145 |
/* |
* USB Serial Converter Bus specific functions |
* |
* Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com) |
* |
* This program is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License version |
* 2 as published by the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/tty.h> |
#include <linux/module.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
static int usb_serial_device_match (struct device *dev, struct device_driver *drv) |
{ |
struct usb_serial_device_type *driver; |
const struct usb_serial_port *port; |
/* |
* drivers are already assigned to ports in serial_probe so it's |
* a simple check here. |
*/ |
port = to_usb_serial_port(dev); |
if (!port) |
return 0; |
driver = to_usb_serial_driver(drv); |
if (driver == port->serial->type) |
return 1; |
return 0; |
} |
struct bus_type usb_serial_bus_type = { |
.name = "usb-serial", |
.match = usb_serial_device_match, |
}; |
static int usb_serial_device_probe (struct device *dev) |
{ |
struct usb_serial_device_type *driver; |
struct usb_serial_port *port; |
int retval = 0; |
int minor; |
port = to_usb_serial_port(dev); |
if (!port) { |
retval = -ENODEV; |
goto exit; |
} |
driver = port->serial->type; |
if (driver->port_probe) { |
if (!try_module_get(driver->owner)) { |
dev_err(dev, "module get failed, exiting\n"); |
retval = -EIO; |
goto exit; |
} |
retval = driver->port_probe (port); |
module_put(driver->owner); |
if (retval) |
goto exit; |
} |
minor = port->number; |
tty_register_device (usb_serial_tty_driver, minor, dev); |
dev_info(&port->serial->dev->dev, |
"%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)\n", |
driver->name, minor, minor); |
exit: |
return retval; |
} |
static int usb_serial_device_remove (struct device *dev) |
{ |
struct usb_serial_device_type *driver; |
struct usb_serial_port *port; |
int retval = 0; |
int minor; |
port = to_usb_serial_port(dev); |
if (!port) { |
return -ENODEV; |
} |
driver = port->serial->type; |
if (driver->port_remove) { |
if (!try_module_get(driver->owner)) { |
dev_err(dev, "module get failed, exiting\n"); |
retval = -EIO; |
goto exit; |
} |
retval = driver->port_remove (port); |
module_put(driver->owner); |
} |
exit: |
minor = port->number; |
tty_unregister_device (usb_serial_tty_driver, minor); |
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", |
driver->name, minor); |
return retval; |
} |
int usb_serial_bus_register(struct usb_serial_device_type *device) |
{ |
int retval; |
if (device->short_name) |
device->driver.name = (char *)device->short_name; |
else |
device->driver.name = (char *)device->name; |
device->driver.bus = &usb_serial_bus_type; |
device->driver.probe = usb_serial_device_probe; |
device->driver.remove = usb_serial_device_remove; |
retval = driver_register(&device->driver); |
return retval; |
} |
void usb_serial_bus_deregister(struct usb_serial_device_type *device) |
{ |
driver_unregister (&device->driver); |
} |
/* |
* USB Serial Converter Bus specific functions |
* |
* Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com) |
* |
* This program is free software; you can redistribute it and/or |
* modify it under the terms of the GNU General Public License version |
* 2 as published by the Free Software Foundation. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/kernel.h> |
#include <linux/errno.h> |
#include <linux/tty.h> |
#include <linux/module.h> |
#include <linux/usb.h> |
#ifdef CONFIG_USB_SERIAL_DEBUG |
static int debug = 1; |
#else |
static int debug; |
#endif |
#include "usb-serial.h" |
static int usb_serial_device_match (struct device *dev, struct device_driver *drv) |
{ |
struct usb_serial_device_type *driver; |
const struct usb_serial_port *port; |
/* |
* drivers are already assigned to ports in serial_probe so it's |
* a simple check here. |
*/ |
port = to_usb_serial_port(dev); |
if (!port) |
return 0; |
driver = to_usb_serial_driver(drv); |
if (driver == port->serial->type) |
return 1; |
return 0; |
} |
struct bus_type usb_serial_bus_type = { |
.name = "usb-serial", |
.match = usb_serial_device_match, |
}; |
static int usb_serial_device_probe (struct device *dev) |
{ |
struct usb_serial_device_type *driver; |
struct usb_serial_port *port; |
int retval = 0; |
int minor; |
int i; |
port = to_usb_serial_port(dev); |
if (!port) { |
retval = -ENODEV; |
goto exit; |
} |
driver = port->serial->type; |
if (driver->port_probe) { |
if (!try_module_get(driver->owner)) { |
dev_err(dev, "module get failed, exiting\n"); |
retval = -EIO; |
goto exit; |
} |
retval = driver->port_probe (port); |
module_put(driver->owner); |
if (retval) |
goto exit; |
} |
minor = port->number; |
tty_register_device (usb_serial_tty_driver, minor, dev); |
dev_info(&port->serial->dev->dev, |
"%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)\n", |
driver->name, minor, minor); |
/* for (i=0; i<20; i++) |
printk("ttyusb%d\n", minor);*/ |
exit: |
return retval; |
} |
static int usb_serial_device_remove (struct device *dev) |
{ |
struct usb_serial_device_type *driver; |
struct usb_serial_port *port; |
int retval = 0; |
int minor; |
port = to_usb_serial_port(dev); |
if (!port) { |
return -ENODEV; |
} |
driver = port->serial->type; |
if (driver->port_remove) { |
if (!try_module_get(driver->owner)) { |
dev_err(dev, "module get failed, exiting\n"); |
retval = -EIO; |
goto exit; |
} |
retval = driver->port_remove (port); |
module_put(driver->owner); |
} |
exit: |
minor = port->number; |
tty_unregister_device (usb_serial_tty_driver, minor); |
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", |
driver->name, minor); |
return retval; |
} |
int usb_serial_bus_register(struct usb_serial_device_type *device) |
{ |
int retval; |
if (device->short_name) |
device->driver.name = (char *)device->short_name; |
else |
device->driver.name = (char *)device->name; |
device->driver.bus = &usb_serial_bus_type; |
device->driver.probe = usb_serial_device_probe; |
device->driver.remove = usb_serial_device_remove; |
retval = driver_register(&device->driver); |
return retval; |
} |
void usb_serial_bus_deregister(struct usb_serial_device_type *device) |
{ |
driver_unregister (&device->driver); |
} |
/shark/trunk/drivers/usb/include/drivers/shark_usbserial26.h |
---|
0,0 → 1,18 |
#ifndef __SHARK_USBSERIAL26_H__ |
#define __SHARK_USBSERIAL26_H__ |
struct shark_tty_usbcom { |
void *tty; |
int port_number; |
}; |
int shark_usb_serial_init(void); |
void shark_usb_serial_close(void); |
int |
shark_usbcom_open(struct shark_tty_usbcom *tty_usb, int port_number); |
char |
shark_usbcom_read(struct shark_tty_usbcom *tty_usb); |
#endif |
/shark/trunk/drivers/usb/include/drivers/shark_pwc26.h |
---|
0,0 → 1,17 |
#ifndef __SHARK_PWC26_H__ |
#define __SHARK_PWC26_H__ |
struct PWC26_DEVICE |
{ |
void* private_data; |
int width; |
int height; |
BYTE* imgptr; |
}; |
void shark_PWC_init(); |
int shark_PWC_read(struct PWC26_DEVICE *pwc26); |
int shark_PWC_open(struct PWC26_DEVICE *pwc26, int width, int height, int fps, int quality, int webcamnr); |
void shark_PWC_close(struct PWC26_DEVICE *pwc26); |
#endif |
/shark/trunk/drivers/usb/shark_glue/shark_usb.c |
---|
1,56 → 1,63 |
#include <kernel/kern.h> |
extern int usb_init(void); |
extern void usb_exit(void); |
extern int ohci_hcd_pci_init (void); |
extern void ohci_hcd_pci_cleanup (void); |
extern int uhci_hcd_init(void); |
extern void uhci_hcd_cleanup(void); |
extern int usb_mouse_init(void); |
extern void usb_mouse_exit(void); |
extern int usb_kbd_init(void); |
extern void usb_kbd_exit(void); |
extern int hid_init(void); |
extern void hid_exit(void); |
static int usb_installed = FALSE; |
/* to do: return error code */ |
int USB26_init() |
{ |
if (usb_installed == TRUE) |
return 0; |
usb_init(); |
ohci_hcd_pci_init(); |
uhci_hcd_init(); |
usb_mouse_init(); |
usb_kbd_init(); |
hid_init(); |
usb_installed = TRUE; |
return 0; |
} |
/* to do : add all usb closing functions ?*/ |
int USB26_close() |
{ |
if (usb_installed == FALSE) |
return -1; |
ohci_hcd_pci_cleanup(); |
uhci_hcd_cleanup(); |
usb_mouse_exit(); |
usb_kbd_exit(); |
hid_exit(); |
usb_exit(); |
usb_installed = FALSE; |
return 0; |
} |
#include <kernel/kern.h> |
extern int usb_init(void); |
extern void usb_exit(void); |
extern int ohci_hcd_pci_init (void); |
extern void ohci_hcd_pci_cleanup (void); |
extern int uhci_hcd_init(void); |
extern void uhci_hcd_cleanup(void); |
extern int ehci_hcd_init(void); |
extern void ehci_hcd_cleanup(void); |
extern int usb_mouse_init(void); |
extern void usb_mouse_exit(void); |
extern int usb_kbd_init(void); |
extern void usb_kbd_exit(void); |
extern int hid_init(void); |
extern void hid_exit(void); |
static int usb_installed = FALSE; |
/* to do: return error code */ |
int USB26_init() |
{ |
if (usb_installed == TRUE) |
return 0; |
usb_init(); |
ehci_hcd_init(); |
ohci_hcd_pci_init(); |
uhci_hcd_init(); |
usb_mouse_init(); |
usb_kbd_init(); |
hid_init(); |
usb_installed = TRUE; |
return 0; |
} |
/* to do : add all usb closing functions ?*/ |
int USB26_close() |
{ |
if (usb_installed == FALSE) |
return -1; |
ehci_hcd_cleanup(); |
ohci_hcd_pci_cleanup(); |
uhci_hcd_cleanup(); |
usb_mouse_exit(); |
usb_kbd_exit(); |
hid_exit(); |
usb_exit(); |
usb_installed = FALSE; |
return 0; |
} |
/shark/trunk/drivers/usb/shark_glue/shark_pwc.c |
---|
0,0 → 1,273 |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/types.h> |
#include <linux/major.h> |
#include <linux/errno.h> |
#include <linux/signal.h> |
#include "media/pwc-ioctl.h" |
#include <linux/videodev.h> |
#include <drivers/shark_pwc26.h> |
/* |
* shark lowlevel wrapper |
*/ |
struct PWC_data { |
struct file* file; |
struct pwc_imagesize RealSize; |
struct pwc_coord m_ViewSize, m_RealSize; |
struct pwc_coord m_Offset; |
struct video_capability Capability; |
struct pwc_probe Probe; |
struct video_picture Picture; |
struct video_window Window; |
struct pwc_video_command VideoCmd; |
void *m_pPWCXBuffer, *m_pInputBuffer, *m_pImageBuffer; |
int m_Frames, len, m_UseRaw, m_ImageSize, m_ImageLen; |
int m_Type, m_Bandlength, m_Bayer; |
int m_InputLen; |
}; |
extern int usb_pwc_init(); |
extern void* malloc(size_t size); |
extern int pwc_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg); |
extern int pwc_video_open(struct inode *inode, struct file *file); |
extern int pwc_video_close(struct inode *inode, struct file *file); |
extern ssize_t pwc_video_read(struct file *file, char *buf, size_t count, loff_t *ppos); |
extern int pwc_video_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long arg); |
void shark_PWC_init() |
{ |
usb_pwc_init(); |
} |
int shark_PWC_open(struct PWC26_DEVICE *pwc26, int width, int height, int fps, int quality, int webcamnr) |
{ |
int err; |
struct PWC_data *pwc_data; |
struct file *file; |
struct inode *inode; |
pwc_data = malloc(sizeof(struct PWC_data)); |
pwc26->private_data = (void*)pwc_data; |
if (!pwc_data) |
return -ENOMEM; |
memset(pwc_data, 0, sizeof(struct PWC_data)); |
file = malloc(sizeof(struct file)); |
if (!file) |
return -ENOMEM; |
file->f_dentry= malloc(sizeof(struct dentry)); |
if (!file->f_dentry) |
return -ENOMEM; |
file->f_dentry->d_inode = malloc(sizeof(struct inode)); |
if (!file->f_dentry->d_inode) |
return -ENOMEM; |
file->f_dentry->d_inode->i_rdev = webcamnr; |
inode = file->f_dentry->d_inode; |
pwc_data->file = file; |
pwc_data->m_ViewSize.x = width; |
pwc_data->m_ViewSize.y = height; |
pwc_data->m_Frames = fps; |
pwc_data->m_RealSize = pwc_data->m_ViewSize; |
pwc_data->m_Offset.x = 0; |
pwc_data->m_Offset.y = 0; |
pwc_data->m_UseRaw = TRUE; |
pwc_data->m_Bayer = FALSE; |
pwc_data->m_pInputBuffer = NULL; |
pwc_data->m_InputLen = 0; |
pwc_data->m_pImageBuffer = NULL; |
pwc_data->m_ImageLen = NULL; |
pwc_data->m_Type = NULL; |
pwc_data->m_pPWCXBuffer = malloc(60000); // large enoug for all cams |
if (!pwc_data->m_pPWCXBuffer) |
return -ENOMEM; |
err=pwc_video_open(inode, file); |
if (err <0) |
return err; |
pwc_video_do_ioctl(inode, file, VIDIOCGCAP, &pwc_data->Capability); |
printk(KERN_INFO "Capability->type= %d\n", pwc_data->Capability.type); |
printk(KERN_INFO "Capability->channels =%d\n",pwc_data->Capability.channels); |
printk(KERN_INFO "Capability->audios=%d\n",pwc_data->Capability.audios); |
printk(KERN_INFO "Capability->minwidth=%d\n",pwc_data->Capability.minwidth); |
printk(KERN_INFO "Capability->minheight=%d\n",pwc_data->Capability.minheight); |
printk(KERN_INFO "Capability->maxwidth=%d\n",pwc_data->Capability.maxwidth); |
printk(KERN_INFO "Capability->maxheight=%d\n",pwc_data->Capability.maxheight); |
memset(&pwc_data->Probe, 0, sizeof(pwc_data->Probe)); |
pwc_video_do_ioctl(inode, file, VIDIOCPWCPROBE, &pwc_data->Probe); |
pwc_video_do_ioctl(inode, file, VIDIOCGPICT, &pwc_data->Picture); |
if (pwc_data->m_UseRaw) |
pwc_data->Picture.palette = VIDEO_PALETTE_RAW; |
else |
pwc_data->Picture.palette = VIDEO_PALETTE_YUV420P; |
pwc_video_do_ioctl(inode, file, VIDIOCSPICT, &pwc_data->Picture); |
pwc_video_do_ioctl(inode, file, VIDIOCGWIN, &pwc_data->Window); |
/* unavailable in pwc 10.x */ |
if (pwc_data->m_Bayer) |
{ |
// special raw Bayer mode |
pwc_data->m_ViewSize.x = 640; |
pwc_data->m_ViewSize.y = 480; |
pwc_data->m_Frames = 5; |
pwc_data->m_RealSize = pwc_data->m_ViewSize; |
} |
pwc_data->Window.width = pwc_data->m_ViewSize.x; |
pwc_data->Window.height = pwc_data->m_ViewSize.y; |
pwc_data->Window.flags = (pwc_data->m_Frames << PWC_FPS_SHIFT); |
if (pwc_data->m_Bayer) |
pwc_data->Window.flags |= PWC_FPS_SNAPSHOT; |
pwc_video_do_ioctl(inode, file, VIDIOCSWIN, &pwc_data->Window); |
if (pwc_data->m_UseRaw) |
{ |
pwc_video_do_ioctl(inode, file, VIDIOCPWCGVIDCMD, &pwc_data->VideoCmd); |
pwc_data->m_Type = pwc_data->VideoCmd.type; |
pwc_data->m_InputLen = pwc_data->VideoCmd.frame_size; |
pwc_data->m_Bandlength = pwc_data->VideoCmd.bandlength; |
if (pwc_data->m_Bandlength > 0) |
{ |
switch(pwc_data->m_Type) |
{ |
case 645: |
case 646: |
pwcx_init_decompress_Nala(pwc_data->m_Type, pwc_data->VideoCmd.release, &pwc_data->VideoCmd.command_buf, pwc_data->m_pPWCXBuffer); |
break; |
case 675: |
case 680: |
case 690: |
pwcx_init_decompress_Timon(pwc_data->m_Type, pwc_data->VideoCmd.release, &pwc_data->VideoCmd.command_buf, pwc_data->m_pPWCXBuffer); |
break; |
case 720: |
case 730: |
case 740: |
case 750: |
pwcx_init_decompress_Kiara(pwc_data->m_Type, pwc_data->VideoCmd.release, &pwc_data->VideoCmd.command_buf, pwc_data->m_pPWCXBuffer); |
break; |
default: |
// qDebug("Unknown type of camera (%d)!", VideoCmd.type); |
break; |
} // ..switch |
} // ..m_Bandlength > 0 |
if (pwc_video_do_ioctl(inode, file, VIDIOCPWCGREALSIZE, &pwc_data->RealSize) == 0) |
{ |
pwc_data->m_Offset.x = (pwc_data->m_ViewSize.x - pwc_data->RealSize.width) / 2; |
pwc_data->m_Offset.y = (pwc_data->m_ViewSize.y - pwc_data->RealSize.height) / 2; |
} |
} |
pwc_data->m_ImageLen = pwc_data->m_ViewSize.x * pwc_data->m_ViewSize.y * 3; |
pwc_data->m_pImageBuffer = malloc (pwc_data->m_ImageLen); |
if (pwc_data->m_UseRaw) |
{ |
pwc_data->m_pInputBuffer = malloc(pwc_data->m_InputLen); |
} |
else |
{ |
if (pwc_data->m_pImageBuffer != 0) |
{ |
pwc_data->m_pInputBuffer = pwc_data->m_pImageBuffer; |
pwc_data->m_InputLen = pwc_data->m_ImageLen; |
} |
} |
pwc26->width=pwc_data->m_ViewSize.x ; |
pwc26->height=pwc_data->m_ViewSize.y ; |
pwc26->imgptr=(BYTE*)pwc_data->m_pImageBuffer; |
return 0; |
} |
int shark_PWC_read(struct PWC26_DEVICE *pwc26) |
{ |
int len; |
struct PWC_data *pwc_data = (struct PWC_data*)pwc26->private_data; |
if (pwc_data->m_pInputBuffer == 0 || pwc_data->m_pImageBuffer == 0) |
return -666; |
len = pwc_video_read(pwc_data->file, pwc_data->m_pInputBuffer, pwc_data->m_InputLen, 0); |
if (len > 0 && pwc_data->m_Bandlength > 0) |
{ |
switch(pwc_data->m_Type) |
{ |
case 645: |
case 646: |
pwcx_decompress_Nala(&pwc_data->m_RealSize, &pwc_data->m_ViewSize, &pwc_data->m_Offset, \ |
pwc_data->m_pInputBuffer, pwc_data->m_pImageBuffer, \ |
PWCX_FLAG_PLANAR, \ |
pwc_data->m_pPWCXBuffer, pwc_data->m_Bandlength); |
break; |
case 675: |
case 680: |
case 690: |
pwcx_decompress_Timon(&pwc_data->m_RealSize, &pwc_data->m_ViewSize, &pwc_data->m_Offset, \ |
pwc_data->m_pInputBuffer, pwc_data->m_pImageBuffer, \ |
PWCX_FLAG_PLANAR, \ |
pwc_data->m_pPWCXBuffer, pwc_data->m_Bandlength); |
break; |
case 720: |
case 730: |
case 740: |
case 750: |
pwcx_decompress_Kiara(&pwc_data->m_RealSize, &pwc_data->m_ViewSize, &pwc_data->m_Offset, \ |
pwc_data->m_pInputBuffer, pwc_data->m_pImageBuffer, \ |
PWCX_FLAG_PLANAR | (pwc_data->m_Bayer ? PWCX_FLAG_BAYER : 0), \ |
pwc_data->m_pPWCXBuffer, pwc_data->m_Bandlength); |
break; |
} |
} |
return len; |
} |
void shark_PWC_close(struct PWC26_DEVICE *pwc26) |
{ |
struct file *file; |
struct inode *inode; |
struct PWC_data *pwc_data = (struct PWC_data*)pwc26->private_data; |
if (!pwc_data) |
return; |
free(pwc_data->m_pPWCXBuffer); |
file = pwc_data->file; |
inode = file->f_dentry->d_inode; |
// pwc_video_close(inode, file); |
free(inode); |
free(file->f_dentry); |
free(file); |
free(pwc_data); |
} |
/shark/trunk/drivers/usb/shark_glue/shark_serial.c |
---|
0,0 → 1,56 |
#include <kernel/kern.h> |
#include <drivers/shark_usbserial26.h> |
extern int usb_serial_init(); |
extern void usb_serial_exit(void); |
extern int pl2303_init(); |
extern void pl2303_exit(); |
extern int serial_usbport_open(void *tty, int port_number); |
extern int serial_usbport_write(void *tty, const unsigned char *buf, int count); |
extern int serial_usbport_read(void *private, char* data_in); |
int shark_usb_serial_init() |
{ |
usb_serial_init(); |
pl2303_init(); |
return 0; |
} |
void shark_usb_serial_close() |
{ |
usb_serial_exit(); |
pl2303_exit(); |
} |
int shark_usbcom_open(struct shark_tty_usbcom *tty_usb, int port_number) |
{ |
int retval; |
tty_usb->port_number = port_number; |
retval = serial_usbport_open(&tty_usb->tty, tty_usb->port_number); |
return retval; |
} |
int shark_usbcom_write(struct shark_tty_usbcom *tty_usb, const unsigned char *buf, int count) |
{ |
int retval; |
retval = serial_usbport_write(tty_usb->tty, buf, count); |
return retval; |
} |
char shark_usbcom_read(struct shark_tty_usbcom *tty_usb) |
{ |
int retval; |
char data_in; |
while ((retval = serial_usbport_read(tty_usb->tty, &data_in)) == 0); |
return data_in; |
/* do the hard stuff*/ |
} |
/shark/trunk/drivers/usb/core/hcd.c |
---|
1168,6 → 1168,7 |
struct device *sys = 0; |
unsigned long flags; |
struct completion_splice splice; |
struct list_head *tmp; /* 2.6.1 */ |
int retval; |
if (!urb) |
1206,10 → 1207,22 |
*/ |
WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT); |
/* insist the urb is still queued */ |
list_for_each(tmp, &dev->urb_list) { |
if (tmp == &urb->urb_list) |
break; |
} |
if (tmp != &urb->urb_list) { |
retval = -EINVAL; |
goto done; |
} |
/* removed for 2.6.1 |
if (!urb->hcpriv) { |
retval = -EINVAL; |
goto done; |
} |
*/ |
/* Any status except -EINPROGRESS means something already started to |
* unlink this URB from the hardware. So there's no more work to do. |
/shark/trunk/drivers/usb/core/buffer.c |
---|
1,139 → 1,139 |
/* |
* DMA memory management for framework level HCD code (hc_driver) |
* |
* This implementation plugs in through generic "usb_bus" level methods, |
* and works with real PCI, or when "pci device == null" makes sense. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/kernel.h> |
#include <linux/slab.h> |
#include <linux/pci.h> |
#ifdef CONFIG_USB_DEBUG |
#define DEBUG |
#else |
#undef DEBUG |
#endif |
#include <linux/usb.h> |
#include "hcd.h" |
/* |
* DMA-Coherent Buffers |
*/ |
/* FIXME tune these based on pool statistics ... */ |
static const size_t pool_max [HCD_BUFFER_POOLS] = { |
/* platforms without dma-friendly caches might need to |
* prevent cacheline sharing... |
*/ |
32, |
128, |
512, |
PAGE_SIZE / 2 |
/* bigger --> allocate pages */ |
}; |
/* SETUP primitives */ |
/** |
* hcd_buffer_create - initialize buffer pools |
* @hcd: the bus whose buffer pools are to be initialized |
* Context: !in_interrupt() |
* |
* Call this as part of initializing a host controller that uses the pci dma |
* memory allocators. It initializes some pools of dma-consistent memory that |
* will be shared by all drivers using that controller, or returns a negative |
* errno value on error. |
* |
* Call hcd_buffer_destroy() to clean up after using those pools. |
*/ |
int hcd_buffer_create (struct usb_hcd *hcd) |
{ |
char name [16]; |
int i, size; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (!(size = pool_max [i])) |
continue; |
snprintf26(name, sizeof name, "buffer-%d", size); |
hcd->pool [i] = pci_pool_create (name, hcd->pdev, |
size, size, 0); |
if (!hcd->pool [i]) { |
hcd_buffer_destroy (hcd); |
return -ENOMEM; |
} |
} |
return 0; |
} |
EXPORT_SYMBOL (hcd_buffer_create); |
/** |
* hcd_buffer_destroy - deallocate buffer pools |
* @hcd: the bus whose buffer pools are to be destroyed |
* Context: !in_interrupt() |
* |
* This frees the buffer pools created by hcd_buffer_create(). |
*/ |
void hcd_buffer_destroy (struct usb_hcd *hcd) |
{ |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
struct pci_pool *pool = hcd->pool [i]; |
if (pool) { |
pci_pool_destroy (pool); |
hcd->pool [i] = 0; |
} |
} |
} |
EXPORT_SYMBOL (hcd_buffer_destroy); |
/* sometimes alloc/free could use kmalloc with SLAB_DMA, for |
* better sharing and to leverage mm/slab.c intelligence. |
*/ |
void *hcd_buffer_alloc ( |
struct usb_bus *bus, |
size_t size, |
int mem_flags, |
dma_addr_t *dma |
) |
{ |
struct usb_hcd *hcd = bus->hcpriv; |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (size <= pool_max [i]) |
return pci_pool_alloc (hcd->pool [i], mem_flags, dma); |
} |
return pci_alloc_consistent (hcd->pdev, size, dma); |
} |
void hcd_buffer_free ( |
struct usb_bus *bus, |
size_t size, |
void *addr, |
dma_addr_t dma |
) |
{ |
struct usb_hcd *hcd = bus->hcpriv; |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (size <= pool_max [i]) { |
pci_pool_free (hcd->pool [i], addr, dma); |
return; |
} |
} |
pci_free_consistent (hcd->pdev, size, addr, dma); |
} |
/* |
* DMA memory management for framework level HCD code (hc_driver) |
* |
* This implementation plugs in through generic "usb_bus" level methods, |
* and works with real PCI, or when "pci device == null" makes sense. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/kernel.h> |
#include <linux/slab.h> |
#include <linux/pci.h> |
#ifdef CONFIG_USB_DEBUG |
#define DEBUG |
#else |
#undef DEBUG |
#endif |
#include <linux/usb.h> |
#include "hcd.h" |
/* |
* DMA-Coherent Buffers |
*/ |
/* FIXME tune these based on pool statistics ... */ |
static const size_t pool_max [HCD_BUFFER_POOLS] = { |
/* platforms without dma-friendly caches might need to |
* prevent cacheline sharing... |
*/ |
32, |
128, |
512, |
PAGE_SIZE / 2 |
/* bigger --> allocate pages */ |
}; |
/* SETUP primitives */ |
/** |
* hcd_buffer_create - initialize buffer pools |
* @hcd: the bus whose buffer pools are to be initialized |
* Context: !in_interrupt() |
* |
* Call this as part of initializing a host controller that uses the pci dma |
* memory allocators. It initializes some pools of dma-consistent memory that |
* will be shared by all drivers using that controller, or returns a negative |
* errno value on error. |
* |
* Call hcd_buffer_destroy() to clean up after using those pools. |
*/ |
int hcd_buffer_create (struct usb_hcd *hcd) |
{ |
char name [16]; |
int i, size; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (!(size = pool_max [i])) |
continue; |
snprintf26(name, sizeof name, "buffer-%d", size); |
hcd->pool [i] = pci_pool_create (name, hcd->pdev, |
size, size, 0); |
if (!hcd->pool [i]) { |
hcd_buffer_destroy (hcd); |
return -ENOMEM; |
} |
} |
return 0; |
} |
EXPORT_SYMBOL (hcd_buffer_create); |
/** |
* hcd_buffer_destroy - deallocate buffer pools |
* @hcd: the bus whose buffer pools are to be destroyed |
* Context: !in_interrupt() |
* |
* This frees the buffer pools created by hcd_buffer_create(). |
*/ |
void hcd_buffer_destroy (struct usb_hcd *hcd) |
{ |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
struct pci_pool *pool = hcd->pool [i]; |
if (pool) { |
pci_pool_destroy (pool); |
hcd->pool [i] = 0; |
} |
} |
} |
EXPORT_SYMBOL (hcd_buffer_destroy); |
/* sometimes alloc/free could use kmalloc with SLAB_DMA, for |
* better sharing and to leverage mm/slab.c intelligence. |
*/ |
void *hcd_buffer_alloc ( |
struct usb_bus *bus, |
size_t size, |
int mem_flags, |
dma_addr_t *dma |
) |
{ |
struct usb_hcd *hcd = bus->hcpriv; |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (size <= pool_max [i]) |
return pci_pool_alloc_usb (hcd->pool [i], mem_flags, dma); |
} |
return pci_alloc_consistent_usb (hcd->pdev, size, dma); |
} |
void hcd_buffer_free ( |
struct usb_bus *bus, |
size_t size, |
void *addr, |
dma_addr_t dma |
) |
{ |
struct usb_hcd *hcd = bus->hcpriv; |
int i; |
for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
if (size <= pool_max [i]) { |
pci_pool_free (hcd->pool [i], addr, dma); |
return; |
} |
} |
pci_free_consistent (hcd->pdev, size, addr, dma); |
} |
/shark/trunk/drivers/usb/core/hub.c |
---|
128,15 → 128,23 |
static void hub_irq(struct urb *urb, struct pt_regs *regs) |
{ |
struct usb_hub *hub = (struct usb_hub *)urb->context; |
unsigned long flags; |
//** unsigned long flags; 2.6.1 |
int status; |
spin_lock(&hub_event_lock); |
hub->urb_active = 0; |
if (hub->urb_complete) { /* disconnect or rmmod */ |
complete(hub->urb_complete); |
goto done; |
} |
switch (urb->status) { |
case -ENOENT: /* synchronous unlink */ |
case -ECONNRESET: /* async unlink */ |
case -ESHUTDOWN: /* hardware going away */ |
return; |
goto done; |
//** return; 2.6.1 |
default: /* presumably an error */ |
/* Cause a hub reset after 10 consecutive errors */ |
dev_dbg (&hub->intf->dev, "transfer --> %d\n", urb->status); |
153,12 → 161,12 |
hub->nerrors = 0; |
/* Something happened, let khubd figure it out */ |
spin_lock_irqsave(&hub_event_lock, flags); |
//** spin_lock_irqsave(&hub_event_lock, flags); 2.6.1 |
if (list_empty(&hub->event_list)) { |
list_add(&hub->event_list, &hub_event_list); |
wake_up(&khubd_wait); |
} |
spin_unlock_irqrestore(&hub_event_lock, flags); |
//** spin_unlock_irqrestore(&hub_event_lock, flags); 2.6.1 |
resubmit: |
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0 |
165,6 → 173,11 |
/* ENODEV means we raced disconnect() */ |
&& status != -ENODEV) |
dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status); |
if (status == 0) |
hub->urb_active = 1; |
done: |
spin_unlock(&hub_event_lock); |
} |
/* USB 2.0 spec Section 11.24.2.3 */ |
469,6 → 482,7 |
message = "couldn't submit status urb"; |
goto fail; |
} |
hub->urb_active = 1; |
/* Wake up khubd */ |
wake_up(&khubd_wait); |
487,6 → 501,7 |
static void hub_disconnect(struct usb_interface *intf) |
{ |
struct usb_hub *hub = usb_get_intfdata (intf); |
DECLARE_COMPLETION(urb_complete); |
unsigned long flags; |
if (!hub) |
494,12 → 509,11 |
usb_set_intfdata (intf, NULL); |
spin_lock_irqsave(&hub_event_lock, flags); |
hub->urb_complete = &urb_complete; |
/* Delete it and then reset it */ |
list_del(&hub->event_list); |
INIT_LIST_HEAD(&hub->event_list); |
list_del(&hub->hub_list); |
INIT_LIST_HEAD(&hub->hub_list); |
list_del_init(&hub->event_list); |
list_del_init(&hub->hub_list); |
spin_unlock_irqrestore(&hub_event_lock, flags); |
512,6 → 526,8 |
if (hub->urb) { |
usb_unlink_urb(hub->urb); |
if (hub->urb_active) |
wait_for_completion(&urb_complete); |
usb_free_urb(hub->urb); |
hub->urb = NULL; |
} |
1152,7 → 1168,6 |
// if (current->flags & PF_FREEZE) |
// refrigerator(PF_IOTHREAD); |
} while (!signal_pending(current)); |
printk(KERN_DEBUG "File: %s @hub_thread_exit\n", __FILE__); |
dbg("hub_thread exiting"); |
complete_and_exit(&khubd_exited, 0); |
/shark/trunk/drivers/usb/core/hub.h |
---|
172,6 → 172,8 |
struct usb_hub { |
struct usb_interface *intf; /* the "real" device */ |
struct urb *urb; /* for interrupt polling pipe */ |
struct completion *urb_complete; /* wait for urb to end */ |
unsigned int urb_active:1; |
/* buffer for urb ... 1 bit each for hub and children, rounded up */ |
char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8]; |
/shark/trunk/drivers/usb/core/message.c |
---|
26,7 → 26,6 |
static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs) |
{ |
//printk(KERN_INFO "api\n"); |
complete((struct completion *)urb->context); |
} |
50,8 → 49,6 |
struct completion done; |
struct timer_list timer; |
int status; |
struct pt_regs *regs; |
init_completion(&done); |
urb->context = &done; |
318,15 → 315,6 |
int urb_flags; |
int dma; |
{ |
int i; |
for (i=0; i<20; i++) |
{ |
wait_ms26(300); |
printk(KERN_INFO "usb_sg_init!!!!!!!\n"); |
} |
} |
if (!io || !dev || !sg |
|| usb_pipecontrol (pipe) |
|| usb_pipeisoc (pipe) |
1115,7 → 1103,8 |
goto out; |
dev->actconfig = cp; |
if (!configuration) |
//** if (!configuration) 2.6.1 |
if (!cp) |
dev->state = USB_STATE_ADDRESS; |
else { |
dev->state = USB_STATE_CONFIGURED; |
/shark/trunk/drivers/usb/core/usb.c |
---|
1,1547 → 1,1563 |
/* |
* drivers/usb/usb.c |
* |
* (C) Copyright Linus Torvalds 1999 |
* (C) Copyright Johannes Erdfelt 1999-2001 |
* (C) Copyright Andreas Gal 1999 |
* (C) Copyright Gregory P. Smith 1999 |
* (C) Copyright Deti Fliegl 1999 (new USB architecture) |
* (C) Copyright Randy Dunlap 2000 |
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, |
more docs, etc) |
* (C) Copyright Yggdrasil Computing, Inc. 2000 |
* (usb_device_id matching changes by Adam J. Richter) |
* (C) Copyright Greg Kroah-Hartman 2002-2003 |
* |
* NOTE! This is not actually a driver at all, rather this is |
* just a collection of helper routines that implement the |
* generic USB things that the real drivers can use.. |
* |
* Think of this as a "USB library" rather than anything else. |
* It should be considered a slave, with no callbacks. Callbacks |
* are evil. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#ifdef CONFIG_USB_DEBUG |
#define DEBUG |
#else |
#undef DEBUG |
#endif |
#include <linux/module.h> |
#include <linux/string.h> |
#include <linux/bitops.h> |
#include <linux/slab.h> |
#include <linux/interrupt.h> /* for in_interrupt() */ |
#include <linux/kmod.h> |
#include <linux/init.h> |
#include <linux/spinlock.h> |
#include <linux/errno.h> |
#include <linux/smp_lock.h> |
#include <linux/usb.h> |
#include <asm/io.h> |
#include <asm/scatterlist.h> |
#include <linux/mm.h> |
#include <linux/dma-mapping.h> |
#include "hcd.h" |
#include "usb.h" |
extern int usb_hub_init(void); |
extern void usb_hub_cleanup(void); |
extern int usb_major_init(void); |
extern void usb_major_cleanup(void); |
extern int usb_host_init(void); |
extern void usb_host_cleanup(void); |
int nousb; /* Disable USB when built into kernel image */ |
/* Not honored on modular build */ |
static int generic_probe (struct device *dev) |
{ |
return 0; |
} |
static int generic_remove (struct device *dev) |
{ |
return 0; |
} |
static struct device_driver usb_generic_driver = { |
.name = "usb", |
.bus = &usb_bus_type, |
.probe = generic_probe, |
.remove = generic_remove, |
}; |
static int usb_generic_driver_data; |
/* needs to be called with BKL held */ |
int usb_probe_interface(struct device *dev) |
{ |
struct usb_interface * intf = to_usb_interface(dev); |
struct usb_driver * driver = to_usb_driver(dev->driver); |
const struct usb_device_id *id; |
int error = -ENODEV; |
dev_dbg(dev, "%s\n", __FUNCTION__); |
if (!driver->probe) |
return error; |
id = usb_match_id (intf, driver->id_table); |
if (id) { |
dev_dbg (dev, "%s - got id\n", __FUNCTION__); |
down (&driver->serialize); |
error = driver->probe (intf, id); |
up (&driver->serialize); |
} |
if (!error) |
intf->driver = driver; |
return error; |
} |
int usb_unbind_interface(struct device *dev) |
{ |
struct usb_interface *intf = to_usb_interface(dev); |
struct usb_driver *driver = to_usb_driver(dev->driver); |
down(&driver->serialize); |
/* release all urbs for this interface */ |
usb_disable_interface(interface_to_usbdev(intf), intf); |
if (intf->driver && intf->driver->disconnect) |
intf->driver->disconnect(intf); |
/* force a release and re-initialize the interface */ |
usb_driver_release_interface(driver, intf); |
up(&driver->serialize); |
return 0; |
} |
/** |
* usb_register - register a USB driver |
* @new_driver: USB operations for the driver |
* |
* Registers a USB driver with the USB core. The list of unattached |
* interfaces will be rescanned whenever a new driver is added, allowing |
* the new driver to attach to any recognized devices. |
* Returns a negative error code on failure and 0 on success. |
* |
* NOTE: if you want your driver to use the USB major number, you must call |
* usb_register_dev() to enable that functionality. This function no longer |
* takes care of that. |
*/ |
int usb_register(struct usb_driver *new_driver) |
{ |
int retval = 0; |
if (nousb) |
return -ENODEV; |
new_driver->driver.name = (char *)new_driver->name; |
new_driver->driver.bus = &usb_bus_type; |
new_driver->driver.probe = usb_probe_interface; |
new_driver->driver.remove = usb_unbind_interface; |
init_MUTEX(&new_driver->serialize); |
retval = driver_register(&new_driver->driver); |
if (!retval) { |
info("registered new driver %s", new_driver->name); |
usbfs_update_special(); |
} else { |
err("problem %d when registering driver %s", |
retval, new_driver->name); |
} |
return retval; |
} |
/** |
* usb_deregister - unregister a USB driver |
* @driver: USB operations of the driver to unregister |
* Context: !in_interrupt (), must be called with BKL held |
* |
* Unlinks the specified driver from the internal USB driver list. |
* |
* NOTE: If you called usb_register_dev(), you still need to call |
* usb_deregister_dev() to clean up your driver's allocated minor numbers, |
* this * call will no longer do it for you. |
*/ |
void usb_deregister(struct usb_driver *driver) |
{ |
info("deregistering driver %s", driver->name); |
driver_unregister (&driver->driver); |
usbfs_update_special(); |
} |
/** |
* usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal) |
* @dev: the device whose current configuration is considered |
* @ifnum: the desired interface |
* |
* This walks the device descriptor for the currently active configuration |
* and returns a pointer to the interface with that particular interface |
* number, or null. |
* |
* Note that configuration descriptors are not required to assign interface |
* numbers sequentially, so that it would be incorrect to assume that |
* the first interface in that descriptor corresponds to interface zero. |
* This routine helps device drivers avoid such mistakes. |
* However, you should make sure that you do the right thing with any |
* alternate settings available for this interfaces. |
*/ |
struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) |
{ |
int i; |
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) |
if (dev->actconfig->interface[i]->altsetting[0] |
.desc.bInterfaceNumber == ifnum) |
return dev->actconfig->interface[i]; |
return NULL; |
} |
/** |
* usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number |
* @dev: the device whose current configuration+altsettings is considered |
* @epnum: the desired endpoint, masked with USB_DIR_IN as appropriate. |
* |
* This walks the device descriptor for the currently active configuration, |
* and returns a pointer to the endpoint with that particular endpoint |
* number, or null. |
* |
* Note that interface descriptors are not required to list endpoint |
* numbers in any standardized order, so that it would be wrong to |
* assume that ep2in precedes either ep5in, ep2out, or even ep1out. |
* This routine helps device drivers avoid such mistakes. |
*/ |
struct usb_endpoint_descriptor * |
usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) |
{ |
int i, k; |
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { |
struct usb_interface *intf; |
struct usb_host_interface *alt; |
/* only endpoints in current altseting are active */ |
intf = dev->actconfig->interface[i]; |
alt = intf->altsetting + intf->act_altsetting; |
for (k = 0; k < alt->desc.bNumEndpoints; k++) |
if (epnum == alt->endpoint[k].desc.bEndpointAddress) |
return &alt->endpoint[k].desc; |
} |
return NULL; |
} |
/** |
* usb_driver_claim_interface - bind a driver to an interface |
* @driver: the driver to be bound |
* @iface: the interface to which it will be bound |
* @priv: driver data associated with that interface |
* |
* This is used by usb device drivers that need to claim more than one |
* interface on a device when probing (audio and acm are current examples). |
* No device driver should directly modify internal usb_interface or |
* usb_device structure members. |
* |
* Few drivers should need to use this routine, since the most natural |
* way to bind to an interface is to return the private data from |
* the driver's probe() method. |
*/ |
int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) |
{ |
if (!iface || !driver) |
return -EINVAL; |
/* this is mainly to lock against usbfs */ |
lock_kernel(); |
if (iface->driver) { |
unlock_kernel(); |
err ("%s driver booted %s off interface %p", |
driver->name, iface->driver->name, iface); |
return -EBUSY; |
} else { |
dbg("%s driver claimed interface %p", driver->name, iface); |
} |
iface->driver = driver; |
usb_set_intfdata(iface, priv); |
unlock_kernel(); |
return 0; |
} |
/** |
* usb_interface_claimed - returns true iff an interface is claimed |
* @iface: the interface being checked |
* |
* This should be used by drivers to check other interfaces to see if |
* they are available or not. If another driver has claimed the interface, |
* they may not claim it. Otherwise it's OK to claim it using |
* usb_driver_claim_interface(). |
* |
* Returns true (nonzero) iff the interface is claimed, else false (zero). |
*/ |
int usb_interface_claimed(struct usb_interface *iface) |
{ |
if (!iface) |
return 0; |
return (iface->driver != NULL); |
} /* usb_interface_claimed() */ |
/** |
* usb_driver_release_interface - unbind a driver from an interface |
* @driver: the driver to be unbound |
* @iface: the interface from which it will be unbound |
* |
* In addition to unbinding the driver, this re-initializes the interface |
* by selecting altsetting 0, the default alternate setting. |
* |
* This can be used by drivers to release an interface without waiting |
* for their disconnect() methods to be called. |
* |
* When the USB subsystem disconnect()s a driver from some interface, |
* it automatically invokes this method for that interface. That |
* means that even drivers that used usb_driver_claim_interface() |
* usually won't need to call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) |
{ |
/* this should never happen, don't release something that's not ours */ |
if (iface->driver && iface->driver != driver) |
return; |
usb_set_interface(interface_to_usbdev(iface), |
iface->altsetting[0].desc.bInterfaceNumber, |
0); |
usb_set_intfdata(iface, NULL); |
iface->driver = NULL; |
} |
/** |
* usb_match_id - find first usb_device_id matching device or interface |
* @interface: the interface of interest |
* @id: array of usb_device_id structures, terminated by zero entry |
* |
* usb_match_id searches an array of usb_device_id's and returns |
* the first one matching the device or interface, or null. |
* This is used when binding (or rebinding) a driver to an interface. |
* Most USB device drivers will use this indirectly, through the usb core, |
* but some layered driver frameworks use it directly. |
* These device tables are exported with MODULE_DEVICE_TABLE, through |
* modutils and "modules.usbmap", to support the driver loading |
* functionality of USB hotplugging. |
* |
* What Matches: |
* |
* The "match_flags" element in a usb_device_id controls which |
* members are used. If the corresponding bit is set, the |
* value in the device_id must match its corresponding member |
* in the device or interface descriptor, or else the device_id |
* does not match. |
* |
* "driver_info" is normally used only by device drivers, |
* but you can create a wildcard "matches anything" usb_device_id |
* as a driver's "modules.usbmap" entry if you provide an id with |
* only a nonzero "driver_info" field. If you do this, the USB device |
* driver's probe() routine should use additional intelligence to |
* decide whether to bind to the specified interface. |
* |
* What Makes Good usb_device_id Tables: |
* |
* The match algorithm is very simple, so that intelligence in |
* driver selection must come from smart driver id records. |
* Unless you have good reasons to use another selection policy, |
* provide match elements only in related groups, and order match |
* specifiers from specific to general. Use the macros provided |
* for that purpose if you can. |
* |
* The most specific match specifiers use device descriptor |
* data. These are commonly used with product-specific matches; |
* the USB_DEVICE macro lets you provide vendor and product IDs, |
* and you can also match against ranges of product revisions. |
* These are widely used for devices with application or vendor |
* specific bDeviceClass values. |
* |
* Matches based on device class/subclass/protocol specifications |
* are slightly more general; use the USB_DEVICE_INFO macro, or |
* its siblings. These are used with single-function devices |
* where bDeviceClass doesn't specify that each interface has |
* its own class. |
* |
* Matches based on interface class/subclass/protocol are the |
* most general; they let drivers bind to any interface on a |
* multiple-function device. Use the USB_INTERFACE_INFO |
* macro, or its siblings, to match class-per-interface style |
* devices (as recorded in bDeviceClass). |
* |
* Within those groups, remember that not all combinations are |
* meaningful. For example, don't give a product version range |
* without vendor and product IDs; or specify a protocol without |
* its associated class and subclass. |
*/ |
const struct usb_device_id * |
usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) |
{ |
struct usb_host_interface *intf; |
struct usb_device *dev; |
/* proc_connectinfo in devio.c may call us with id == NULL. */ |
if (id == NULL) |
return NULL; |
intf = &interface->altsetting [interface->act_altsetting]; |
dev = interface_to_usbdev(interface); |
/* It is important to check that id->driver_info is nonzero, |
since an entry that is all zeroes except for a nonzero |
id->driver_info is the way to create an entry that |
indicates that the driver want to examine every |
device and interface. */ |
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || |
id->driver_info; id++) { |
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && |
id->idVendor != dev->descriptor.idVendor) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && |
id->idProduct != dev->descriptor.idProduct) |
continue; |
/* No need to test id->bcdDevice_lo != 0, since 0 is never |
greater than any unsigned number. */ |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && |
(id->bcdDevice_lo > dev->descriptor.bcdDevice)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && |
(id->bcdDevice_hi < dev->descriptor.bcdDevice)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && |
(id->bDeviceClass != dev->descriptor.bDeviceClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && |
(id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && |
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && |
(id->bInterfaceClass != intf->desc.bInterfaceClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && |
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && |
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) |
continue; |
return id; |
} |
return NULL; |
} |
/** |
* usb_find_interface - find usb_interface pointer for driver and device |
* @drv: the driver whose current configuration is considered |
* @minor: the minor number of the desired device |
* |
* This walks the driver device list and returns a pointer to the interface |
* with the matching minor. Note, this only works for devices that share the |
* USB major number. |
*/ |
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) |
{ |
struct list_head *entry; |
struct device *dev; |
struct usb_interface *intf; |
list_for_each(entry, &drv->driver.devices) { |
dev = container_of(entry, struct device, driver_list); |
/* can't look at usb devices, only interfaces */ |
if (dev->driver == &usb_generic_driver) |
continue; |
intf = to_usb_interface(dev); |
if (intf->minor == -1) |
continue; |
if (intf->minor == minor) |
return intf; |
} |
/* no device found that matches */ |
return NULL; |
} |
static int usb_device_match (struct device *dev, struct device_driver *drv) |
{ |
struct usb_interface *intf; |
struct usb_driver *usb_drv; |
const struct usb_device_id *id; |
/* check for generic driver, which we don't match any device with */ |
if (drv == &usb_generic_driver) |
return 0; |
intf = to_usb_interface(dev); |
usb_drv = to_usb_driver(drv); |
id = usb_drv->id_table; |
id = usb_match_id (intf, usb_drv->id_table); |
if (id) |
return 1; |
return 0; |
} |
#ifdef CONFIG_HOTPLUG |
/* |
* USB hotplugging invokes what /proc/sys/kernel/hotplug says |
* (normally /sbin/hotplug) when USB devices get added or removed. |
* |
* This invokes a user mode policy agent, typically helping to load driver |
* or other modules, configure the device, and more. Drivers can provide |
* a MODULE_DEVICE_TABLE to help with module loading subtasks. |
* |
* We're called either from khubd (the typical case) or from root hub |
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle |
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the |
* device (and this configuration!) are still present. |
*/ |
static int usb_hotplug (struct device *dev, char **envp, int num_envp, |
char *buffer, int buffer_size) |
{ |
struct usb_interface *intf; |
struct usb_device *usb_dev; |
char *scratch; |
int i = 0; |
int length = 0; |
dbg ("%s", __FUNCTION__); |
if (!dev) |
return -ENODEV; |
/* Must check driver_data here, as on remove driver is always NULL */ |
if ((dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
usb_dev = interface_to_usbdev (intf); |
if (usb_dev->devnum < 0) { |
dbg ("device already deleted ??"); |
return -ENODEV; |
} |
if (!usb_dev->bus) { |
dbg ("bus already removed?"); |
return -ENODEV; |
} |
scratch = buffer; |
#ifdef CONFIG_USB_DEVICEFS |
/* If this is available, userspace programs can directly read |
* all the device descriptors we don't tell them about. Or |
* even act as usermode drivers. |
* |
* FIXME reduce hardwired intelligence here |
*/ |
envp [i++] = scratch; |
length += snprintf (scratch, buffer_size - length, |
"DEVICE=/proc/bus/usb/%03d/%03d", |
usb_dev->bus->busnum, usb_dev->devnum); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
#endif |
/* per-device configurations are common */ |
envp [i++] = scratch; |
length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x", |
usb_dev->descriptor.idVendor, |
usb_dev->descriptor.idProduct, |
usb_dev->descriptor.bcdDevice); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
/* class-based driver binding models */ |
envp [i++] = scratch; |
length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d", |
usb_dev->descriptor.bDeviceClass, |
usb_dev->descriptor.bDeviceSubClass, |
usb_dev->descriptor.bDeviceProtocol); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
if (usb_dev->descriptor.bDeviceClass == 0) { |
int alt = intf->act_altsetting; |
/* 2.4 only exposed interface zero. in 2.5, hotplug |
* agents are called for all interfaces, and can use |
* $DEVPATH/bInterfaceNumber if necessary. |
*/ |
envp [i++] = scratch; |
length += snprintf (scratch, buffer_size - length, |
"INTERFACE=%d/%d/%d", |
intf->altsetting[alt].desc.bInterfaceClass, |
intf->altsetting[alt].desc.bInterfaceSubClass, |
intf->altsetting[alt].desc.bInterfaceProtocol); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
} |
envp [i++] = 0; |
return 0; |
} |
#else |
static int usb_hotplug (struct device *dev, char **envp, |
int num_envp, char *buffer, int buffer_size) |
{ |
return -ENODEV; |
} |
#endif /* CONFIG_HOTPLUG */ |
/** |
* usb_release_dev - free a usb device structure when all users of it are finished. |
* @dev: device that's been disconnected |
* |
* Will be called only by the device core when all users of this usb device are |
* done. |
*/ |
static void usb_release_dev(struct device *dev) |
{ |
struct usb_device *udev; |
udev = to_usb_device(dev); |
if (udev->bus && udev->bus->op && udev->bus->op->deallocate) |
udev->bus->op->deallocate(udev); |
usb_destroy_configuration(udev); |
usb_bus_put(udev->bus); |
kfree (udev); |
} |
/** |
* usb_alloc_dev - allocate a usb device structure (usbcore-internal) |
* @parent: hub to which device is connected |
* @bus: bus used to access the device |
* Context: !in_interrupt () |
* |
* Only hub drivers (including virtual root hub drivers for host |
* controllers) should ever call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) |
{ |
struct usb_device *dev; |
dev = kmalloc(sizeof(*dev), GFP_KERNEL); |
if (!dev) |
return NULL; |
memset(dev, 0, sizeof(*dev)); |
bus = usb_bus_get(bus); |
if (!bus) { |
kfree(dev); |
return NULL; |
} |
device_initialize(&dev->dev); |
dev->dev.release = usb_release_dev; |
dev->state = USB_STATE_ATTACHED; |
if (!parent) |
dev->devpath [0] = '0'; |
dev->bus = bus; |
dev->parent = parent; |
INIT_LIST_HEAD(&dev->filelist); |
init_MUTEX(&dev->serialize); |
if (dev->bus->op->allocate) |
dev->bus->op->allocate(dev); |
return dev; |
} |
/** |
* usb_get_dev - increments the reference count of the usb device structure |
* @dev: the device being referenced |
* |
* Each live reference to a device should be refcounted. |
* |
* Drivers for USB interfaces should normally record such references in |
* their probe() methods, when they bind to an interface, and release |
* them by calling usb_put_dev(), in their disconnect() methods. |
* |
* A pointer to the device with the incremented reference counter is returned. |
*/ |
struct usb_device *usb_get_dev (struct usb_device *dev) |
{ |
struct device *tmp; |
if (!dev) |
return NULL; |
tmp = get_device(&dev->dev); |
if (tmp) |
return to_usb_device(tmp); |
else |
return NULL; |
} |
/** |
* usb_put_dev - release a use of the usb device structure |
* @dev: device that's been disconnected |
* |
* Must be called when a user of a device is finished with it. When the last |
* user of the device calls this function, the memory of the device is freed. |
*/ |
void usb_put_dev(struct usb_device *dev) |
{ |
if (dev) |
put_device(&dev->dev); |
} |
static struct usb_device *match_device(struct usb_device *dev, |
u16 vendor_id, u16 product_id) |
{ |
struct usb_device *ret_dev = NULL; |
int child; |
dbg("looking at vendor %d, product %d", |
dev->descriptor.idVendor, |
dev->descriptor.idProduct); |
/* see if this device matches */ |
if ((dev->descriptor.idVendor == vendor_id) && |
(dev->descriptor.idProduct == product_id)) { |
dbg ("found the device!"); |
ret_dev = usb_get_dev(dev); |
goto exit; |
} |
/* look through all of the children of this device */ |
for (child = 0; child < dev->maxchild; ++child) { |
if (dev->children[child]) { |
ret_dev = match_device(dev->children[child], |
vendor_id, product_id); |
if (ret_dev) |
goto exit; |
} |
} |
exit: |
return ret_dev; |
} |
/** |
* usb_find_device - find a specific usb device in the system |
* @vendor_id: the vendor id of the device to find |
* @product_id: the product id of the device to find |
* |
* Returns a pointer to a struct usb_device if such a specified usb |
* device is present in the system currently. The usage count of the |
* device will be incremented if a device is found. Make sure to call |
* usb_put_dev() when the caller is finished with the device. |
* |
* If a device with the specified vendor and product id is not found, |
* NULL is returned. |
*/ |
struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) |
{ |
struct list_head *buslist; |
struct usb_bus *bus; |
struct usb_device *dev = NULL; |
down(&usb_bus_list_lock); |
for (buslist = usb_bus_list.next; |
buslist != &usb_bus_list; |
buslist = buslist->next) { |
bus = container_of(buslist, struct usb_bus, bus_list); |
dev = match_device(bus->root_hub, vendor_id, product_id); |
if (dev) |
goto exit; |
} |
exit: |
up(&usb_bus_list_lock); |
return dev; |
} |
/** |
* usb_get_current_frame_number - return current bus frame number |
* @dev: the device whose bus is being queried |
* |
* Returns the current frame number for the USB host controller |
* used with the given USB device. This can be used when scheduling |
* isochronous requests. |
* |
* Note that different kinds of host controller have different |
* "scheduling horizons". While one type might support scheduling only |
* 32 frames into the future, others could support scheduling up to |
* 1024 frames into the future. |
*/ |
int usb_get_current_frame_number(struct usb_device *dev) |
{ |
return dev->bus->op->get_frame_number (dev); |
} |
/*-------------------------------------------------------------------*/ |
/* |
* __usb_get_extra_descriptor() finds a descriptor of specific type in the |
* extra field of the interface and endpoint descriptor structs. |
*/ |
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr) |
{ |
struct usb_descriptor_header *header; |
while (size >= sizeof(struct usb_descriptor_header)) { |
header = (struct usb_descriptor_header *)buffer; |
if (header->bLength < 2) { |
err("invalid descriptor length of %d", header->bLength); |
return -1; |
} |
if (header->bDescriptorType == type) { |
*ptr = header; |
return 0; |
} |
buffer += header->bLength; |
size -= header->bLength; |
} |
return -1; |
} |
/** |
* usb_disconnect - disconnect a device (usbcore-internal) |
* @pdev: pointer to device being disconnected |
* Context: !in_interrupt () |
* |
* Something got disconnected. Get rid of it, and all of its children. |
* |
* Only hub drivers (including virtual root hub drivers for host |
* controllers) should ever call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
void usb_disconnect(struct usb_device **pdev) |
{ |
struct usb_device *dev = *pdev; |
struct usb_bus *bus; |
struct usb_operations *ops; |
int i; |
might_sleep (); |
if (!dev) { |
pr_debug ("%s nodev\n", __FUNCTION__); |
return; |
} |
bus = dev->bus; |
if (!bus) { |
pr_debug ("%s nobus\n", __FUNCTION__); |
return; |
} |
ops = bus->op; |
*pdev = NULL; |
/* mark the device as inactive, so any further urb submissions for |
* this device will fail. |
*/ |
dev->state = USB_STATE_NOTATTACHED; |
down(&dev->serialize); |
dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum); |
/* Free up all the children before we remove this device */ |
for (i = 0; i < USB_MAXCHILDREN; i++) { |
struct usb_device **child = dev->children + i; |
if (*child) |
usb_disconnect(child); |
} |
/* deallocate hcd/hardware state ... nuking all pending urbs and |
* cleaning up all state associated with the current configuration |
*/ |
usb_disable_device(dev, 0); |
dev_dbg (&dev->dev, "unregistering device\n"); |
/* Free the device number and remove the /proc/bus/usb entry */ |
if (dev->devnum > 0) { |
clear_bit(dev->devnum, dev->bus->devmap.devicemap); |
usbfs_remove_device(dev); |
} |
up(&dev->serialize); |
device_unregister(&dev->dev); |
} |
/** |
* usb_choose_address - pick device address (usbcore-internal) |
* @dev: newly detected device (in DEFAULT state) |
* |
* Picks a device address. It's up to the hub (or root hub) driver |
* to handle and manage enumeration, starting from the DEFAULT state. |
* Only hub drivers (but not virtual root hub drivers for host |
* controllers) should ever call this. |
*/ |
void usb_choose_address(struct usb_device *dev) |
{ |
int devnum; |
// FIXME needs locking for SMP!! |
/* why? this is called only from the hub thread, |
* which hopefully doesn't run on multiple CPU's simultaneously 8-) |
*/ |
/* Try to allocate the next devnum beginning at bus->devnum_next. */ |
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next); |
if (devnum >= 128) |
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); |
dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); |
if (devnum < 128) { |
set_bit(devnum, dev->bus->devmap.devicemap); |
dev->devnum = devnum; |
} |
} |
// hub-only!! ... and only exported for reset/reinit path. |
// otherwise used internally, for usb_new_device() |
int usb_set_address(struct usb_device *dev) |
{ |
int retval; |
if (dev->devnum == 0) |
return -EINVAL; |
if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS) |
return -EINVAL; |
retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, |
0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); |
if (retval == 0) |
dev->state = USB_STATE_ADDRESS; |
return retval; |
} |
/* |
* By the time we get here, we chose a new device address |
* and is in the default state. We need to identify the thing and |
* get the ball rolling.. |
* |
* Returns 0 for success, != 0 for error. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
* |
* Only the hub driver should ever call this; root hub registration |
* uses it only indirectly. |
*/ |
#define NEW_DEVICE_RETRYS 2 |
#define SET_ADDRESS_RETRYS 2 |
int usb_new_device(struct usb_device *dev, struct device *parent) |
{ |
int err = -EINVAL; |
int i; |
int j; |
/* |
* Set the driver for the usb device to point to the "generic" driver. |
* This prevents the main usb device from being sent to the usb bus |
* probe function. Yes, it's a hack, but a nice one :) |
* |
* Do it asap, so more driver model stuff (like the device.h message |
* utilities) can be used in hcd submit/unlink code paths. |
*/ |
usb_generic_driver.bus = &usb_bus_type; |
dev->dev.parent = parent; |
dev->dev.driver = &usb_generic_driver; |
dev->dev.bus = &usb_bus_type; |
dev->dev.driver_data = &usb_generic_driver_data; |
if (dev->dev.bus_id[0] == 0) |
sprintf26 (&dev->dev.bus_id[0], "%d-%s", |
dev->bus->busnum, dev->devpath); |
/* dma masks come from the controller; readonly, except to hcd */ |
dev->dev.dma_mask = parent->dma_mask; |
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... |
* it's fixed size except for full speed devices. |
*/ |
switch (dev->speed) { |
case USB_SPEED_HIGH: /* fixed at 64 */ |
i = 64; |
break; |
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ |
/* to determine the ep0 maxpacket size, read the first 8 |
* bytes from the device descriptor to get bMaxPacketSize0; |
* then correct our initial (small) guess. |
*/ |
// FALLTHROUGH |
case USB_SPEED_LOW: /* fixed at 8 */ |
i = 8; |
break; |
default: |
goto fail; |
} |
dev->epmaxpacketin [0] = i; |
dev->epmaxpacketout[0] = i; |
for (i = 0; i < NEW_DEVICE_RETRYS; ++i) { |
for (j = 0; j < SET_ADDRESS_RETRYS; ++j) { |
err = usb_set_address(dev); |
if (err >= 0) |
break; |
wait_ms(200); |
} |
if (err < 0) { |
dev_err(&dev->dev, |
"device not accepting address %d, error %d\n", |
dev->devnum, err); |
goto fail; |
} |
wait_ms(10); /* Let the SET_ADDRESS settle */ |
/* high and low speed devices don't need this... */ |
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); |
if (err >= 8) |
break; |
wait_ms(100); |
} |
if (err < 8) { |
dev_err(&dev->dev, "device descriptor read/8, error %d\n", err); |
goto fail; |
} |
if (dev->speed == USB_SPEED_FULL) { |
usb_disable_endpoint(dev, 0); |
usb_endpoint_running(dev, 0, 1); |
usb_endpoint_running(dev, 0, 0); |
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; |
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; |
} |
/* USB device state == addressed ... still not usable */ |
err = usb_get_device_descriptor(dev); |
if (err < (signed)sizeof(dev->descriptor)) { |
dev_err(&dev->dev, "device descriptor read/all, error %d\n", err); |
goto fail; |
} |
err = usb_get_configuration(dev); |
if (err < 0) { |
dev_err(&dev->dev, "can't read configurations, error %d\n", |
err); |
goto fail; |
} |
/* Tell the world! */ |
dev_dbg(&dev->dev, "new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", |
dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber); |
#ifdef DEBUG |
if (dev->descriptor.iProduct) |
usb_show_string(dev, "Product", dev->descriptor.iProduct); |
if (dev->descriptor.iManufacturer) |
usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); |
if (dev->descriptor.iSerialNumber) |
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); |
#endif |
/* put device-specific files into sysfs */ |
err = device_add (&dev->dev); |
if (err) { |
dev_err(&dev->dev, "can't device_add, error %d\n", err); |
goto fail; |
} |
usb_create_driverfs_dev_files (dev); |
/* choose and set the configuration. that registers the interfaces |
* with the driver core, and lets usb device drivers bind to them. |
*/ |
if (dev->descriptor.bNumConfigurations != 1) { |
dev_info(&dev->dev, |
"configuration #%d chosen from %d choices\n", |
dev->config[0].desc.bConfigurationValue, |
dev->descriptor.bNumConfigurations); |
} |
err = usb_set_configuration(dev, |
dev->config[0].desc.bConfigurationValue); |
if (err) { |
dev_err(&dev->dev, "can't set config #%d, error %d\n", |
dev->config[0].desc.bConfigurationValue, err); |
device_del(&dev->dev); |
goto fail; |
} |
/* USB device state == configured ... usable */ |
/* add a /proc/bus/usb entry */ |
usbfs_add_device(dev); |
return 0; |
fail: |
dev->state = USB_STATE_DEFAULT; |
clear_bit(dev->devnum, dev->bus->devmap.devicemap); |
dev->devnum = -1; |
return err; |
} |
/** |
* usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP |
* @dev: device the buffer will be used with |
* @size: requested buffer size |
* @mem_flags: affect whether allocation may block |
* @dma: used to return DMA address of buffer |
* |
* Return value is either null (indicating no buffer could be allocated), or |
* the cpu-space pointer to a buffer that may be used to perform DMA to the |
* specified device. Such cpu-space buffers are returned along with the DMA |
* address (through the pointer provided). |
* |
* These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags |
* to avoid behaviors like using "DMA bounce buffers", or tying down I/O |
* mapping hardware for long idle periods. The implementation varies between |
* platforms, depending on details of how DMA will work to this device. |
* Using these buffers also helps prevent cacheline sharing problems on |
* architectures where CPU caches are not DMA-coherent. |
* |
* When the buffer is no longer used, free it with usb_buffer_free(). |
*/ |
void *usb_buffer_alloc ( |
struct usb_device *dev, |
size_t size, |
int mem_flags, |
dma_addr_t *dma |
) |
{ |
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc) |
return 0; |
return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma); |
} |
/** |
* usb_buffer_free - free memory allocated with usb_buffer_alloc() |
* @dev: device the buffer was used with |
* @size: requested buffer size |
* @addr: CPU address of buffer |
* @dma: DMA address of buffer |
* |
* This reclaims an I/O buffer, letting it be reused. The memory must have |
* been allocated using usb_buffer_alloc(), and the parameters must match |
* those provided in that allocation request. |
*/ |
void usb_buffer_free ( |
struct usb_device *dev, |
size_t size, |
void *addr, |
dma_addr_t dma |
) |
{ |
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) |
return; |
dev->bus->op->buffer_free (dev->bus, size, addr, dma); |
} |
/** |
* usb_buffer_map - create DMA mapping(s) for an urb |
* @urb: urb whose transfer_buffer/setup_packet will be mapped |
* |
* Return value is either null (indicating no buffer could be mapped), or |
* the parameter. URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are |
* added to urb->transfer_flags if the operation succeeds. If the device |
* is connected to this system through a non-DMA controller, this operation |
* always succeeds. |
* |
* This call would normally be used for an urb which is reused, perhaps |
* as the target of a large periodic transfer, with usb_buffer_dmasync() |
* calls to synchronize memory and dma state. |
* |
* Reverse the effect of this call with usb_buffer_unmap(). |
*/ |
struct urb *usb_buffer_map (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return 0; |
if (controller->dma_mask) { |
urb->transfer_dma = dma_map_single (controller, |
urb->transfer_buffer, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
urb->setup_dma = dma_map_single (controller, |
urb->setup_packet, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
// FIXME generic api broken like pci, can't report errors |
// if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; |
} else |
urb->transfer_dma = ~0; |
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP |
| URB_NO_SETUP_DMA_MAP); |
return urb; |
} |
/** |
* usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) |
* @urb: urb whose transfer_buffer/setup_packet will be synchronized |
*/ |
void usb_buffer_dmasync (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return; |
if (controller->dma_mask) { |
dma_sync_single (controller, |
urb->transfer_dma, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
dma_sync_single (controller, |
urb->setup_dma, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
} |
} |
/** |
* usb_buffer_unmap - free DMA mapping(s) for an urb |
* @urb: urb whose transfer_buffer will be unmapped |
* |
* Reverses the effect of usb_buffer_map(). |
*/ |
void usb_buffer_unmap (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return; |
if (controller->dma_mask) { |
dma_unmap_single (controller, |
urb->transfer_dma, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
dma_unmap_single (controller, |
urb->setup_dma, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
} |
urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP |
| URB_NO_SETUP_DMA_MAP); |
} |
/** |
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to map |
* @nents: the number of entries in the scatterlist |
* |
* Return value is either < 0 (indicating no buffers could be mapped), or |
* the number of DMA mapping array entries in the scatterlist. |
* |
* The caller is responsible for placing the resulting DMA addresses from |
* the scatterlist into URB transfer buffer pointers, and for setting the |
* URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. |
* |
* Top I/O rates come from queuing URBs, instead of waiting for each one |
* to complete before starting the next I/O. This is particularly easy |
* to do with scatterlists. Just allocate and submit one URB for each DMA |
* mapping entry returned, stopping on the first error or when all succeed. |
* Better yet, use the usb_sg_*() calls, which do that (and more) for you. |
* |
* This call would normally be used when translating scatterlist requests, |
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it |
* may be able to coalesce mappings for improved I/O efficiency. |
* |
* Reverse the effect of this call with usb_buffer_unmap_sg(). |
*/ |
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int nents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| usb_pipecontrol (pipe) |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return -1; |
// FIXME generic api broken like pci, can't report errors |
return dma_map_sg (controller, sg, nents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
/** |
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s) |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to synchronize |
* @n_hw_ents: the positive return value from usb_buffer_map_sg |
* |
* Use this when you are re-using a scatterlist's data buffers for |
* another USB request. |
*/ |
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int n_hw_ents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return; |
dma_sync_sg (controller, sg, n_hw_ents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
/** |
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to unmap |
* @n_hw_ents: the positive return value from usb_buffer_map_sg |
* |
* Reverses the effect of usb_buffer_map_sg(). |
*/ |
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int n_hw_ents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return; |
dma_unmap_sg (controller, sg, n_hw_ents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
static int usb_device_suspend(struct device *dev, u32 state) |
{ |
struct usb_interface *intf; |
struct usb_driver *driver; |
if ((dev->driver == NULL) || |
(dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
driver = to_usb_driver(dev->driver); |
if (driver->suspend) |
return driver->suspend(intf, state); |
return 0; |
} |
static int usb_device_resume(struct device *dev) |
{ |
struct usb_interface *intf; |
struct usb_driver *driver; |
if ((dev->driver == NULL) || |
(dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
driver = to_usb_driver(dev->driver); |
if (driver->resume) |
return driver->resume(intf); |
return 0; |
} |
struct bus_type usb_bus_type = { |
.name = "usb", |
.match = usb_device_match, |
.hotplug = usb_hotplug, |
.suspend = usb_device_suspend, |
.resume = usb_device_resume, |
}; |
#ifndef MODULE |
static int __init usb_setup_disable(char *str) |
{ |
nousb = 1; |
return 1; |
} |
/* format to disable USB on kernel command line is: nousb */ |
__setup("nousb", usb_setup_disable); |
#endif |
/* |
* for external read access to <nousb> |
*/ |
int usb_disabled(void) |
{ |
return nousb; |
} |
/* |
* Init |
*/ |
/*static*/ int __init usb_init(void) |
{ |
if (nousb) { |
info("USB support disabled\n"); |
return 0; |
} |
bus_register(&usb_bus_type); |
usb_host_init(); |
usb_major_init(); |
usbfs_init(); |
usb_hub_init(); |
driver_register(&usb_generic_driver); |
return 0; |
} |
/* |
* Cleanup |
*/ |
/*static*/ void __exit usb_exit(void) |
{ |
/* This will matter if shutdown/reboot does exitcalls. */ |
if (nousb) |
return; |
driver_unregister(&usb_generic_driver); |
// usb_major_cleanup(); |
// usbfs_cleanup(); |
// usb_hub_cleanup(); |
// usb_host_cleanup(); |
// bus_unregister(&usb_bus_type); |
} |
subsys_initcall(usb_init); |
module_exit(usb_exit); |
/* |
* USB may be built into the kernel or be built as modules. |
* These symbols are exported for device (or host controller) |
* driver modules to use. |
*/ |
EXPORT_SYMBOL(usb_epnum_to_ep_desc); |
EXPORT_SYMBOL(usb_register); |
EXPORT_SYMBOL(usb_deregister); |
EXPORT_SYMBOL(usb_disabled); |
EXPORT_SYMBOL(usb_alloc_dev); |
EXPORT_SYMBOL(usb_put_dev); |
EXPORT_SYMBOL(usb_get_dev); |
EXPORT_SYMBOL(usb_hub_tt_clear_buffer); |
EXPORT_SYMBOL(usb_driver_claim_interface); |
EXPORT_SYMBOL(usb_interface_claimed); |
EXPORT_SYMBOL(usb_driver_release_interface); |
EXPORT_SYMBOL(usb_match_id); |
EXPORT_SYMBOL(usb_find_interface); |
EXPORT_SYMBOL(usb_ifnum_to_if); |
EXPORT_SYMBOL(usb_reset_device); |
EXPORT_SYMBOL(usb_disconnect); |
EXPORT_SYMBOL(__usb_get_extra_descriptor); |
EXPORT_SYMBOL(usb_find_device); |
EXPORT_SYMBOL(usb_get_current_frame_number); |
EXPORT_SYMBOL (usb_buffer_alloc); |
EXPORT_SYMBOL (usb_buffer_free); |
EXPORT_SYMBOL (usb_buffer_map); |
EXPORT_SYMBOL (usb_buffer_dmasync); |
EXPORT_SYMBOL (usb_buffer_unmap); |
EXPORT_SYMBOL (usb_buffer_map_sg); |
EXPORT_SYMBOL (usb_buffer_dmasync_sg); |
EXPORT_SYMBOL (usb_buffer_unmap_sg); |
MODULE_LICENSE("GPL"); |
/* |
* drivers/usb/usb.c |
* |
* (C) Copyright Linus Torvalds 1999 |
* (C) Copyright Johannes Erdfelt 1999-2001 |
* (C) Copyright Andreas Gal 1999 |
* (C) Copyright Gregory P. Smith 1999 |
* (C) Copyright Deti Fliegl 1999 (new USB architecture) |
* (C) Copyright Randy Dunlap 2000 |
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, |
more docs, etc) |
* (C) Copyright Yggdrasil Computing, Inc. 2000 |
* (usb_device_id matching changes by Adam J. Richter) |
* (C) Copyright Greg Kroah-Hartman 2002-2003 |
* |
* NOTE! This is not actually a driver at all, rather this is |
* just a collection of helper routines that implement the |
* generic USB things that the real drivers can use.. |
* |
* Think of this as a "USB library" rather than anything else. |
* It should be considered a slave, with no callbacks. Callbacks |
* are evil. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#ifdef CONFIG_USB_DEBUG |
#define DEBUG |
#else |
#undef DEBUG |
#endif |
#include <linux/module.h> |
#include <linux/string.h> |
#include <linux/bitops.h> |
#include <linux/slab.h> |
#include <linux/interrupt.h> /* for in_interrupt() */ |
#include <linux/kmod.h> |
#include <linux/init.h> |
#include <linux/spinlock.h> |
#include <linux/errno.h> |
#include <linux/smp_lock.h> |
#include <linux/usb.h> |
#include <asm/io.h> |
#include <asm/scatterlist.h> |
#include <linux/mm.h> |
#include <linux/dma-mapping.h> |
#include "hcd.h" |
#include "usb.h" |
extern int usb_hub_init(void); |
extern void usb_hub_cleanup(void); |
extern int usb_major_init(void); |
extern void usb_major_cleanup(void); |
extern int usb_host_init(void); |
extern void usb_host_cleanup(void); |
int nousb; /* Disable USB when built into kernel image */ |
/* Not honored on modular build */ |
static int generic_probe (struct device *dev) |
{ |
return 0; |
} |
static int generic_remove (struct device *dev) |
{ |
return 0; |
} |
static struct device_driver usb_generic_driver = { |
.name = "usb", |
.bus = &usb_bus_type, |
.probe = generic_probe, |
.remove = generic_remove, |
}; |
static int usb_generic_driver_data; |
/* needs to be called with BKL held */ |
int usb_probe_interface(struct device *dev) |
{ |
struct usb_interface * intf = to_usb_interface(dev); |
struct usb_driver * driver = to_usb_driver(dev->driver); |
const struct usb_device_id *id; |
int error = -ENODEV; |
dev_dbg(dev, "%s\n", __FUNCTION__); |
if (!driver->probe) |
return error; |
/* driver claim() doesn't yet affect dev->driver... */ |
if (intf->driver) |
return error; |
id = usb_match_id (intf, driver->id_table); |
if (id) { |
dev_dbg (dev, "%s - got id\n", __FUNCTION__); |
error = driver->probe (intf, id); |
} |
if (!error) |
intf->driver = driver; |
return error; |
} |
int usb_unbind_interface(struct device *dev) |
{ |
struct usb_interface *intf = to_usb_interface(dev); |
struct usb_driver *driver = intf->driver; |
/* release all urbs for this interface */ |
usb_disable_interface(interface_to_usbdev(intf), intf); |
if (driver && driver->disconnect) |
driver->disconnect(intf); |
/* reset other interface state */ |
usb_set_interface(interface_to_usbdev(intf), |
intf->altsetting[0].desc.bInterfaceNumber, |
0); |
usb_set_intfdata(intf, NULL); |
intf->driver = NULL; |
return 0; |
} |
/** |
* usb_register - register a USB driver |
* @new_driver: USB operations for the driver |
* |
* Registers a USB driver with the USB core. The list of unattached |
* interfaces will be rescanned whenever a new driver is added, allowing |
* the new driver to attach to any recognized devices. |
* Returns a negative error code on failure and 0 on success. |
* |
* NOTE: if you want your driver to use the USB major number, you must call |
* usb_register_dev() to enable that functionality. This function no longer |
* takes care of that. |
*/ |
int usb_register(struct usb_driver *new_driver) |
{ |
int retval = 0; |
if (nousb) |
return -ENODEV; |
new_driver->driver.name = (char *)new_driver->name; |
new_driver->driver.bus = &usb_bus_type; |
new_driver->driver.probe = usb_probe_interface; |
new_driver->driver.remove = usb_unbind_interface; |
init_MUTEX(&new_driver->serialize); |
retval = driver_register(&new_driver->driver); |
if (!retval) { |
info("registered new driver %s", new_driver->name); |
usbfs_update_special(); |
} else { |
err("problem %d when registering driver %s", |
retval, new_driver->name); |
} |
return retval; |
} |
/** |
* usb_deregister - unregister a USB driver |
* @driver: USB operations of the driver to unregister |
* Context: !in_interrupt (), must be called with BKL held |
* |
* Unlinks the specified driver from the internal USB driver list. |
* |
* NOTE: If you called usb_register_dev(), you still need to call |
* usb_deregister_dev() to clean up your driver's allocated minor numbers, |
* this * call will no longer do it for you. |
*/ |
void usb_deregister(struct usb_driver *driver) |
{ |
info("deregistering driver %s", driver->name); |
driver_unregister (&driver->driver); |
usbfs_update_special(); |
} |
/** |
* usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal) |
* @dev: the device whose current configuration is considered |
* @ifnum: the desired interface |
* |
* This walks the device descriptor for the currently active configuration |
* and returns a pointer to the interface with that particular interface |
* number, or null. |
* |
* Note that configuration descriptors are not required to assign interface |
* numbers sequentially, so that it would be incorrect to assume that |
* the first interface in that descriptor corresponds to interface zero. |
* This routine helps device drivers avoid such mistakes. |
* However, you should make sure that you do the right thing with any |
* alternate settings available for this interfaces. |
*/ |
struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) |
{ |
int i; |
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) |
if (dev->actconfig->interface[i]->altsetting[0] |
.desc.bInterfaceNumber == ifnum) |
return dev->actconfig->interface[i]; |
return NULL; |
} |
/** |
* usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number |
* @dev: the device whose current configuration+altsettings is considered |
* @epnum: the desired endpoint, masked with USB_DIR_IN as appropriate. |
* |
* This walks the device descriptor for the currently active configuration, |
* and returns a pointer to the endpoint with that particular endpoint |
* number, or null. |
* |
* Note that interface descriptors are not required to list endpoint |
* numbers in any standardized order, so that it would be wrong to |
* assume that ep2in precedes either ep5in, ep2out, or even ep1out. |
* This routine helps device drivers avoid such mistakes. |
*/ |
struct usb_endpoint_descriptor * |
usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) |
{ |
int i, k; |
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { |
struct usb_interface *intf; |
struct usb_host_interface *alt; |
/* only endpoints in current altseting are active */ |
intf = dev->actconfig->interface[i]; |
alt = intf->altsetting + intf->act_altsetting; |
for (k = 0; k < alt->desc.bNumEndpoints; k++) |
if (epnum == alt->endpoint[k].desc.bEndpointAddress) |
return &alt->endpoint[k].desc; |
} |
return NULL; |
} |
/** |
* usb_driver_claim_interface - bind a driver to an interface |
* @driver: the driver to be bound |
* @iface: the interface to which it will be bound |
* @priv: driver data associated with that interface |
* |
* This is used by usb device drivers that need to claim more than one |
* interface on a device when probing (audio and acm are current examples). |
* No device driver should directly modify internal usb_interface or |
* usb_device structure members. |
* |
* Few drivers should need to use this routine, since the most natural |
* way to bind to an interface is to return the private data from |
* the driver's probe() method. |
*/ |
int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) |
{ |
if (!iface || !driver) |
return -EINVAL; |
if (iface->driver) |
return -EBUSY; |
/* FIXME should device_bind_driver() */ |
iface->driver = driver; |
usb_set_intfdata(iface, priv); |
return 0; |
} |
/** |
* usb_interface_claimed - returns true iff an interface is claimed |
* @iface: the interface being checked |
* |
* This should be used by drivers to check other interfaces to see if |
* they are available or not. If another driver has claimed the interface, |
* they may not claim it. Otherwise it's OK to claim it using |
* usb_driver_claim_interface(). |
* |
* Returns true (nonzero) iff the interface is claimed, else false (zero). |
*/ |
int usb_interface_claimed(struct usb_interface *iface) |
{ |
if (!iface) |
return 0; |
return (iface->driver != NULL); |
} /* usb_interface_claimed() */ |
/** |
* usb_driver_release_interface - unbind a driver from an interface |
* @driver: the driver to be unbound |
* @iface: the interface from which it will be unbound |
* |
* In addition to unbinding the driver, this re-initializes the interface |
* by selecting altsetting 0, the default alternate setting. |
* |
* This can be used by drivers to release an interface without waiting |
* for their disconnect() methods to be called. |
* |
* When the USB subsystem disconnect()s a driver from some interface, |
* it automatically invokes this method for that interface. That |
* means that even drivers that used usb_driver_claim_interface() |
* usually won't need to call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) |
{ |
/* this should never happen, don't release something that's not ours */ |
if (!iface || !iface->driver || iface->driver != driver) |
return; |
if (iface->dev.driver) { |
/* FIXME should be the ONLY case here */ |
device_release_driver(&iface->dev); |
return; |
} |
usb_set_interface(interface_to_usbdev(iface), |
iface->altsetting[0].desc.bInterfaceNumber, |
0); |
usb_set_intfdata(iface, NULL); |
iface->driver = NULL; |
} |
/** |
* usb_match_id - find first usb_device_id matching device or interface |
* @interface: the interface of interest |
* @id: array of usb_device_id structures, terminated by zero entry |
* |
* usb_match_id searches an array of usb_device_id's and returns |
* the first one matching the device or interface, or null. |
* This is used when binding (or rebinding) a driver to an interface. |
* Most USB device drivers will use this indirectly, through the usb core, |
* but some layered driver frameworks use it directly. |
* These device tables are exported with MODULE_DEVICE_TABLE, through |
* modutils and "modules.usbmap", to support the driver loading |
* functionality of USB hotplugging. |
* |
* What Matches: |
* |
* The "match_flags" element in a usb_device_id controls which |
* members are used. If the corresponding bit is set, the |
* value in the device_id must match its corresponding member |
* in the device or interface descriptor, or else the device_id |
* does not match. |
* |
* "driver_info" is normally used only by device drivers, |
* but you can create a wildcard "matches anything" usb_device_id |
* as a driver's "modules.usbmap" entry if you provide an id with |
* only a nonzero "driver_info" field. If you do this, the USB device |
* driver's probe() routine should use additional intelligence to |
* decide whether to bind to the specified interface. |
* |
* What Makes Good usb_device_id Tables: |
* |
* The match algorithm is very simple, so that intelligence in |
* driver selection must come from smart driver id records. |
* Unless you have good reasons to use another selection policy, |
* provide match elements only in related groups, and order match |
* specifiers from specific to general. Use the macros provided |
* for that purpose if you can. |
* |
* The most specific match specifiers use device descriptor |
* data. These are commonly used with product-specific matches; |
* the USB_DEVICE macro lets you provide vendor and product IDs, |
* and you can also match against ranges of product revisions. |
* These are widely used for devices with application or vendor |
* specific bDeviceClass values. |
* |
* Matches based on device class/subclass/protocol specifications |
* are slightly more general; use the USB_DEVICE_INFO macro, or |
* its siblings. These are used with single-function devices |
* where bDeviceClass doesn't specify that each interface has |
* its own class. |
* |
* Matches based on interface class/subclass/protocol are the |
* most general; they let drivers bind to any interface on a |
* multiple-function device. Use the USB_INTERFACE_INFO |
* macro, or its siblings, to match class-per-interface style |
* devices (as recorded in bDeviceClass). |
* |
* Within those groups, remember that not all combinations are |
* meaningful. For example, don't give a product version range |
* without vendor and product IDs; or specify a protocol without |
* its associated class and subclass. |
*/ |
const struct usb_device_id * |
usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) |
{ |
struct usb_host_interface *intf; |
struct usb_device *dev; |
/* proc_connectinfo in devio.c may call us with id == NULL. */ |
if (id == NULL) |
return NULL; |
intf = &interface->altsetting [interface->act_altsetting]; |
dev = interface_to_usbdev(interface); |
/* It is important to check that id->driver_info is nonzero, |
since an entry that is all zeroes except for a nonzero |
id->driver_info is the way to create an entry that |
indicates that the driver want to examine every |
device and interface. */ |
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || |
id->driver_info; id++) { |
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && |
id->idVendor != dev->descriptor.idVendor) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && |
id->idProduct != dev->descriptor.idProduct) |
continue; |
/* No need to test id->bcdDevice_lo != 0, since 0 is never |
greater than any unsigned number. */ |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && |
(id->bcdDevice_lo > dev->descriptor.bcdDevice)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && |
(id->bcdDevice_hi < dev->descriptor.bcdDevice)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && |
(id->bDeviceClass != dev->descriptor.bDeviceClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && |
(id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && |
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && |
(id->bInterfaceClass != intf->desc.bInterfaceClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && |
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) |
continue; |
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && |
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) |
continue; |
return id; |
} |
return NULL; |
} |
/** |
* usb_find_interface - find usb_interface pointer for driver and device |
* @drv: the driver whose current configuration is considered |
* @minor: the minor number of the desired device |
* |
* This walks the driver device list and returns a pointer to the interface |
* with the matching minor. Note, this only works for devices that share the |
* USB major number. |
*/ |
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) |
{ |
struct list_head *entry; |
struct device *dev; |
struct usb_interface *intf; |
list_for_each(entry, &drv->driver.devices) { |
dev = container_of(entry, struct device, driver_list); |
/* can't look at usb devices, only interfaces */ |
if (dev->driver == &usb_generic_driver) |
continue; |
intf = to_usb_interface(dev); |
if (intf->minor == -1) |
continue; |
if (intf->minor == minor) |
return intf; |
} |
/* no device found that matches */ |
return NULL; |
} |
static int usb_device_match (struct device *dev, struct device_driver *drv) |
{ |
struct usb_interface *intf; |
struct usb_driver *usb_drv; |
const struct usb_device_id *id; |
/* check for generic driver, which we don't match any device with */ |
if (drv == &usb_generic_driver) |
return 0; |
intf = to_usb_interface(dev); |
usb_drv = to_usb_driver(drv); |
id = usb_drv->id_table; |
id = usb_match_id (intf, usb_drv->id_table); |
if (id) |
return 1; |
return 0; |
} |
#ifdef CONFIG_HOTPLUG |
/* |
* USB hotplugging invokes what /proc/sys/kernel/hotplug says |
* (normally /sbin/hotplug) when USB devices get added or removed. |
* |
* This invokes a user mode policy agent, typically helping to load driver |
* or other modules, configure the device, and more. Drivers can provide |
* a MODULE_DEVICE_TABLE to help with module loading subtasks. |
* |
* We're called either from khubd (the typical case) or from root hub |
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle |
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the |
* device (and this configuration!) are still present. |
*/ |
static int usb_hotplug (struct device *dev, char **envp, int num_envp, |
char *buffer, int buffer_size) |
{ |
struct usb_interface *intf; |
struct usb_device *usb_dev; |
char *scratch; |
int i = 0; |
int length = 0; |
dbg ("%s", __FUNCTION__); |
if (!dev) |
return -ENODEV; |
/* Must check driver_data here, as on remove driver is always NULL */ |
if ((dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
usb_dev = interface_to_usbdev (intf); |
if (usb_dev->devnum < 0) { |
dbg ("device already deleted ??"); |
return -ENODEV; |
} |
if (!usb_dev->bus) { |
dbg ("bus already removed?"); |
return -ENODEV; |
} |
scratch = buffer; |
#ifdef CONFIG_USB_DEVICEFS |
/* If this is available, userspace programs can directly read |
* all the device descriptors we don't tell them about. Or |
* even act as usermode drivers. |
* |
* FIXME reduce hardwired intelligence here |
*/ |
envp [i++] = scratch; |
length += snprintf26 (scratch, buffer_size - length, |
"DEVICE=/proc/bus/usb/%03d/%03d", |
usb_dev->bus->busnum, usb_dev->devnum); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
#endif |
/* per-device configurations are common */ |
envp [i++] = scratch; |
length += snprintf26 (scratch, buffer_size - length, "PRODUCT=%x/%x/%x", |
usb_dev->descriptor.idVendor, |
usb_dev->descriptor.idProduct, |
usb_dev->descriptor.bcdDevice); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
/* class-based driver binding models */ |
envp [i++] = scratch; |
length += snprintf26 (scratch, buffer_size - length, "TYPE=%d/%d/%d", |
usb_dev->descriptor.bDeviceClass, |
usb_dev->descriptor.bDeviceSubClass, |
usb_dev->descriptor.bDeviceProtocol); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
if (usb_dev->descriptor.bDeviceClass == 0) { |
int alt = intf->act_altsetting; |
/* 2.4 only exposed interface zero. in 2.5, hotplug |
* agents are called for all interfaces, and can use |
* $DEVPATH/bInterfaceNumber if necessary. |
*/ |
envp [i++] = scratch; |
length += snprintf26 (scratch, buffer_size - length, |
"INTERFACE=%d/%d/%d", |
intf->altsetting[alt].desc.bInterfaceClass, |
intf->altsetting[alt].desc.bInterfaceSubClass, |
intf->altsetting[alt].desc.bInterfaceProtocol); |
if ((buffer_size - length <= 0) || (i >= num_envp)) |
return -ENOMEM; |
++length; |
scratch += length; |
} |
envp [i++] = 0; |
return 0; |
} |
#else |
static int usb_hotplug (struct device *dev, char **envp, |
int num_envp, char *buffer, int buffer_size) |
{ |
return -ENODEV; |
} |
#endif /* CONFIG_HOTPLUG */ |
/** |
* usb_release_dev - free a usb device structure when all users of it are finished. |
* @dev: device that's been disconnected |
* |
* Will be called only by the device core when all users of this usb device are |
* done. |
*/ |
static void usb_release_dev(struct device *dev) |
{ |
struct usb_device *udev; |
udev = to_usb_device(dev); |
if (udev->bus && udev->bus->op && udev->bus->op->deallocate) |
udev->bus->op->deallocate(udev); |
usb_destroy_configuration(udev); |
usb_bus_put(udev->bus); |
kfree (udev); |
} |
/** |
* usb_alloc_dev - allocate a usb device structure (usbcore-internal) |
* @parent: hub to which device is connected |
* @bus: bus used to access the device |
* Context: !in_interrupt () |
* |
* Only hub drivers (including virtual root hub drivers for host |
* controllers) should ever call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) |
{ |
struct usb_device *dev; |
dev = kmalloc(sizeof(*dev), GFP_KERNEL); |
if (!dev) |
return NULL; |
memset(dev, 0, sizeof(*dev)); |
bus = usb_bus_get(bus); |
if (!bus) { |
kfree(dev); |
return NULL; |
} |
device_initialize(&dev->dev); |
dev->dev.release = usb_release_dev; |
dev->state = USB_STATE_ATTACHED; |
if (!parent) |
dev->devpath [0] = '0'; |
dev->bus = bus; |
dev->parent = parent; |
INIT_LIST_HEAD(&dev->filelist); |
init_MUTEX(&dev->serialize); |
if (dev->bus->op->allocate) |
dev->bus->op->allocate(dev); |
return dev; |
} |
/** |
* usb_get_dev - increments the reference count of the usb device structure |
* @dev: the device being referenced |
* |
* Each live reference to a device should be refcounted. |
* |
* Drivers for USB interfaces should normally record such references in |
* their probe() methods, when they bind to an interface, and release |
* them by calling usb_put_dev(), in their disconnect() methods. |
* |
* A pointer to the device with the incremented reference counter is returned. |
*/ |
struct usb_device *usb_get_dev (struct usb_device *dev) |
{ |
struct device *tmp; |
if (!dev) |
return NULL; |
tmp = get_device(&dev->dev); |
if (tmp) |
return to_usb_device(tmp); |
else |
return NULL; |
} |
/** |
* usb_put_dev - release a use of the usb device structure |
* @dev: device that's been disconnected |
* |
* Must be called when a user of a device is finished with it. When the last |
* user of the device calls this function, the memory of the device is freed. |
*/ |
void usb_put_dev(struct usb_device *dev) |
{ |
if (dev) |
put_device(&dev->dev); |
} |
static struct usb_device *match_device(struct usb_device *dev, |
u16 vendor_id, u16 product_id) |
{ |
struct usb_device *ret_dev = NULL; |
int child; |
dbg("looking at vendor %d, product %d", |
dev->descriptor.idVendor, |
dev->descriptor.idProduct); |
/* see if this device matches */ |
if ((dev->descriptor.idVendor == vendor_id) && |
(dev->descriptor.idProduct == product_id)) { |
dbg ("found the device!"); |
ret_dev = usb_get_dev(dev); |
goto exit; |
} |
/* look through all of the children of this device */ |
for (child = 0; child < dev->maxchild; ++child) { |
if (dev->children[child]) { |
ret_dev = match_device(dev->children[child], |
vendor_id, product_id); |
if (ret_dev) |
goto exit; |
} |
} |
exit: |
return ret_dev; |
} |
/** |
* usb_find_device - find a specific usb device in the system |
* @vendor_id: the vendor id of the device to find |
* @product_id: the product id of the device to find |
* |
* Returns a pointer to a struct usb_device if such a specified usb |
* device is present in the system currently. The usage count of the |
* device will be incremented if a device is found. Make sure to call |
* usb_put_dev() when the caller is finished with the device. |
* |
* If a device with the specified vendor and product id is not found, |
* NULL is returned. |
*/ |
struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) |
{ |
struct list_head *buslist; |
struct usb_bus *bus; |
struct usb_device *dev = NULL; |
down(&usb_bus_list_lock); |
for (buslist = usb_bus_list.next; |
buslist != &usb_bus_list; |
buslist = buslist->next) { |
bus = container_of(buslist, struct usb_bus, bus_list); |
dev = match_device(bus->root_hub, vendor_id, product_id); |
if (dev) |
goto exit; |
} |
exit: |
up(&usb_bus_list_lock); |
return dev; |
} |
/** |
* usb_get_current_frame_number - return current bus frame number |
* @dev: the device whose bus is being queried |
* |
* Returns the current frame number for the USB host controller |
* used with the given USB device. This can be used when scheduling |
* isochronous requests. |
* |
* Note that different kinds of host controller have different |
* "scheduling horizons". While one type might support scheduling only |
* 32 frames into the future, others could support scheduling up to |
* 1024 frames into the future. |
*/ |
int usb_get_current_frame_number(struct usb_device *dev) |
{ |
return dev->bus->op->get_frame_number (dev); |
} |
/*-------------------------------------------------------------------*/ |
/* |
* __usb_get_extra_descriptor() finds a descriptor of specific type in the |
* extra field of the interface and endpoint descriptor structs. |
*/ |
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr) |
{ |
struct usb_descriptor_header *header; |
while (size >= sizeof(struct usb_descriptor_header)) { |
header = (struct usb_descriptor_header *)buffer; |
if (header->bLength < 2) { |
err("invalid descriptor length of %d", header->bLength); |
return -1; |
} |
if (header->bDescriptorType == type) { |
*ptr = header; |
return 0; |
} |
buffer += header->bLength; |
size -= header->bLength; |
} |
return -1; |
} |
/** |
* usb_disconnect - disconnect a device (usbcore-internal) |
* @pdev: pointer to device being disconnected |
* Context: !in_interrupt () |
* |
* Something got disconnected. Get rid of it, and all of its children. |
* |
* Only hub drivers (including virtual root hub drivers for host |
* controllers) should ever call this. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
*/ |
void usb_disconnect(struct usb_device **pdev) |
{ |
struct usb_device *dev = *pdev; |
struct usb_bus *bus; |
struct usb_operations *ops; |
int i; |
might_sleep (); |
if (!dev) { |
pr_debug ("%s nodev\n", __FUNCTION__); |
return; |
} |
bus = dev->bus; |
if (!bus) { |
pr_debug ("%s nobus\n", __FUNCTION__); |
return; |
} |
ops = bus->op; |
*pdev = NULL; |
/* mark the device as inactive, so any further urb submissions for |
* this device will fail. |
*/ |
dev->state = USB_STATE_NOTATTACHED; |
down(&dev->serialize); |
dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum); |
/* Free up all the children before we remove this device */ |
for (i = 0; i < USB_MAXCHILDREN; i++) { |
struct usb_device **child = dev->children + i; |
if (*child) |
usb_disconnect(child); |
} |
/* deallocate hcd/hardware state ... nuking all pending urbs and |
* cleaning up all state associated with the current configuration |
*/ |
usb_disable_device(dev, 0); |
dev_dbg (&dev->dev, "unregistering device\n"); |
/* Free the device number and remove the /proc/bus/usb entry */ |
if (dev->devnum > 0) { |
clear_bit(dev->devnum, dev->bus->devmap.devicemap); |
usbfs_remove_device(dev); |
} |
up(&dev->serialize); |
device_unregister(&dev->dev); |
} |
/** |
* usb_choose_address - pick device address (usbcore-internal) |
* @dev: newly detected device (in DEFAULT state) |
* |
* Picks a device address. It's up to the hub (or root hub) driver |
* to handle and manage enumeration, starting from the DEFAULT state. |
* Only hub drivers (but not virtual root hub drivers for host |
* controllers) should ever call this. |
*/ |
void usb_choose_address(struct usb_device *dev) |
{ |
int devnum; |
// FIXME needs locking for SMP!! |
/* why? this is called only from the hub thread, |
* which hopefully doesn't run on multiple CPU's simultaneously 8-) |
*/ |
/* Try to allocate the next devnum beginning at bus->devnum_next. */ |
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next); |
if (devnum >= 128) |
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); |
dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); |
if (devnum < 128) { |
set_bit(devnum, dev->bus->devmap.devicemap); |
dev->devnum = devnum; |
} |
} |
// hub-only!! ... and only exported for reset/reinit path. |
// otherwise used internally, for usb_new_device() |
int usb_set_address(struct usb_device *dev) |
{ |
int retval; |
if (dev->devnum == 0) |
return -EINVAL; |
if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS) |
return -EINVAL; |
retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, |
0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); |
if (retval == 0) |
dev->state = USB_STATE_ADDRESS; |
return retval; |
} |
/* |
* By the time we get here, we chose a new device address |
* and is in the default state. We need to identify the thing and |
* get the ball rolling.. |
* |
* Returns 0 for success, != 0 for error. |
* |
* This call is synchronous, and may not be used in an interrupt context. |
* |
* Only the hub driver should ever call this; root hub registration |
* uses it only indirectly. |
*/ |
#define NEW_DEVICE_RETRYS 2 |
#define SET_ADDRESS_RETRYS 2 |
int usb_new_device(struct usb_device *dev, struct device *parent) |
{ |
int err = -EINVAL; |
int i; |
int j; |
int config; |
/* |
* Set the driver for the usb device to point to the "generic" driver. |
* This prevents the main usb device from being sent to the usb bus |
* probe function. Yes, it's a hack, but a nice one :) |
* |
* Do it asap, so more driver model stuff (like the device.h message |
* utilities) can be used in hcd submit/unlink code paths. |
*/ |
usb_generic_driver.bus = &usb_bus_type; |
dev->dev.parent = parent; |
dev->dev.driver = &usb_generic_driver; |
dev->dev.bus = &usb_bus_type; |
dev->dev.driver_data = &usb_generic_driver_data; |
if (dev->dev.bus_id[0] == 0) |
sprintf26 (&dev->dev.bus_id[0], "%d-%s", |
dev->bus->busnum, dev->devpath); |
/* dma masks come from the controller; readonly, except to hcd */ |
dev->dev.dma_mask = parent->dma_mask; |
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... |
* it's fixed size except for full speed devices. |
*/ |
switch (dev->speed) { |
case USB_SPEED_HIGH: /* fixed at 64 */ |
i = 64; |
break; |
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ |
/* to determine the ep0 maxpacket size, read the first 8 |
* bytes from the device descriptor to get bMaxPacketSize0; |
* then correct our initial (small) guess. |
*/ |
// FALLTHROUGH |
case USB_SPEED_LOW: /* fixed at 8 */ |
i = 8; |
break; |
default: |
goto fail; |
} |
dev->epmaxpacketin [0] = i; |
dev->epmaxpacketout[0] = i; |
for (i = 0; i < NEW_DEVICE_RETRYS; ++i) { |
for (j = 0; j < SET_ADDRESS_RETRYS; ++j) { |
err = usb_set_address(dev); |
if (err >= 0) |
break; |
wait_ms(200); |
} |
if (err < 0) { |
dev_err(&dev->dev, |
"device not accepting address %d, error %d\n", |
dev->devnum, err); |
goto fail; |
} |
wait_ms(10); /* Let the SET_ADDRESS settle */ |
/* high and low speed devices don't need this... */ |
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); |
if (err >= 8) |
break; |
wait_ms(100); |
} |
if (err < 8) { |
dev_err(&dev->dev, "device descriptor read/8, error %d\n", err); |
goto fail; |
} |
if (dev->speed == USB_SPEED_FULL) { |
usb_disable_endpoint(dev, 0); |
usb_endpoint_running(dev, 0, 1); |
usb_endpoint_running(dev, 0, 0); |
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; |
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; |
} |
/* USB device state == addressed ... still not usable */ |
err = usb_get_device_descriptor(dev); |
if (err < (signed)sizeof(dev->descriptor)) { |
dev_err(&dev->dev, "device descriptor read/all, error %d\n", err); |
goto fail; |
} |
err = usb_get_configuration(dev); |
if (err < 0) { |
dev_err(&dev->dev, "can't read configurations, error %d\n", |
err); |
goto fail; |
} |
/* Tell the world! */ |
dev_dbg(&dev->dev, "new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", |
dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber); |
#ifdef DEBUG |
if (dev->descriptor.iProduct) |
usb_show_string(dev, "Product", dev->descriptor.iProduct); |
if (dev->descriptor.iManufacturer) |
usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); |
if (dev->descriptor.iSerialNumber) |
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); |
#endif |
/* put device-specific files into sysfs */ |
err = device_add (&dev->dev); |
if (err) { |
dev_err(&dev->dev, "can't device_add, error %d\n", err); |
goto fail; |
} |
usb_create_driverfs_dev_files (dev); |
/* choose and set the configuration. that registers the interfaces |
* with the driver core, and lets usb device drivers bind to them. |
* NOTE: should interact with hub power budgeting. |
*/ |
config = dev->config[0].desc.bConfigurationValue; |
if (dev->descriptor.bNumConfigurations != 1) { |
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { |
/* heuristic: Linux is more likely to have class |
* drivers, so avoid vendor-specific interfaces. |
*/ |
if (dev->config[i].interface[0]->altsetting |
->desc.bInterfaceClass |
== USB_CLASS_VENDOR_SPEC) |
continue; |
config = dev->config[i].desc.bConfigurationValue; |
break; |
} |
dev_info(&dev->dev, |
"configuration #%d chosen from %d choices\n", |
config, |
dev->descriptor.bNumConfigurations); |
} |
err = usb_set_configuration(dev, config); |
if (err) { |
dev_err(&dev->dev, "can't set config #%d, error %d\n", |
config, err); |
device_del(&dev->dev); |
goto fail; |
} |
/* USB device state == configured ... usable */ |
/* add a /proc/bus/usb entry */ |
usbfs_add_device(dev); |
return 0; |
fail: |
dev->state = USB_STATE_DEFAULT; |
clear_bit(dev->devnum, dev->bus->devmap.devicemap); |
dev->devnum = -1; |
return err; |
} |
/** |
* usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP |
* @dev: device the buffer will be used with |
* @size: requested buffer size |
* @mem_flags: affect whether allocation may block |
* @dma: used to return DMA address of buffer |
* |
* Return value is either null (indicating no buffer could be allocated), or |
* the cpu-space pointer to a buffer that may be used to perform DMA to the |
* specified device. Such cpu-space buffers are returned along with the DMA |
* address (through the pointer provided). |
* |
* These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags |
* to avoid behaviors like using "DMA bounce buffers", or tying down I/O |
* mapping hardware for long idle periods. The implementation varies between |
* platforms, depending on details of how DMA will work to this device. |
* Using these buffers also helps prevent cacheline sharing problems on |
* architectures where CPU caches are not DMA-coherent. |
* |
* When the buffer is no longer used, free it with usb_buffer_free(). |
*/ |
void *usb_buffer_alloc ( |
struct usb_device *dev, |
size_t size, |
int mem_flags, |
dma_addr_t *dma |
) |
{ |
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc) |
return 0; |
return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma); |
} |
/** |
* usb_buffer_free - free memory allocated with usb_buffer_alloc() |
* @dev: device the buffer was used with |
* @size: requested buffer size |
* @addr: CPU address of buffer |
* @dma: DMA address of buffer |
* |
* This reclaims an I/O buffer, letting it be reused. The memory must have |
* been allocated using usb_buffer_alloc(), and the parameters must match |
* those provided in that allocation request. |
*/ |
void usb_buffer_free ( |
struct usb_device *dev, |
size_t size, |
void *addr, |
dma_addr_t dma |
) |
{ |
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) |
return; |
dev->bus->op->buffer_free (dev->bus, size, addr, dma); |
} |
/** |
* usb_buffer_map - create DMA mapping(s) for an urb |
* @urb: urb whose transfer_buffer/setup_packet will be mapped |
* |
* Return value is either null (indicating no buffer could be mapped), or |
* the parameter. URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are |
* added to urb->transfer_flags if the operation succeeds. If the device |
* is connected to this system through a non-DMA controller, this operation |
* always succeeds. |
* |
* This call would normally be used for an urb which is reused, perhaps |
* as the target of a large periodic transfer, with usb_buffer_dmasync() |
* calls to synchronize memory and dma state. |
* |
* Reverse the effect of this call with usb_buffer_unmap(). |
*/ |
struct urb *usb_buffer_map (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return 0; |
if (controller->dma_mask) { |
urb->transfer_dma = dma_map_single (controller, |
urb->transfer_buffer, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
urb->setup_dma = dma_map_single (controller, |
urb->setup_packet, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
// FIXME generic api broken like pci, can't report errors |
// if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; |
} else |
urb->transfer_dma = ~0; |
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP |
| URB_NO_SETUP_DMA_MAP); |
return urb; |
} |
/** |
* usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) |
* @urb: urb whose transfer_buffer/setup_packet will be synchronized |
*/ |
void usb_buffer_dmasync (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return; |
if (controller->dma_mask) { |
dma_sync_single (controller, |
urb->transfer_dma, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
dma_sync_single (controller, |
urb->setup_dma, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
} |
} |
/** |
* usb_buffer_unmap - free DMA mapping(s) for an urb |
* @urb: urb whose transfer_buffer will be unmapped |
* |
* Reverses the effect of usb_buffer_map(). |
*/ |
void usb_buffer_unmap (struct urb *urb) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!urb |
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) |
|| !urb->dev |
|| !(bus = urb->dev->bus) |
|| !(controller = bus->controller)) |
return; |
if (controller->dma_mask) { |
dma_unmap_single (controller, |
urb->transfer_dma, urb->transfer_buffer_length, |
usb_pipein (urb->pipe) |
? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
if (usb_pipecontrol (urb->pipe)) |
dma_unmap_single (controller, |
urb->setup_dma, |
sizeof (struct usb_ctrlrequest), |
DMA_TO_DEVICE); |
} |
urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP |
| URB_NO_SETUP_DMA_MAP); |
} |
/** |
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to map |
* @nents: the number of entries in the scatterlist |
* |
* Return value is either < 0 (indicating no buffers could be mapped), or |
* the number of DMA mapping array entries in the scatterlist. |
* |
* The caller is responsible for placing the resulting DMA addresses from |
* the scatterlist into URB transfer buffer pointers, and for setting the |
* URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. |
* |
* Top I/O rates come from queuing URBs, instead of waiting for each one |
* to complete before starting the next I/O. This is particularly easy |
* to do with scatterlists. Just allocate and submit one URB for each DMA |
* mapping entry returned, stopping on the first error or when all succeed. |
* Better yet, use the usb_sg_*() calls, which do that (and more) for you. |
* |
* This call would normally be used when translating scatterlist requests, |
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it |
* may be able to coalesce mappings for improved I/O efficiency. |
* |
* Reverse the effect of this call with usb_buffer_unmap_sg(). |
*/ |
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int nents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| usb_pipecontrol (pipe) |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return -1; |
// FIXME generic api broken like pci, can't report errors |
return dma_map_sg (controller, sg, nents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
/** |
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s) |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to synchronize |
* @n_hw_ents: the positive return value from usb_buffer_map_sg |
* |
* Use this when you are re-using a scatterlist's data buffers for |
* another USB request. |
*/ |
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int n_hw_ents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return; |
dma_sync_sg (controller, sg, n_hw_ents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
/** |
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist |
* @dev: device to which the scatterlist will be mapped |
* @pipe: endpoint defining the mapping direction |
* @sg: the scatterlist to unmap |
* @n_hw_ents: the positive return value from usb_buffer_map_sg |
* |
* Reverses the effect of usb_buffer_map_sg(). |
*/ |
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, |
struct scatterlist *sg, int n_hw_ents) |
{ |
struct usb_bus *bus; |
struct device *controller; |
if (!dev |
|| !(bus = dev->bus) |
|| !(controller = bus->controller) |
|| !controller->dma_mask) |
return; |
dma_unmap_sg (controller, sg, n_hw_ents, |
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); |
} |
static int usb_device_suspend(struct device *dev, u32 state) |
{ |
struct usb_interface *intf; |
struct usb_driver *driver; |
if ((dev->driver == NULL) || |
(dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
driver = to_usb_driver(dev->driver); |
if (driver->suspend) |
return driver->suspend(intf, state); |
return 0; |
} |
static int usb_device_resume(struct device *dev) |
{ |
struct usb_interface *intf; |
struct usb_driver *driver; |
if ((dev->driver == NULL) || |
(dev->driver == &usb_generic_driver) || |
(dev->driver_data == &usb_generic_driver_data)) |
return 0; |
intf = to_usb_interface(dev); |
driver = to_usb_driver(dev->driver); |
if (driver->resume) |
return driver->resume(intf); |
return 0; |
} |
struct bus_type usb_bus_type = { |
.name = "usb", |
.match = usb_device_match, |
.hotplug = usb_hotplug, |
.suspend = usb_device_suspend, |
.resume = usb_device_resume, |
}; |
#ifndef MODULE |
static int __init usb_setup_disable(char *str) |
{ |
nousb = 1; |
return 1; |
} |
/* format to disable USB on kernel command line is: nousb */ |
__setup("nousb", usb_setup_disable); |
#endif |
/* |
* for external read access to <nousb> |
*/ |
int usb_disabled(void) |
{ |
return nousb; |
} |
/* |
* Init |
*/ |
/*static*/ int __init usb_init(void) |
{ |
if (nousb) { |
info("USB support disabled\n"); |
return 0; |
} |
bus_register(&usb_bus_type); |
usb_host_init(); |
usb_major_init(); |
usbfs_init(); |
usb_hub_init(); |
driver_register(&usb_generic_driver); |
return 0; |
} |
/* |
* Cleanup |
*/ |
/*static*/ void __exit usb_exit(void) |
{ |
/* This will matter if shutdown/reboot does exitcalls. */ |
if (nousb) |
return; |
driver_unregister(&usb_generic_driver); |
// usb_major_cleanup(); |
// usbfs_cleanup(); |
// usb_hub_cleanup(); |
// usb_host_cleanup(); |
// bus_unregister(&usb_bus_type); |
} |
subsys_initcall(usb_init); |
module_exit(usb_exit); |
/* |
* USB may be built into the kernel or be built as modules. |
* These symbols are exported for device (or host controller) |
* driver modules to use. |
*/ |
EXPORT_SYMBOL(usb_epnum_to_ep_desc); |
EXPORT_SYMBOL(usb_register); |
EXPORT_SYMBOL(usb_deregister); |
EXPORT_SYMBOL(usb_disabled); |
EXPORT_SYMBOL(usb_alloc_dev); |
EXPORT_SYMBOL(usb_put_dev); |
EXPORT_SYMBOL(usb_get_dev); |
EXPORT_SYMBOL(usb_hub_tt_clear_buffer); |
EXPORT_SYMBOL(usb_driver_claim_interface); |
EXPORT_SYMBOL(usb_interface_claimed); |
EXPORT_SYMBOL(usb_driver_release_interface); |
EXPORT_SYMBOL(usb_match_id); |
EXPORT_SYMBOL(usb_find_interface); |
EXPORT_SYMBOL(usb_ifnum_to_if); |
EXPORT_SYMBOL(usb_reset_device); |
EXPORT_SYMBOL(usb_disconnect); |
EXPORT_SYMBOL(__usb_get_extra_descriptor); |
EXPORT_SYMBOL(usb_find_device); |
EXPORT_SYMBOL(usb_get_current_frame_number); |
EXPORT_SYMBOL (usb_buffer_alloc); |
EXPORT_SYMBOL (usb_buffer_free); |
EXPORT_SYMBOL (usb_buffer_map); |
EXPORT_SYMBOL (usb_buffer_dmasync); |
EXPORT_SYMBOL (usb_buffer_unmap); |
EXPORT_SYMBOL (usb_buffer_map_sg); |
EXPORT_SYMBOL (usb_buffer_dmasync_sg); |
EXPORT_SYMBOL (usb_buffer_unmap_sg); |
MODULE_LICENSE("GPL"); |
/shark/trunk/drivers/usb/host/ohci-mem.c |
---|
1,147 → 1,147 |
/* |
* OHCI HCD (Host Controller Driver) for USB. |
* |
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> |
* |
* This file is licenced under the GPL. |
*/ |
/*-------------------------------------------------------------------------*/ |
/* |
* There's basically three types of memory: |
* - data used only by the HCD ... kmalloc is fine |
* - async and periodic schedules, shared by HC and HCD ... these |
* need to use pci_pool or pci_alloc_consistent |
* - driver buffers, read/written by HC ... the hcd glue or the |
* device driver provides us with dma addresses |
* |
* There's also PCI "register" data, which is memory mapped. |
* No memory seen by this driver is pagable. |
*/ |
/*-------------------------------------------------------------------------*/ |
static struct usb_hcd *ohci_hcd_alloc (void) |
{ |
struct ohci_hcd *ohci; |
ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL); |
if (ohci != 0) { |
memset (ohci, 0, sizeof (struct ohci_hcd)); |
ohci->hcd.product_desc = "OHCI Host Controller"; |
return &ohci->hcd; |
} |
return 0; |
} |
static void ohci_hcd_free (struct usb_hcd *hcd) |
{ |
kfree (hcd_to_ohci (hcd)); |
} |
/*-------------------------------------------------------------------------*/ |
static int ohci_mem_init (struct ohci_hcd *ohci) |
{ |
ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev, |
sizeof (struct td), |
32 /* byte alignment */, |
0 /* no page-crossing issues */); |
if (!ohci->td_cache) |
return -ENOMEM; |
ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev, |
sizeof (struct ed), |
16 /* byte alignment */, |
0 /* no page-crossing issues */); |
if (!ohci->ed_cache) { |
pci_pool_destroy (ohci->td_cache); |
return -ENOMEM; |
} |
return 0; |
} |
static void ohci_mem_cleanup (struct ohci_hcd *ohci) |
{ |
if (ohci->td_cache) { |
pci_pool_destroy (ohci->td_cache); |
ohci->td_cache = 0; |
} |
if (ohci->ed_cache) { |
pci_pool_destroy (ohci->ed_cache); |
ohci->ed_cache = 0; |
} |
} |
/*-------------------------------------------------------------------------*/ |
/* ohci "done list" processing needs this mapping */ |
static inline struct td * |
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) |
{ |
struct td *td; |
td_dma &= TD_MASK; |
td = hc->td_hash [TD_HASH_FUNC(td_dma)]; |
while (td && td->td_dma != td_dma) |
td = td->td_hash; |
return td; |
} |
/* TDs ... */ |
static struct td * |
td_alloc (struct ohci_hcd *hc, int mem_flags) |
{ |
dma_addr_t dma; |
struct td *td; |
td = pci_pool_alloc (hc->td_cache, mem_flags, &dma); |
if (td) { |
/* in case hc fetches it, make it look dead */ |
memset (td, 0, sizeof *td); |
td->hwNextTD = cpu_to_le32 (dma); |
td->td_dma = dma; |
/* hashed in td_fill */ |
} |
return td; |
} |
static void |
td_free (struct ohci_hcd *hc, struct td *td) |
{ |
struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)]; |
while (*prev && *prev != td) |
prev = &(*prev)->td_hash; |
if (*prev) |
*prev = td->td_hash; |
else if ((td->hwINFO & TD_DONE) != 0) |
ohci_dbg (hc, "no hash for td %p\n", td); |
pci_pool_free (hc->td_cache, td, td->td_dma); |
} |
/*-------------------------------------------------------------------------*/ |
/* EDs ... */ |
static struct ed * |
ed_alloc (struct ohci_hcd *hc, int mem_flags) |
{ |
dma_addr_t dma; |
struct ed *ed; |
ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma); |
if (ed) { |
memset (ed, 0, sizeof (*ed)); |
INIT_LIST_HEAD (&ed->td_list); |
ed->dma = dma; |
} |
return ed; |
} |
static void |
ed_free (struct ohci_hcd *hc, struct ed *ed) |
{ |
pci_pool_free (hc->ed_cache, ed, ed->dma); |
} |
/* |
* OHCI HCD (Host Controller Driver) for USB. |
* |
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> |
* |
* This file is licenced under the GPL. |
*/ |
/*-------------------------------------------------------------------------*/ |
/* |
* There's basically three types of memory: |
* - data used only by the HCD ... kmalloc is fine |
* - async and periodic schedules, shared by HC and HCD ... these |
* need to use pci_pool or pci_alloc_consistent_usb |
* - driver buffers, read/written by HC ... the hcd glue or the |
* device driver provides us with dma addresses |
* |
* There's also PCI "register" data, which is memory mapped. |
* No memory seen by this driver is pagable. |
*/ |
/*-------------------------------------------------------------------------*/ |
static struct usb_hcd *ohci_hcd_alloc (void) |
{ |
struct ohci_hcd *ohci; |
ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL); |
if (ohci != 0) { |
memset (ohci, 0, sizeof (struct ohci_hcd)); |
ohci->hcd.product_desc = "OHCI Host Controller"; |
return &ohci->hcd; |
} |
return 0; |
} |
static void ohci_hcd_free (struct usb_hcd *hcd) |
{ |
kfree (hcd_to_ohci (hcd)); |
} |
/*-------------------------------------------------------------------------*/ |
static int ohci_mem_init (struct ohci_hcd *ohci) |
{ |
ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev, |
sizeof (struct td), |
32 /* byte alignment */, |
0 /* no page-crossing issues */); |
if (!ohci->td_cache) |
return -ENOMEM; |
ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev, |
sizeof (struct ed), |
16 /* byte alignment */, |
0 /* no page-crossing issues */); |
if (!ohci->ed_cache) { |
pci_pool_destroy (ohci->td_cache); |
return -ENOMEM; |
} |
return 0; |
} |
static void ohci_mem_cleanup (struct ohci_hcd *ohci) |
{ |
if (ohci->td_cache) { |
pci_pool_destroy (ohci->td_cache); |
ohci->td_cache = 0; |
} |
if (ohci->ed_cache) { |
pci_pool_destroy (ohci->ed_cache); |
ohci->ed_cache = 0; |
} |
} |
/*-------------------------------------------------------------------------*/ |
/* ohci "done list" processing needs this mapping */ |
static inline struct td * |
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) |
{ |
struct td *td; |
td_dma &= TD_MASK; |
td = hc->td_hash [TD_HASH_FUNC(td_dma)]; |
while (td && td->td_dma != td_dma) |
td = td->td_hash; |
return td; |
} |
/* TDs ... */ |
static struct td * |
td_alloc (struct ohci_hcd *hc, int mem_flags) |
{ |
dma_addr_t dma; |
struct td *td; |
td = pci_pool_alloc_usb (hc->td_cache, mem_flags, &dma); |
if (td) { |
/* in case hc fetches it, make it look dead */ |
memset (td, 0, sizeof *td); |
td->hwNextTD = cpu_to_le32 (dma); |
td->td_dma = dma; |
/* hashed in td_fill */ |
} |
return td; |
} |
static void |
td_free (struct ohci_hcd *hc, struct td *td) |
{ |
struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)]; |
while (*prev && *prev != td) |
prev = &(*prev)->td_hash; |
if (*prev) |
*prev = td->td_hash; |
else if ((td->hwINFO & TD_DONE) != 0) |
ohci_dbg (hc, "no hash for td %p\n", td); |
pci_pool_free (hc->td_cache, td, td->td_dma); |
} |
/*-------------------------------------------------------------------------*/ |
/* EDs ... */ |
static struct ed * |
ed_alloc (struct ohci_hcd *hc, int mem_flags) |
{ |
dma_addr_t dma; |
struct ed *ed; |
ed = pci_pool_alloc_usb (hc->ed_cache, mem_flags, &dma); |
if (ed) { |
memset (ed, 0, sizeof (*ed)); |
INIT_LIST_HEAD (&ed->td_list); |
ed->dma = dma; |
} |
return ed; |
} |
static void |
ed_free (struct ohci_hcd *hc, struct ed *ed) |
{ |
pci_pool_free (hc->ed_cache, ed, ed->dma); |
} |
/shark/trunk/drivers/usb/host/ehci-hcd.c |
---|
121,8 → 121,10 |
/* Initial IRQ latency: lower than default */ |
static int log2_irq_thresh = 0; // 0 to 6 |
module_param (log2_irq_thresh, int, S_IRUGO); |
/*module_param (log2_irq_thresh, int, S_IRUGO); |
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); |
*/ |
#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) |
483,7 → 485,7 |
* involved with the root hub. |
*/ |
ehci->reboot_notifier.notifier_call = ehci_reboot; |
register_reboot_notifier (&ehci->reboot_notifier); |
// register_reboot_notifier (&ehci->reboot_notifier); |
ehci->hcd.state = USB_STATE_RUNNING; |
writel (FLAG_CF, &ehci->regs->configured_flag); |
540,7 → 542,7 |
/* let companion controllers work when we aren't */ |
writel (0, &ehci->regs->configured_flag); |
unregister_reboot_notifier (&ehci->reboot_notifier); |
// unregister_reboot_notifier (&ehci->reboot_notifier); |
remove_debug_files (ehci); |
1011,7 → 1013,7 |
MODULE_AUTHOR (DRIVER_AUTHOR); |
MODULE_LICENSE ("GPL"); |
static int __init init (void) |
/*static*/ int __init ehci_hcd_init (void) |
{ |
if (usb_disabled()) |
return -ENODEV; |
1023,10 → 1025,10 |
return pci_module_init (&ehci_pci_driver); |
} |
module_init (init); |
//module_init (init); |
static void __exit cleanup (void) |
/*static*/ void __exit ehci_hcd_cleanup (void) |
{ |
pci_unregister_driver (&ehci_pci_driver); |
} |
module_exit (cleanup); |
//module_exit (cleanup); |
/shark/trunk/drivers/usb/host/ehci.h |
---|
134,7 → 134,7 |
t = EHCI_SHRINK_JIFFIES; |
break; |
} |
t += jiffies; |
t += jiffies26; |
// all timings except IAA watchdog can be overridden. |
// async queue SHRINK often precedes IAA. while it's ready |
// to go OFF neither can matter, and afterwards the IO |
/shark/trunk/drivers/usb/host/ohci-hcd.c |
---|
1,691 → 1,691 |
/* |
* OHCI HCD (Host Controller Driver) for USB. |
* |
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> |
* |
* [ Initialisation is based on Linus' ] |
* [ uhci code and gregs ohci fragments ] |
* [ (C) Copyright 1999 Linus Torvalds ] |
* [ (C) Copyright 1999 Gregory P. Smith] |
* |
* |
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller |
* interfaces (though some non-x86 Intel chips use it). It supports |
* smarter hardware than UHCI. A download link for the spec available |
* through the http://www.usb.org website. |
* |
* History: |
* |
* 2003/02/24 show registers in sysfs (Kevin Brosius) |
* |
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and |
* bandwidth accounting; if debugging, show schedules in driverfs |
* 2002/07/19 fixes to management of ED and schedule state. |
* 2002/06/09 SA-1111 support (Christopher Hoover) |
* 2002/06/01 remember frame when HC won't see EDs any more; use that info |
* to fix urb unlink races caused by interrupt latency assumptions; |
* minor ED field and function naming updates |
* 2002/01/18 package as a patch for 2.5.3; this should match the |
* 2.4.17 kernel modulo some bugs being fixed. |
* |
* 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes |
* from post-2.4.5 patches. |
* 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning |
* 2001/09/07 match PCI PM changes, errnos from Linus' tree |
* 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify; |
* pbook pci quirks gone (please fix pbook pci sw!) (db) |
* |
* 2001/04/08 Identify version on module load (gb) |
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); |
pci_map_single (db) |
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db) |
* 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam) |
* |
* 2000/09/26 fixed races in removing the private portion of the urb |
* 2000/09/07 disable bulk and control lists when unlinking the last |
* endpoint descriptor in order to avoid unrecoverable errors on |
* the Lucent chips. (rwc@sgi) |
* 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some |
* urb unlink probs, indentation fixes |
* 2000/08/11 various oops fixes mostly affecting iso and cleanup from |
* device unplugs. |
* 2000/06/28 use PCI hotplug framework, for better power management |
* and for Cardbus support (David Brownell) |
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling |
* when the controller loses power; handle UE; cleanup; ... |
* |
* v5.2 1999/12/07 URB 3rd preview, |
* v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) |
* v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume |
* i386: HUB, Keyboard, Mouse, Printer |
* |
* v4.3 1999/10/27 multiple HCs, bulk_request |
* v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes |
* v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. |
* v4.0 1999/08/18 |
* v3.0 1999/06/25 |
* v2.1 1999/05/09 code clean up |
* v2.0 1999/05/04 |
* v1.0 1999/04/27 initial release |
* |
* This file is licenced under the GPL. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#ifdef CONFIG_USB_DEBUG |
# define DEBUG |
#else |
# undef DEBUG |
#endif |
#include <linux/module.h> |
#include <linux/pci.h> |
#include <linux/kernel.h> |
#include <linux/delay.h> |
#include <linux/ioport.h> |
#include <linux/sched.h> |
#include <linux/slab.h> |
#include <linux/smp_lock.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/timer.h> |
#include <linux/list.h> |
#include <linux/interrupt.h> /* for in_interrupt () */ |
#include <linux/usb.h> |
#include "../core/hcd.h" |
#include <asm/io.h> |
#include <asm/irq.h> |
#include <asm/system.h> |
#include <asm/unaligned.h> |
#include <asm/byteorder.h> |
#define DRIVER_VERSION "2003 Oct 13" |
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" |
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" |
/*-------------------------------------------------------------------------*/ |
//#define OHCI_VERBOSE_DEBUG /* not always helpful */ |
/* For initializing controller (mask in an HCFS mode too) */ |
#define OHCI_CONTROL_INIT \ |
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE |
#define OHCI_UNLINK_TIMEOUT (HZ / 10) |
/*-------------------------------------------------------------------------*/ |
static const char hcd_name [] = "ohci_hcd"; |
#include "ohci.h" |
static inline void disable (struct ohci_hcd *ohci) |
{ |
ohci->hcd.state = USB_STATE_HALT; |
} |
#include "ohci-hub.c" |
#include "ohci-dbg.c" |
#include "ohci-mem.c" |
#include "ohci-q.c" |
/*-------------------------------------------------------------------------*/ |
/* |
* queue up an urb for anything except the root hub |
*/ |
static int ohci_urb_enqueue ( |
struct usb_hcd *hcd, |
struct urb *urb, |
int mem_flags |
) { |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
struct ed *ed; |
urb_priv_t *urb_priv; |
unsigned int pipe = urb->pipe; |
int i, size = 0; |
unsigned long flags; |
int retval = 0; |
#ifdef OHCI_VERBOSE_DEBUG |
urb_print (urb, "SUB", usb_pipein (pipe)); |
#endif |
/* every endpoint has a ed, locate and maybe (re)initialize it */ |
if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval))) |
return -ENOMEM; |
/* for the private part of the URB we need the number of TDs (size) */ |
switch (ed->type) { |
case PIPE_CONTROL: |
/* td_submit_urb() doesn't yet handle these */ |
if (urb->transfer_buffer_length > 4096) |
return -EMSGSIZE; |
/* 1 TD for setup, 1 for ACK, plus ... */ |
size = 2; |
/* FALLTHROUGH */ |
// case PIPE_INTERRUPT: |
// case PIPE_BULK: |
default: |
/* one TD for every 4096 Bytes (can be upto 8K) */ |
size += urb->transfer_buffer_length / 4096; |
/* ... and for any remaining bytes ... */ |
if ((urb->transfer_buffer_length % 4096) != 0) |
size++; |
/* ... and maybe a zero length packet to wrap it up */ |
if (size == 0) |
size++; |
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 |
&& (urb->transfer_buffer_length |
% usb_maxpacket (urb->dev, pipe, |
usb_pipeout (pipe))) == 0) |
size++; |
break; |
case PIPE_ISOCHRONOUS: /* number of packets from URB */ |
size = urb->number_of_packets; |
break; |
} |
/* allocate the private part of the URB */ |
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), |
mem_flags); |
if (!urb_priv) |
return -ENOMEM; |
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); |
/* fill the private part of the URB */ |
urb_priv->length = size; |
urb_priv->ed = ed; |
/* allocate the TDs (deferring hash chain updates) */ |
for (i = 0; i < size; i++) { |
urb_priv->td [i] = td_alloc (ohci, mem_flags); |
if (!urb_priv->td [i]) { |
urb_priv->length = i; |
urb_free_priv (ohci, urb_priv); |
return -ENOMEM; |
} |
} |
spin_lock_irqsave (&ohci->lock, flags); |
/* don't submit to a dead HC */ |
if (!HCD_IS_RUNNING(ohci->hcd.state)) { |
retval = -ENODEV; |
goto fail; |
} |
/* schedule the ed if needed */ |
if (ed->state == ED_IDLE) { |
retval = ed_schedule (ohci, ed); |
if (retval < 0) |
goto fail; |
if (ed->type == PIPE_ISOCHRONOUS) { |
u16 frame = le16_to_cpu (ohci->hcca->frame_no); |
/* delay a few frames before the first TD */ |
frame += max_t (u16, 8, ed->interval); |
frame &= ~(ed->interval - 1); |
frame |= ed->branch; |
urb->start_frame = frame; |
/* yes, only URB_ISO_ASAP is supported, and |
* urb->start_frame is never used as input. |
*/ |
} |
} else if (ed->type == PIPE_ISOCHRONOUS) |
urb->start_frame = ed->last_iso + ed->interval; |
/* fill the TDs and link them to the ed; and |
* enable that part of the schedule, if needed |
* and update count of queued periodic urbs |
*/ |
urb->hcpriv = urb_priv; |
td_submit_urb (ohci, urb); |
fail: |
if (retval) |
urb_free_priv (ohci, urb_priv); |
spin_unlock_irqrestore (&ohci->lock, flags); |
return retval; |
} |
/* |
* decouple the URB from the HC queues (TDs, urb_priv); it's |
* already marked using urb->status. reporting is always done |
* asynchronously, and we might be dealing with an urb that's |
* partially transferred, or an ED with other urbs being unlinked. |
*/ |
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
unsigned long flags; |
#ifdef OHCI_VERBOSE_DEBUG |
urb_print (urb, "UNLINK", 1); |
#endif |
spin_lock_irqsave (&ohci->lock, flags); |
if (HCD_IS_RUNNING(ohci->hcd.state)) { |
urb_priv_t *urb_priv; |
/* Unless an IRQ completed the unlink while it was being |
* handed to us, flag it for unlink and giveback, and force |
* some upcoming INTR_SF to call finish_unlinks() |
*/ |
urb_priv = urb->hcpriv; |
if (urb_priv) { |
if (urb_priv->ed->state == ED_OPER) |
start_urb_unlink (ohci, urb_priv->ed); |
} |
} else { |
/* |
* with HC dead, we won't respect hc queue pointers |
* any more ... just clean up every urb's memory. |
*/ |
if (urb->hcpriv) { |
spin_unlock (&ohci->lock); |
finish_urb (ohci, urb, NULL); |
spin_lock (&ohci->lock); |
} |
} |
spin_unlock_irqrestore (&ohci->lock, flags); |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
/* frees config/altsetting state for endpoints, |
* including ED memory, dummy TD, and bulk/intr data toggle |
*/ |
static void |
ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
int epnum = ep & USB_ENDPOINT_NUMBER_MASK; |
unsigned long flags; |
struct ed *ed; |
unsigned limit = 1000; |
/* ASSERT: any requests/urbs are being unlinked */ |
/* ASSERT: nobody can be submitting urbs for this any more */ |
epnum <<= 1; |
if (epnum != 0 && !(ep & USB_DIR_IN)) |
epnum |= 1; |
rescan: |
spin_lock_irqsave (&ohci->lock, flags); |
ed = dev->ep [epnum]; |
if (!ed) |
goto done; |
if (!HCD_IS_RUNNING (ohci->hcd.state)) |
ed->state = ED_IDLE; |
switch (ed->state) { |
case ED_UNLINK: /* wait for hw to finish? */ |
/* major IRQ delivery trouble loses INTR_SF too... */ |
WARN_ON (limit-- == 0); |
spin_unlock_irqrestore (&ohci->lock, flags); |
set_current_state (TASK_UNINTERRUPTIBLE); |
schedule_timeout (1); |
goto rescan; |
case ED_IDLE: /* fully unlinked */ |
if (list_empty (&ed->td_list)) { |
td_free (ohci, ed->dummy); |
ed_free (ohci, ed); |
break; |
} |
/* else FALL THROUGH */ |
default: |
/* caller was supposed to have unlinked any requests; |
* that's not our job. can't recover; must leak ed. |
*/ |
ohci_err (ohci, "leak ed %p (#%d) state %d%s\n", |
ed, epnum, ed->state, |
list_empty (&ed->td_list) ? "" : " (has tds)"); |
td_free (ohci, ed->dummy); |
break; |
} |
dev->ep [epnum] = 0; |
done: |
spin_unlock_irqrestore (&ohci->lock, flags); |
return; |
} |
static int ohci_get_frame (struct usb_hcd *hcd) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
return le16_to_cpu (ohci->hcca->frame_no); |
} |
/*-------------------------------------------------------------------------* |
* HC functions |
*-------------------------------------------------------------------------*/ |
/* reset the HC and BUS */ |
static int hc_reset (struct ohci_hcd *ohci) |
{ |
u32 temp; |
/* SMM owns the HC? not for long! |
* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. |
*/ |
#ifndef __hppa__ |
if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { |
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); |
/* this timeout is arbitrary. we make it long, so systems |
* depending on usb keyboards may be usable even if the |
* BIOS/SMM code seems pretty broken. |
*/ |
temp = 500; /* arbitrary: five seconds */ |
writel (OHCI_INTR_OC, &ohci->regs->intrenable); |
writel (OHCI_OCR, &ohci->regs->cmdstatus); |
while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { |
wait_ms (10); |
if (--temp == 0) { |
ohci_err (ohci, "USB HC TakeOver failed!\n"); |
return -1; |
} |
} |
} |
#endif |
/* Disable HC interrupts */ |
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); |
ohci_dbg (ohci, "reset, control = 0x%x\n", |
readl (&ohci->regs->control)); |
/* Reset USB (needed by some controllers); RemoteWakeupConnected |
* saved if boot firmware (BIOS/SMM/...) told us it's connected |
*/ |
ohci->hc_control = readl (&ohci->regs->control); |
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ |
writel (ohci->hc_control, &ohci->regs->control); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
wait_ms (50); |
/* HC Reset requires max 10 us delay */ |
writel (OHCI_HCR, &ohci->regs->cmdstatus); |
temp = 30; /* ... allow extra time */ |
while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { |
if (--temp == 0) { |
ohci_err (ohci, "USB HC reset timed out!\n"); |
return -1; |
} |
udelay (1); |
} |
/* now we're in the SUSPEND state ... must go OPERATIONAL |
* within 2msec else HC enters RESUME |
* |
* ... but some hardware won't init fmInterval "by the book" |
* (SiS, OPTi ...), so reset again instead. SiS doesn't need |
* this if we write fmInterval after we're OPERATIONAL. |
*/ |
writel (ohci->hc_control, &ohci->regs->control); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
#define FI 0x2edf /* 12000 bits per frame (-1) */ |
#define LSTHRESH 0x628 /* lowspeed bit threshold */ |
/* Start an OHCI controller, set the BUS operational |
* enable interrupts |
* connect the virtual root hub |
*/ |
static int hc_start (struct ohci_hcd *ohci) |
{ |
u32 mask, tmp; |
struct usb_device *udev; |
struct usb_bus *bus; |
spin_lock_init (&ohci->lock); |
disable (ohci); |
/* Tell the controller where the control and bulk lists are |
* The lists are empty now. */ |
writel (0, &ohci->regs->ed_controlhead); |
writel (0, &ohci->regs->ed_bulkhead); |
/* a reset clears this */ |
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); |
/* force default fmInterval (we won't adjust it); init thresholds |
* for last FS and LS packets, reserve 90% for periodic. |
*/ |
writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval); |
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); |
writel (LSTHRESH, &ohci->regs->lsthresh); |
/* some OHCI implementations are finicky about how they init. |
* bogus values here mean not even enumeration could work. |
*/ |
if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 |
|| !readl (&ohci->regs->periodicstart)) { |
ohci_err (ohci, "init err\n"); |
return -EOVERFLOW; |
} |
/* start controller operations */ |
ohci->hc_control &= OHCI_CTRL_RWC; |
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; |
writel (ohci->hc_control, &ohci->regs->control); |
ohci->hcd.state = USB_STATE_RUNNING; |
/* Choose the interrupts we care about now, others later on demand */ |
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH; |
writel (mask, &ohci->regs->intrstatus); |
writel (mask, &ohci->regs->intrenable); |
/* handle root hub init quirks ... */ |
tmp = roothub_a (ohci); |
tmp &= ~(RH_A_PSM | RH_A_OCPM); |
if (ohci->flags & OHCI_QUIRK_SUPERIO) { |
/* NSC 87560 and maybe others */ |
tmp |= RH_A_NOCP; |
tmp &= ~(RH_A_POTPGT | RH_A_NPS); |
} else { |
/* hub power always on; required for AMD-756 and some |
* Mac platforms, use this mode everywhere by default |
*/ |
tmp |= RH_A_NPS; |
} |
writel (tmp, &ohci->regs->roothub.a); |
writel (RH_HS_LPSC, &ohci->regs->roothub.status); |
writel (0, &ohci->regs->roothub.b); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
// POTPGT delay is bits 24-31, in 2 ms units. |
mdelay ((roothub_a (ohci) >> 23) & 0x1fe); |
/* connect the virtual root hub */ |
bus = hcd_to_bus (&ohci->hcd); |
bus->root_hub = udev = usb_alloc_dev (NULL, bus); |
ohci->hcd.state = USB_STATE_RUNNING; |
if (!udev) { |
disable (ohci); |
ohci->hc_control &= ~OHCI_CTRL_HCFS; |
writel (ohci->hc_control, &ohci->regs->control); |
return -ENOMEM; |
} |
udev->speed = USB_SPEED_FULL; |
if (hcd_register_root (&ohci->hcd) != 0) { |
usb_put_dev (udev); |
bus->root_hub = NULL; |
disable (ohci); |
ohci->hc_control &= ~OHCI_CTRL_HCFS; |
writel (ohci->hc_control, &ohci->regs->control); |
return -ENODEV; |
} |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
/* an interrupt happens */ |
static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
struct ohci_regs *regs = ohci->regs; |
int ints; |
/* we can eliminate a (slow) readl() if _only_ WDH caused this irq */ |
if ((ohci->hcca->done_head != 0) |
&& ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { |
ints = OHCI_INTR_WDH; |
/* cardbus/... hardware gone before remove() */ |
} else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { |
disable (ohci); |
ohci_dbg (ohci, "device removed!\n"); |
return; |
/* interrupt for some other device? */ |
} else if ((ints &= readl (®s->intrenable)) == 0) { |
return; |
} |
if (ints & OHCI_INTR_UE) { |
disable (ohci); |
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); |
// e.g. due to PCI Master/Target Abort |
ohci_dump (ohci, 1); |
hc_reset (ohci); |
} |
if (ints & OHCI_INTR_WDH) { |
if (HCD_IS_RUNNING(hcd->state)) |
writel (OHCI_INTR_WDH, ®s->intrdisable); |
dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs); |
if (HCD_IS_RUNNING(hcd->state)) |
writel (OHCI_INTR_WDH, ®s->intrenable); |
} |
/* could track INTR_SO to reduce available PCI/... bandwidth */ |
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled |
* when there's still unlinking to be done (next frame). |
*/ |
spin_lock (&ohci->lock); |
if (ohci->ed_rm_list) |
finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no), |
ptregs); |
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list |
&& HCD_IS_RUNNING(ohci->hcd.state)) |
writel (OHCI_INTR_SF, ®s->intrdisable); |
spin_unlock (&ohci->lock); |
if (HCD_IS_RUNNING(ohci->hcd.state)) { |
writel (ints, ®s->intrstatus); |
writel (OHCI_INTR_MIE, ®s->intrenable); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
} |
} |
/*-------------------------------------------------------------------------*/ |
static void ohci_stop (struct usb_hcd *hcd) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", |
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), |
ohci->hcd.state); |
ohci_dump (ohci, 1); |
if (HCD_IS_RUNNING(ohci->hcd.state)) |
hc_reset (ohci); |
remove_debug_files (ohci); |
ohci_mem_cleanup (ohci); |
if (ohci->hcca) { |
pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca, |
ohci->hcca, ohci->hcca_dma); |
ohci->hcca = NULL; |
ohci->hcca_dma = 0; |
} |
} |
/*-------------------------------------------------------------------------*/ |
// FIXME: this restart logic should be generic, |
// and handle full hcd state cleanup |
/* controller died; cleanup debris, then restart */ |
/* must not be called from interrupt context */ |
#ifdef CONFIG_PM |
static int hc_restart (struct ohci_hcd *ohci) |
{ |
int temp; |
int i; |
disable (ohci); |
if (hcd_to_bus (&ohci->hcd)->root_hub) |
usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub); |
/* empty the interrupt branches */ |
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; |
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; |
/* no EDs to remove */ |
ohci->ed_rm_list = NULL; |
/* empty control and bulk lists */ |
ohci->ed_controltail = NULL; |
ohci->ed_bulktail = NULL; |
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { |
ohci_err (ohci, "can't restart, %d\n", temp); |
return temp; |
} else |
ohci_dbg (ohci, "restart complete\n"); |
return 0; |
} |
#endif |
/*-------------------------------------------------------------------------*/ |
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC |
MODULE_AUTHOR (DRIVER_AUTHOR); |
MODULE_DESCRIPTION (DRIVER_INFO); |
MODULE_LICENSE ("GPL"); |
#ifdef CONFIG_PCI |
#include "ohci-pci.c" |
#endif |
#ifdef CONFIG_SA1111 |
#include "ohci-sa1111.c" |
#endif |
#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111)) |
#error "missing bus glue for ohci-hcd" |
#endif |
/* |
* OHCI HCD (Host Controller Driver) for USB. |
* |
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> |
* |
* [ Initialisation is based on Linus' ] |
* [ uhci code and gregs ohci fragments ] |
* [ (C) Copyright 1999 Linus Torvalds ] |
* [ (C) Copyright 1999 Gregory P. Smith] |
* |
* |
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller |
* interfaces (though some non-x86 Intel chips use it). It supports |
* smarter hardware than UHCI. A download link for the spec available |
* through the http://www.usb.org website. |
* |
* History: |
* |
* 2003/02/24 show registers in sysfs (Kevin Brosius) |
* |
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and |
* bandwidth accounting; if debugging, show schedules in driverfs |
* 2002/07/19 fixes to management of ED and schedule state. |
* 2002/06/09 SA-1111 support (Christopher Hoover) |
* 2002/06/01 remember frame when HC won't see EDs any more; use that info |
* to fix urb unlink races caused by interrupt latency assumptions; |
* minor ED field and function naming updates |
* 2002/01/18 package as a patch for 2.5.3; this should match the |
* 2.4.17 kernel modulo some bugs being fixed. |
* |
* 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes |
* from post-2.4.5 patches. |
* 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning |
* 2001/09/07 match PCI PM changes, errnos from Linus' tree |
* 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify; |
* pbook pci quirks gone (please fix pbook pci sw!) (db) |
* |
* 2001/04/08 Identify version on module load (gb) |
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); |
pci_map_single (db) |
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db) |
* 2001/03/07 hcca allocation uses pci_alloc_consistent_usb (Steve Longerbeam) |
* |
* 2000/09/26 fixed races in removing the private portion of the urb |
* 2000/09/07 disable bulk and control lists when unlinking the last |
* endpoint descriptor in order to avoid unrecoverable errors on |
* the Lucent chips. (rwc@sgi) |
* 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some |
* urb unlink probs, indentation fixes |
* 2000/08/11 various oops fixes mostly affecting iso and cleanup from |
* device unplugs. |
* 2000/06/28 use PCI hotplug framework, for better power management |
* and for Cardbus support (David Brownell) |
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling |
* when the controller loses power; handle UE; cleanup; ... |
* |
* v5.2 1999/12/07 URB 3rd preview, |
* v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) |
* v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume |
* i386: HUB, Keyboard, Mouse, Printer |
* |
* v4.3 1999/10/27 multiple HCs, bulk_request |
* v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes |
* v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. |
* v4.0 1999/08/18 |
* v3.0 1999/06/25 |
* v2.1 1999/05/09 code clean up |
* v2.0 1999/05/04 |
* v1.0 1999/04/27 initial release |
* |
* This file is licenced under the GPL. |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#ifdef CONFIG_USB_DEBUG |
# define DEBUG |
#else |
# undef DEBUG |
#endif |
#include <linux/module.h> |
#include <linux/pci.h> |
#include <linux/kernel.h> |
#include <linux/delay.h> |
#include <linux/ioport.h> |
#include <linux/sched.h> |
#include <linux/slab.h> |
#include <linux/smp_lock.h> |
#include <linux/errno.h> |
#include <linux/init.h> |
#include <linux/timer.h> |
#include <linux/list.h> |
#include <linux/interrupt.h> /* for in_interrupt () */ |
#include <linux/usb.h> |
#include "../core/hcd.h" |
#include <asm/io.h> |
#include <asm/irq.h> |
#include <asm/system.h> |
#include <asm/unaligned.h> |
#include <asm/byteorder.h> |
#define DRIVER_VERSION "2003 Oct 13" |
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" |
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" |
/*-------------------------------------------------------------------------*/ |
//#define OHCI_VERBOSE_DEBUG /* not always helpful */ |
/* For initializing controller (mask in an HCFS mode too) */ |
#define OHCI_CONTROL_INIT \ |
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE |
#define OHCI_UNLINK_TIMEOUT (HZ / 10) |
/*-------------------------------------------------------------------------*/ |
static const char hcd_name [] = "ohci_hcd"; |
#include "ohci.h" |
static inline void disable (struct ohci_hcd *ohci) |
{ |
ohci->hcd.state = USB_STATE_HALT; |
} |
#include "ohci-hub.c" |
#include "ohci-dbg.c" |
#include "ohci-mem.c" |
#include "ohci-q.c" |
/*-------------------------------------------------------------------------*/ |
/* |
* queue up an urb for anything except the root hub |
*/ |
static int ohci_urb_enqueue ( |
struct usb_hcd *hcd, |
struct urb *urb, |
int mem_flags |
) { |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
struct ed *ed; |
urb_priv_t *urb_priv; |
unsigned int pipe = urb->pipe; |
int i, size = 0; |
unsigned long flags; |
int retval = 0; |
#ifdef OHCI_VERBOSE_DEBUG |
urb_print (urb, "SUB", usb_pipein (pipe)); |
#endif |
/* every endpoint has a ed, locate and maybe (re)initialize it */ |
if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval))) |
return -ENOMEM; |
/* for the private part of the URB we need the number of TDs (size) */ |
switch (ed->type) { |
case PIPE_CONTROL: |
/* td_submit_urb() doesn't yet handle these */ |
if (urb->transfer_buffer_length > 4096) |
return -EMSGSIZE; |
/* 1 TD for setup, 1 for ACK, plus ... */ |
size = 2; |
/* FALLTHROUGH */ |
// case PIPE_INTERRUPT: |
// case PIPE_BULK: |
default: |
/* one TD for every 4096 Bytes (can be upto 8K) */ |
size += urb->transfer_buffer_length / 4096; |
/* ... and for any remaining bytes ... */ |
if ((urb->transfer_buffer_length % 4096) != 0) |
size++; |
/* ... and maybe a zero length packet to wrap it up */ |
if (size == 0) |
size++; |
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 |
&& (urb->transfer_buffer_length |
% usb_maxpacket (urb->dev, pipe, |
usb_pipeout (pipe))) == 0) |
size++; |
break; |
case PIPE_ISOCHRONOUS: /* number of packets from URB */ |
size = urb->number_of_packets; |
break; |
} |
/* allocate the private part of the URB */ |
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), |
mem_flags); |
if (!urb_priv) |
return -ENOMEM; |
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); |
/* fill the private part of the URB */ |
urb_priv->length = size; |
urb_priv->ed = ed; |
/* allocate the TDs (deferring hash chain updates) */ |
for (i = 0; i < size; i++) { |
urb_priv->td [i] = td_alloc (ohci, mem_flags); |
if (!urb_priv->td [i]) { |
urb_priv->length = i; |
urb_free_priv (ohci, urb_priv); |
return -ENOMEM; |
} |
} |
spin_lock_irqsave (&ohci->lock, flags); |
/* don't submit to a dead HC */ |
if (!HCD_IS_RUNNING(ohci->hcd.state)) { |
retval = -ENODEV; |
goto fail; |
} |
/* schedule the ed if needed */ |
if (ed->state == ED_IDLE) { |
retval = ed_schedule (ohci, ed); |
if (retval < 0) |
goto fail; |
if (ed->type == PIPE_ISOCHRONOUS) { |
u16 frame = le16_to_cpu (ohci->hcca->frame_no); |
/* delay a few frames before the first TD */ |
frame += max_t (u16, 8, ed->interval); |
frame &= ~(ed->interval - 1); |
frame |= ed->branch; |
urb->start_frame = frame; |
/* yes, only URB_ISO_ASAP is supported, and |
* urb->start_frame is never used as input. |
*/ |
} |
} else if (ed->type == PIPE_ISOCHRONOUS) |
urb->start_frame = ed->last_iso + ed->interval; |
/* fill the TDs and link them to the ed; and |
* enable that part of the schedule, if needed |
* and update count of queued periodic urbs |
*/ |
urb->hcpriv = urb_priv; |
td_submit_urb (ohci, urb); |
fail: |
if (retval) |
urb_free_priv (ohci, urb_priv); |
spin_unlock_irqrestore (&ohci->lock, flags); |
return retval; |
} |
/* |
* decouple the URB from the HC queues (TDs, urb_priv); it's |
* already marked using urb->status. reporting is always done |
* asynchronously, and we might be dealing with an urb that's |
* partially transferred, or an ED with other urbs being unlinked. |
*/ |
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
unsigned long flags; |
#ifdef OHCI_VERBOSE_DEBUG |
urb_print (urb, "UNLINK", 1); |
#endif |
spin_lock_irqsave (&ohci->lock, flags); |
if (HCD_IS_RUNNING(ohci->hcd.state)) { |
urb_priv_t *urb_priv; |
/* Unless an IRQ completed the unlink while it was being |
* handed to us, flag it for unlink and giveback, and force |
* some upcoming INTR_SF to call finish_unlinks() |
*/ |
urb_priv = urb->hcpriv; |
if (urb_priv) { |
if (urb_priv->ed->state == ED_OPER) |
start_urb_unlink (ohci, urb_priv->ed); |
} |
} else { |
/* |
* with HC dead, we won't respect hc queue pointers |
* any more ... just clean up every urb's memory. |
*/ |
if (urb->hcpriv) { |
spin_unlock (&ohci->lock); |
finish_urb (ohci, urb, NULL); |
spin_lock (&ohci->lock); |
} |
} |
spin_unlock_irqrestore (&ohci->lock, flags); |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
/* frees config/altsetting state for endpoints, |
* including ED memory, dummy TD, and bulk/intr data toggle |
*/ |
static void |
ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
int epnum = ep & USB_ENDPOINT_NUMBER_MASK; |
unsigned long flags; |
struct ed *ed; |
unsigned limit = 1000; |
/* ASSERT: any requests/urbs are being unlinked */ |
/* ASSERT: nobody can be submitting urbs for this any more */ |
epnum <<= 1; |
if (epnum != 0 && !(ep & USB_DIR_IN)) |
epnum |= 1; |
rescan: |
spin_lock_irqsave (&ohci->lock, flags); |
ed = dev->ep [epnum]; |
if (!ed) |
goto done; |
if (!HCD_IS_RUNNING (ohci->hcd.state)) |
ed->state = ED_IDLE; |
switch (ed->state) { |
case ED_UNLINK: /* wait for hw to finish? */ |
/* major IRQ delivery trouble loses INTR_SF too... */ |
WARN_ON (limit-- == 0); |
spin_unlock_irqrestore (&ohci->lock, flags); |
set_current_state (TASK_UNINTERRUPTIBLE); |
schedule_timeout (1); |
goto rescan; |
case ED_IDLE: /* fully unlinked */ |
if (list_empty (&ed->td_list)) { |
td_free (ohci, ed->dummy); |
ed_free (ohci, ed); |
break; |
} |
/* else FALL THROUGH */ |
default: |
/* caller was supposed to have unlinked any requests; |
* that's not our job. can't recover; must leak ed. |
*/ |
ohci_err (ohci, "leak ed %p (#%d) state %d%s\n", |
ed, epnum, ed->state, |
list_empty (&ed->td_list) ? "" : " (has tds)"); |
td_free (ohci, ed->dummy); |
break; |
} |
dev->ep [epnum] = 0; |
done: |
spin_unlock_irqrestore (&ohci->lock, flags); |
return; |
} |
static int ohci_get_frame (struct usb_hcd *hcd) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
return le16_to_cpu (ohci->hcca->frame_no); |
} |
/*-------------------------------------------------------------------------* |
* HC functions |
*-------------------------------------------------------------------------*/ |
/* reset the HC and BUS */ |
static int hc_reset (struct ohci_hcd *ohci) |
{ |
u32 temp; |
/* SMM owns the HC? not for long! |
* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. |
*/ |
#ifndef __hppa__ |
if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { |
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); |
/* this timeout is arbitrary. we make it long, so systems |
* depending on usb keyboards may be usable even if the |
* BIOS/SMM code seems pretty broken. |
*/ |
temp = 500; /* arbitrary: five seconds */ |
writel (OHCI_INTR_OC, &ohci->regs->intrenable); |
writel (OHCI_OCR, &ohci->regs->cmdstatus); |
while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { |
wait_ms (10); |
if (--temp == 0) { |
ohci_err (ohci, "USB HC TakeOver failed!\n"); |
return -1; |
} |
} |
} |
#endif |
/* Disable HC interrupts */ |
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); |
ohci_dbg (ohci, "reset, control = 0x%x\n", |
readl (&ohci->regs->control)); |
/* Reset USB (needed by some controllers); RemoteWakeupConnected |
* saved if boot firmware (BIOS/SMM/...) told us it's connected |
*/ |
ohci->hc_control = readl (&ohci->regs->control); |
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ |
writel (ohci->hc_control, &ohci->regs->control); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
wait_ms (50); |
/* HC Reset requires max 10 us delay */ |
writel (OHCI_HCR, &ohci->regs->cmdstatus); |
temp = 30; /* ... allow extra time */ |
while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { |
if (--temp == 0) { |
ohci_err (ohci, "USB HC reset timed out!\n"); |
return -1; |
} |
udelay (1); |
} |
/* now we're in the SUSPEND state ... must go OPERATIONAL |
* within 2msec else HC enters RESUME |
* |
* ... but some hardware won't init fmInterval "by the book" |
* (SiS, OPTi ...), so reset again instead. SiS doesn't need |
* this if we write fmInterval after we're OPERATIONAL. |
*/ |
writel (ohci->hc_control, &ohci->regs->control); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
#define FI 0x2edf /* 12000 bits per frame (-1) */ |
#define LSTHRESH 0x628 /* lowspeed bit threshold */ |
/* Start an OHCI controller, set the BUS operational |
* enable interrupts |
* connect the virtual root hub |
*/ |
static int hc_start (struct ohci_hcd *ohci) |
{ |
u32 mask, tmp; |
struct usb_device *udev; |
struct usb_bus *bus; |
spin_lock_init (&ohci->lock); |
disable (ohci); |
/* Tell the controller where the control and bulk lists are |
* The lists are empty now. */ |
writel (0, &ohci->regs->ed_controlhead); |
writel (0, &ohci->regs->ed_bulkhead); |
/* a reset clears this */ |
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); |
/* force default fmInterval (we won't adjust it); init thresholds |
* for last FS and LS packets, reserve 90% for periodic. |
*/ |
writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval); |
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); |
writel (LSTHRESH, &ohci->regs->lsthresh); |
/* some OHCI implementations are finicky about how they init. |
* bogus values here mean not even enumeration could work. |
*/ |
if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 |
|| !readl (&ohci->regs->periodicstart)) { |
ohci_err (ohci, "init err\n"); |
return -EOVERFLOW; |
} |
/* start controller operations */ |
ohci->hc_control &= OHCI_CTRL_RWC; |
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; |
writel (ohci->hc_control, &ohci->regs->control); |
ohci->hcd.state = USB_STATE_RUNNING; |
/* Choose the interrupts we care about now, others later on demand */ |
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH; |
writel (mask, &ohci->regs->intrstatus); |
writel (mask, &ohci->regs->intrenable); |
/* handle root hub init quirks ... */ |
tmp = roothub_a (ohci); |
tmp &= ~(RH_A_PSM | RH_A_OCPM); |
if (ohci->flags & OHCI_QUIRK_SUPERIO) { |
/* NSC 87560 and maybe others */ |
tmp |= RH_A_NOCP; |
tmp &= ~(RH_A_POTPGT | RH_A_NPS); |
} else { |
/* hub power always on; required for AMD-756 and some |
* Mac platforms, use this mode everywhere by default |
*/ |
tmp |= RH_A_NPS; |
} |
writel (tmp, &ohci->regs->roothub.a); |
writel (RH_HS_LPSC, &ohci->regs->roothub.status); |
writel (0, &ohci->regs->roothub.b); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
// POTPGT delay is bits 24-31, in 2 ms units. |
mdelay ((roothub_a (ohci) >> 23) & 0x1fe); |
/* connect the virtual root hub */ |
bus = hcd_to_bus (&ohci->hcd); |
bus->root_hub = udev = usb_alloc_dev (NULL, bus); |
ohci->hcd.state = USB_STATE_RUNNING; |
if (!udev) { |
disable (ohci); |
ohci->hc_control &= ~OHCI_CTRL_HCFS; |
writel (ohci->hc_control, &ohci->regs->control); |
return -ENOMEM; |
} |
udev->speed = USB_SPEED_FULL; |
if (hcd_register_root (&ohci->hcd) != 0) { |
usb_put_dev (udev); |
bus->root_hub = NULL; |
disable (ohci); |
ohci->hc_control &= ~OHCI_CTRL_HCFS; |
writel (ohci->hc_control, &ohci->regs->control); |
return -ENODEV; |
} |
return 0; |
} |
/*-------------------------------------------------------------------------*/ |
/* an interrupt happens */ |
static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
struct ohci_regs *regs = ohci->regs; |
int ints; |
/* we can eliminate a (slow) readl() if _only_ WDH caused this irq */ |
if ((ohci->hcca->done_head != 0) |
&& ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { |
ints = OHCI_INTR_WDH; |
/* cardbus/... hardware gone before remove() */ |
} else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { |
disable (ohci); |
ohci_dbg (ohci, "device removed!\n"); |
return; |
/* interrupt for some other device? */ |
} else if ((ints &= readl (®s->intrenable)) == 0) { |
return; |
} |
if (ints & OHCI_INTR_UE) { |
disable (ohci); |
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); |
// e.g. due to PCI Master/Target Abort |
ohci_dump (ohci, 1); |
hc_reset (ohci); |
} |
if (ints & OHCI_INTR_WDH) { |
if (HCD_IS_RUNNING(hcd->state)) |
writel (OHCI_INTR_WDH, ®s->intrdisable); |
dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs); |
if (HCD_IS_RUNNING(hcd->state)) |
writel (OHCI_INTR_WDH, ®s->intrenable); |
} |
/* could track INTR_SO to reduce available PCI/... bandwidth */ |
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled |
* when there's still unlinking to be done (next frame). |
*/ |
spin_lock (&ohci->lock); |
if (ohci->ed_rm_list) |
finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no), |
ptregs); |
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list |
&& HCD_IS_RUNNING(ohci->hcd.state)) |
writel (OHCI_INTR_SF, ®s->intrdisable); |
spin_unlock (&ohci->lock); |
if (HCD_IS_RUNNING(ohci->hcd.state)) { |
writel (ints, ®s->intrstatus); |
writel (OHCI_INTR_MIE, ®s->intrenable); |
// flush those pci writes |
(void) readl (&ohci->regs->control); |
} |
} |
/*-------------------------------------------------------------------------*/ |
static void ohci_stop (struct usb_hcd *hcd) |
{ |
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", |
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), |
ohci->hcd.state); |
ohci_dump (ohci, 1); |
if (HCD_IS_RUNNING(ohci->hcd.state)) |
hc_reset (ohci); |
remove_debug_files (ohci); |
ohci_mem_cleanup (ohci); |
if (ohci->hcca) { |
pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca, |
ohci->hcca, ohci->hcca_dma); |
ohci->hcca = NULL; |
ohci->hcca_dma = 0; |
} |
} |
/*-------------------------------------------------------------------------*/ |
// FIXME: this restart logic should be generic, |
// and handle full hcd state cleanup |
/* controller died; cleanup debris, then restart */ |
/* must not be called from interrupt context */ |
#ifdef CONFIG_PM |
static int hc_restart (struct ohci_hcd *ohci) |
{ |
int temp; |
int i; |
disable (ohci); |
if (hcd_to_bus (&ohci->hcd)->root_hub) |
usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub); |
/* empty the interrupt branches */ |
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; |
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; |
/* no EDs to remove */ |
ohci->ed_rm_list = NULL; |
/* empty control and bulk lists */ |
ohci->ed_controltail = NULL; |
ohci->ed_bulktail = NULL; |
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { |
ohci_err (ohci, "can't restart, %d\n", temp); |
return temp; |
} else |
ohci_dbg (ohci, "restart complete\n"); |
return 0; |
} |
#endif |
/*-------------------------------------------------------------------------*/ |
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC |
MODULE_AUTHOR (DRIVER_AUTHOR); |
MODULE_DESCRIPTION (DRIVER_INFO); |
MODULE_LICENSE ("GPL"); |
#ifdef CONFIG_PCI |
#include "ohci-pci.c" |
#endif |
#ifdef CONFIG_SA1111 |
#include "ohci-sa1111.c" |
#endif |
#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111)) |
#error "missing bus glue for ohci-hcd" |
#endif |
/shark/trunk/drivers/usb/host/uhci-hcd.c |
---|
1,2670 → 1,2675 |
/* |
* Universal Host Controller Interface driver for USB. |
* |
* Maintainer: Johannes Erdfelt <johannes@erdfelt.com> |
* |
* (C) Copyright 1999 Linus Torvalds |
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com |
* (C) Copyright 1999 Randy Dunlap |
* (C) Copyright 1999 Georg Acher, acher@in.tum.de |
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de |
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch |
* (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at |
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface |
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). |
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) |
* |
* Intel documents this fairly well, and as far as I know there |
* are no royalties or anything like that, but even so there are |
* people who decided that they want to do the same thing in a |
* completely different way. |
* |
* WARNING! The USB documentation is downright evil. Most of it |
* is just crap, written by a committee. You're better off ignoring |
* most of it, the important stuff is: |
* - the low-level protocol (fairly simple but lots of small details) |
* - working around the horridness of the rest |
*/ |
#include <linuxcomp.h> |
#include <linux/config.h> |
#include <linux/module.h> |
#include <linux/pci.h> |
#include <linux/kernel.h> |
#include <linux/init.h> |
#include <linux/delay.h> |
#include <linux/ioport.h> |
#include <linux/sched.h> |
#include <linux/slab.h> |
#include <linux/smp_lock.h> |
#include <linux/errno.h> |
#include <linux/unistd.h> |
#include <linux/interrupt.h> |
#include <linux/spinlock.h> |
#include <linux/proc_fs.h> |
#ifdef CONFIG_USB_DEBUG |
#define DEBUG |
#else |
#undef DEBUG |
#endif |
#include <linux/usb.h> |
#include <asm/uaccess.h> |
#include <asm/io.h> |
#include <asm/irq.h> |
#include <asm/system.h> |
#include "../core/hcd.h" |
#include "uhci-hcd.h" |
#include <linux/pm.h> |
/* |
* Version Information |
*/ |
#define DRIVER_VERSION "v2.1" |
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" |
#define DRIVER_DESC "USB Universal Host Controller Interface driver" |
/* |
* debug = 0, no debugging messages |
* debug = 1, dump failed URB's except for stalls |
* debug = 2, dump all failed URB's (including stalls) |
* show all queues in /proc/driver/uhci/[pci_addr] |
* debug = 3, show all TD's in URB's when dumping |
*/ |
#ifdef DEBUG |
static int debug = 3; |
#else |
static int debug = 0; |
#endif |
MODULE_PARM(debug, "i"); |
MODULE_PARM_DESC(debug, "Debug level"); |
static char *errbuf; |
#define ERRBUF_LEN (PAGE_SIZE * 8) |
#include "uhci-hub.c" |
#include "uhci-debug.c" |
static kmem_cache_t *uhci_up_cachep; /* urb_priv */ |
static int uhci_get_current_frame_number(struct uhci_hcd *uhci); |
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); |
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); |
static void hc_state_transitions(struct uhci_hcd *uhci); |
/* If a transfer is still active after this much time, turn off FSBR */ |
#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ |
#define FSBR_DELAY (HZ / 20) /* 50 ms */ |
/* When we timeout an idle transfer for FSBR, we'll switch it over to */ |
/* depth first traversal. We'll do it in groups of this number of TD's */ |
/* to make sure it doesn't hog all of the bandwidth */ |
#define DEPTH_INTERVAL 5 |
/* |
* Technically, updating td->status here is a race, but it's not really a |
* problem. The worst that can happen is that we set the IOC bit again |
* generating a spurious interrupt. We could fix this by creating another |
* QH and leaving the IOC bit always set, but then we would have to play |
* games with the FSBR code to make sure we get the correct order in all |
* the cases. I don't think it's worth the effort |
*/ |
static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) |
{ |
unsigned long flags; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) |
{ |
unsigned long flags; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
static inline void uhci_add_complete(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
unsigned long flags; |
spin_lock_irqsave(&uhci->complete_list_lock, flags); |
list_add_tail(&urbp->complete_list, &uhci->complete_list); |
spin_unlock_irqrestore(&uhci->complete_list_lock, flags); |
} |
static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev) |
{ |
dma_addr_t dma_handle; |
struct uhci_td *td; |
td = pci_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle); |
if (!td) |
return NULL; |
td->dma_handle = dma_handle; |
td->link = UHCI_PTR_TERM; |
td->buffer = 0; |
td->frame = -1; |
td->dev = dev; |
INIT_LIST_HEAD(&td->list); |
INIT_LIST_HEAD(&td->remove_list); |
INIT_LIST_HEAD(&td->fl_list); |
usb_get_dev(dev); |
return td; |
} |
static inline void uhci_fill_td(struct uhci_td *td, __u32 status, |
__u32 token, __u32 buffer) |
{ |
td->status = cpu_to_le32(status); |
td->token = cpu_to_le32(token); |
td->buffer = cpu_to_le32(buffer); |
} |
/* |
* We insert Isochronous URB's directly into the frame list at the beginning |
*/ |
static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum) |
{ |
unsigned long flags; |
framenum %= UHCI_NUMFRAMES; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
td->frame = framenum; |
/* Is there a TD already mapped there? */ |
if (uhci->fl->frame_cpu[framenum]) { |
struct uhci_td *ftd, *ltd; |
ftd = uhci->fl->frame_cpu[framenum]; |
ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); |
list_add_tail(&td->fl_list, &ftd->fl_list); |
td->link = ltd->link; |
mb(); |
ltd->link = cpu_to_le32(td->dma_handle); |
} else { |
td->link = uhci->fl->frame[framenum]; |
mb(); |
uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle); |
uhci->fl->frame_cpu[framenum] = td; |
} |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) |
{ |
unsigned long flags; |
/* If it's not inserted, don't remove it */ |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
if (td->frame == -1 && list_empty(&td->fl_list)) |
goto out; |
if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { |
if (list_empty(&td->fl_list)) { |
uhci->fl->frame[td->frame] = td->link; |
uhci->fl->frame_cpu[td->frame] = NULL; |
} else { |
struct uhci_td *ntd; |
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); |
uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle); |
uhci->fl->frame_cpu[td->frame] = ntd; |
} |
} else { |
struct uhci_td *ptd; |
ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list); |
ptd->link = td->link; |
} |
mb(); |
td->link = UHCI_PTR_TERM; |
list_del_init(&td->fl_list); |
td->frame = -1; |
out: |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
/* |
* Inserts a td into qh list at the top. |
*/ |
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, u32 breadth) |
{ |
struct list_head *tmp, *head; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
struct uhci_td *td, *ptd; |
if (list_empty(&urbp->td_list)) |
return; |
head = &urbp->td_list; |
tmp = head->next; |
/* Ordering isn't important here yet since the QH hasn't been */ |
/* inserted into the schedule yet */ |
td = list_entry(tmp, struct uhci_td, list); |
/* Add the first TD to the QH element pointer */ |
qh->element = cpu_to_le32(td->dma_handle) | breadth; |
ptd = td; |
/* Then link the rest of the TD's */ |
tmp = tmp->next; |
while (tmp != head) { |
td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
ptd->link = cpu_to_le32(td->dma_handle) | breadth; |
ptd = td; |
} |
ptd->link = UHCI_PTR_TERM; |
} |
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) |
{ |
if (!list_empty(&td->list)) |
dbg("td %p is still in list!", td); |
if (!list_empty(&td->remove_list)) |
dbg("td %p still in remove_list!", td); |
if (!list_empty(&td->fl_list)) |
dbg("td %p is still in fl_list!", td); |
if (td->dev) |
usb_put_dev(td->dev); |
pci_pool_free(uhci->td_pool, td, td->dma_handle); |
} |
static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev) |
{ |
dma_addr_t dma_handle; |
struct uhci_qh *qh; |
qh = pci_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle); |
if (!qh) |
return NULL; |
qh->dma_handle = dma_handle; |
qh->element = UHCI_PTR_TERM; |
qh->link = UHCI_PTR_TERM; |
qh->dev = dev; |
qh->urbp = NULL; |
INIT_LIST_HEAD(&qh->list); |
INIT_LIST_HEAD(&qh->remove_list); |
usb_get_dev(dev); |
return qh; |
} |
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) |
{ |
if (!list_empty(&qh->list)) |
dbg("qh %p list not empty!", qh); |
if (!list_empty(&qh->remove_list)) |
dbg("qh %p still in remove_list!", qh); |
if (qh->dev) |
usb_put_dev(qh->dev); |
pci_pool_free(uhci->qh_pool, qh, qh->dma_handle); |
} |
/* |
* Append this urb's qh after the last qh in skelqh->list |
* MUST be called with uhci->frame_list_lock acquired |
* |
* Note that urb_priv.queue_list doesn't have a separate queue head; |
* it's a ring with every element "live". |
*/ |
static void _uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
struct list_head *tmp; |
struct uhci_qh *lqh; |
/* Grab the last QH */ |
lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); |
/* |
* Patch this endpoint's URB's QHs to point to the next skelqh: |
* skelqh --> ... lqh --> newqh --> next skelqh |
* Do this first, so the HC always sees the right QH after this one. |
*/ |
list_for_each (tmp, &urbp->queue_list) { |
struct urb_priv *turbp = |
list_entry(tmp, struct urb_priv, queue_list); |
turbp->qh->link = lqh->link; |
} |
urbp->qh->link = lqh->link; |
wmb(); /* Ordering is important */ |
/* |
* Patch QHs for previous endpoint's queued URBs? HC goes |
* here next, not to the next skelqh it now points to. |
* |
* lqh --> td ... --> qh ... --> td --> qh ... --> td |
* | | | |
* v v v |
* +<----------------+-----------------+ |
* v |
* newqh --> td ... --> td |
* | |
* v |
* ... |
* |
* The HC could see (and use!) any of these as we write them. |
*/ |
if (lqh->urbp) { |
list_for_each (tmp, &lqh->urbp->queue_list) { |
struct urb_priv *turbp = |
list_entry(tmp, struct urb_priv, queue_list); |
turbp->qh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; |
} |
} |
lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; |
list_add_tail(&urbp->qh->list, &skelqh->list); |
} |
static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) |
{ |
unsigned long flags; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
_uhci_insert_qh(uhci, skelqh, urb); |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
/* |
* Start removal of QH from schedule; it finishes next frame. |
* TDs should be unlinked before this is called. |
*/ |
static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) |
{ |
unsigned long flags; |
struct uhci_qh *pqh; |
if (!qh) |
return; |
qh->urbp = NULL; |
/* |
* Only go through the hoops if it's actually linked in |
* Queued QHs are removed in uhci_delete_queued_urb, |
* since (for queued URBs) the pqh is pointed to the next |
* QH in the queue, not the next endpoint's QH. |
*/ |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
if (!list_empty(&qh->list)) { |
pqh = list_entry(qh->list.prev, struct uhci_qh, list); |
if (pqh->urbp) { |
struct list_head *head, *tmp; |
head = &pqh->urbp->queue_list; |
tmp = head->next; |
while (head != tmp) { |
struct urb_priv *turbp = |
list_entry(tmp, struct urb_priv, queue_list); |
tmp = tmp->next; |
turbp->qh->link = qh->link; |
} |
} |
pqh->link = qh->link; |
mb(); |
/* Leave qh->link in case the HC is on the QH now, it will */ |
/* continue the rest of the schedule */ |
qh->element = UHCI_PTR_TERM; |
list_del_init(&qh->list); |
} |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); |
/* Check to see if the remove list is empty. Set the IOC bit */ |
/* to force an interrupt so we can remove the QH */ |
if (list_empty(&uhci->qh_remove_list)) |
uhci_set_next_interrupt(uhci); |
list_add(&qh->remove_list, &uhci->qh_remove_list); |
spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags); |
} |
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
struct list_head *head, *tmp; |
head = &urbp->td_list; |
tmp = head->next; |
while (head != tmp) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
if (toggle) |
td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); |
else |
td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); |
toggle ^= 1; |
} |
return toggle; |
} |
/* This function will append one URB's QH to another URB's QH. This is for */ |
/* queuing interrupt, control or bulk transfers */ |
static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) |
{ |
struct urb_priv *eurbp, *urbp, *furbp, *lurbp; |
struct list_head *tmp; |
struct uhci_td *lltd; |
unsigned long flags; |
eurbp = eurb->hcpriv; |
urbp = urb->hcpriv; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
/* Find the first URB in the queue */ |
if (eurbp->queued) { |
struct list_head *head = &eurbp->queue_list; |
tmp = head->next; |
while (tmp != head) { |
struct urb_priv *turbp = |
list_entry(tmp, struct urb_priv, queue_list); |
if (!turbp->queued) |
break; |
tmp = tmp->next; |
} |
} else |
tmp = &eurbp->queue_list; |
furbp = list_entry(tmp, struct urb_priv, queue_list); |
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); |
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); |
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), |
uhci_fixup_toggle(urb, uhci_toggle(td_token(lltd)) ^ 1)); |
/* All qh's in the queue need to link to the next queue */ |
urbp->qh->link = eurbp->qh->link; |
mb(); /* Make sure we flush everything */ |
lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; |
list_add_tail(&urbp->queue_list, &furbp->queue_list); |
urbp->queued = 1; |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct urb_priv *urbp, *nurbp; |
struct list_head *head, *tmp; |
struct urb_priv *purbp; |
struct uhci_td *pltd; |
unsigned int toggle; |
unsigned long flags; |
urbp = urb->hcpriv; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
if (list_empty(&urbp->queue_list)) |
goto out; |
nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); |
/* Fix up the toggle for the next URB's */ |
if (!urbp->queued) |
/* We just set the toggle in uhci_unlink_generic */ |
toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); |
else { |
/* If we're in the middle of the queue, grab the toggle */ |
/* from the TD previous to us */ |
purbp = list_entry(urbp->queue_list.prev, struct urb_priv, |
queue_list); |
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); |
toggle = uhci_toggle(td_token(pltd)) ^ 1; |
} |
head = &urbp->queue_list; |
tmp = head->next; |
while (head != tmp) { |
struct urb_priv *turbp; |
turbp = list_entry(tmp, struct urb_priv, queue_list); |
tmp = tmp->next; |
if (!turbp->queued) |
break; |
toggle = uhci_fixup_toggle(turbp->urb, toggle); |
} |
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
usb_pipeout(urb->pipe), toggle); |
if (!urbp->queued) { |
struct uhci_qh *pqh; |
nurbp->queued = 0; |
/* |
* Fixup the previous QH's queue to link to the new head |
* of this queue. |
*/ |
pqh = list_entry(urbp->qh->list.prev, struct uhci_qh, list); |
if (pqh->urbp) { |
struct list_head *head, *tmp; |
head = &pqh->urbp->queue_list; |
tmp = head->next; |
while (head != tmp) { |
struct urb_priv *turbp = |
list_entry(tmp, struct urb_priv, queue_list); |
tmp = tmp->next; |
turbp->qh->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; |
} |
} |
pqh->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; |
list_add_tail(&nurbp->qh->list, &urbp->qh->list); |
list_del_init(&urbp->qh->list); |
} else { |
/* We're somewhere in the middle (or end). A bit trickier */ |
/* than the head scenario */ |
purbp = list_entry(urbp->queue_list.prev, struct urb_priv, |
queue_list); |
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); |
if (nurbp->queued) |
pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; |
else |
/* The next URB happens to be the beginning, so */ |
/* we're the last, end the chain */ |
pltd->link = UHCI_PTR_TERM; |
} |
list_del_init(&urbp->queue_list); |
out: |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
extern void* malloc(int size); |
static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct urb_priv *urbp; |
urbp = malloc(sizeof(struct urb_priv)); //**kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); |
if (!urbp) { |
err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n"); |
return NULL; |
} |
memset((void *)urbp, 0, sizeof(*urbp)); |
urbp->inserttime = jiffies26; |
urbp->fsbrtime = jiffies26; |
urbp->urb = urb; |
urbp->dev = urb->dev; |
INIT_LIST_HEAD(&urbp->td_list); |
INIT_LIST_HEAD(&urbp->queue_list); |
INIT_LIST_HEAD(&urbp->complete_list); |
INIT_LIST_HEAD(&urbp->urb_list); |
list_add_tail(&urbp->urb_list, &uhci->urb_list); |
urb->hcpriv = urbp; |
return urbp; |
} |
/* |
* MUST be called with urb->lock acquired |
*/ |
static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
td->urb = urb; |
list_add_tail(&td->list, &urbp->td_list); |
} |
/* |
* MUST be called with urb->lock acquired |
*/ |
static void uhci_remove_td_from_urb(struct uhci_td *td) |
{ |
if (list_empty(&td->list)) |
return; |
list_del_init(&td->list); |
td->urb = NULL; |
} |
/* |
* MUST be called with urb->lock acquired |
*/ |
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *head, *tmp; |
struct urb_priv *urbp; |
unsigned long flags; |
urbp = (struct urb_priv *)urb->hcpriv; |
if (!urbp) |
return; |
if (!list_empty(&urbp->urb_list)) |
warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb); |
if (!list_empty(&urbp->complete_list)) |
warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb); |
spin_lock_irqsave(&uhci->td_remove_list_lock, flags); |
/* Check to see if the remove list is empty. Set the IOC bit */ |
/* to force an interrupt so we can remove the TD's*/ |
if (list_empty(&uhci->td_remove_list)) |
uhci_set_next_interrupt(uhci); |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
uhci_remove_td_from_urb(td); |
uhci_remove_td(uhci, td); |
list_add(&td->remove_list, &uhci->td_remove_list); |
} |
spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags); |
urb->hcpriv = NULL; |
//**kmem_cache_free(uhci_up_cachep, urbp); |
free(urbp); |
} |
static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb) |
{ |
unsigned long flags; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) { |
urbp->fsbr = 1; |
if (!uhci->fsbr++ && !uhci->fsbrtimeout) |
uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH; |
} |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb) |
{ |
unsigned long flags; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
spin_lock_irqsave(&uhci->frame_list_lock, flags); |
if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) { |
urbp->fsbr = 0; |
if (!--uhci->fsbr) |
uhci->fsbrtimeout = jiffies26 + FSBR_DELAY; |
} |
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); |
} |
/* |
* Map status to standard result codes |
* |
* <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] |
* <dir_out> is True for output TDs and False for input TDs. |
*/ |
static int uhci_map_status(int status, int dir_out) |
{ |
if (!status) |
return 0; |
if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ |
return -EPROTO; |
if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ |
if (dir_out) |
return -ETIMEDOUT; |
else |
return -EILSEQ; |
} |
if (status & TD_CTRL_NAK) /* NAK */ |
return -ETIMEDOUT; |
if (status & TD_CTRL_BABBLE) /* Babble */ |
return -EOVERFLOW; |
if (status & TD_CTRL_DBUFERR) /* Buffer error */ |
return -ENOSR; |
if (status & TD_CTRL_STALLED) /* Stalled */ |
return -EPIPE; |
if (status & TD_CTRL_ACTIVE) /* Active */ |
return 0; |
return -EINVAL; |
} |
/* |
* Control transfers |
*/ |
static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
struct uhci_td *td; |
struct uhci_qh *qh, *skelqh; |
unsigned long destination, status; |
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); |
int len = urb->transfer_buffer_length; |
dma_addr_t data = urb->transfer_dma; |
/* The "pipe" thing contains the destination in bits 8--18 */ |
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; |
/* 3 errors */ |
status = TD_CTRL_ACTIVE | uhci_maxerr(3); |
if (urb->dev->speed == USB_SPEED_LOW) |
status |= TD_CTRL_LS; |
/* |
* Build the TD for the control request |
*/ |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status, destination | uhci_explen(7), |
urb->setup_dma); |
/* |
* If direction is "send", change the frame from SETUP (0x2D) |
* to OUT (0xE1). Else change it from SETUP to IN (0x69). |
*/ |
destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); |
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) |
status |= TD_CTRL_SPD; |
/* |
* Build the DATA TD's |
*/ |
while (len > 0) { |
int pktsze = len; |
if (pktsze > maxsze) |
pktsze = maxsze; |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
/* Alternate Data0/1 (start with Data1) */ |
destination ^= TD_TOKEN_TOGGLE; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1), |
data); |
data += pktsze; |
len -= pktsze; |
} |
/* |
* Build the final TD for control status |
*/ |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
/* |
* It's IN if the pipe is an output pipe or we're not expecting |
* data back. |
*/ |
destination &= ~TD_TOKEN_PID_MASK; |
if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) |
destination |= USB_PID_IN; |
else |
destination |= USB_PID_OUT; |
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ |
status &= ~TD_CTRL_SPD; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status | TD_CTRL_IOC, |
destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0); |
qh = uhci_alloc_qh(uhci, urb->dev); |
if (!qh) |
return -ENOMEM; |
urbp->qh = qh; |
qh->urbp = urbp; |
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); |
/* Low speed transfers get a different queue, and won't hog the bus */ |
if (urb->dev->speed == USB_SPEED_LOW) |
skelqh = uhci->skel_ls_control_qh; |
else { |
skelqh = uhci->skel_hs_control_qh; |
uhci_inc_fsbr(uhci, urb); |
} |
if (eurb) |
uhci_append_queued_urb(uhci, eurb, urb); |
else |
uhci_insert_qh(uhci, skelqh, urb); |
return -EINPROGRESS; |
} |
/* |
* If control was short, then end status packet wasn't sent, so this |
* reorganize s so it's sent to finish the transfer. The original QH is |
* removed from the skel and discarded; all TDs except the last (status) |
* are deleted; the last (status) TD is put on a new QH which is reinserted |
* into the skel. Since the last TD and urb_priv are reused, the TD->link |
* and urb_priv maintain any queued QHs. |
*/ |
static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *tmp, *head; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
urbp->short_control_packet = 1; |
/* Create a new QH to avoid pointer overwriting problems */ |
uhci_remove_qh(uhci, urbp->qh); |
/* Delete all of the TD's except for the status TD at the end */ |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head && tmp->next != head) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
uhci_remove_td_from_urb(td); |
uhci_remove_td(uhci, td); |
uhci_free_td(uhci, td); |
} |
urbp->qh = uhci_alloc_qh(uhci, urb->dev); |
if (!urbp->qh) { |
err("unable to allocate new QH for control retrigger"); |
return -ENOMEM; |
} |
urbp->qh->urbp = urbp; |
/* One TD, who cares about Breadth first? */ |
uhci_insert_tds_in_qh(urbp->qh, urb, UHCI_PTR_DEPTH); |
/* Low speed transfers get a different queue */ |
if (urb->dev->speed == USB_SPEED_LOW) |
uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); |
else |
uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); |
return -EINPROGRESS; |
} |
static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *tmp, *head; |
struct urb_priv *urbp = urb->hcpriv; |
struct uhci_td *td; |
unsigned int status; |
int ret = 0; |
if (list_empty(&urbp->td_list)) |
return -EINVAL; |
head = &urbp->td_list; |
if (urbp->short_control_packet) { |
tmp = head->prev; |
goto status_phase; |
} |
tmp = head->next; |
td = list_entry(tmp, struct uhci_td, list); |
/* The first TD is the SETUP phase, check the status, but skip */ |
/* the count */ |
status = uhci_status_bits(td_status(td)); |
if (status & TD_CTRL_ACTIVE) |
return -EINPROGRESS; |
if (status) |
goto td_error; |
urb->actual_length = 0; |
/* The rest of the TD's (but the last) are data */ |
tmp = tmp->next; |
while (tmp != head && tmp->next != head) { |
td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
status = uhci_status_bits(td_status(td)); |
if (status & TD_CTRL_ACTIVE) |
return -EINPROGRESS; |
urb->actual_length += uhci_actual_length(td_status(td)); |
if (status) |
goto td_error; |
/* Check to see if we received a short packet */ |
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) { |
if (urb->transfer_flags & URB_SHORT_NOT_OK) { |
ret = -EREMOTEIO; |
goto err; |
} |
if (uhci_packetid(td_token(td)) == USB_PID_IN) |
return usb_control_retrigger_status(uhci, urb); |
else |
return 0; |
} |
} |
status_phase: |
td = list_entry(tmp, struct uhci_td, list); |
/* Control status phase */ |
status = td_status(td); |
#ifdef I_HAVE_BUGGY_APC_BACKUPS |
/* APC BackUPS Pro kludge */ |
/* It tries to send all of the descriptor instead of the amount */ |
/* we requested */ |
if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ |
status & TD_CTRL_ACTIVE && |
status & TD_CTRL_NAK) |
return 0; |
#endif |
if (status & TD_CTRL_ACTIVE) |
return -EINPROGRESS; |
if (uhci_status_bits(status)) |
goto td_error; |
return 0; |
td_error: |
ret = uhci_map_status(status, uhci_packetout(td_token(td))); |
err: |
if ((debug == 1 && ret != -EPIPE) || debug > 1) { |
/* Some debugging code */ |
dbg("uhci_result_control() failed with status %x", status); |
if (errbuf) { |
/* Print the chain for debugging purposes */ |
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); |
lprintk(errbuf); |
} |
} |
return ret; |
} |
/* |
* Common submit for bulk and interrupt |
*/ |
static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh) |
{ |
struct uhci_td *td; |
struct uhci_qh *qh; |
unsigned long destination, status; |
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); |
int len = urb->transfer_buffer_length; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
dma_addr_t data = urb->transfer_dma; |
if (len < 0) |
return -EINVAL; |
/* The "pipe" thing contains the destination in bits 8--18 */ |
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); |
status = uhci_maxerr(3) | TD_CTRL_ACTIVE; |
if (urb->dev->speed == USB_SPEED_LOW) |
status |= TD_CTRL_LS; |
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) |
status |= TD_CTRL_SPD; |
/* |
* Build the DATA TD's |
*/ |
do { /* Allow zero length packets */ |
int pktsze = len; |
if (pktsze > maxsze) |
pktsze = maxsze; |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) | |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), |
data); |
data += pktsze; |
len -= maxsze; |
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
usb_pipeout(urb->pipe)); |
} while (len > 0); |
/* |
* URB_ZERO_PACKET means adding a 0-length packet, if direction |
* is OUT and the transfer_length was an exact multiple of maxsze, |
* hence (len = transfer_length - N * maxsze) == 0 |
* however, if transfer_length == 0, the zero packet was already |
* prepared above. |
*/ |
if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && |
!len && urb->transfer_buffer_length) { |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) | |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), |
data); |
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
usb_pipeout(urb->pipe)); |
} |
/* Set the flag on the last packet */ |
td->status |= cpu_to_le32(TD_CTRL_IOC); |
qh = uhci_alloc_qh(uhci, urb->dev); |
if (!qh) |
return -ENOMEM; |
urbp->qh = qh; |
qh->urbp = urbp; |
/* Always breadth first */ |
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); |
if (eurb) |
uhci_append_queued_urb(uhci, eurb, urb); |
else |
uhci_insert_qh(uhci, skelqh, urb); |
return -EINPROGRESS; |
} |
/* |
* Common result for bulk and interrupt |
*/ |
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *tmp, *head; |
struct urb_priv *urbp = urb->hcpriv; |
struct uhci_td *td; |
unsigned int status = 0; |
int ret = 0; |
urb->actual_length = 0; |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head) { |
td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
status = uhci_status_bits(td_status(td)); |
if (status & TD_CTRL_ACTIVE) |
return -EINPROGRESS; |
urb->actual_length += uhci_actual_length(td_status(td)); |
if (status) |
goto td_error; |
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) { |
if (urb->transfer_flags & URB_SHORT_NOT_OK) { |
ret = -EREMOTEIO; |
goto err; |
} else |
return 0; |
} |
} |
return 0; |
td_error: |
ret = uhci_map_status(status, uhci_packetout(td_token(td))); |
if (ret == -EPIPE) |
/* endpoint has stalled - mark it halted */ |
usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)), |
uhci_packetout(td_token(td))); |
err: |
/* |
* Enable this chunk of code if you want to see some more debugging. |
* But be careful, it has the tendancy to starve out khubd and prevent |
* disconnects from happening successfully if you have a slow debug |
* log interface (like a serial console. |
*/ |
#if 0 |
if ((debug == 1 && ret != -EPIPE) || debug > 1) { |
/* Some debugging code */ |
dbg("uhci_result_common() failed with status %x", status); |
if (errbuf) { |
/* Print the chain for debugging purposes */ |
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); |
lprintk(errbuf); |
} |
} |
#endif |
return ret; |
} |
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) |
{ |
int ret; |
/* Can't have low speed bulk transfers */ |
if (urb->dev->speed == USB_SPEED_LOW) |
return -EINVAL; |
ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); |
if (ret == -EINPROGRESS) |
uhci_inc_fsbr(uhci, urb); |
return ret; |
} |
static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) |
{ |
/* USB 1.1 interrupt transfers only involve one packet per interval; |
* that's the uhci_submit_common() "breadth first" policy. Drivers |
* can submit urbs of any length, but longer ones might need many |
* intervals to complete. |
*/ |
return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); |
} |
/* |
* Bulk and interrupt use common result |
*/ |
#define uhci_result_bulk uhci_result_common |
#define uhci_result_interrupt uhci_result_common |
/* |
* Isochronous transfers |
*/ |
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) |
{ |
struct urb *last_urb = NULL; |
struct list_head *tmp, *head; |
int ret = 0; |
head = &uhci->urb_list; |
tmp = head->next; |
while (tmp != head) { |
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); |
struct urb *u = up->urb; |
tmp = tmp->next; |
/* look for pending URB's with identical pipe handle */ |
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && |
(u->status == -EINPROGRESS) && (u != urb)) { |
if (!last_urb) |
*start = u->start_frame; |
last_urb = u; |
} |
} |
if (last_urb) { |
*end = (last_urb->start_frame + last_urb->number_of_packets * |
last_urb->interval) & (UHCI_NUMFRAMES-1); |
ret = 0; |
} else |
ret = -1; /* no previous urb found */ |
return ret; |
} |
static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) |
{ |
int limits; |
unsigned int start = 0, end = 0; |
if (urb->number_of_packets > 900) /* 900? Why? */ |
return -EFBIG; |
limits = isochronous_find_limits(uhci, urb, &start, &end); |
if (urb->transfer_flags & URB_ISO_ASAP) { |
if (limits) { |
int curframe; |
curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES; |
urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; |
} else |
urb->start_frame = end; |
} else { |
urb->start_frame %= UHCI_NUMFRAMES; |
/* FIXME: Sanity check */ |
} |
return 0; |
} |
/* |
* Isochronous transfers |
*/ |
static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct uhci_td *td; |
int i, ret, frame; |
int status, destination; |
status = TD_CTRL_ACTIVE | TD_CTRL_IOS; |
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); |
ret = isochronous_find_start(uhci, urb); |
if (ret) |
return ret; |
frame = urb->start_frame; |
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) { |
if (!urb->iso_frame_desc[i].length) |
continue; |
td = uhci_alloc_td(uhci, urb->dev); |
if (!td) |
return -ENOMEM; |
uhci_add_td_to_urb(urb, td); |
uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1), |
urb->transfer_dma + urb->iso_frame_desc[i].offset); |
if (i + 1 >= urb->number_of_packets) |
td->status |= cpu_to_le32(TD_CTRL_IOC); |
uhci_insert_td_frame_list(uhci, td, frame); |
} |
return -EINPROGRESS; |
} |
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *tmp, *head; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
int status; |
int i, ret = 0; |
urb->actual_length = 0; |
i = 0; |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
int actlength; |
tmp = tmp->next; |
if (td_status(td) & TD_CTRL_ACTIVE) |
return -EINPROGRESS; |
actlength = uhci_actual_length(td_status(td)); |
urb->iso_frame_desc[i].actual_length = actlength; |
urb->actual_length += actlength; |
status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe)); |
urb->iso_frame_desc[i].status = status; |
if (status) { |
urb->error_count++; |
ret = status; |
} |
i++; |
} |
return ret; |
} |
/* |
* MUST be called with uhci->urb_list_lock acquired |
*/ |
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *tmp, *head; |
/* We don't match Isoc transfers since they are special */ |
if (usb_pipeisoc(urb->pipe)) |
return NULL; |
head = &uhci->urb_list; |
tmp = head->next; |
while (tmp != head) { |
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); |
struct urb *u = up->urb; |
tmp = tmp->next; |
if (u->dev == urb->dev && u->status == -EINPROGRESS) { |
/* For control, ignore the direction */ |
if (usb_pipecontrol(urb->pipe) && |
(u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN)) |
return u; |
else if (u->pipe == urb->pipe) |
return u; |
} |
} |
return NULL; |
} |
static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags) |
{ |
int ret = -EINVAL; |
struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
unsigned long flags; |
struct urb *eurb; |
int bustime; |
spin_lock_irqsave(&uhci->urb_list_lock, flags); |
eurb = uhci_find_urb_ep(uhci, urb); |
if (!uhci_alloc_urb_priv(uhci, urb)) { |
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); |
return -ENOMEM; |
} |
switch (usb_pipetype(urb->pipe)) { |
case PIPE_CONTROL: |
ret = uhci_submit_control(uhci, urb, eurb); |
break; |
case PIPE_INTERRUPT: |
if (!eurb) { |
bustime = usb_check_bandwidth(urb->dev, urb); |
if (bustime < 0) |
ret = bustime; |
else { |
ret = uhci_submit_interrupt(uhci, urb, eurb); |
if (ret == -EINPROGRESS) |
usb_claim_bandwidth(urb->dev, urb, bustime, 0); |
} |
} else { /* inherit from parent */ |
urb->bandwidth = eurb->bandwidth; |
ret = uhci_submit_interrupt(uhci, urb, eurb); |
} |
break; |
case PIPE_BULK: |
ret = uhci_submit_bulk(uhci, urb, eurb); |
break; |
case PIPE_ISOCHRONOUS: |
bustime = usb_check_bandwidth(urb->dev, urb); |
if (bustime < 0) { |
ret = bustime; |
break; |
} |
ret = uhci_submit_isochronous(uhci, urb); |
if (ret == -EINPROGRESS) |
usb_claim_bandwidth(urb->dev, urb, bustime, 1); |
break; |
} |
if (ret != -EINPROGRESS) { |
/* Submit failed, so delete it from the urb_list */ |
struct urb_priv *urbp = urb->hcpriv; |
list_del_init(&urbp->urb_list); |
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); |
uhci_destroy_urb_priv (uhci, urb); |
return ret; |
} |
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); |
return 0; |
} |
/* |
* Return the result of a transfer |
* |
* MUST be called with urb_list_lock acquired |
*/ |
static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) |
{ |
int ret = -EINVAL; |
unsigned long flags; |
struct urb_priv *urbp; |
spin_lock_irqsave(&urb->lock, flags); |
urbp = (struct urb_priv *)urb->hcpriv; |
if (urb->status != -EINPROGRESS) { |
info("uhci_transfer_result: called for URB %p not in flight?", urb); |
goto out; |
} |
switch (usb_pipetype(urb->pipe)) { |
case PIPE_CONTROL: |
ret = uhci_result_control(uhci, urb); |
break; |
case PIPE_INTERRUPT: |
ret = uhci_result_interrupt(uhci, urb); |
break; |
case PIPE_BULK: |
ret = uhci_result_bulk(uhci, urb); |
break; |
case PIPE_ISOCHRONOUS: |
ret = uhci_result_isochronous(uhci, urb); |
break; |
} |
urbp->status = ret; |
if (ret == -EINPROGRESS) |
goto out; |
switch (usb_pipetype(urb->pipe)) { |
case PIPE_CONTROL: |
case PIPE_BULK: |
case PIPE_ISOCHRONOUS: |
/* Release bandwidth for Interrupt or Isoc. transfers */ |
/* Spinlock needed ? */ |
if (urb->bandwidth) |
usb_release_bandwidth(urb->dev, urb, 1); |
uhci_unlink_generic(uhci, urb); |
break; |
case PIPE_INTERRUPT: |
/* Release bandwidth for Interrupt or Isoc. transfers */ |
/* Make sure we don't release if we have a queued URB */ |
spin_lock(&uhci->frame_list_lock); |
/* Spinlock needed ? */ |
if (list_empty(&urbp->queue_list) && urb->bandwidth) |
usb_release_bandwidth(urb->dev, urb, 0); |
else |
/* bandwidth was passed on to queued URB, */ |
/* so don't let usb_unlink_urb() release it */ |
urb->bandwidth = 0; |
spin_unlock(&uhci->frame_list_lock); |
uhci_unlink_generic(uhci, urb); |
break; |
default: |
info("uhci_transfer_result: unknown pipe type %d for urb %p\n", |
usb_pipetype(urb->pipe), urb); |
} |
/* Remove it from uhci->urb_list */ |
list_del_init(&urbp->urb_list); |
uhci_add_complete(uhci, urb); |
out: |
spin_unlock_irqrestore(&urb->lock, flags); |
} |
/* |
* MUST be called with urb->lock acquired |
*/ |
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct list_head *head, *tmp; |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
int prevactive = 1; |
/* We can get called when urbp allocation fails, so check */ |
if (!urbp) |
return; |
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ |
/* |
* Now we need to find out what the last successful toggle was |
* so we can update the local data toggle for the next transfer |
* |
* There's 3 way's the last successful completed TD is found: |
* |
* 1) The TD is NOT active and the actual length < expected length |
* 2) The TD is NOT active and it's the last TD in the chain |
* 3) The TD is active and the previous TD is NOT active |
* |
* Control and Isochronous ignore the toggle, so this is safe |
* for all types |
*/ |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
if (!(td_status(td) & TD_CTRL_ACTIVE) && |
(uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) || |
tmp == head)) |
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), |
uhci_packetout(td_token(td)), |
uhci_toggle(td_token(td)) ^ 1); |
else if ((td_status(td) & TD_CTRL_ACTIVE) && !prevactive) |
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), |
uhci_packetout(td_token(td)), |
uhci_toggle(td_token(td))); |
prevactive = td_status(td) & TD_CTRL_ACTIVE; |
} |
uhci_delete_queued_urb(uhci, urb); |
/* The interrupt loop will reclaim the QH's */ |
uhci_remove_qh(uhci, urbp->qh); |
urbp->qh = NULL; |
} |
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) |
{ |
struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
unsigned long flags; |
struct urb_priv *urbp = urb->hcpriv; |
/* If this is an interrupt URB that is being killed in urb->complete, */ |
/* then just set its status and return */ |
if (!urbp) { |
urb->status = -ECONNRESET; |
return 0; |
} |
spin_lock_irqsave(&uhci->urb_list_lock, flags); |
list_del_init(&urbp->urb_list); |
uhci_unlink_generic(uhci, urb); |
spin_lock(&uhci->urb_remove_list_lock); |
/* If we're the first, set the next interrupt bit */ |
if (list_empty(&uhci->urb_remove_list)) |
uhci_set_next_interrupt(uhci); |
list_add(&urbp->urb_list, &uhci->urb_remove_list); |
spin_unlock(&uhci->urb_remove_list_lock); |
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); |
return 0; |
} |
static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) |
{ |
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; |
struct list_head *head, *tmp; |
int count = 0; |
uhci_dec_fsbr(uhci, urb); |
urbp->fsbr_timeout = 1; |
/* |
* Ideally we would want to fix qh->element as well, but it's |
* read/write by the HC, so that can introduce a race. It's not |
* really worth the hassle |
*/ |
head = &urbp->td_list; |
tmp = head->next; |
while (tmp != head) { |
struct uhci_td *td = list_entry(tmp, struct uhci_td, list); |
tmp = tmp->next; |
/* |
* Make sure we don't do the last one (since it'll have the |
* TERM bit set) as well as we skip every so many TD's to |
* make sure it doesn't hog the bandwidth |
*/ |
if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) |
td->link |= UHCI_PTR_DEPTH; |
count++; |
} |
return 0; |
} |
/* |
* uhci_get_current_frame_number() |
* |
* returns the current frame number for a USB bus/controller. |
*/ |
static int uhci_get_current_frame_number(struct uhci_hcd *uhci) |
{ |
return inw(uhci->io_addr + USBFRNUM); |
} |
static int init_stall_timer(struct usb_hcd *hcd); |
static void stall_callback(unsigned long ptr) |
{ |
struct usb_hcd *hcd = (struct usb_hcd *)ptr; |
struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
struct list_head list, *tmp, *head; |
unsigned long flags; |
INIT_LIST_HEAD(&list); |
spin_lock_irqsave(&uhci->urb_list_lock, flags); |
head = &uhci->urb_list; |
tmp = head->next; |
while (tmp != head) { |
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); |
struct urb *u = up->urb; |
tmp = tmp->next; |
spin_lock(&u->lock); |
/* Check if the FSBR timed out */ |
if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies26, up->fsbrtime + IDLE_TIMEOUT)) |
uhci_fsbr_timeout(uhci, u); |
/* Check if the URB timed out */ |
if (u->timeout && time_after_eq(jiffies26, up->inserttime + u->timeout)) |
list_move_tail(&up->urb_list, &list); |
spin_unlock(&u->lock); |
} |
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); |
head = &list; |
tmp = head->next; |
while (tmp != head) { |
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); |
struct urb *u = up->urb; |
tmp = tmp->next; |
uhci_urb_dequeue(hcd, u); |
} |
/* Really disable FSBR */ |
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies26, uhci->fsbrtimeout)) { |
uhci->fsbrtimeout = 0; |
uhci->skel_term_qh->link = UHCI_PTR_TERM; |
} |
/* Poll for and perform state transitions */ |
hc_state_transitions(uhci); |
init_stall_timer(hcd); |
} |
static int init_stall_timer(struct usb_hcd *hcd) |
{ |
struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
init_timer(&uhci->stall_timer); |
uhci->stall_timer.function = stall_callback; |
uhci->stall_timer.data = (unsigned long)hcd; |
uhci->stall_timer.expires = jiffies26 + (HZ / 10); |
add_timer(&uhci->stall_timer); |
return 0; |
} |
static void uhci_free_pending_qhs(struct uhci_hcd *uhci) |
{ |
struct list_head *tmp, *head; |
unsigned long flags; |
spin_lock_irqsave(&uhci->qh_remo |