Commit f0dbe47d authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linuxusb.bkbits.net/linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 2ce059ec f123a327
18-Dec-2001 26-Apr-2002
The EHCI driver is used to talk to high speed USB 2.0 devices using The EHCI driver is used to talk to high speed USB 2.0 devices using
USB 2.0-capable host controller hardware. The USB 2.0 standard is USB 2.0-capable host controller hardware. The USB 2.0 standard is
...@@ -19,6 +19,9 @@ interact with the EHCI controller through a "Transaction Translator" ...@@ -19,6 +19,9 @@ interact with the EHCI controller through a "Transaction Translator"
(TT) in the hub, which turns low or full speed transactions into (TT) in the hub, which turns low or full speed transactions into
high speed "split transactions" that don't waste transfer bandwidth. high speed "split transactions" that don't waste transfer bandwidth.
At this writing, this driver has been seen to work with implementations
of EHCI from (in alphabetical order): Intel, NEC, Philips, and VIA.
At this writing, high speed devices are finally beginning to appear. At this writing, high speed devices are finally beginning to appear.
While usb-storage devices have been available for some time (working While usb-storage devices have been available for some time (working
quite speedily on the 2.4 version of this driver), hubs have only quite speedily on the 2.4 version of this driver), hubs have only
...@@ -36,7 +39,7 @@ APIs exposed to USB device drivers. ...@@ -36,7 +39,7 @@ APIs exposed to USB device drivers.
FUNCTIONALITY FUNCTIONALITY
This driver is regularly tested on x86 hardware, and has also been This driver is regularly tested on x86 hardware, and has also been
used on PPC hardware so big/little endianneess issues should be gone. used on PPC hardware so big/little endianness issues should be gone.
It's believed to do all the right PCI magic so that I/O works even on It's believed to do all the right PCI magic so that I/O works even on
systems with interesting DMA mapping issues. systems with interesting DMA mapping issues.
...@@ -51,11 +54,13 @@ scheduling for interrupt transfers, which means among other things that ...@@ -51,11 +54,13 @@ scheduling for interrupt transfers, which means among other things that
connecting USB 1.1 hubs, keyboards, and mice to USB 2.0 hubs won't work. connecting USB 1.1 hubs, keyboards, and mice to USB 2.0 hubs won't work.
Connect them to USB 1.1 hubs, or to a root hub. Connect them to USB 1.1 hubs, or to a root hub.
Isochronous (ISO) transfer support is not yet working. No production Isochronous (ISO) transfer support is also newly functional. No production
high speed devices are available which would need it (though high quality high speed devices are available which would need it (though high quality
webcams are in the works!). Note that split transaction support for ISO webcams are in the works!). Note that split transaction support for ISO
transfers can't share much code with the code for high speed ISO transfers, transfers can't share much code with the code for high speed ISO transfers,
since EHCI represents these with a different data structure. since EHCI represents these with a different data structure. So for now,
most USB audio and video devices have the same restrictions as hubs, mice,
and keyboards: don't connect them using high speed USB hubs.
The EHCI root hub code should hand off USB 1.1 devices to its companion The EHCI root hub code should hand off USB 1.1 devices to its companion
controller. This driver doesn't need to know anything about those controller. This driver doesn't need to know anything about those
...@@ -99,6 +104,8 @@ Device drivers shouldn't care whether they're running over EHCI or not, ...@@ -99,6 +104,8 @@ Device drivers shouldn't care whether they're running over EHCI or not,
but they may want to check for "usb_device->speed == USB_SPEED_HIGH". but they may want to check for "usb_device->speed == USB_SPEED_HIGH".
High speed devices can do things that full speed (or low speed) ones High speed devices can do things that full speed (or low speed) ones
can't, such as "high bandwidth" periodic (interrupt or ISO) transfers. can't, such as "high bandwidth" periodic (interrupt or ISO) transfers.
Also, some values in device descriptors (such as polling intervals for
periodic transfers) use different encodings when operating at high speed.
PERFORMANCE PERFORMANCE
......
...@@ -69,3 +69,11 @@ CONFIG_USB_BANDWIDTH ...@@ -69,3 +69,11 @@ CONFIG_USB_BANDWIDTH
If you say N here, these conditions will cause warning messages If you say N here, these conditions will cause warning messages
about USB bandwidth usage to be logged and some devices or about USB bandwidth usage to be logged and some devices or
drivers may not work correctly. drivers may not work correctly.
CONFIG_USB_DYNAMIC_MINORS
If you say Y here, the USB subsystem will use dynamic minor
allocation for any device that uses the USB major number.
This means that you can have more than 16 of a single type
of device (like USB printers).
If you are unsure about this, say N here.
...@@ -107,7 +107,11 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H ...@@ -107,7 +107,11 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
#define USBLP_REQ_RESET 0x02 #define USBLP_REQ_RESET 0x02
#define USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST 0x00 /* HP Vendor-specific */ #define USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST 0x00 /* HP Vendor-specific */
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USBLP_MINORS 256
#else
#define USBLP_MINORS 16 #define USBLP_MINORS 16
#endif
#define USBLP_MINOR_BASE 0 #define USBLP_MINOR_BASE 0
#define USBLP_WRITE_TIMEOUT (5*HZ) /* 5 seconds */ #define USBLP_WRITE_TIMEOUT (5*HZ) /* 5 seconds */
...@@ -208,6 +212,8 @@ static int usblp_select_alts(struct usblp *usblp); ...@@ -208,6 +212,8 @@ static int usblp_select_alts(struct usblp *usblp);
static int usblp_set_protocol(struct usblp *usblp, int protocol); static int usblp_set_protocol(struct usblp *usblp, int protocol);
static int usblp_cache_device_id_string(struct usblp *usblp); static int usblp_cache_device_id_string(struct usblp *usblp);
/* forward reference to make our lives easier */
extern struct usb_driver usblp_driver;
/* /*
* Functions for usblp control messages. * Functions for usblp control messages.
...@@ -366,6 +372,7 @@ static void usblp_cleanup (struct usblp *usblp) ...@@ -366,6 +372,7 @@ static void usblp_cleanup (struct usblp *usblp)
{ {
devfs_unregister (usblp->devfs); devfs_unregister (usblp->devfs);
usblp_table [usblp->minor] = NULL; usblp_table [usblp->minor] = NULL;
usb_deregister_dev (&usblp_driver, 1, usblp->minor);
info("usblp%d: removed", usblp->minor); info("usblp%d: removed", usblp->minor);
kfree (usblp->writeurb->transfer_buffer); kfree (usblp->writeurb->transfer_buffer);
...@@ -801,12 +808,14 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -801,12 +808,14 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum,
init_waitqueue_head(&usblp->wait); init_waitqueue_head(&usblp->wait);
usblp->ifnum = ifnum; usblp->ifnum = ifnum;
/* Look for a free usblp_table entry. */ if (usb_register_dev(&usblp_driver, 1, &usblp->minor)) {
while (usblp_table[usblp->minor]) { /* Look for a free usblp_table entry on our own. */
usblp->minor++; while (usblp_table[usblp->minor]) {
if (usblp->minor >= USBLP_MINORS) { usblp->minor++;
err("no more free usblp devices"); if (usblp->minor >= USBLP_MINORS) {
goto abort; err("no more free usblp devices");
goto abort;
}
} }
} }
......
...@@ -4,11 +4,8 @@ ...@@ -4,11 +4,8 @@
bool ' USB verbose debug messages' CONFIG_USB_DEBUG bool ' USB verbose debug messages' CONFIG_USB_DEBUG
comment 'Miscellaneous USB options' comment 'Miscellaneous USB options'
bool ' USB device filesystem' CONFIG_USB_DEVICEFS bool ' USB device filesystem' CONFIG_USB_DEVICEFS
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT
bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH dep_bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH $CONFIG_EXPERIMENTAL
else dep_bool ' Dynamic USB minor allocation (EXPERIMENTAL)' CONFIG_USB_DYNAMIC_MINORS $CONFIG_EXPERIMENTAL
define_bool CONFIG_USB_BANDWIDTH n
fi
bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
more docs, etc) more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000 * (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter) * (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002
* *
* NOTE! This is not actually a driver at all, rather this is * NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the * just a collection of helper routines that implement the
...@@ -59,7 +60,47 @@ LIST_HEAD(usb_driver_list); ...@@ -59,7 +60,47 @@ LIST_HEAD(usb_driver_list);
devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */
static struct usb_driver *usb_minors[256]; #define MAX_USB_MINORS 256
static struct usb_driver *usb_minors[MAX_USB_MINORS];
static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
static int usb_register_minors (struct usb_driver *driver, int num_minors, int start_minor)
{
int i;
dbg("registering %d minors, starting at %d", num_minors, start_minor);
if (start_minor + num_minors >= MAX_USB_MINORS)
return -EINVAL;
spin_lock (&minor_lock);
for (i = start_minor; i < (start_minor + num_minors); ++i)
if (usb_minors[i]) {
spin_unlock (&minor_lock);
err("minor %d is already in use, error registering %s driver",
i, driver->name);
return -EINVAL;
}
for (i = start_minor; i < (start_minor + num_minors); ++i)
usb_minors[i] = driver;
spin_unlock (&minor_lock);
return 0;
}
static void usb_deregister_minors (struct usb_driver *driver, int num_minors, int start_minor)
{
int i;
dbg ("%s is removing %d minors starting at %d", driver->name,
num_minors, start_minor);
spin_lock (&minor_lock);
for (i = start_minor; i < (start_minor + num_minors); ++i)
usb_minors[i] = NULL;
spin_unlock (&minor_lock);
}
/** /**
* usb_register - register a USB driver * usb_register - register a USB driver
...@@ -72,18 +113,20 @@ static struct usb_driver *usb_minors[256]; ...@@ -72,18 +113,20 @@ static struct usb_driver *usb_minors[256];
*/ */
int usb_register(struct usb_driver *new_driver) int usb_register(struct usb_driver *new_driver)
{ {
int i; int retval = 0;
if ((new_driver->fops) && (new_driver->num_minors == 0)) {
err ("%s driver must specify num_minors", new_driver->name);
return -EINVAL;
}
#ifndef CONFIG_USB_DYNAMIC_MINORS
if (new_driver->fops != NULL) { if (new_driver->fops != NULL) {
for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i) { retval = usb_register_minors (new_driver, new_driver->num_minors, new_driver->minor);
if (usb_minors[i]) { if (retval)
err("error registering %s driver", new_driver->name); return retval;
return -EINVAL;
}
}
for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i)
usb_minors[i] = new_driver;
} }
#endif
info("registered new driver %s", new_driver->name); info("registered new driver %s", new_driver->name);
...@@ -96,9 +139,96 @@ int usb_register(struct usb_driver *new_driver) ...@@ -96,9 +139,96 @@ int usb_register(struct usb_driver *new_driver)
usbfs_update_special(); usbfs_update_special();
return 0; return retval;
} }
/**
* usb_register_dev - register a USB device, and ask for a minor number
* @new_driver: USB operations for the driver
* @num_minors: number of minor numbers requested for this device
* @start_minor: place to put the new starting minor number
*
* Used to ask the USB core for a new minor number for a device that has
* just showed up. This is used to dynamically allocate minor numbers
* from the pool of USB reserved minor numbers.
*
* This should be called by all drivers that use the USB major number.
* This only returns a good value of CONFIG_USB_DYNAMIC_MINORS is
* selected by the user.
*
* usb_deregister_dev() should be called when the driver is done with
* the minor numbers given out by this function.
*
* Returns -ENODEV if CONFIG_USB_DYNAMIC_MINORS is not enabled in this
* kernel, -EINVAL if something bad happens with trying to register a
* device, and 0 on success, alone with a value that the driver should
* use in start_minor.
*/
#ifdef CONFIG_USB_DYNAMIC_MINORS
int usb_register_dev (struct usb_driver *new_driver, int num_minors, int *start_minor)
{
int i;
int j;
int good_spot;
int retval = -EINVAL;
dbg ("%s is asking for %d minors", new_driver->name, num_minors);
if (new_driver->fops == NULL)
goto exit;
*start_minor = 0;
spin_lock (&minor_lock);
for (i = 0; i < MAX_USB_MINORS; ++i) {
if (usb_minors[i])
continue;
good_spot = 1;
for (j = 1; j <= num_minors-1; ++j)
if (usb_minors[i+j]) {
good_spot = 0;
break;
}
if (good_spot == 0)
continue;
*start_minor = i;
spin_unlock (&minor_lock);
retval = usb_register_minors (new_driver, num_minors, *start_minor);
if (retval) {
/* someone snuck in here, so let's start looking all over again */
spin_lock (&minor_lock);
i = 0;
continue;
}
goto exit;
}
spin_unlock (&minor_lock);
exit:
return retval;
}
/**
* usb_deregister_dev - deregister a USB device's dynamic minor.
* @driver: USB operations for the driver
* @num_minors: number of minor numbers to put back.
* @start_minor: the starting minor number
*
* Used in conjunction with usb_register_dev(). This function is called
* when the USB driver is finished with the minor numbers gotten from a
* call to usb_register_dev() (usually when the device is disconnected
* from the system.)
*
* This should be called by all drivers that use the USB major number.
*/
void usb_deregister_dev (struct usb_driver *driver, int num_minors, int start_minor)
{
usb_deregister_minors (driver, num_minors, start_minor);
}
#endif /* CONFIG_USB_DYNAMIC_MINORS */
/** /**
* usb_scan_devices - scans all unclaimed USB interfaces * usb_scan_devices - scans all unclaimed USB interfaces
* Context: !in_interrupt () * Context: !in_interrupt ()
...@@ -177,12 +307,13 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) ...@@ -177,12 +307,13 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
void usb_deregister(struct usb_driver *driver) void usb_deregister(struct usb_driver *driver)
{ {
struct list_head *tmp; struct list_head *tmp;
int i;
info("deregistering driver %s", driver->name); info("deregistering driver %s", driver->name);
#ifndef CONFIG_USB_DYNAMIC_MINORS
if (driver->fops != NULL) if (driver->fops != NULL)
for (i = driver->minor; i < driver->minor + driver->num_minors; ++i) usb_deregister_minors (driver, driver->num_minors, driver->minor);
usb_minors[i] = NULL; #endif
/* /*
* first we remove the driver, to be sure it doesn't get used by * first we remove the driver, to be sure it doesn't get used by
...@@ -2524,14 +2655,14 @@ int usb_new_device(struct usb_device *dev) ...@@ -2524,14 +2655,14 @@ int usb_new_device(struct usb_device *dev)
static int usb_open(struct inode * inode, struct file * file) static int usb_open(struct inode * inode, struct file * file)
{ {
int minor = minor(inode->i_rdev); int minor = minor(inode->i_rdev);
struct usb_driver *c = usb_minors[minor]; struct usb_driver *c;
int err = -ENODEV; int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL; struct file_operations *old_fops, *new_fops = NULL;
/* spin_lock (&minor_lock);
* No load-on-demand? Randy, could you ACK that it's really not c = usb_minors[minor];
* supposed to be done? -- AV spin_unlock (&minor_lock);
*/
if (!c || !(new_fops = fops_get(c->fops))) if (!c || !(new_fops = fops_get(c->fops)))
return err; return err;
old_fops = file->f_op; old_fops = file->f_op;
...@@ -2623,9 +2754,13 @@ EXPORT_SYMBOL(usb_register); ...@@ -2623,9 +2754,13 @@ EXPORT_SYMBOL(usb_register);
EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_scan_devices); EXPORT_SYMBOL(usb_scan_devices);
#ifdef CONFIG_USB_DYNAMIC_MINORS
EXPORT_SYMBOL(usb_register_dev);
EXPORT_SYMBOL(usb_deregister_dev);
#endif
EXPORT_SYMBOL(usb_alloc_dev); EXPORT_SYMBOL(usb_alloc_dev);
EXPORT_SYMBOL(usb_free_dev); EXPORT_SYMBOL(usb_free_dev);
EXPORT_SYMBOL(usb_inc_dev_use);
EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum); EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum);
EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_claim_interface);
......
...@@ -91,7 +91,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -91,7 +91,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
if (!(temp & PORT_CONNECT)) if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0; ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
set_bit (i, buf); if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
status = STS_PCD; status = STS_PCD;
} }
} }
...@@ -141,7 +144,7 @@ static int ehci_hub_control ( ...@@ -141,7 +144,7 @@ static int ehci_hub_control (
) { ) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params); int ports = HCS_N_PORTS (ehci->hcs_params);
u32 temp; u32 temp, status;
unsigned long flags; unsigned long flags;
int retval = 0; int retval = 0;
...@@ -219,22 +222,22 @@ static int ehci_hub_control ( ...@@ -219,22 +222,22 @@ static int ehci_hub_control (
if (!wIndex || wIndex > ports) if (!wIndex || wIndex > ports)
goto error; goto error;
wIndex--; wIndex--;
memset (buf, 0, 4); status = 0;
temp = readl (&ehci->regs->port_status [wIndex]); temp = readl (&ehci->regs->port_status [wIndex]);
// wPortChange bits // wPortChange bits
if (temp & PORT_CSC) if (temp & PORT_CSC)
set_bit (USB_PORT_FEAT_C_CONNECTION, buf); status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC) if (temp & PORT_PEC)
set_bit (USB_PORT_FEAT_C_ENABLE, buf); status |= 1 << USB_PORT_FEAT_C_ENABLE;
// USB_PORT_FEAT_C_SUSPEND // USB_PORT_FEAT_C_SUSPEND
if (temp & PORT_OCC) if (temp & PORT_OCC)
set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf); status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resets must GetPortStatus to complete it!! */ /* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET) if ((temp & PORT_RESET)
&& jiffies > ehci->reset_done [wIndex]) { && jiffies > ehci->reset_done [wIndex]) {
set_bit (USB_PORT_FEAT_C_RESET, buf); status |= 1 << USB_PORT_FEAT_C_RESET;
/* force reset to complete */ /* force reset to complete */
writel (temp & ~PORT_RESET, writel (temp & ~PORT_RESET,
...@@ -252,26 +255,27 @@ static int ehci_hub_control ( ...@@ -252,26 +255,27 @@ static int ehci_hub_control (
// don't show wPortStatus if it's owned by a companion hc // don't show wPortStatus if it's owned by a companion hc
if (!(temp & PORT_OWNER)) { if (!(temp & PORT_OWNER)) {
if (temp & PORT_CONNECT) { if (temp & PORT_CONNECT) {
set_bit (USB_PORT_FEAT_CONNECTION, buf); status |= 1 << USB_PORT_FEAT_CONNECTION;
set_bit (USB_PORT_FEAT_HIGHSPEED, buf); status |= 1 << USB_PORT_FEAT_HIGHSPEED;
} }
if (temp & PORT_PE) if (temp & PORT_PE)
set_bit (USB_PORT_FEAT_ENABLE, buf); status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & PORT_SUSPEND) if (temp & PORT_SUSPEND)
set_bit (USB_PORT_FEAT_SUSPEND, buf); status |= 1 << USB_PORT_FEAT_SUSPEND;
if (temp & PORT_OC) if (temp & PORT_OC)
set_bit (USB_PORT_FEAT_OVER_CURRENT, buf); status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (temp & PORT_RESET) if (temp & PORT_RESET)
set_bit (USB_PORT_FEAT_RESET, buf); status |= 1 << USB_PORT_FEAT_RESET;
if (temp & PORT_POWER) if (temp & PORT_POWER)
set_bit (USB_PORT_FEAT_POWER, buf); status |= 1 << USB_PORT_FEAT_POWER;
} }
#ifndef EHCI_VERBOSE_DEBUG #ifndef EHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ if (status & ~0xffff) /* only if wPortChange is interesting */
#endif #endif
dbg_port (hcd, "GetStatus", wIndex + 1, temp); dbg_port (hcd, "GetStatus", wIndex + 1, temp);
cpu_to_le32s ((u32 *) buf); // we "know" this alignment is good, caller used kmalloc()...
*((u32 *) buf) = cpu_to_le32 (status);
break; break;
case SetHubFeature: case SetHubFeature:
switch (wValue) { switch (wValue) {
......
...@@ -96,7 +96,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -96,7 +96,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
| RH_PS_OCIC | RH_PS_PRSC; | RH_PS_OCIC | RH_PS_PRSC;
if (status) { if (status) {
changed = 1; changed = 1;
set_bit (i + 1, buf); if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
} }
} }
return changed ? length : 0; return changed ? length : 0;
......
...@@ -119,8 +119,12 @@ ...@@ -119,8 +119,12 @@
#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND #define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND
#define TO_GET_READY TO_DEFAULT_COMMAND #define TO_GET_READY TO_DEFAULT_COMMAND
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define MDC800_DEVICE_MINOR_BASE 0
#else
/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */ /* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
#define MDC800_DEVICE_MINOR_BASE 32 #define MDC800_DEVICE_MINOR_BASE 32
#endif
/************************************************************************** /**************************************************************************
...@@ -176,6 +180,7 @@ struct mdc800_data ...@@ -176,6 +180,7 @@ struct mdc800_data
int pic_index; // Cache for the Imagesize (-1 for nothing cached ) int pic_index; // Cache for the Imagesize (-1 for nothing cached )
int pic_len; int pic_len;
int minor;
}; };
...@@ -470,6 +475,8 @@ static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum, ...@@ -470,6 +475,8 @@ static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum,
down (&mdc800->io_lock); down (&mdc800->io_lock);
usb_register_dev (&mdc800_usb_driver, 1, &mdc800->minor);
mdc800->dev=dev; mdc800->dev=dev;
mdc800->open=0; mdc800->open=0;
...@@ -525,6 +532,8 @@ static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr) ...@@ -525,6 +532,8 @@ static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
if (mdc800->state == NOT_CONNECTED) if (mdc800->state == NOT_CONNECTED)
return; return;
usb_deregister_dev (&mdc800_usb_driver, 1, mdc800->minor);
mdc800->state=NOT_CONNECTED; mdc800->state=NOT_CONNECTED;
usb_unlink_urb (mdc800->irq_urb); usb_unlink_urb (mdc800->irq_urb);
......
...@@ -953,9 +953,11 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum, ...@@ -953,9 +953,11 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum,
down(&scn_mutex); down(&scn_mutex);
for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) { if (usb_register_dev(&scanner_driver, 1, &scn_minor)) {
if (!p_scn_table[scn_minor]) for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
break; if (!p_scn_table[scn_minor])
break;
}
} }
/* Check to make sure that the last slot isn't already taken */ /* Check to make sure that the last slot isn't already taken */
...@@ -1086,6 +1088,7 @@ disconnect_scanner(struct usb_device *dev, void *ptr) ...@@ -1086,6 +1088,7 @@ disconnect_scanner(struct usb_device *dev, void *ptr)
dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
devfs_unregister(scn->devfs); devfs_unregister(scn->devfs);
usb_deregister_dev(&scanner_driver, 1, scn->scn_minor);
p_scn_table[scn->scn_minor] = NULL; p_scn_table[scn->scn_minor] = NULL;
usb_free_urb(scn->scn_irq); usb_free_urb(scn->scn_irq);
up (&(scn->sem)); up (&(scn->sem));
......
...@@ -233,8 +233,13 @@ MODULE_DEVICE_TABLE (usb, scanner_device_ids); ...@@ -233,8 +233,13 @@ MODULE_DEVICE_TABLE (usb, scanner_device_ids);
#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest) #define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest)
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define SCN_MAX_MNR 256
#define SCN_BASE_MNR 0
#else
#define SCN_MAX_MNR 16 /* We're allocated 16 minors */ #define SCN_MAX_MNR 16 /* We're allocated 16 minors */
#define SCN_BASE_MNR 48 /* USB Scanners start at minor 48 */ #define SCN_BASE_MNR 48 /* USB Scanners start at minor 48 */
#endif
static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */ static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */
......
...@@ -1271,6 +1271,7 @@ void hid_init_reports(struct hid_device *hid) ...@@ -1271,6 +1271,7 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 #define USB_DEVICE_ID_ATEN_UC100KM 0x2004
#define USB_DEVICE_ID_ATEN_CS124U 0x2202 #define USB_DEVICE_ID_ATEN_CS124U 0x2202
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 #define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
struct hid_blacklist { struct hid_blacklist {
__u16 idVendor; __u16 idVendor;
...@@ -1288,6 +1289,7 @@ struct hid_blacklist { ...@@ -1288,6 +1289,7 @@ struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ 0, 0 } { 0, 0 }
}; };
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
* e-mail - mail your message to Paul Stewart <stewart@wetlogic.net> * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
*/ */
#define HIDDEV_MINOR_BASE 96 #include <linux/config.h>
#define HIDDEV_MINORS 16
#define HIDDEV_BUFFER_SIZE 64
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -39,6 +36,15 @@ ...@@ -39,6 +36,15 @@
#include "hid.h" #include "hid.h"
#include <linux/hiddev.h> #include <linux/hiddev.h>
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define HIDDEV_MINOR_BASE 0
#define HIDDEV_MINORS 256
#else
#define HIDDEV_MINOR_BASE 96
#define HIDDEV_MINORS 16
#endif
#define HIDDEV_BUFFER_SIZE 64
struct hiddev { struct hiddev {
int exist; int exist;
int open; int open;
...@@ -62,6 +68,9 @@ struct hiddev_list { ...@@ -62,6 +68,9 @@ struct hiddev_list {
static struct hiddev *hiddev_table[HIDDEV_MINORS]; static struct hiddev *hiddev_table[HIDDEV_MINORS];
static devfs_handle_t hiddev_devfs_handle; static devfs_handle_t hiddev_devfs_handle;
/* forward reference to make our lives easier */
extern struct usb_driver hiddev_driver;
/* /*
* Find a report, given the report's type and ID. The ID can be specified * Find a report, given the report's type and ID. The ID can be specified
* indirectly by REPORT_ID_FIRST (which returns the first report of the given * indirectly by REPORT_ID_FIRST (which returns the first report of the given
...@@ -184,6 +193,7 @@ static int hiddev_fasync(int fd, struct file *file, int on) ...@@ -184,6 +193,7 @@ static int hiddev_fasync(int fd, struct file *file, int on)
static void hiddev_cleanup(struct hiddev *hiddev) static void hiddev_cleanup(struct hiddev *hiddev)
{ {
devfs_unregister(hiddev->devfs); devfs_unregister(hiddev->devfs);
usb_deregister_dev(&hiddev_driver, 1, hiddev->minor);
hiddev_table[hiddev->minor] = NULL; hiddev_table[hiddev->minor] = NULL;
kfree(hiddev); kfree(hiddev);
} }
...@@ -605,10 +615,12 @@ int hiddev_connect(struct hid_device *hid) ...@@ -605,10 +615,12 @@ int hiddev_connect(struct hid_device *hid)
if (i == hid->maxapplication) if (i == hid->maxapplication)
return -1; return -1;
for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++); if (usb_register_dev (&hiddev_driver, 1, &minor)) {
if (minor == HIDDEV_MINORS) { for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
printk(KERN_ERR "hiddev: no more free hiddev devices\n"); if (minor == HIDDEV_MINORS) {
return -1; printk(KERN_ERR "hiddev: no more free hiddev devices\n");
return -1;
}
} }
if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
O_TARGET := media.o O_TARGET := media.o
export-objs := ov511.o pwc-uncompress.o export-objs := ov511.o pwc-uncompress.o usbvideo.o
pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
......
...@@ -52,12 +52,17 @@ ...@@ -52,12 +52,17 @@
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define NRDABUSB 256
#else
#define NRDABUSB 4 #define NRDABUSB 4
#endif
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
static dabusb_t dabusb[NRDABUSB]; static dabusb_t dabusb[NRDABUSB];
static int buffers = 256; static int buffers = 256;
extern struct usb_driver dabusb_driver;
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
...@@ -734,15 +739,18 @@ static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum, ...@@ -734,15 +739,18 @@ static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum,
if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999) if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999)
return NULL; return NULL;
devnum = dabusb_find_struct (); if (usb_register_dev (&dabusb_driver, 1, &devnum)) {
if (devnum == -1) devnum = dabusb_find_struct ();
return NULL; if (devnum == -1)
return NULL;
}
s = &dabusb[devnum]; s = &dabusb[devnum];
down (&s->mutex); down (&s->mutex);
s->remove_pending = 0; s->remove_pending = 0;
s->usbdev = usbdev; s->usbdev = usbdev;
s->devnum = devnum;
if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) { if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) {
err("set_configuration failed"); err("set_configuration failed");
...@@ -777,6 +785,7 @@ static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) ...@@ -777,6 +785,7 @@ static void dabusb_disconnect (struct usb_device *usbdev, void *ptr)
dbg("dabusb_disconnect"); dbg("dabusb_disconnect");
usb_deregister_dev (&dabusb_driver, 1, s->devnum);
s->remove_pending = 1; s->remove_pending = 1;
wake_up (&s->wait); wake_up (&s->wait);
if (s->state == _started) if (s->state == _started)
......
...@@ -6,7 +6,11 @@ typedef struct ...@@ -6,7 +6,11 @@ typedef struct
unsigned int pipe; unsigned int pipe;
}bulk_transfer_t,*pbulk_transfer_t; }bulk_transfer_t,*pbulk_transfer_t;
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define DABUSB_MINOR 0
#else
#define DABUSB_MINOR 240 /* some unassigned USB minor */ #define DABUSB_MINOR 240 /* some unassigned USB minor */
#endif
#define DABUSB_VERSION 0x1000 #define DABUSB_VERSION 0x1000
#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) #define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t)
#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) #define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int)
...@@ -31,6 +35,7 @@ typedef struct ...@@ -31,6 +35,7 @@ typedef struct
unsigned int overruns; unsigned int overruns;
int readptr; int readptr;
int opened; int opened;
int devnum;
struct list_head free_buff_list; struct list_head free_buff_list;
struct list_head rec_buff_list; struct list_head rec_buff_list;
} dabusb_t,*pdabusb_t; } dabusb_t,*pdabusb_t;
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#define __NO_VERSION__ /* Temporary: usbvideo is not a module yet */
#include <linux/module.h> #include <linux/module.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
...@@ -55,6 +54,24 @@ static int usbvideo_default_procfs_write_proc( ...@@ -55,6 +54,24 @@ static int usbvideo_default_procfs_write_proc(
unsigned long count, void *data); unsigned long count, void *data);
#endif #endif
static void usbvideo_Disconnect(struct usb_device *dev, void *ptr);
static void usbvideo_CameraRelease(uvd_t *uvd);
static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
static int usbvideo_v4l_open(struct inode *inode, struct file *file);
static int usbvideo_v4l_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
static int usbvideo_v4l_close(struct inode *inode, struct file *file);
static int usbvideo_StartDataPump(uvd_t *uvd);
static void usbvideo_StopDataPump(uvd_t *uvd);
static int usbvideo_GetFrame(uvd_t *uvd, int frameNum);
static int usbvideo_NewFrame(uvd_t *uvd, int framenum);
static void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd,
usbvideo_frame_t *frame);
/*******************************/ /*******************************/
/* Memory management functions */ /* Memory management functions */
/*******************************/ /*******************************/
...@@ -73,7 +90,7 @@ unsigned long usbvideo_kvirt_to_pa(unsigned long adr) ...@@ -73,7 +90,7 @@ unsigned long usbvideo_kvirt_to_pa(unsigned long adr)
return ret; return ret;
} }
void *usbvideo_rvmalloc(unsigned long size) static void *usbvideo_rvmalloc(unsigned long size)
{ {
void *mem; void *mem;
unsigned long adr; unsigned long adr;
...@@ -94,7 +111,7 @@ void *usbvideo_rvmalloc(unsigned long size) ...@@ -94,7 +111,7 @@ void *usbvideo_rvmalloc(unsigned long size)
return mem; return mem;
} }
void usbvideo_rvfree(void *mem, unsigned long size) static void usbvideo_rvfree(void *mem, unsigned long size)
{ {
unsigned long adr; unsigned long adr;
...@@ -110,13 +127,13 @@ void usbvideo_rvfree(void *mem, unsigned long size) ...@@ -110,13 +127,13 @@ void usbvideo_rvfree(void *mem, unsigned long size)
vfree(mem); vfree(mem);
} }
void RingQueue_Initialize(RingQueue_t *rq) static void RingQueue_Initialize(RingQueue_t *rq)
{ {
assert(rq != NULL); assert(rq != NULL);
init_waitqueue_head(&rq->wqh); init_waitqueue_head(&rq->wqh);
} }
void RingQueue_Allocate(RingQueue_t *rq, int rqLen) static void RingQueue_Allocate(RingQueue_t *rq, int rqLen)
{ {
assert(rq != NULL); assert(rq != NULL);
assert(rqLen > 0); assert(rqLen > 0);
...@@ -125,14 +142,14 @@ void RingQueue_Allocate(RingQueue_t *rq, int rqLen) ...@@ -125,14 +142,14 @@ void RingQueue_Allocate(RingQueue_t *rq, int rqLen)
assert(rq->queue != NULL); assert(rq->queue != NULL);
} }
int RingQueue_IsAllocated(const RingQueue_t *rq) static int RingQueue_IsAllocated(const RingQueue_t *rq)
{ {
if (rq == NULL) if (rq == NULL)
return 0; return 0;
return (rq->queue != NULL) && (rq->length > 0); return (rq->queue != NULL) && (rq->length > 0);
} }
void RingQueue_Free(RingQueue_t *rq) static void RingQueue_Free(RingQueue_t *rq)
{ {
assert(rq != NULL); assert(rq != NULL);
if (RingQueue_IsAllocated(rq)) { if (RingQueue_IsAllocated(rq)) {
...@@ -154,6 +171,8 @@ int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len) ...@@ -154,6 +171,8 @@ int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len)
return len; return len;
} }
EXPORT_SYMBOL(RingQueue_Dequeue);
int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n) int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n)
{ {
int enqueued = 0; int enqueued = 0;
...@@ -184,6 +203,8 @@ int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n) ...@@ -184,6 +203,8 @@ int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n)
return enqueued; return enqueued;
} }
EXPORT_SYMBOL(RingQueue_Enqueue);
int RingQueue_GetLength(const RingQueue_t *rq) int RingQueue_GetLength(const RingQueue_t *rq)
{ {
int ri, wi; int ri, wi;
...@@ -200,7 +221,9 @@ int RingQueue_GetLength(const RingQueue_t *rq) ...@@ -200,7 +221,9 @@ int RingQueue_GetLength(const RingQueue_t *rq)
return wi + (rq->length - ri); return wi + (rq->length - ri);
} }
void RingQueue_InterruptibleSleepOn(RingQueue_t *rq) EXPORT_SYMBOL(RingQueue_GetLength);
static void RingQueue_InterruptibleSleepOn(RingQueue_t *rq)
{ {
assert(rq != NULL); assert(rq != NULL);
interruptible_sleep_on(&rq->wqh); interruptible_sleep_on(&rq->wqh);
...@@ -213,6 +236,8 @@ void RingQueue_WakeUpInterruptible(RingQueue_t *rq) ...@@ -213,6 +236,8 @@ void RingQueue_WakeUpInterruptible(RingQueue_t *rq)
wake_up_interruptible(&rq->wqh); wake_up_interruptible(&rq->wqh);
} }
EXPORT_SYMBOL(RingQueue_WakeUpInterruptible);
/* /*
* usbvideo_VideosizeToString() * usbvideo_VideosizeToString()
* *
...@@ -222,7 +247,7 @@ void RingQueue_WakeUpInterruptible(RingQueue_t *rq) ...@@ -222,7 +247,7 @@ void RingQueue_WakeUpInterruptible(RingQueue_t *rq)
* 07-Aug-2000 Created. * 07-Aug-2000 Created.
* 19-Oct-2000 Reworked for usbvideo module. * 19-Oct-2000 Reworked for usbvideo module.
*/ */
void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
{ {
char tmp[40]; char tmp[40];
int n; int n;
...@@ -241,8 +266,8 @@ void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) ...@@ -241,8 +266,8 @@ void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
* History: * History:
* 01-Feb-2000 Created. * 01-Feb-2000 Created.
*/ */
void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, static void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame,
int x, int y, int ch) int x, int y, int ch)
{ {
static const unsigned short digits[16] = { static const unsigned short digits[16] = {
0xF6DE, /* 0 */ 0xF6DE, /* 0 */
...@@ -296,8 +321,8 @@ void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, ...@@ -296,8 +321,8 @@ void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame,
* History: * History:
* 01-Feb-2000 Created. * 01-Feb-2000 Created.
*/ */
void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, static void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame,
int x, int y, const char *str) int x, int y, const char *str)
{ {
while (*str) { while (*str) {
usbvideo_OverlayChar(uvd, frame, x, y, *str); usbvideo_OverlayChar(uvd, frame, x, y, *str);
...@@ -314,7 +339,7 @@ void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, ...@@ -314,7 +339,7 @@ void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame,
* History: * History:
* 01-Feb-2000 Created. * 01-Feb-2000 Created.
*/ */
void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame) static void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame)
{ {
const int y_diff = 8; const int y_diff = 8;
char tmp[16]; char tmp[16];
...@@ -437,7 +462,7 @@ void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame) ...@@ -437,7 +462,7 @@ void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame)
* History: * History:
* 14-Jan-2000 Corrected default multiplier. * 14-Jan-2000 Corrected default multiplier.
*/ */
void usbvideo_ReportStatistics(const uvd_t *uvd) static void usbvideo_ReportStatistics(const uvd_t *uvd)
{ {
if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { if ((uvd != NULL) && (uvd->stats.urb_count > 0)) {
unsigned long allPackets, badPackets, goodPackets, percent; unsigned long allPackets, badPackets, goodPackets, percent;
...@@ -549,6 +574,8 @@ void usbvideo_DrawLine( ...@@ -549,6 +574,8 @@ void usbvideo_DrawLine(
} }
} }
EXPORT_SYMBOL(usbvideo_DrawLine);
/* /*
* usbvideo_TestPattern() * usbvideo_TestPattern()
* *
...@@ -636,6 +663,8 @@ void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode) ...@@ -636,6 +663,8 @@ void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode)
usbvideo_OverlayStats(uvd, frame); usbvideo_OverlayStats(uvd, frame);
} }
EXPORT_SYMBOL(usbvideo_TestPattern);
/* /*
* usbvideo_HexDump() * usbvideo_HexDump()
* *
...@@ -663,6 +692,8 @@ void usbvideo_HexDump(const unsigned char *data, int len) ...@@ -663,6 +692,8 @@ void usbvideo_HexDump(const unsigned char *data, int len)
printk("%s\n", tmp); printk("%s\n", tmp);
} }
EXPORT_SYMBOL(usbvideo_HexDump);
/* Debugging aid */ /* Debugging aid */
void usbvideo_SayAndWait(const char *what) void usbvideo_SayAndWait(const char *what)
{ {
...@@ -672,6 +703,8 @@ void usbvideo_SayAndWait(const char *what) ...@@ -672,6 +703,8 @@ void usbvideo_SayAndWait(const char *what)
interruptible_sleep_on_timeout (&wq, HZ*3); /* Timeout */ interruptible_sleep_on_timeout (&wq, HZ*3); /* Timeout */
} }
EXPORT_SYMBOL(usbvideo_SayAndWait);
/* ******************************************************************** */ /* ******************************************************************** */
static void usbvideo_ClientIncModCount(uvd_t *uvd) static void usbvideo_ClientIncModCount(uvd_t *uvd)
...@@ -825,6 +858,8 @@ int usbvideo_register( ...@@ -825,6 +858,8 @@ int usbvideo_register(
return 0; return 0;
} }
EXPORT_SYMBOL(usbvideo_register);
/* /*
* usbvideo_Deregister() * usbvideo_Deregister()
* *
...@@ -886,6 +921,8 @@ void usbvideo_Deregister(usbvideo_t **pCams) ...@@ -886,6 +921,8 @@ void usbvideo_Deregister(usbvideo_t **pCams)
*pCams = NULL; *pCams = NULL;
} }
EXPORT_SYMBOL(usbvideo_Deregister);
/* /*
* usbvideo_Disconnect() * usbvideo_Disconnect()
* *
...@@ -908,7 +945,7 @@ void usbvideo_Deregister(usbvideo_t **pCams) ...@@ -908,7 +945,7 @@ void usbvideo_Deregister(usbvideo_t **pCams)
* 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
* 19-Oct-2000 Moved to usbvideo module. * 19-Oct-2000 Moved to usbvideo module.
*/ */
void usbvideo_Disconnect(struct usb_device *dev, void *ptr) static void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
{ {
static const char proc[] = "usbvideo_Disconnect"; static const char proc[] = "usbvideo_Disconnect";
uvd_t *uvd = (uvd_t *) ptr; uvd_t *uvd = (uvd_t *) ptr;
...@@ -958,7 +995,7 @@ void usbvideo_Disconnect(struct usb_device *dev, void *ptr) ...@@ -958,7 +995,7 @@ void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
* History: * History:
* 27-Jan-2000 Created. * 27-Jan-2000 Created.
*/ */
void usbvideo_CameraRelease(uvd_t *uvd) static void usbvideo_CameraRelease(uvd_t *uvd)
{ {
static const char proc[] = "usbvideo_CameraRelease"; static const char proc[] = "usbvideo_CameraRelease";
if (uvd == NULL) { if (uvd == NULL) {
...@@ -1079,6 +1116,8 @@ uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams) ...@@ -1079,6 +1116,8 @@ uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams)
return uvd; return uvd;
} }
EXPORT_SYMBOL(usbvideo_AllocateDevice);
int usbvideo_RegisterVideoDevice(uvd_t *uvd) int usbvideo_RegisterVideoDevice(uvd_t *uvd)
{ {
static const char proc[] = "usbvideo_RegisterVideoDevice"; static const char proc[] = "usbvideo_RegisterVideoDevice";
...@@ -1139,9 +1178,11 @@ int usbvideo_RegisterVideoDevice(uvd_t *uvd) ...@@ -1139,9 +1178,11 @@ int usbvideo_RegisterVideoDevice(uvd_t *uvd)
return 0; return 0;
} }
EXPORT_SYMBOL(usbvideo_RegisterVideoDevice);
/* ******************************************************************** */ /* ******************************************************************** */
int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
{ {
uvd_t *uvd = file->private_data; uvd_t *uvd = file->private_data;
unsigned long start = vma->vm_start; unsigned long start = vma->vm_start;
...@@ -1185,7 +1226,7 @@ int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -1185,7 +1226,7 @@ int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
* 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
* 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
*/ */
int usbvideo_v4l_open(struct inode *inode, struct file *file) static int usbvideo_v4l_open(struct inode *inode, struct file *file)
{ {
static const char proc[] = "usbvideo_v4l_open"; static const char proc[] = "usbvideo_v4l_open";
struct video_device *dev = video_devdata(file); struct video_device *dev = video_devdata(file);
...@@ -1300,7 +1341,7 @@ int usbvideo_v4l_open(struct inode *inode, struct file *file) ...@@ -1300,7 +1341,7 @@ int usbvideo_v4l_open(struct inode *inode, struct file *file)
* 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
* 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep.
*/ */
int usbvideo_v4l_close(struct inode *inode, struct file *file) static int usbvideo_v4l_close(struct inode *inode, struct file *file)
{ {
static const char proc[] = "usbvideo_v4l_close"; static const char proc[] = "usbvideo_v4l_close";
struct video_device *dev = file->private_data; struct video_device *dev = file->private_data;
...@@ -1554,7 +1595,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, ...@@ -1554,7 +1595,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
return 0; return 0;
} }
int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl); return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);
...@@ -1571,7 +1612,7 @@ int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, ...@@ -1571,7 +1612,7 @@ int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
* 20-Oct-2000 Created. * 20-Oct-2000 Created.
* 01-Nov-2000 Added mutex (uvd->lock). * 01-Nov-2000 Added mutex (uvd->lock).
*/ */
int usbvideo_v4l_read(struct file *file, char *buf, static int usbvideo_v4l_read(struct file *file, char *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
static const char proc[] = "usbvideo_v4l_read"; static const char proc[] = "usbvideo_v4l_read";
...@@ -1809,7 +1850,7 @@ static void usbvideo_IsocIrq(struct urb *urb) ...@@ -1809,7 +1850,7 @@ static void usbvideo_IsocIrq(struct urb *urb)
* of hardcoded values. Simplified by using for loop, * of hardcoded values. Simplified by using for loop,
* allowed any number of URBs. * allowed any number of URBs.
*/ */
int usbvideo_StartDataPump(uvd_t *uvd) static int usbvideo_StartDataPump(uvd_t *uvd)
{ {
static const char proc[] = "usbvideo_StartDataPump"; static const char proc[] = "usbvideo_StartDataPump";
struct usb_device *dev = uvd->dev; struct usb_device *dev = uvd->dev;
...@@ -1885,7 +1926,7 @@ int usbvideo_StartDataPump(uvd_t *uvd) ...@@ -1885,7 +1926,7 @@ int usbvideo_StartDataPump(uvd_t *uvd)
* 22-Jan-2000 Corrected order of actions to work after surprise removal. * 22-Jan-2000 Corrected order of actions to work after surprise removal.
* 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
*/ */
void usbvideo_StopDataPump(uvd_t *uvd) static void usbvideo_StopDataPump(uvd_t *uvd)
{ {
static const char proc[] = "usbvideo_StopDataPump"; static const char proc[] = "usbvideo_StopDataPump";
int i, j; int i, j;
...@@ -1929,7 +1970,7 @@ void usbvideo_StopDataPump(uvd_t *uvd) ...@@ -1929,7 +1970,7 @@ void usbvideo_StopDataPump(uvd_t *uvd)
* 29-Mar-00 Added copying of previous frame into the current one. * 29-Mar-00 Added copying of previous frame into the current one.
* 6-Aug-00 Added model 3 video sizes, removed redundant width, height. * 6-Aug-00 Added model 3 video sizes, removed redundant width, height.
*/ */
int usbvideo_NewFrame(uvd_t *uvd, int framenum) static int usbvideo_NewFrame(uvd_t *uvd, int framenum)
{ {
usbvideo_frame_t *frame; usbvideo_frame_t *frame;
int n; int n;
...@@ -2004,7 +2045,7 @@ int usbvideo_NewFrame(uvd_t *uvd, int framenum) ...@@ -2004,7 +2045,7 @@ int usbvideo_NewFrame(uvd_t *uvd, int framenum)
* FLAGS_NO_DECODING set. Therefore, any regular build of any driver * FLAGS_NO_DECODING set. Therefore, any regular build of any driver
* based on usbvideo can use this feature at any time. * based on usbvideo can use this feature at any time.
*/ */
void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame) static void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame)
{ {
int n; int n;
...@@ -2034,7 +2075,7 @@ void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame) ...@@ -2034,7 +2075,7 @@ void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame)
} }
} }
int usbvideo_GetFrame(uvd_t *uvd, int frameNum) static int usbvideo_GetFrame(uvd_t *uvd, int frameNum)
{ {
static const char proc[] = "usbvideo_GetFrame"; static const char proc[] = "usbvideo_GetFrame";
usbvideo_frame_t *frame = &uvd->frame[frameNum]; usbvideo_frame_t *frame = &uvd->frame[frameNum];
...@@ -2225,6 +2266,8 @@ void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame) ...@@ -2225,6 +2266,8 @@ void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame)
usbvideo_OverlayStats(uvd, frame); usbvideo_OverlayStats(uvd, frame);
} }
EXPORT_SYMBOL(usbvideo_DeinterlaceFrame);
/* /*
* usbvideo_SoftwareContrastAdjustment() * usbvideo_SoftwareContrastAdjustment()
* *
...@@ -2235,7 +2278,8 @@ void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame) ...@@ -2235,7 +2278,8 @@ void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame)
* History: * History:
* 09-Feb-2001 Created. * 09-Feb-2001 Created.
*/ */
void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame) static void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd,
usbvideo_frame_t *frame)
{ {
static const char proc[] = "usbvideo_SoftwareContrastAdjustment"; static const char proc[] = "usbvideo_SoftwareContrastAdjustment";
int i, j, v4l_linesize; int i, j, v4l_linesize;
......
...@@ -304,35 +304,22 @@ typedef struct s_usbvideo_t usbvideo_t; ...@@ -304,35 +304,22 @@ typedef struct s_usbvideo_t usbvideo_t;
#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ #define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
void RingQueue_Initialize(RingQueue_t *rq);
void RingQueue_Allocate(RingQueue_t *rq, int rqLen);
int RingQueue_IsAllocated(const RingQueue_t *rq);
void RingQueue_Free(RingQueue_t *rq);
int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len); int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len);
int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n); int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n);
int RingQueue_GetLength(const RingQueue_t *rq); int RingQueue_GetLength(const RingQueue_t *rq);
void RingQueue_InterruptibleSleepOn(RingQueue_t *rq);
void RingQueue_WakeUpInterruptible(RingQueue_t *rq); void RingQueue_WakeUpInterruptible(RingQueue_t *rq);
void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame);
void usbvideo_DrawLine( void usbvideo_DrawLine(
usbvideo_frame_t *frame, usbvideo_frame_t *frame,
int x1, int y1, int x1, int y1,
int x2, int y2, int x2, int y2,
unsigned char cr, unsigned char cg, unsigned char cb); unsigned char cr, unsigned char cg, unsigned char cb);
void usbvideo_HexDump(const unsigned char *data, int len); void usbvideo_HexDump(const unsigned char *data, int len);
void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, int ch);
void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, const char *str);
void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame);
void usbvideo_ReportStatistics(const uvd_t *uvd);
void usbvideo_SayAndWait(const char *what); void usbvideo_SayAndWait(const char *what);
void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode); void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode);
void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs);
/* Memory allocation routines */ /* Memory allocation routines */
unsigned long usbvideo_kvirt_to_pa(unsigned long adr); unsigned long usbvideo_kvirt_to_pa(unsigned long adr);
void *usbvideo_rvmalloc(unsigned long size);
void usbvideo_rvfree(void *mem, unsigned long size);
int usbvideo_register( int usbvideo_register(
usbvideo_t **pCams, usbvideo_t **pCams,
...@@ -344,24 +331,10 @@ int usbvideo_register( ...@@ -344,24 +331,10 @@ int usbvideo_register(
uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams); uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams);
int usbvideo_RegisterVideoDevice(uvd_t *uvd); int usbvideo_RegisterVideoDevice(uvd_t *uvd);
void usbvideo_Deregister(usbvideo_t **uvt); void usbvideo_Deregister(usbvideo_t **uvt);
void usbvideo_Disconnect(struct usb_device *dev, void *ptr);
void usbvideo_CameraRelease(uvd_t *uvd);
int usbvideo_v4l_close(struct inode *inode, struct file *file);
int usbvideo_v4l_initialize(struct video_device *dev); int usbvideo_v4l_initialize(struct video_device *dev);
int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
int usbvideo_v4l_open(struct inode *inode, struct file *file);
int usbvideo_v4l_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
int usbvideo_GetFrame(uvd_t *uvd, int frameNum);
int usbvideo_NewFrame(uvd_t *uvd, int framenum);
int usbvideo_StartDataPump(uvd_t *uvd);
void usbvideo_StopDataPump(uvd_t *uvd);
void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame); void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame);
void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame);
/* /*
* This code performs bounds checking - use it when working with * This code performs bounds checking - use it when working with
......
...@@ -60,12 +60,17 @@ do { \ ...@@ -60,12 +60,17 @@ do { \
/* Auerswald Vendor ID */ /* Auerswald Vendor ID */
#define ID_AUERSWALD 0x09BF #define ID_AUERSWALD 0x09BF
#ifndef AUER_MINOR_BASE /* allow external override */ #ifdef CONFIG_USB_DYNAMIC_MINORS
/* we can have up to 256 devices at once */
#define AUER_MINOR_BASE 0
#define AUER_MAX_DEVICES 256
#else
#define AUER_MINOR_BASE 112 /* auerswald driver minor number */ #define AUER_MINOR_BASE 112 /* auerswald driver minor number */
#endif
/* we can have up to this number of device plugged in at once */ /* we can have up to this number of device plugged in at once */
#define AUER_MAX_DEVICES 16 #define AUER_MAX_DEVICES 16
#endif
/* prefix for the device descriptors in /dev/usb */ /* prefix for the device descriptors in /dev/usb */
#define AU_PREFIX "auer" #define AU_PREFIX "auer"
...@@ -284,6 +289,7 @@ typedef struct ...@@ -284,6 +289,7 @@ typedef struct
/* Forwards */ /* Forwards */
static void auerswald_ctrlread_complete (struct urb * urb); static void auerswald_ctrlread_complete (struct urb * urb);
static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp); static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp);
extern struct usb_driver auerswald_driver;
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
...@@ -1941,16 +1947,18 @@ static void *auerswald_probe (struct usb_device *usbdev, unsigned int ifnum, ...@@ -1941,16 +1947,18 @@ static void *auerswald_probe (struct usb_device *usbdev, unsigned int ifnum,
auerbuf_init (&cp->bufctl); auerbuf_init (&cp->bufctl);
init_waitqueue_head (&cp->bufferwait); init_waitqueue_head (&cp->bufferwait);
/* find a free slot in the device table */
down (&dev_table_mutex); down (&dev_table_mutex);
for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) { if (usb_register_dev (&auerswald_driver, 1, &dtindex)) {
if (dev_table[dtindex] == NULL) /* find a free slot in the device table */
break; for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) {
} if (dev_table[dtindex] == NULL)
if ( dtindex >= AUER_MAX_DEVICES) { break;
err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES); }
up (&dev_table_mutex); if ( dtindex >= AUER_MAX_DEVICES) {
goto pfail; err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES);
up (&dev_table_mutex);
goto pfail;
}
} }
/* Give the device a name */ /* Give the device a name */
...@@ -2081,6 +2089,9 @@ static void auerswald_disconnect (struct usb_device *usbdev, void *driver_contex ...@@ -2081,6 +2089,9 @@ static void auerswald_disconnect (struct usb_device *usbdev, void *driver_contex
/* Nobody can see this device any more */ /* Nobody can see this device any more */
devfs_unregister (cp->devfs); devfs_unregister (cp->devfs);
/* give back our USB minor number */
usb_deregister_dev (&auerswald_driver, 1, cp->dtindex);
/* Stop the interrupt endpoint */ /* Stop the interrupt endpoint */
auerswald_int_release (cp); auerswald_int_release (cp);
...@@ -2181,6 +2192,7 @@ static void __exit auerswald_cleanup (void) ...@@ -2181,6 +2192,7 @@ static void __exit auerswald_cleanup (void)
MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_DESC); MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
module_init (auerswald_init); module_init (auerswald_init);
module_exit (auerswald_cleanup); module_exit (auerswald_cleanup);
......
...@@ -315,14 +315,16 @@ brlvger_probe (struct usb_device *dev, unsigned ifnum, ...@@ -315,14 +315,16 @@ brlvger_probe (struct usb_device *dev, unsigned ifnum,
down(&reserve_sem); down(&reserve_sem);
for( i = 0; i < MAX_NR_BRLVGER_DEVS; i++ ) if (usb_register_dev(&brlvger_driver, 1, &i)) {
if( display_table[i] == NULL ) for( i = 0; i < MAX_NR_BRLVGER_DEVS; i++ )
break; if( display_table[i] == NULL )
break;
if( i == MAX_NR_BRLVGER_DEVS ) {
err( "This driver cannot handle more than %d " if( i == MAX_NR_BRLVGER_DEVS ) {
"braille displays", MAX_NR_BRLVGER_DEVS); err( "This driver cannot handle more than %d "
goto error; "braille displays", MAX_NR_BRLVGER_DEVS);
goto error;
}
} }
if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){ if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){
...@@ -423,7 +425,8 @@ brlvger_disconnect(struct usb_device *dev, void *ptr) ...@@ -423,7 +425,8 @@ brlvger_disconnect(struct usb_device *dev, void *ptr)
info("Display %d disconnecting", priv->subminor); info("Display %d disconnecting", priv->subminor);
devfs_unregister(priv->devfs); devfs_unregister(priv->devfs);
usb_deregister_dev(&brlvger_driver, 1, priv->subminor);
down(&disconnect_sem); down(&disconnect_sem);
display_table[priv->subminor] = NULL; display_table[priv->subminor] = NULL;
up(&disconnect_sem); up(&disconnect_sem);
......
...@@ -255,6 +255,8 @@ PEGASUS_DEV( "SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201, ...@@ -255,6 +255,8 @@ PEGASUS_DEV( "SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201,
DEFAULT_GPIO_RESET | PEGASUS_II) DEFAULT_GPIO_RESET | PEGASUS_II)
PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
......
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
#define CONFIG_USB_PL2301 #define CONFIG_USB_PL2301
#define DRIVER_VERSION "06-Apr-2002" #define DRIVER_VERSION "26-Apr-2002"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -484,14 +484,6 @@ static int genelink_free (struct usbnet *dev) ...@@ -484,14 +484,6 @@ static int genelink_free (struct usbnet *dev)
return 0; return 0;
} }
#else
static int genelink_check_connect (struct usbnet *dev)
{
dbg ("%s: assuming peer is connected", dev->net.name);
return 0;
}
#endif #endif
// reset the device status // reset the device status
...@@ -623,12 +615,15 @@ static const struct driver_info genelink_info = { ...@@ -623,12 +615,15 @@ static const struct driver_info genelink_info = {
description: "Genesys GeneLink", description: "Genesys GeneLink",
flags: FLAG_FRAMING_GL | FLAG_NO_SETINT, flags: FLAG_FRAMING_GL | FLAG_NO_SETINT,
reset: genelink_reset, reset: genelink_reset,
check_connect: genelink_check_connect,
rx_fixup: genelink_rx_fixup, rx_fixup: genelink_rx_fixup,
tx_fixup: genelink_tx_fixup, tx_fixup: genelink_tx_fixup,
in: 1, out: 2, in: 1, out: 2,
epsize: 64, epsize: 64,
#ifdef GENELINK_ACK
check_connect: genelink_check_connect,
#endif
}; };
#endif /* CONFIG_USB_GENESYS */ #endif /* CONFIG_USB_GENESYS */
...@@ -652,11 +647,15 @@ static const struct driver_info genelink_info = { ...@@ -652,11 +647,15 @@ static const struct driver_info genelink_info = {
* *
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
static int linuxdev_check_connect (struct usbnet *dev)
{
return 0; // by definition, always connected
}
static const struct driver_info linuxdev_info = { static const struct driver_info linuxdev_info = {
description: "Linux Device", description: "Linux Device",
// no reset defined (yet?) // no reset defined (yet?)
// no check_connect needed! check_connect: linuxdev_check_connect,
in: 2, out: 1, in: 2, out: 1,
epsize: 64, epsize: 64,
}; };
...@@ -1169,21 +1168,11 @@ static int pl_reset (struct usbnet *dev) ...@@ -1169,21 +1168,11 @@ static int pl_reset (struct usbnet *dev)
PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E); PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
} }
static int pl_check_connect (struct usbnet *dev)
{
// FIXME test interrupt data PL_PEER_E bit
// plus, there's some handshake done by
// the prolific win32 driver...
dbg ("%s: assuming peer is connected", dev->net.name);
return 0;
}
static const struct driver_info prolific_info = { static const struct driver_info prolific_info = {
description: "Prolific PL-2301/PL-2302", description: "Prolific PL-2301/PL-2302",
flags: FLAG_NO_SETINT, flags: FLAG_NO_SETINT,
/* some PL-2302 versions seem to fail usb_set_interface() */ /* some PL-2302 versions seem to fail usb_set_interface() */
reset: pl_reset, reset: pl_reset,
check_connect: pl_check_connect,
in: 3, out: 2, in: 3, out: 2,
epsize: 64, epsize: 64,
...@@ -1567,7 +1556,7 @@ static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr) ...@@ -1567,7 +1556,7 @@ static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr)
if (dev->driver_info->check_connect) { if (dev->driver_info->check_connect) {
struct ethtool_value edata = { ETHTOOL_GLINK }; struct ethtool_value edata = { ETHTOOL_GLINK };
edata.data = dev->driver_info->check_connect (dev); edata.data = dev->driver_info->check_connect (dev) == 0;
if (copy_to_user (useraddr, &edata, sizeof (edata))) if (copy_to_user (useraddr, &edata, sizeof (edata)))
return -EFAULT; return -EFAULT;
return 0; return 0;
......
...@@ -28,6 +28,14 @@ ...@@ -28,6 +28,14 @@
Change History Change History
Wed Apr 25 12:00:00 PST 2002 (Keyspan)
Started with Hugh Blemings' code dated Jan 17, 2002. All adapters
now supported (including QI and QW). Modified port open, port
close, and send setup() logic to fix various data and endpoint
synchronization bugs and device LED status bugs. Changed keyspan_
write_room() to accurately return transmit buffer availability.
Changed forwardingLength from 1 to 16 for all adapters.
Fri Oct 12 16:45:00 EST 2001 Fri Oct 12 16:45:00 EST 2001
Preliminary USA-19QI and USA-28 support (both test OK for me, YMMV) Preliminary USA-19QI and USA-28 support (both test OK for me, YMMV)
...@@ -98,7 +106,7 @@ ...@@ -98,7 +106,7 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v1.1.2" #define DRIVER_VERSION "v1.1.3"
#define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu" #define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
#define DRIVER_DESC "Keyspan USB to Serial Converter Driver" #define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
...@@ -423,9 +431,10 @@ static void usa26_indat_callback(struct urb *urb) ...@@ -423,9 +431,10 @@ static void usa26_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev; urb->dev = port->serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if (port->open_count)
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
} dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
}
return; return;
} }
...@@ -461,7 +470,7 @@ static void usa26_outcont_callback(struct urb *urb) ...@@ -461,7 +470,7 @@ static void usa26_outcont_callback(struct urb *urb)
if (p_priv->resend_cont) { if (p_priv->resend_cont) {
dbg ("%s - sending setup", __FUNCTION__); dbg ("%s - sending setup", __FUNCTION__);
keyspan_usa26_send_setup(port->serial, port, 0); keyspan_usa26_send_setup(port->serial, port, p_priv->resend_cont - 1);
} }
} }
...@@ -519,12 +528,12 @@ static void usa26_instat_callback(struct urb *urb) ...@@ -519,12 +528,12 @@ static void usa26_instat_callback(struct urb *urb)
/* wake_up_interruptible(&p_priv->open_wait); */ /* wake_up_interruptible(&p_priv->open_wait); */
} }
exit:
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = serial->dev; urb->dev = serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
} }
exit:
} }
static void usa26_glocont_callback(struct urb *urb) static void usa26_glocont_callback(struct urb *urb)
...@@ -572,9 +581,10 @@ static void usa28_indat_callback(struct urb *urb) ...@@ -572,9 +581,10 @@ static void usa28_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev; urb->dev = port->serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if (port->open_count)
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
} dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
}
p_priv->in_flip ^= 1; p_priv->in_flip ^= 1;
urb = p_priv->in_urbs[p_priv->in_flip]; urb = p_priv->in_urbs[p_priv->in_flip];
...@@ -596,7 +606,7 @@ static void usa28_outcont_callback(struct urb *urb) ...@@ -596,7 +606,7 @@ static void usa28_outcont_callback(struct urb *urb)
if (p_priv->resend_cont) { if (p_priv->resend_cont) {
dbg ("%s - sending setup", __FUNCTION__); dbg ("%s - sending setup", __FUNCTION__);
keyspan_usa28_send_setup(port->serial, port, 0); keyspan_usa28_send_setup(port->serial, port, p_priv->resend_cont - 1);
} }
} }
...@@ -653,12 +663,12 @@ static void usa28_instat_callback(struct urb *urb) ...@@ -653,12 +663,12 @@ static void usa28_instat_callback(struct urb *urb)
/* wake_up_interruptible(&p_priv->open_wait); */ /* wake_up_interruptible(&p_priv->open_wait); */
} }
exit:
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = serial->dev; urb->dev = serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
} }
exit:
} }
static void usa28_glocont_callback(struct urb *urb) static void usa28_glocont_callback(struct urb *urb)
...@@ -683,7 +693,7 @@ static void usa49_glocont_callback(struct urb *urb) ...@@ -683,7 +693,7 @@ static void usa49_glocont_callback(struct urb *urb)
if (p_priv->resend_cont) { if (p_priv->resend_cont) {
dbg ("%s - sending setup", __FUNCTION__); dbg ("%s - sending setup", __FUNCTION__);
keyspan_usa49_send_setup(serial, port, 0); keyspan_usa49_send_setup(serial, port, p_priv->resend_cont - 1);
break; break;
} }
} }
...@@ -745,13 +755,13 @@ static void usa49_instat_callback(struct urb *urb) ...@@ -745,13 +755,13 @@ static void usa49_instat_callback(struct urb *urb)
/* wake_up_interruptible(&p_priv->open_wait); */ /* wake_up_interruptible(&p_priv->open_wait); */
} }
exit:
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = serial->dev; urb->dev = serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
} }
exit:
} }
static void usa49_inack_callback(struct urb *urb) static void usa49_inack_callback(struct urb *urb)
...@@ -805,9 +815,10 @@ static void usa49_indat_callback(struct urb *urb) ...@@ -805,9 +815,10 @@ static void usa49_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */ /* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev; urb->dev = port->serial->dev;
if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { if (port->open_count)
dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
} dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
}
} }
/* not used, usa-49 doesn't have per-port control endpoints */ /* not used, usa-49 doesn't have per-port control endpoints */
...@@ -820,9 +831,27 @@ static void usa49_outcont_callback(struct urb *urb) ...@@ -820,9 +831,27 @@ static void usa49_outcont_callback(struct urb *urb)
static int keyspan_write_room (struct usb_serial_port *port) static int keyspan_write_room (struct usb_serial_port *port)
{ {
struct keyspan_port_private *p_priv;
const struct keyspan_device_details *d_details;
int flip;
struct urb *this_urb;
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
return (32); p_priv = (struct keyspan_port_private *)(port->private);
d_details = p_priv->device_details;
flip = p_priv->out_flip;
/* Check both endpoints to see if any are available. */
if ((this_urb = p_priv->out_urbs[flip]) != 0) {
if (this_urb->status != -EINPROGRESS)
return (63);
flip = (flip + 1) & d_details->outdat_endp_flip;
if ((this_urb = p_priv->out_urbs[flip]) != 0)
if (this_urb->status != -EINPROGRESS)
return (63);
}
return (0);
} }
...@@ -873,7 +902,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) ...@@ -873,7 +902,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp)
if ((urb = p_priv->out_urbs[i]) == NULL) if ((urb = p_priv->out_urbs[i]) == NULL)
continue; continue;
urb->dev = serial->dev; urb->dev = serial->dev;
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
} }
keyspan_send_setup(port, 1); keyspan_send_setup(port, 1);
...@@ -909,8 +938,12 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) ...@@ -909,8 +938,12 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp)
p_priv->rts_state = 0; p_priv->rts_state = 0;
p_priv->dtr_state = 0; p_priv->dtr_state = 0;
if (serial->dev) if (serial->dev) {
keyspan_send_setup(port, 1); keyspan_send_setup(port, 2);
/* pilot-xfer seems to work best with this delay */
mdelay(100);
keyspan_set_termios(port, NULL);
}
/*while (p_priv->outcont_urb->status == -EINPROGRESS) { /*while (p_priv->outcont_urb->status == -EINPROGRESS) {
dbg("%s - urb in progress", __FUNCTION__); dbg("%s - urb in progress", __FUNCTION__);
...@@ -922,7 +955,7 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) ...@@ -922,7 +955,7 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp)
if (serial->dev) { if (serial->dev) {
/* Stop reading/writing urbs */ /* Stop reading/writing urbs */
stop_urb(p_priv->inack_urb); stop_urb(p_priv->inack_urb);
stop_urb(p_priv->outcont_urb); /* stop_urb(p_priv->outcont_urb); */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
stop_urb(p_priv->in_urbs[i]); stop_urb(p_priv->in_urbs[i]);
stop_urb(p_priv->out_urbs[i]); stop_urb(p_priv->out_urbs[i]);
...@@ -1365,7 +1398,10 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, ...@@ -1365,7 +1398,10 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
return -1; return -1;
} }
p_priv->resend_cont = 1; /* Save reset port val for resend.
Don't overwrite resend for close condition. */
if (p_priv->resend_cont != 3)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) { if (this_urb->status == -EINPROGRESS) {
/* dbg ("%s - already writing", __FUNCTION__); */ /* dbg ("%s - already writing", __FUNCTION__); */
return(-1); return(-1);
...@@ -1414,12 +1450,26 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, ...@@ -1414,12 +1450,26 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
msg.ctsFlowControl = (p_priv->flow_control == flow_cts); msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
msg.xonFlowControl = 0; msg.xonFlowControl = 0;
msg.setFlowControl = 0xff; msg.setFlowControl = 0xff;
msg.forwardingLength = 16;
msg.forwardingLength = 1;
msg.xonChar = 17; msg.xonChar = 17;
msg.xoffChar = 19; msg.xoffChar = 19;
if (reset_port) { /* Opening port */
if (reset_port == 1) {
msg._txOn = 1;
msg._txOff = 0;
msg.txFlush = 0;
msg.txBreak = 0;
msg.rxOn = 1;
msg.rxOff = 0;
msg.rxFlush = 1;
msg.rxForward = 0;
msg.returnStatus = 0;
msg.resetDataToggle = 0xff;
}
/* Closing port */
else if (reset_port == 2) {
msg._txOn = 0; msg._txOn = 0;
msg._txOff = 1; msg._txOff = 1;
msg.txFlush = 0; msg.txFlush = 0;
...@@ -1429,14 +1479,16 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, ...@@ -1429,14 +1479,16 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
msg.rxFlush = 1; msg.rxFlush = 1;
msg.rxForward = 0; msg.rxForward = 0;
msg.returnStatus = 0; msg.returnStatus = 0;
msg.resetDataToggle = 0xff; msg.resetDataToggle = 0;
} }
/* Sending intermediate configs */
else { else {
msg._txOn = (! p_priv->break_on); msg._txOn = (! p_priv->break_on);
msg._txOff = 0; msg._txOff = 0;
msg.txFlush = 0; msg.txFlush = 0;
msg.txBreak = (p_priv->break_on); msg.txBreak = (p_priv->break_on);
msg.rxOn = 1; msg.rxOn = 0;
msg.rxOff = 0; msg.rxOff = 0;
msg.rxFlush = 0; msg.rxFlush = 0;
msg.rxForward = 0; msg.rxForward = 0;
...@@ -1496,7 +1548,10 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, ...@@ -1496,7 +1548,10 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
return -1; return -1;
} }
p_priv->resend_cont = 1; /* Save reset port val for resend.
Don't overwrite resend for close condition. */
if (p_priv->resend_cont != 3)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) { if (this_urb->status == -EINPROGRESS) {
dbg ("%s already writing", __FUNCTION__); dbg ("%s already writing", __FUNCTION__);
return(-1); return(-1);
...@@ -1522,7 +1577,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, ...@@ -1522,7 +1577,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
msg.rts = p_priv->rts_state; msg.rts = p_priv->rts_state;
msg.dtr = p_priv->dtr_state; msg.dtr = p_priv->dtr_state;
msg.forwardingLength = 1; msg.forwardingLength = 16;
msg.forwardMs = 10; msg.forwardMs = 10;
msg.breakThreshold = 45; msg.breakThreshold = 45;
msg.xonChar = 17; msg.xonChar = 17;
...@@ -1530,8 +1585,22 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, ...@@ -1530,8 +1585,22 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
/*msg.returnStatus = 1; /*msg.returnStatus = 1;
msg.resetDataToggle = 0xff;*/ msg.resetDataToggle = 0xff;*/
/* Opening port */
if (reset_port) { if (reset_port == 1) {
msg._txOn = 1;
msg._txOff = 0;
msg.txFlush = 0;
msg.txForceXoff = 0;
msg.txBreak = 0;
msg.rxOn = 1;
msg.rxOff = 0;
msg.rxFlush = 1;
msg.rxForward = 0;
msg.returnStatus = 0;
msg.resetDataToggle = 0xff;
}
/* Closing port */
else if (reset_port == 2) {
msg._txOn = 0; msg._txOn = 0;
msg._txOff = 1; msg._txOff = 1;
msg.txFlush = 0; msg.txFlush = 0;
...@@ -1542,15 +1611,16 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, ...@@ -1542,15 +1611,16 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
msg.rxFlush = 1; msg.rxFlush = 1;
msg.rxForward = 0; msg.rxForward = 0;
msg.returnStatus = 0; msg.returnStatus = 0;
msg.resetDataToggle = 0xff; msg.resetDataToggle = 0;
} }
/* Sending intermediate configs */
else { else {
msg._txOn = (! p_priv->break_on); msg._txOn = (! p_priv->break_on);
msg._txOff = 0; msg._txOff = 0;
msg.txFlush = 0; msg.txFlush = 0;
msg.txForceXoff = 0; msg.txForceXoff = 0;
msg.txBreak = (p_priv->break_on); msg.txBreak = (p_priv->break_on);
msg.rxOn = 1; msg.rxOn = 0;
msg.rxOff = 0; msg.rxOff = 0;
msg.rxFlush = 0; msg.rxFlush = 0;
msg.rxForward = 0; msg.rxForward = 0;
...@@ -1610,7 +1680,10 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, ...@@ -1610,7 +1680,10 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
return -1; return -1;
} }
p_priv->resend_cont = 1; /* Save reset port val for resend.
Don't overwrite resend for close condition. */
if (p_priv->resend_cont != 3)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) { if (this_urb->status == -EINPROGRESS) {
/* dbg ("%s - already writing", __FUNCTION__); */ /* dbg ("%s - already writing", __FUNCTION__); */
return(-1); return(-1);
...@@ -1663,11 +1736,27 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, ...@@ -1663,11 +1736,27 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
msg.xonFlowControl = 0; msg.xonFlowControl = 0;
msg.setFlowControl = 0xff; msg.setFlowControl = 0xff;
msg.forwardingLength = 1; msg.forwardingLength = 16;
msg.xonChar = 17; msg.xonChar = 17;
msg.xoffChar = 19; msg.xoffChar = 19;
if (reset_port) { /* Opening port */
if (reset_port == 1) {
msg._txOn = 1;
msg._txOff = 0;
msg.txFlush = 0;
msg.txBreak = 0;
msg.rxOn = 1;
msg.rxOff = 0;
msg.rxFlush = 1;
msg.rxForward = 0;
msg.returnStatus = 0;
msg.resetDataToggle = 0xff;
msg.enablePort = 1;
msg.disablePort = 0;
}
/* Closing port */
else if (reset_port == 2) {
msg._txOn = 0; msg._txOn = 0;
msg._txOff = 1; msg._txOff = 1;
msg.txFlush = 0; msg.txFlush = 0;
...@@ -1677,23 +1766,23 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, ...@@ -1677,23 +1766,23 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
msg.rxFlush = 1; msg.rxFlush = 1;
msg.rxForward = 0; msg.rxForward = 0;
msg.returnStatus = 0; msg.returnStatus = 0;
msg.resetDataToggle = 0xff; msg.resetDataToggle = 0;
msg.enablePort = 0; msg.enablePort = 0;
msg.disablePort = 0xff; msg.disablePort = 1;
} }
/* Sending intermediate configs */
else { else {
msg._txOn = (! p_priv->break_on); msg._txOn = (! p_priv->break_on);
msg._txOff = 0; msg._txOff = 0;
msg.txFlush = 0; msg.txFlush = 0;
msg.txBreak = (p_priv->break_on); msg.txBreak = (p_priv->break_on);
msg.rxOn = 1; msg.rxOn = 0;
msg.rxOff = 0; msg.rxOff = 0;
msg.rxFlush = 0; msg.rxFlush = 0;
msg.rxForward = 0; msg.rxForward = 0;
msg.returnStatus = 0; msg.returnStatus = 0;
msg.resetDataToggle = 0x0; msg.resetDataToggle = 0x0;
msg.enablePort = 0xff; msg.enablePort = 0;
msg.disablePort = 0; msg.disablePort = 0;
} }
......
...@@ -85,12 +85,20 @@ static struct usb_device_id skel_table [] = { ...@@ -85,12 +85,20 @@ static struct usb_device_id skel_table [] = {
MODULE_DEVICE_TABLE (usb, skel_table); MODULE_DEVICE_TABLE (usb, skel_table);
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* if the user wants to use dynamic minor numbers, then we can have up to 256
* devices
*/
#define USB_SKEL_MINOR_BASE 0
#define MAX_DEVICES 256
#else
/* Get a minor range for your devices from the usb maintainer */ /* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 200 #define USB_SKEL_MINOR_BASE 200
/* we can have up to this number of device plugged in at once */ /* we can have up to this number of device plugged in at once */
#define MAX_DEVICES 16 #define MAX_DEVICES 16
#endif
/* Structure to hold all of our device specific stuff */ /* Structure to hold all of our device specific stuff */
struct usb_skel { struct usb_skel {
...@@ -192,9 +200,6 @@ static struct usb_driver skel_driver = { ...@@ -192,9 +200,6 @@ static struct usb_driver skel_driver = {
}; };
/** /**
* usb_skel_debug_data * usb_skel_debug_data
*/ */
...@@ -529,15 +534,17 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru ...@@ -529,15 +534,17 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru
return NULL; return NULL;
} }
/* select a "subminor" number (part of a minor number) */
down (&minor_table_mutex); down (&minor_table_mutex);
for (minor = 0; minor < MAX_DEVICES; ++minor) { if (usb_register_dev (&skel_driver, 1, &minor)) {
if (minor_table[minor] == NULL) /* we could not get a dynamic minor, so lets find one of our own */
break; for (minor = 0; minor < MAX_DEVICES; ++minor) {
} if (minor_table[minor] == NULL)
if (minor >= MAX_DEVICES) { break;
info ("Too many devices plugged in, can not handle this device."); }
goto exit; if (minor >= MAX_DEVICES) {
info ("Too many devices plugged in, can not handle this device.");
goto exit;
}
} }
/* allocate memory for our device state and intialize it */ /* allocate memory for our device state and intialize it */
...@@ -642,8 +649,11 @@ static void skel_disconnect(struct usb_device *udev, void *ptr) ...@@ -642,8 +649,11 @@ static void skel_disconnect(struct usb_device *udev, void *ptr)
minor = dev->minor; minor = dev->minor;
/* remove our devfs node */ /* remove our devfs node */
devfs_unregister(dev->devfs); devfs_unregister (dev->devfs);
/* give back our dynamic minor */
usb_deregister_dev (&skel_driver, 1, minor);
/* if the device is not opened, then we clean up right now */ /* if the device is not opened, then we clean up right now */
if (!dev->open_count) { if (!dev->open_count) {
up (&dev->sem); up (&dev->sem);
......
...@@ -30,11 +30,16 @@ ...@@ -30,11 +30,16 @@
#define BRLVGER_DISPLAY_OFF 3 #define BRLVGER_DISPLAY_OFF 3
#define BRLVGER_BUZZ 4 #define BRLVGER_BUZZ 4
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define MAX_NR_BRLVGER_DEVS 256
#define BRLVGER_MINOR 0
#else
/* Number of supported devices, and range of covered minors */ /* Number of supported devices, and range of covered minors */
#define MAX_NR_BRLVGER_DEVS 2 #define MAX_NR_BRLVGER_DEVS 4
/* Base minor for the char devices */ /* Base minor for the char devices */
#define BRLVGER_MINOR 128 #define BRLVGER_MINOR 128
#endif
/* Size of some fields */ /* Size of some fields */
#define BRLVGER_HWVER_SIZE 2 #define BRLVGER_HWVER_SIZE 2
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define __LINUX_USB_H #define __LINUX_USB_H
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h>
/* USB constants */ /* USB constants */
...@@ -317,6 +318,223 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, ...@@ -317,6 +318,223 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
__usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,\ __usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,\
type,(void**)ptr) type,(void**)ptr)
/* -------------------------------------------------------------------------- */
/* Host Controller Driver (HCD) support */
struct usb_operations;
#define DEVNUM_ROUND_ROBIN /***** OPTION *****/
/*
* Allocated per bus we have
*/
struct usb_bus {
int busnum; /* Bus number (in order of reg) */
char *bus_name; /* stable id (PCI slot_name etc) */
#ifdef DEVNUM_ROUND_ROBIN
int devnum_next; /* Next open device number in round-robin allocation */
#endif /* DEVNUM_ROUND_ROBIN */
struct usb_devmap devmap; /* device address allocation map */
struct usb_operations *op; /* Operations (specific to the HC) */
struct usb_device *root_hub; /* Root hub */
struct list_head bus_list; /* list of busses */
void *hcpriv; /* Host Controller private data */
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
* Units: microseconds/frame.
* Limits: Full/low speed reserve 90%,
* while high speed reserves 80%.
*/
int bandwidth_int_reqs; /* number of Interrupt requests */
int bandwidth_isoc_reqs; /* number of Isoc. requests */
struct dentry *dentry; /* usbfs dentry entry for the bus */
atomic_t refcnt;
};
// FIXME: root_hub_string vanishes when "usb_hcd" conversion is done,
// along with pre-hcd versions of the OHCI and UHCI drivers.
extern int usb_root_hub_string(int id, int serial,
char *type, __u8 *data, int len);
/*
* As of USB 2.0, full/low speed devices are segregated into trees.
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
* The other type grows from high speed hubs when they connect to
* full/low speed devices using "Transaction Translators" (TTs).
*
* TTs should only be known to the hub driver, and high speed bus
* drivers (only EHCI for now). They affect periodic scheduling and
* sometimes control/bulk error recovery.
*/
struct usb_tt {
struct usb_device *hub; /* upstream highspeed hub */
int multi; /* true means one TT per port */
};
/* -------------------------------------------------------------------------- */
/* This is arbitrary.
* From USB 2.0 spec Table 11-13, offset 7, a hub can
* have up to 255 ports. The most yet reported is 10.
*/
#define USB_MAXCHILDREN (16)
struct usb_device {
int devnum; /* Address on USB bus */
char devpath [16]; /* Use in messages: /port/port/... */
enum {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
USB_SPEED_HIGH /* usb 2.0 */
} speed;
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
atomic_t refcnt; /* Reference count */
struct semaphore serialize;
unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */
/* [0] = IN, [1] = OUT */
int epmaxpacketin[16]; /* INput endpoint specific maximums */
int epmaxpacketout[16]; /* OUTput endpoint specific maximums */
struct usb_device *parent; /* our hub, unless we're the root */
struct usb_bus *bus; /* Bus we're part of */
struct device dev; /* Generic device interface */
struct usb_device_descriptor descriptor;/* Descriptor */
struct usb_config_descriptor *config; /* All of the configs */
struct usb_config_descriptor *actconfig;/* the active configuration */
char **rawdescriptors; /* Raw descriptors for each config */
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
void *hcpriv; /* Host Controller private data */
struct list_head filelist;
struct dentry *dentry; /* usbfs dentry entry for the device */
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
* of this same device.
*
* Each instance needs its own set of data structures.
*/
int maxchild; /* Number of ports if hub */
struct usb_device *children[USB_MAXCHILDREN];
};
/* for when layers above USB add new non-USB drivers */
extern void usb_scan_devices(void);
/* mostly for devices emulating SCSI over USB */
extern int usb_reset_device(struct usb_device *dev);
/* for drivers using iso endpoints */
extern int usb_get_current_frame_number (struct usb_device *usb_dev);
/**
* usb_inc_dev_use - record another reference to a device
* @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 usb_dec_dev_use(), in their disconnect() methods.
*/
static inline void usb_inc_dev_use (struct usb_device *dev)
{
atomic_inc (&dev->refcnt);
}
/**
* usb_dec_dev_use - drop a reference to a device
* @dev: the device no longer being referenced
*
* Each live reference to a device should be refcounted.
*
* Drivers for USB interfaces should normally release such references in
* their disconnect() methods, and record them in probe().
*
* Note that driver disconnect() methods must guarantee that when they
* return, all of their outstanding references to the device (and its
* interfaces) are cleaned up. That means that all pending URBs from
* this driver must have completed, and that no more copies of the device
* handle are saved in driver records (including other kernel threads).
*/
static inline void usb_dec_dev_use (struct usb_device *dev)
{
if (atomic_dec_and_test (&dev->refcnt)) {
/* May only go to zero when usbcore finishes
* usb_disconnect() processing: khubd or HCDs.
*
* If you hit this BUG() it's likely a problem
* with some driver's disconnect() routine.
*/
BUG ();
}
}
/* used these for multi-interface device registration */
extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
extern void usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void* priv);
extern int usb_interface_claimed(struct usb_interface *iface);
extern void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface);
const struct usb_device_id *usb_match_id(struct usb_device *dev,
struct usb_interface *interface,
const struct usb_device_id *id);
/**
* usb_make_path - returns stable device path in the usb tree
* @dev: the device whose path is being constructed
* @buf: where to put the string
* @size: how big is "buf"?
*
* Returns length of the string (> 0) or negative if size was too small.
*
* This identifier is intended to be "stable", reflecting physical paths in
* hardware such as physical bus addresses for host controllers or ports on
* USB hubs. That makes it stay the same until systems are physically
* reconfigured, by re-cabling a tree of USB devices or by moving USB host
* controllers. Adding and removing devices, including virtual root hubs
* in host controller driver modules, does not change these path identifers;
* neither does rebooting or re-enumerating. These are more useful identifiers
* than changeable ("unstable") ones like bus numbers or device addresses.
*
* With a partial exception for devices connected to USB 2.0 root hubs, these
* identifiers are also predictable: so long as the device tree isn't changed,
* plugging any USB device into a given hub port always gives it the same path.
* Because of the use of "companion" controllers, devices connected to ports on
* USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are
* high speed, and a different one if they are full or low speed.
*/
static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size)
{
int actual;
actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath);
return (actual >= size) ? -1 : actual;
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /*
...@@ -559,6 +777,14 @@ struct usb_driver { ...@@ -559,6 +777,14 @@ struct usb_driver {
extern int usb_register(struct usb_driver *); extern int usb_register(struct usb_driver *);
extern void usb_deregister(struct usb_driver *); extern void usb_deregister(struct usb_driver *);
#ifndef CONFIG_USB_DYNAMIC_MINORS
static inline int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor) { return -ENODEV; }
static inline void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor) {}
#else
extern int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor);
extern void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor);
#endif
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* /*
...@@ -826,10 +1052,14 @@ static inline void usb_fill_bulk_urb (struct urb *urb, ...@@ -826,10 +1052,14 @@ static inline void usb_fill_bulk_urb (struct urb *urb,
* @buffer_length: length of the transfer buffer * @buffer_length: length of the transfer buffer
* @complete: pointer to the usb_complete_t function * @complete: pointer to the usb_complete_t function
* @context: what to set the urb context to. * @context: what to set the urb context to.
* @interval: what to set the urb interval to. * @interval: what to set the urb interval to, encoded like
* the endpoint descriptor's bInterval value.
* *
* Initializes a interrupt urb with the proper information needed to submit * Initializes a interrupt urb with the proper information needed to submit
* it to a device. * it to a device.
* Note that high speed interrupt endpoints use a logarithmic encoding of
* the endpoint interval, and express polling intervals in microframes
* (eight per millisecond) rather than in frames (one per millisecond).
*/ */
static inline void usb_fill_int_urb (struct urb *urb, static inline void usb_fill_int_urb (struct urb *urb,
struct usb_device *dev, struct usb_device *dev,
...@@ -847,7 +1077,10 @@ static inline void usb_fill_int_urb (struct urb *urb, ...@@ -847,7 +1077,10 @@ static inline void usb_fill_int_urb (struct urb *urb,
urb->transfer_buffer_length = buffer_length; urb->transfer_buffer_length = buffer_length;
urb->complete = complete; urb->complete = complete;
urb->context = context; urb->context = context;
urb->interval = interval; if (dev->speed == USB_SPEED_HIGH)
urb->interval = 1 << (interval - 1);
else
urb->interval = interval;
urb->start_frame = -1; urb->start_frame = -1;
} }
...@@ -908,221 +1141,6 @@ extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); ...@@ -908,221 +1141,6 @@ extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Host Controller Driver (HCD) support */
struct usb_operations;
#define DEVNUM_ROUND_ROBIN /***** OPTION *****/
/*
* Allocated per bus we have
*/
struct usb_bus {
int busnum; /* Bus number (in order of reg) */
char *bus_name; /* stable id (PCI slot_name etc) */
#ifdef DEVNUM_ROUND_ROBIN
int devnum_next; /* Next open device number in round-robin allocation */
#endif /* DEVNUM_ROUND_ROBIN */
struct usb_devmap devmap; /* Device map */
struct usb_operations *op; /* Operations (specific to the HC) */
struct usb_device *root_hub; /* Root hub */
struct list_head bus_list;
void *hcpriv; /* Host Controller private data */
int bandwidth_allocated; /* on this Host Controller; */
/* applies to Int. and Isoc. pipes; */
/* measured in microseconds/frame; */
/* range is 0..900, where 900 = */
/* 90% of a 1-millisecond frame */
int bandwidth_int_reqs; /* number of Interrupt requesters */
int bandwidth_isoc_reqs; /* number of Isoc. requesters */
struct dentry *dentry; /* usbfs dentry entry for the bus */
atomic_t refcnt;
};
// FIXME: root_hub_string vanishes when "usb_hcd" conversion is done,
// along with pre-hcd versions of the OHCI and UHCI drivers.
extern int usb_root_hub_string(int id, int serial,
char *type, __u8 *data, int len);
/*
* As of USB 2.0, full/low speed devices are segregated into trees.
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
* The other type grows from high speed hubs when they connect to
* full/low speed devices using "Transaction Translators" (TTs).
*
* TTs should only be known to the hub driver, and high speed bus
* drivers (only EHCI for now). They affect periodic scheduling and
* sometimes control/bulk error recovery.
*/
struct usb_tt {
struct usb_device *hub; /* upstream highspeed hub */
int multi; /* true means one TT per port */
};
/* -------------------------------------------------------------------------- */
/* This is arbitrary.
* From USB 2.0 spec Table 11-13, offset 7, a hub can
* have up to 255 ports. The most yet reported is 10.
*/
#define USB_MAXCHILDREN (16)
struct usb_device {
int devnum; /* Address on USB bus */
char devpath [16]; /* Use in messages: /port/port/... */
enum {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
USB_SPEED_HIGH /* usb 2.0 */
} speed;
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
atomic_t refcnt; /* Reference count */
struct semaphore serialize;
unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */
/* [0] = IN, [1] = OUT */
int epmaxpacketin[16]; /* INput endpoint specific maximums */
int epmaxpacketout[16]; /* OUTput endpoint specific maximums */
struct usb_device *parent;
struct usb_bus *bus; /* Bus we're part of */
struct device dev; /* Generic device interface */
struct usb_device_descriptor descriptor;/* Descriptor */
struct usb_config_descriptor *config; /* All of the configs */
struct usb_config_descriptor *actconfig;/* the active configuration */
char **rawdescriptors; /* Raw descriptors for each config */
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
void *hcpriv; /* Host Controller private data */
struct list_head filelist;
struct dentry *dentry; /* usbfs dentry entry for the device */
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
* of this same device.
*
* Each instance needs its own set of data structures.
*/
int maxchild; /* Number of ports if hub */
struct usb_device *children[USB_MAXCHILDREN];
};
/* for when layers above USB add new non-USB drivers */
extern void usb_scan_devices(void);
/* mostly for devices emulating SCSI over USB */
extern int usb_reset_device(struct usb_device *dev);
/* for drivers using iso endpoints */
extern int usb_get_current_frame_number (struct usb_device *usb_dev);
/**
* usb_inc_dev_use - record another reference to a device
* @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 usb_dec_dev_use(), in their disconnect() methods.
*/
static inline void usb_inc_dev_use (struct usb_device *dev)
{
atomic_inc (&dev->refcnt);
}
/**
* usb_dec_dev_use - drop a reference to a device
* @dev: the device no longer being referenced
*
* Each live reference to a device should be refcounted.
*
* Drivers for USB interfaces should normally release such references in
* their disconnect() methods, and record them in probe().
*
* Note that driver disconnect() methods must guarantee that when they
* return, all of their outstanding references to the device (and its
* interfaces) are cleaned up. That means that all pending URBs from
* this driver must have completed, and that no more copies of the device
* handle are saved in driver records (including other kernel threads).
*/
static inline void usb_dec_dev_use (struct usb_device *dev)
{
if (atomic_dec_and_test (&dev->refcnt)) {
/* May only go to zero when usbcore finishes
* usb_disconnect() processing: khubd or HCDs.
*
* If you hit this BUG() it's likely a problem
* with some driver's disconnect() routine.
*/
BUG ();
}
}
/* used these for multi-interface device registration */
extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
extern void usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void* priv);
extern int usb_interface_claimed(struct usb_interface *iface);
extern void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface);
const struct usb_device_id *usb_match_id(struct usb_device *dev,
struct usb_interface *interface,
const struct usb_device_id *id);
/**
* usb_make_path - returns stable device path in the usb tree
* @dev: the device whose path is being constructed
* @buf: where to put the string
* @size: how big is "buf"?
*
* Returns length of the string (> 0) or negative if size was too small.
*
* This identifier is intended to be "stable", reflecting physical paths in
* hardware such as physical bus addresses for host controllers or ports on
* USB hubs. That makes it stay the same until systems are physically
* reconfigured, by re-cabling a tree of USB devices or by moving USB host
* controllers. Adding and removing devices, including virtual root hubs
* in host controller driver modules, does not change these path identifers;
* neither does rebooting or re-enumerating. These are more useful identifiers
* than changeable ("unstable") ones like bus numbers or device addresses.
*
* With a partial exception for devices connected to USB 2.0 root hubs, these
* identifiers are also predictable: so long as the device tree isn't changed,
* plugging any USB device into a given hub port always gives it the same path.
* Because of the use of "companion" controllers, devices connected to ports on
* USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are
* high speed, and a different one if they are full or low speed.
*/
static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size)
{
int actual;
actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath);
return (actual >= size) ? -1 : actual;
}
/* -------------------------------------------------------------------------- */
/* /*
* Calling this entity a "pipe" is glorifying it. A USB pipe * Calling this entity a "pipe" is glorifying it. A USB pipe
* is something embarrassingly simple: it basically consists * is something embarrassingly simple: it basically consists
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment