Commit d3b8a1a8 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6

parents 60564a31 0ed0c0c4
...@@ -288,8 +288,8 @@ static void usb_release(struct device *dev) ...@@ -288,8 +288,8 @@ static void usb_release(struct device *dev)
static struct resource udc_resources[] = { static struct resource udc_resources[] = {
/* order is significant! */ /* order is significant! */
{ /* registers */ { /* registers */
.start = IO_ADDRESS(UDC_BASE), .start = UDC_BASE,
.end = IO_ADDRESS(UDC_BASE + 0xff), .end = UDC_BASE + 0xff,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, { /* general IRQ */ }, { /* general IRQ */
.start = IH2_BASE + 20, .start = IH2_BASE + 20,
...@@ -355,8 +355,8 @@ static struct platform_device ohci_device = { ...@@ -355,8 +355,8 @@ static struct platform_device ohci_device = {
static struct resource otg_resources[] = { static struct resource otg_resources[] = {
/* order is significant! */ /* order is significant! */
{ {
.start = IO_ADDRESS(OTG_BASE), .start = OTG_BASE,
.end = IO_ADDRESS(OTG_BASE + 0xff), .end = OTG_BASE + 0xff,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, { }, {
.start = IH2_BASE + 8, .start = IH2_BASE + 8,
......
...@@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/ ...@@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/
......
# #
# USB ATM driver configuration # USB/ATM DSL configuration
# #
comment "USB ATM/DSL drivers"
menu "USB DSL modem support"
depends on USB depends on USB
config USB_ATM config USB_ATM
tristate "Generic USB ATM/DSL core I/O support" tristate "USB DSL modem support"
depends on USB && ATM depends on USB && ATM
select CRC32 select CRC32
default n default n
help help
This provides a library which is used for packet I/O by USB DSL Say Y here if you want to connect a USB Digital Subscriber Line (DSL)
modems, such as the SpeedTouch driver below. modem to your computer's USB port. You will then need to choose your
modem from the list below.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called usb_atm. module will be called usbatm.
config USB_SPEEDTOUCH config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support" tristate "Speedtouch USB support"
depends on USB && ATM depends on USB_ATM
select USB_ATM select FW_LOADER
help help
Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 Say Y here if you have an SpeedTouch USB or SpeedTouch 330
modem. In order to use your modem you will need to install the modem. In order to use your modem you will need to install the
two parts of the firmware, extracted by the user space tools; see two parts of the firmware, extracted by the user space tools; see
<http://www.linux-usb.org/SpeedTouch/> for details. <http://www.linux-usb.org/SpeedTouch/> for details.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called speedtch. module will be called speedtch.
config USB_CXACRU
tristate "Conexant AccessRunner USB support"
depends on USB_ATM
select FW_LOADER
help
Say Y here if you have an ADSL USB modem based on the Conexant
AccessRunner chipset. In order to use your modem you will need to
install the firmware, extracted by the user space tools; see
<http://accessrunner.sourceforge.net/> for details.
To compile this driver as a module, choose M here: the
module will be called cxacru.
config USB_XUSBATM
tristate "Other USB DSL modem support"
depends on USB_ATM
help
Say Y here if you have a DSL USB modem not explicitly supported by
another USB DSL drivers. In order to use your modem you will need to
pass the vendor ID, product ID, and endpoint numbers for transmission
and reception as module parameters. You may need to initialize the
the modem using a user space utility (a firmware loader for example).
To compile this driver as a module, choose M here: the
module will be called xusbatm.
endmenu
# #
# Makefile for the rest of the USB drivers # Makefile for USB ATM/xDSL drivers
# (the ones that don't fit into any other categories)
# #
obj-$(CONFIG_USB_ATM) += usb_atm.o obj-$(CONFIG_USB_CXACRU) += cxacru.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_ATM) += usbatm.o
obj-$(CONFIG_USB_XUSBATM) += xusbatm.o
This diff is collapsed.
This diff is collapsed.
/****************************************************************************** /******************************************************************************
* usb_atm.h - Generic USB xDSL driver core * usbatm.h - Generic USB xDSL driver core
* *
* Copyright (C) 2001, Alcatel * Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
...@@ -21,12 +21,10 @@ ...@@ -21,12 +21,10 @@
* *
******************************************************************************/ ******************************************************************************/
#ifndef _USBATM_H_
#define _USBATM_H_
#include <linux/config.h> #include <linux/config.h>
#include <linux/list.h>
#include <linux/kref.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <asm/semaphore.h>
/* /*
#define DEBUG #define DEBUG
...@@ -37,140 +35,150 @@ ...@@ -37,140 +35,150 @@
# define DEBUG # define DEBUG
#endif #endif
#include <asm/semaphore.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/stringify.h>
#include <linux/usb.h> #include <linux/usb.h>
#ifdef DEBUG #ifdef DEBUG
#define UDSL_ASSERT(x) BUG_ON(!(x)) #define UDSL_ASSERT(x) BUG_ON(!(x))
#else #else
#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0) #define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '%s' at line %d", __stringify(x), __LINE__); } while(0)
#endif #endif
#define UDSL_MAX_RCV_URBS 4 #define usb_err(instance, format, arg...) \
#define UDSL_MAX_SND_URBS 4 dev_err(&(instance)->usb_intf->dev , format , ## arg)
#define UDSL_MAX_RCV_BUFS 8 #define usb_info(instance, format, arg...) \
#define UDSL_MAX_SND_BUFS 8 dev_info(&(instance)->usb_intf->dev , format , ## arg)
#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ #define usb_warn(instance, format, arg...) \
#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ dev_warn(&(instance)->usb_intf->dev , format , ## arg)
#define UDSL_DEFAULT_RCV_URBS 2 #define usb_dbg(instance, format, arg...) \
#define UDSL_DEFAULT_SND_URBS 2 dev_dbg(&(instance)->usb_intf->dev , format , ## arg)
#define UDSL_DEFAULT_RCV_BUFS 4
#define UDSL_DEFAULT_SND_BUFS 4 /* FIXME: move to dev_* once ATM is driver model aware */
#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ #define atm_printk(level, instance, format, arg...) \
#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ printk(level "ATM dev %d: " format , \
(instance)->atm_dev->number , ## arg)
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) #define atm_err(instance, format, arg...) \
atm_printk(KERN_ERR, instance , format , ## arg)
/* receive */ #define atm_info(instance, format, arg...) \
atm_printk(KERN_INFO, instance , format , ## arg)
struct udsl_receive_buffer { #define atm_warn(instance, format, arg...) \
struct list_head list; atm_printk(KERN_WARNING, instance , format , ## arg)
unsigned char *base; #ifdef DEBUG
unsigned int filled_cells; #define atm_dbg(instance, format, arg...) \
}; atm_printk(KERN_DEBUG, instance , format , ## arg)
#else
#define atm_dbg(instance, format, arg...) \
do {} while (0)
#endif
struct udsl_receiver {
struct list_head list;
struct udsl_receive_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_vcc_data { /* mini driver */
/* vpi/vci lookup */
struct list_head list;
short vpi;
int vci;
struct atm_vcc *vcc;
/* raw cell reassembly */ struct usbatm_data;
struct sk_buff *sarb;
};
/* send */ /*
* Assuming all methods exist and succeed, they are called in this order:
*
* bind, heavy_init, atm_start, ..., atm_stop, unbind
*/
struct udsl_send_buffer { struct usbatm_driver {
struct list_head list; struct module *owner;
unsigned char *base;
unsigned char *free_start;
unsigned int free_cells;
};
struct udsl_sender { const char *driver_name;
struct list_head list;
struct udsl_send_buffer *buffer; /*
struct urb *urb; * init device ... can sleep, or cause probe() failure. Drivers with a heavy_init
struct udsl_instance_data *instance; * method can avoid having it called by setting need_heavy_init to zero.
}; */
int (*bind) (struct usbatm_data *, struct usb_interface *,
const struct usb_device_id *id, int *need_heavy_init);
/* additional device initialization that is too slow to be done in probe() */
int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
/* cleanup device ... can sleep, but can't fail */
void (*unbind) (struct usbatm_data *, struct usb_interface *);
struct udsl_control { /* init ATM device ... can sleep, or cause ATM initialization failure */
struct atm_skb_data atm_data; int (*atm_start) (struct usbatm_data *, struct atm_dev *);
unsigned int num_cells;
unsigned int num_entire; /* cleanup ATM device ... can sleep, but can't fail */
unsigned int pdu_padding; void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
unsigned char aal5_trailer[ATM_AAL5_TRAILER];
int in; /* rx endpoint */
int out; /* tx endpoint */
unsigned rx_padding;
unsigned tx_padding;
}; };
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) extern int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
struct usbatm_driver *driver);
extern void usbatm_usb_disconnect(struct usb_interface *intf);
/* main driver data */
enum udsl_status { struct usbatm_channel {
UDSL_NO_FIRMWARE, int endpoint; /* usb pipe */
UDSL_LOADING_FIRMWARE, unsigned int stride; /* ATM cell size + padding */
UDSL_LOADED_FIRMWARE unsigned int buf_size; /* urb buffer size */
spinlock_t lock;
struct list_head list;
struct tasklet_struct tasklet;
struct timer_list delay;
struct usbatm_data *usbatm;
}; };
struct udsl_instance_data { /* main driver data */
struct kref refcount;
struct semaphore serialize; struct usbatm_data {
/******************
* public fields *
******************/
/* USB device part */ /* mini driver */
struct usbatm_driver *driver;
void *driver_data;
char driver_name[16];
/* USB device */
struct usb_device *usb_dev; struct usb_device *usb_dev;
struct usb_interface *usb_intf;
char description[64]; char description[64];
int data_endpoint;
int snd_padding;
int rcv_padding;
const char *driver_name;
/* ATM device part */ /* ATM device */
struct atm_dev *atm_dev; struct atm_dev *atm_dev;
struct list_head vcc_list;
/* firmware */ /********************************
int (*firmware_wait) (struct udsl_instance_data *); * private fields - do not use *
enum udsl_status status; ********************************/
wait_queue_head_t firmware_waiters;
/* receive */ struct kref refcount;
struct udsl_receiver receivers[UDSL_MAX_RCV_URBS]; struct semaphore serialize;
struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
spinlock_t receive_lock; /* heavy init */
struct list_head spare_receivers; int thread_pid;
struct list_head filled_receive_buffers; struct completion thread_started;
struct completion thread_exited;
struct tasklet_struct receive_tasklet; /* ATM device */
struct list_head spare_receive_buffers; struct list_head vcc_list;
/* send */ struct usbatm_channel rx_channel;
struct udsl_sender senders[UDSL_MAX_SND_URBS]; struct usbatm_channel tx_channel;
struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
struct sk_buff_head sndqueue; struct sk_buff_head sndqueue;
spinlock_t send_lock;
struct list_head spare_senders;
struct list_head spare_send_buffers;
struct tasklet_struct send_tasklet;
struct sk_buff *current_skb; /* being emptied */ struct sk_buff *current_skb; /* being emptied */
struct udsl_send_buffer *current_buffer; /* being filled */
struct list_head filled_send_buffers; struct urb *urbs[0];
}; };
extern int udsl_instance_setup(struct usb_device *dev, #endif /* _USBATM_H_ */
struct udsl_instance_data *instance);
extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
extern void udsl_get_instance(struct udsl_instance_data *instance);
extern void udsl_put_instance(struct udsl_instance_data *instance);
/******************************************************************************
* xusbatm.c - dumb usbatm-based driver for modems initialized in userspace
*
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
******************************************************************************/
#include <linux/module.h>
#include <linux/netdevice.h> /* FIXME: required by linux/etherdevice.h */
#include <linux/etherdevice.h> /* for random_ether_addr() */
#include "usbatm.h"
#define XUSBATM_DRIVERS_MAX 8
#define XUSBATM_PARM(name, type, parmtype, desc) \
static type name[XUSBATM_DRIVERS_MAX]; \
static int num_##name; \
module_param_array(name, parmtype, &num_##name, 0444); \
MODULE_PARM_DESC(name, desc)
XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor");
XUSBATM_PARM(product, unsigned short, ushort, "USB device product");
XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number");
XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number");
XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)");
XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)");
static const char xusbatm_driver_name[] = "xusbatm";
static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
static struct usb_driver xusbatm_usb_driver;
static int usb_intf_has_ep(const struct usb_interface *intf, u8 ep)
{
int i, j;
for (i = 0; i < intf->num_altsetting; i++) {
struct usb_host_interface *alt = intf->altsetting;
for (j = 0; j < alt->desc.bNumEndpoints; j++)
if ((alt->endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == ep)
return 1;
}
return 0;
}
static int xusbatm_bind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf, const struct usb_device_id *id,
int *need_heavy_init)
{
struct usb_device *usb_dev = interface_to_usbdev(intf);
int drv_ix = id - xusbatm_usb_ids;
int rx_ep_present = usb_intf_has_ep(intf, rx_endpoint[drv_ix]);
int tx_ep_present = usb_intf_has_ep(intf, tx_endpoint[drv_ix]);
u8 searched_ep = rx_ep_present ? tx_endpoint[drv_ix] : rx_endpoint[drv_ix];
int i, ret;
usb_dbg(usbatm_instance, "%s: binding driver %d: vendor %#x product %#x"
" rx: ep %#x padd %d tx: ep %#x padd %d\n",
__func__, drv_ix, vendor[drv_ix], product[drv_ix],
rx_endpoint[drv_ix], rx_padding[drv_ix],
tx_endpoint[drv_ix], tx_padding[drv_ix]);
if (!rx_ep_present && !tx_ep_present) {
usb_dbg(usbatm_instance, "%s: intf #%d has neither rx (%#x) nor tx (%#x) endpoint\n",
__func__, intf->altsetting->desc.bInterfaceNumber,
rx_endpoint[drv_ix], tx_endpoint[drv_ix]);
return -ENODEV;
}
if (rx_ep_present && tx_ep_present)
return 0;
for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
if (cur_if != intf && usb_intf_has_ep(cur_if, searched_ep)) {
ret = usb_driver_claim_interface(&xusbatm_usb_driver,
cur_if, usbatm_instance);
if (!ret)
usb_err(usbatm_instance, "%s: failed to claim interface #%d (%d)\n",
__func__, cur_if->altsetting->desc.bInterfaceNumber, ret);
return ret;
}
}
usb_err(usbatm_instance, "%s: no interface has endpoint %#x\n",
__func__, searched_ep);
return -ENODEV;
}
static void xusbatm_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf)
{
struct usb_device *usb_dev = interface_to_usbdev(intf);
int i;
usb_dbg(usbatm_instance, "%s entered\n", __func__);
for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
usb_set_intfdata(cur_if, NULL);
usb_driver_release_interface(&xusbatm_usb_driver, cur_if);
}
}
static int xusbatm_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
atm_dbg(usbatm_instance, "%s entered\n", __func__);
/* use random MAC as we've no way to get it from the device */
random_ether_addr(atm_dev->esi);
return 0;
}
static int xusbatm_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return usbatm_usb_probe(intf, id,
xusbatm_drivers + (id - xusbatm_usb_ids));
}
static struct usb_driver xusbatm_usb_driver = {
.owner = THIS_MODULE,
.name = xusbatm_driver_name,
.probe = xusbatm_usb_probe,
.disconnect = usbatm_usb_disconnect,
.id_table = xusbatm_usb_ids
};
static int __init xusbatm_init(void)
{
int i;
dbg("xusbatm_init");
if (!num_vendor ||
num_vendor != num_product ||
num_vendor != num_rx_endpoint ||
num_vendor != num_tx_endpoint) {
warn("malformed module parameters");
return -EINVAL;
}
for (i = 0; i < num_vendor; i++) {
xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
xusbatm_usb_ids[i].idVendor = vendor[i];
xusbatm_usb_ids[i].idProduct = product[i];
xusbatm_drivers[i].owner = THIS_MODULE;
xusbatm_drivers[i].driver_name = xusbatm_driver_name;
xusbatm_drivers[i].bind = xusbatm_bind;
xusbatm_drivers[i].unbind = xusbatm_unbind;
xusbatm_drivers[i].atm_start = xusbatm_atm_start;
xusbatm_drivers[i].in = rx_endpoint[i];
xusbatm_drivers[i].out = tx_endpoint[i];
xusbatm_drivers[i].rx_padding = rx_padding[i];
xusbatm_drivers[i].tx_padding = tx_padding[i];
}
return usb_register(&xusbatm_usb_driver);
}
module_init(xusbatm_init);
static void __exit xusbatm_exit(void)
{
dbg("xusbatm_exit entered");
usb_deregister(&xusbatm_usb_driver);
}
module_exit(xusbatm_exit);
MODULE_AUTHOR("Roman Kagan, Duncan Sands");
MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
...@@ -105,6 +105,111 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int ...@@ -105,6 +105,111 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
#define acm_send_break(acm, ms) \ #define acm_send_break(acm, ms) \
acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
/*
* Write buffer management.
* All of these assume proper locks taken by the caller.
*/
static int acm_wb_alloc(struct acm *acm)
{
int i, wbn;
struct acm_wb *wb;
wbn = acm->write_current;
i = 0;
for (;;) {
wb = &acm->wb[wbn];
if (!wb->use) {
wb->use = 1;
return wbn;
}
wbn = (wbn + 1) % ACM_NWB;
if (++i >= ACM_NWB)
return -1;
}
}
static void acm_wb_free(struct acm *acm, int wbn)
{
acm->wb[wbn].use = 0;
}
static int acm_wb_is_avail(struct acm *acm)
{
int i, n;
n = 0;
for (i = 0; i < ACM_NWB; i++) {
if (!acm->wb[i].use)
n++;
}
return n;
}
static inline int acm_wb_is_used(struct acm *acm, int wbn)
{
return acm->wb[wbn].use;
}
/*
* Finish write.
*/
static void acm_write_done(struct acm *acm)
{
unsigned long flags;
int wbn;
spin_lock_irqsave(&acm->write_lock, flags);
acm->write_ready = 1;
wbn = acm->write_current;
acm_wb_free(acm, wbn);
acm->write_current = (wbn + 1) % ACM_NWB;
spin_unlock_irqrestore(&acm->write_lock, flags);
}
/*
* Poke write.
*/
static int acm_write_start(struct acm *acm)
{
unsigned long flags;
int wbn;
struct acm_wb *wb;
int rc;
spin_lock_irqsave(&acm->write_lock, flags);
if (!acm->dev) {
spin_unlock_irqrestore(&acm->write_lock, flags);
return -ENODEV;
}
if (!acm->write_ready) {
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0; /* A white lie */
}
wbn = acm->write_current;
if (!acm_wb_is_used(acm, wbn)) {
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0;
}
wb = &acm->wb[wbn];
acm->write_ready = 0;
spin_unlock_irqrestore(&acm->write_lock, flags);
acm->writeurb->transfer_buffer = wb->buf;
acm->writeurb->transfer_dma = wb->dmah;
acm->writeurb->transfer_buffer_length = wb->len;
acm->writeurb->dev = acm->dev;
if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
dbg("usb_submit_urb(write bulk) failed: %d", rc);
acm_write_done(acm);
}
return rc;
}
/* /*
* Interrupt handlers for various ACM device responses * Interrupt handlers for various ACM device responses
*/ */
...@@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm) ...@@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm)
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{ {
struct acm *acm = (struct acm *)urb->context; struct acm *acm = (struct acm *)urb->context;
dbg("Entering acm_write_bulk with status %d\n", urb->status);
if (!ACM_READY(acm))
goto out;
if (urb->status) dbg("Entering acm_write_bulk with status %d\n", urb->status);
dbg("nonzero write bulk status received: %d", urb->status);
schedule_work(&acm->work); acm_write_done(acm);
out: acm_write_start(acm);
acm->ready_for_write = 1; if (ACM_READY(acm))
schedule_work(&acm->work);
} }
static void acm_softint(void *private) static void acm_softint(void *private)
...@@ -351,32 +452,33 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c ...@@ -351,32 +452,33 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
{ {
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
int stat; int stat;
unsigned long flags;
int wbn;
struct acm_wb *wb;
dbg("Entering acm_tty_write to write %d bytes,\n", count); dbg("Entering acm_tty_write to write %d bytes,\n", count);
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
if (!acm->ready_for_write)
return 0;
if (!count) if (!count)
return 0; return 0;
count = (count > acm->writesize) ? acm->writesize : count; spin_lock_irqsave(&acm->write_lock, flags);
if ((wbn = acm_wb_alloc(acm)) < 0) {
spin_unlock_irqrestore(&acm->write_lock, flags);
acm_write_start(acm);
return 0;
}
wb = &acm->wb[wbn];
count = (count > acm->writesize) ? acm->writesize : count;
dbg("Get %d bytes...", count); dbg("Get %d bytes...", count);
memcpy(acm->write_buffer, buf, count); memcpy(wb->buf, buf, count);
dbg(" Successfully copied.\n"); wb->len = count;
spin_unlock_irqrestore(&acm->write_lock, flags);
acm->writeurb->transfer_buffer_length = count; if ((stat = acm_write_start(acm)) < 0)
acm->writeurb->dev = acm->dev;
acm->ready_for_write = 0;
stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC);
if (stat < 0) {
dbg("usb_submit_urb(write bulk) failed");
acm->ready_for_write = 1;
return stat; return stat;
}
return count; return count;
} }
...@@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty) ...@@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty)
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
return !acm->ready_for_write ? 0 : acm->writesize; /*
* Do not let the line discipline to know that we have a reserve,
* or it might get too enthusiastic.
*/
return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
} }
static int acm_tty_chars_in_buffer(struct tty_struct *tty) static int acm_tty_chars_in_buffer(struct tty_struct *tty)
...@@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) ...@@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0; /*
* This is inaccurate (overcounts), but it works.
*/
return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize;
} }
static void acm_tty_throttle(struct tty_struct *tty) static void acm_tty_throttle(struct tty_struct *tty)
...@@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ ...@@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
* USB probe and disconnect routines. * USB probe and disconnect routines.
*/ */
/* Little helper: write buffers free */
static void acm_write_buffers_free(struct acm *acm)
{
int i;
struct acm_wb *wb;
for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
}
}
/* Little helper: write buffers allocate */
static int acm_write_buffers_alloc(struct acm *acm)
{
int i;
struct acm_wb *wb;
for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
&wb->dmah);
if (!wb->buf) {
while (i != 0) {
--i;
--wb;
usb_buffer_free(acm->dev, acm->writesize,
wb->buf, wb->dmah);
}
return -ENOMEM;
}
}
return 0;
}
static int acm_probe (struct usb_interface *intf, static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
...@@ -700,7 +842,8 @@ static int acm_probe (struct usb_interface *intf, ...@@ -700,7 +842,8 @@ static int acm_probe (struct usb_interface *intf,
acm->bh.data = (unsigned long) acm; acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm); INIT_WORK(&acm->work, acm_softint, acm);
spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->throttle_lock);
acm->ready_for_write = 1; spin_lock_init(&acm->write_lock);
acm->write_ready = 1;
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) { if (!buf) {
...@@ -716,12 +859,10 @@ static int acm_probe (struct usb_interface *intf, ...@@ -716,12 +859,10 @@ static int acm_probe (struct usb_interface *intf,
} }
acm->read_buffer = buf; acm->read_buffer = buf;
buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma); if (acm_write_buffers_alloc(acm) < 0) {
if (!buf) {
dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4; goto alloc_fail4;
} }
acm->write_buffer = buf;
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) { if (!acm->ctrlurb) {
...@@ -750,9 +891,9 @@ static int acm_probe (struct usb_interface *intf, ...@@ -750,9 +891,9 @@ static int acm_probe (struct usb_interface *intf,
acm->readurb->transfer_dma = acm->read_dma; acm->readurb->transfer_dma = acm->read_dma;
usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
acm->write_buffer, acm->writesize, acm_write_bulk, acm); NULL, acm->writesize, acm_write_bulk, acm);
acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
acm->writeurb->transfer_dma = acm->write_dma; /* acm->writeurb->transfer_dma = 0; */
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
...@@ -775,7 +916,7 @@ static int acm_probe (struct usb_interface *intf, ...@@ -775,7 +916,7 @@ static int acm_probe (struct usb_interface *intf,
alloc_fail6: alloc_fail6:
usb_free_urb(acm->ctrlurb); usb_free_urb(acm->ctrlurb);
alloc_fail5: alloc_fail5:
usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); acm_write_buffers_free(acm);
alloc_fail4: alloc_fail4:
usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
alloc_fail3: alloc_fail3:
...@@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf) ...@@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf)
flush_scheduled_work(); /* wait for acm_softint */ flush_scheduled_work(); /* wait for acm_softint */
usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); acm_write_buffers_free(acm);
usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
......
...@@ -51,14 +51,34 @@ ...@@ -51,14 +51,34 @@
* Internal driver structures. * Internal driver structures.
*/ */
/*
* The only reason to have several buffers is to accomodate assumptions
* in line disciplines. They ask for empty space amount, receive our URB size,
* and proceed to issue several 1-character writes, assuming they will fit.
* The very first write takes a complete URB. Fortunately, this only happens
* when processing onlcr, so we only need 2 buffers.
*/
#define ACM_NWB 2
struct acm_wb {
unsigned char *buf;
dma_addr_t dmah;
int len;
int use;
};
struct acm { struct acm {
struct usb_device *dev; /* the corresponding usb device */ struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */ struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */ struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */ struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */ u8 *ctrl_buffer, *read_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */ dma_addr_t ctrl_dma, read_dma; /* dma handles of buffers */
struct acm_wb wb[ACM_NWB];
int write_current; /* current write buffer */
int write_used; /* number of non-empty write buffers */
int write_ready; /* write urb is not running */
spinlock_t write_lock;
struct usb_cdc_line_coding line; /* bits, stop, parity */ struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */ struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */ struct tasklet_struct bh; /* rx processing */
...@@ -71,7 +91,6 @@ struct acm { ...@@ -71,7 +91,6 @@ struct acm {
unsigned int minor; /* acm minor number */ unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */ unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */ unsigned char clocal; /* termios CLOCAL */
unsigned char ready_for_write; /* write urb can be used */
unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */ unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */
unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int ctrl_caps; /* control capabilities from the class specific header */
}; };
......
...@@ -379,6 +379,8 @@ static int usblp_open(struct inode *inode, struct file *file) ...@@ -379,6 +379,8 @@ static int usblp_open(struct inode *inode, struct file *file)
usblp->writeurb->transfer_buffer_length = 0; usblp->writeurb->transfer_buffer_length = 0;
usblp->wcomplete = 1; /* we begin writeable */ usblp->wcomplete = 1; /* we begin writeable */
usblp->rcomplete = 0; usblp->rcomplete = 0;
usblp->writeurb->status = 0;
usblp->readurb->status = 0;
if (usblp->bidir) { if (usblp->bidir) {
usblp->readcount = 0; usblp->readcount = 0;
...@@ -751,6 +753,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, ...@@ -751,6 +753,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
schedule(); schedule();
} else { } else {
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
down(&usblp->sem);
break; break;
} }
down (&usblp->sem); down (&usblp->sem);
......
...@@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) ...@@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
if (usb_interface_claimed(actconfig->interface[i])) { if (usb_interface_claimed(actconfig->interface[i])) {
dev_warn (&ps->dev->dev, dev_warn (&ps->dev->dev,
"usbfs: interface %d claimed " "usbfs: interface %d claimed by %s "
"while '%s' sets config #%d\n", "while '%s' sets config #%d\n",
actconfig->interface[i] actconfig->interface[i]
->cur_altsetting ->cur_altsetting
->desc.bInterfaceNumber, ->desc.bInterfaceNumber,
actconfig->interface[i]
->dev.driver->name,
current->comm, u); current->comm, u);
#if 0 /* FIXME: enable in 2.6.10 or so */
status = -EBUSY; status = -EBUSY;
break; break;
#endif
} }
} }
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub, ...@@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub,
message = "can't get hub status"; message = "can't get hub status";
goto fail; goto fail;
} }
cpu_to_le16s(&hubstatus); le16_to_cpus(&hubstatus);
if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { if (hdev == hdev->bus->root_hub) {
struct usb_hcd *hcd =
container_of(hdev->bus, struct usb_hcd, self);
hub->power_budget = min(500u, hcd->power_budget) / 2;
} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent); hub->descriptor->bHubContrCurrent);
hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
/ 2; / 2;
}
if (hub->power_budget)
dev_dbg(hub_dev, "%dmA bus power budget for children\n", dev_dbg(hub_dev, "%dmA bus power budget for children\n",
hub->power_budget * 2); hub->power_budget * 2);
}
ret = hub_hub_status(hub, &hubstatus, &hubchange); ret = hub_hub_status(hub, &hubstatus, &hubchange);
...@@ -1727,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev) ...@@ -1727,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev)
struct usb_driver *driver; struct usb_driver *driver;
intf = udev->actconfig->interface[i]; intf = udev->actconfig->interface[i];
if (intf->dev.power.power_state == PMSG_SUSPEND) if (intf->dev.power.power_state == PMSG_ON)
continue; continue;
if (!intf->dev.driver) { if (!intf->dev.driver) {
/* FIXME maybe force to alt 0 */ /* FIXME maybe force to alt 0 */
...@@ -2787,6 +2793,11 @@ static void hub_events(void) ...@@ -2787,6 +2793,11 @@ static void hub_events(void)
hub->activating = 0; hub->activating = 0;
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
if (!hdev->parent)
usb_enable_root_hub_irq(hdev->bus);
loop: loop:
usb_unlock_device(hdev); usb_unlock_device(hdev);
usb_put_intf(intf); usb_put_intf(intf);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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