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)
static struct resource udc_resources[] = {
/* order is significant! */
{ /* registers */
.start = IO_ADDRESS(UDC_BASE),
.end = IO_ADDRESS(UDC_BASE + 0xff),
.start = UDC_BASE,
.end = UDC_BASE + 0xff,
.flags = IORESOURCE_MEM,
}, { /* general IRQ */
.start = IH2_BASE + 20,
......@@ -355,8 +355,8 @@ static struct platform_device ohci_device = {
static struct resource otg_resources[] = {
/* order is significant! */
{
.start = IO_ADDRESS(OTG_BASE),
.end = IO_ADDRESS(OTG_BASE + 0xff),
.start = OTG_BASE,
.end = OTG_BASE + 0xff,
.flags = IORESOURCE_MEM,
}, {
.start = IH2_BASE + 8,
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_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
config USB_ATM
tristate "Generic USB ATM/DSL core I/O support"
tristate "USB DSL modem support"
depends on USB && ATM
select CRC32
default n
help
This provides a library which is used for packet I/O by USB DSL
modems, such as the SpeedTouch driver below.
Say Y here if you want to connect a USB Digital Subscriber Line (DSL)
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
module will be called usb_atm.
module will be called usbatm.
config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support"
depends on USB && ATM
select USB_ATM
tristate "Speedtouch USB support"
depends on USB_ATM
select FW_LOADER
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
two parts of the firmware, extracted by the user space tools; see
<http://www.linux-usb.org/SpeedTouch/> for details.
To compile this driver as a module, choose M here: the
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
# (the ones that don't fit into any other categories)
# Makefile for USB ATM/xDSL drivers
#
obj-$(CONFIG_USB_ATM) += usb_atm.o
obj-$(CONFIG_USB_CXACRU) += cxacru.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) 2003, Duncan Sands, SolNegro, Josep Comas
......@@ -21,12 +21,10 @@
*
******************************************************************************/
#ifndef _USBATM_H_
#define _USBATM_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
......@@ -37,140 +35,150 @@
# define DEBUG
#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>
#ifdef DEBUG
#define UDSL_ASSERT(x) BUG_ON(!(x))
#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
#define UDSL_MAX_RCV_URBS 4
#define UDSL_MAX_SND_URBS 4
#define UDSL_MAX_RCV_BUFS 8
#define UDSL_MAX_SND_BUFS 8
#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
#define UDSL_DEFAULT_RCV_URBS 2
#define UDSL_DEFAULT_SND_URBS 2
#define UDSL_DEFAULT_RCV_BUFS 4
#define UDSL_DEFAULT_SND_BUFS 4
#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
#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)
/* receive */
struct udsl_receive_buffer {
struct list_head list;
unsigned char *base;
unsigned int filled_cells;
};
#define usb_err(instance, format, arg...) \
dev_err(&(instance)->usb_intf->dev , format , ## arg)
#define usb_info(instance, format, arg...) \
dev_info(&(instance)->usb_intf->dev , format , ## arg)
#define usb_warn(instance, format, arg...) \
dev_warn(&(instance)->usb_intf->dev , format , ## arg)
#define usb_dbg(instance, format, arg...) \
dev_dbg(&(instance)->usb_intf->dev , format , ## arg)
/* FIXME: move to dev_* once ATM is driver model aware */
#define atm_printk(level, instance, format, arg...) \
printk(level "ATM dev %d: " format , \
(instance)->atm_dev->number , ## arg)
#define atm_err(instance, format, arg...) \
atm_printk(KERN_ERR, instance , format , ## arg)
#define atm_info(instance, format, arg...) \
atm_printk(KERN_INFO, instance , format , ## arg)
#define atm_warn(instance, format, arg...) \
atm_printk(KERN_WARNING, instance , format , ## arg)
#ifdef DEBUG
#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 {
/* vpi/vci lookup */
struct list_head list;
short vpi;
int vci;
struct atm_vcc *vcc;
/* mini driver */
/* raw cell reassembly */
struct sk_buff *sarb;
};
struct usbatm_data;
/* 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 list_head list;
unsigned char *base;
unsigned char *free_start;
unsigned int free_cells;
};
struct usbatm_driver {
struct module *owner;
struct udsl_sender {
struct list_head list;
struct udsl_send_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
const char *driver_name;
/*
* init device ... can sleep, or cause probe() failure. Drivers with a heavy_init
* 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 {
struct atm_skb_data atm_data;
unsigned int num_cells;
unsigned int num_entire;
unsigned int pdu_padding;
unsigned char aal5_trailer[ATM_AAL5_TRAILER];
/* init ATM device ... can sleep, or cause ATM initialization failure */
int (*atm_start) (struct usbatm_data *, struct atm_dev *);
/* cleanup ATM device ... can sleep, but can't fail */
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
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 {
UDSL_NO_FIRMWARE,
UDSL_LOADING_FIRMWARE,
UDSL_LOADED_FIRMWARE
struct usbatm_channel {
int endpoint; /* usb pipe */
unsigned int stride; /* ATM cell size + padding */
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 {
struct kref refcount;
struct semaphore serialize;
/* main driver data */
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_interface *usb_intf;
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 list_head vcc_list;
/* firmware */
int (*firmware_wait) (struct udsl_instance_data *);
enum udsl_status status;
wait_queue_head_t firmware_waiters;
/********************************
* private fields - do not use *
********************************/
/* receive */
struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
struct kref refcount;
struct semaphore serialize;
spinlock_t receive_lock;
struct list_head spare_receivers;
struct list_head filled_receive_buffers;
/* heavy init */
int thread_pid;
struct completion thread_started;
struct completion thread_exited;
struct tasklet_struct receive_tasklet;
struct list_head spare_receive_buffers;
/* ATM device */
struct list_head vcc_list;
/* send */
struct udsl_sender senders[UDSL_MAX_SND_URBS];
struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
struct usbatm_channel rx_channel;
struct usbatm_channel tx_channel;
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 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,
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);
#endif /* _USBATM_H_ */
/******************************************************************************
* 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
#define acm_send_break(acm, ms) \
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
*/
......@@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm)
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{
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("nonzero write bulk status received: %d", urb->status);
dbg("Entering acm_write_bulk with status %d\n", urb->status);
schedule_work(&acm->work);
out:
acm->ready_for_write = 1;
acm_write_done(acm);
acm_write_start(acm);
if (ACM_READY(acm))
schedule_work(&acm->work);
}
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
{
struct acm *acm = tty->driver_data;
int stat;
unsigned long flags;
int wbn;
struct acm_wb *wb;
dbg("Entering acm_tty_write to write %d bytes,\n", count);
if (!ACM_READY(acm))
return -EINVAL;
if (!acm->ready_for_write)
return 0;
if (!count)
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);
memcpy(acm->write_buffer, buf, count);
dbg(" Successfully copied.\n");
memcpy(wb->buf, buf, count);
wb->len = count;
spin_unlock_irqrestore(&acm->write_lock, flags);
acm->writeurb->transfer_buffer_length = count;
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;
if ((stat = acm_write_start(acm)) < 0)
return stat;
}
return count;
}
......@@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty)
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
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)
......@@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
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)
......@@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
* 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,
const struct usb_device_id *id)
{
......@@ -700,7 +842,8 @@ skip_normal_probe:
acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
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);
if (!buf) {
......@@ -716,12 +859,10 @@ skip_normal_probe:
}
acm->read_buffer = buf;
buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma);
if (!buf) {
if (acm_write_buffers_alloc(acm) < 0) {
dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4;
}
acm->write_buffer = buf;
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) {
......@@ -750,9 +891,9 @@ skip_normal_probe:
acm->readurb->transfer_dma = acm->read_dma;
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_dma = acm->write_dma;
/* acm->writeurb->transfer_dma = 0; */
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
......@@ -775,7 +916,7 @@ alloc_fail7:
alloc_fail6:
usb_free_urb(acm->ctrlurb);
alloc_fail5:
usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
acm_write_buffers_free(acm);
alloc_fail4:
usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
alloc_fail3:
......@@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf)
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->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
......
......@@ -51,14 +51,34 @@
* 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 usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */
u8 *ctrl_buffer, *read_buffer; /* buffers of urbs */
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 work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */
......@@ -71,7 +91,6 @@ struct acm {
unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */
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 int ctrl_caps; /* control capabilities from the class specific header */
};
......
......@@ -379,6 +379,8 @@ static int usblp_open(struct inode *inode, struct file *file)
usblp->writeurb->transfer_buffer_length = 0;
usblp->wcomplete = 1; /* we begin writeable */
usblp->rcomplete = 0;
usblp->writeurb->status = 0;
usblp->readurb->status = 0;
if (usblp->bidir) {
usblp->readcount = 0;
......@@ -751,6 +753,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
schedule();
} else {
set_current_state(TASK_RUNNING);
down(&usblp->sem);
break;
}
down (&usblp->sem);
......
......@@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
if (usb_interface_claimed(actconfig->interface[i])) {
dev_warn (&ps->dev->dev,
"usbfs: interface %d claimed "
"usbfs: interface %d claimed by %s "
"while '%s' sets config #%d\n",
actconfig->interface[i]
->cur_altsetting
->desc.bInterfaceNumber,
actconfig->interface[i]
->dev.driver->name,
current->comm, u);
#if 0 /* FIXME: enable in 2.6.10 or so */
status = -EBUSY;
break;
#endif
}
}
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub,
message = "can't get hub status";
goto fail;
}
cpu_to_le16s(&hubstatus);
if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
le16_to_cpus(&hubstatus);
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",
hub->descriptor->bHubContrCurrent);
hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
/ 2;
}
if (hub->power_budget)
dev_dbg(hub_dev, "%dmA bus power budget for children\n",
hub->power_budget * 2);
}
ret = hub_hub_status(hub, &hubstatus, &hubchange);
......@@ -1727,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev)
struct usb_driver *driver;
intf = udev->actconfig->interface[i];
if (intf->dev.power.power_state == PMSG_SUSPEND)
if (intf->dev.power.power_state == PMSG_ON)
continue;
if (!intf->dev.driver) {
/* FIXME maybe force to alt 0 */
......@@ -2787,6 +2793,11 @@ static void hub_events(void)
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:
usb_unlock_device(hdev);
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