Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 845 → Rev 846

/shark/trunk/drivers/usb/input/hid-input.c
0,0 → 1,641
/*
* $Id: hid-input.c,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* USB HID to Linux Input mapping
*/
 
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
 
#include <linuxcomp.h>
 
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/usb.h>
 
#include "hid.h"
 
#define unk KEY_UNKNOWN
 
static unsigned char hid_keyboard[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
115,114,unk,unk,unk,124,unk,181,182,183,184,185,186,187,188,189,
190,191,192,193,194,195,196,197,198,unk,unk,unk,unk,unk,unk,unk,
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
};
 
static struct {
__s32 x;
__s32 y;
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
static struct input_dev *find_input(struct hid_device *hid, struct hid_field *field)
{
struct list_head *lh;
struct hid_input *hidinput;
 
list_for_each (lh, &hid->inputs) {
int i;
 
hidinput = list_entry(lh, struct hid_input, list);
 
if (! hidinput->report)
continue;
 
for (i = 0; i < hidinput->report->maxfield; i++)
if (hidinput->report->field[i] == field)
return &hidinput->input;
}
 
/* Assume we only have one input and use it */
if (!list_empty(&hid->inputs)) {
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
return &hidinput->input;
}
 
/* This is really a bug */
return NULL;
}
 
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage)
{
struct input_dev *input = &hidinput->input;
struct hid_device *device = hidinput->input.private;
int max;
int is_abs = 0;
unsigned long *bit;
 
switch (usage->hid & HID_USAGE_PAGE) {
 
case HID_UP_KEYBOARD:
 
set_bit(EV_REP, input->evbit);
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
 
if ((usage->hid & HID_USAGE) < 256) {
if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE]))
return;
clear_bit(usage->code, bit);
} else
usage->code = KEY_UNKNOWN;
 
break;
 
case HID_UP_BUTTON:
 
usage->code = ((usage->hid - 1) & 0xf) + 0x100;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
 
switch (field->application) {
case HID_GD_GAMEPAD: usage->code += 0x10;
case HID_GD_JOYSTICK: usage->code += 0x10;
case HID_GD_MOUSE: usage->code += 0x10; break;
default:
if (field->physical == HID_GD_POINTER)
usage->code += 0x10;
break;
}
break;
 
case HID_UP_GENDESK:
 
if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */
switch (usage->hid & 0xf) {
case 0x1: usage->code = KEY_POWER; break;
case 0x2: usage->code = KEY_SLEEP; break;
case 0x3: usage->code = KEY_WAKEUP; break;
default: usage->code = KEY_UNKNOWN; break;
}
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break;
}
 
usage->code = usage->hid & 0xf;
 
if (field->report_size == 1) {
usage->code = BTN_MISC;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break;
}
 
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
break;
}
 
usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
 
if (usage->hid == HID_GD_HATSWITCH) {
usage->code = ABS_HAT0X;
usage->hat_min = field->logical_minimum;
usage->hat_max = field->logical_maximum;
}
break;
 
case HID_UP_LED:
 
usage->code = (usage->hid - 1) & 0xf;
usage->type = EV_LED; bit = input->ledbit; max = LED_MAX;
break;
 
case HID_UP_DIGITIZER:
 
switch (usage->hid & 0xff) {
 
case 0x30: /* TipPressure */
 
if (!test_bit(BTN_TOUCH, input->keybit)) {
device->quirks |= HID_QUIRK_NOTOUCH;
set_bit(EV_KEY, input->evbit);
set_bit(BTN_TOUCH, input->keybit);
}
usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
usage->code = ABS_PRESSURE;
clear_bit(usage->code, bit);
break;
 
case 0x32: /* InRange */
 
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
switch (field->physical & 0xff) {
case 0x21: usage->code = BTN_TOOL_MOUSE; break;
case 0x22: usage->code = BTN_TOOL_FINGER; break;
default: usage->code = BTN_TOOL_PEN; break;
}
break;
 
case 0x3c: /* Invert */
 
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
usage->code = BTN_TOOL_RUBBER;
clear_bit(usage->code, bit);
break;
 
case 0x33: /* Touch */
case 0x42: /* TipSwitch */
case 0x43: /* TipSwitch2 */
 
device->quirks &= ~HID_QUIRK_NOTOUCH;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
usage->code = BTN_TOUCH;
clear_bit(usage->code, bit);
break;
 
case 0x44: /* BarrelSwitch */
 
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
usage->code = BTN_STYLUS;
clear_bit(usage->code, bit);
break;
 
default: goto unknown;
}
break;
 
case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */
 
set_bit(EV_REP, input->evbit);
switch (usage->hid & HID_USAGE) {
case 0x000: usage->code = 0; break;
case 0x034: usage->code = KEY_SLEEP; break;
case 0x036: usage->code = BTN_MISC; break;
case 0x08a: usage->code = KEY_WWW; break;
case 0x095: usage->code = KEY_HELP; break;
 
case 0x0b0: usage->code = KEY_PLAY; break;
case 0x0b1: usage->code = KEY_PAUSE; break;
case 0x0b2: usage->code = KEY_RECORD; break;
case 0x0b3: usage->code = KEY_FASTFORWARD; break;
case 0x0b4: usage->code = KEY_REWIND; break;
case 0x0b5: usage->code = KEY_NEXTSONG; break;
case 0x0b6: usage->code = KEY_PREVIOUSSONG; break;
case 0x0b7: usage->code = KEY_STOPCD; break;
case 0x0b8: usage->code = KEY_EJECTCD; break;
case 0x0cd: usage->code = KEY_PLAYPAUSE; break;
case 0x0e0: is_abs = 1;
usage->code = ABS_VOLUME;
break;
case 0x0e2: usage->code = KEY_MUTE; break;
case 0x0e5: usage->code = KEY_BASSBOOST; break;
case 0x0e9: usage->code = KEY_VOLUMEUP; break;
case 0x0ea: usage->code = KEY_VOLUMEDOWN; break;
 
case 0x183: usage->code = KEY_CONFIG; break;
case 0x18a: usage->code = KEY_MAIL; break;
case 0x192: usage->code = KEY_CALC; break;
case 0x194: usage->code = KEY_FILE; break;
case 0x21a: usage->code = KEY_UNDO; break;
case 0x21b: usage->code = KEY_COPY; break;
case 0x21c: usage->code = KEY_CUT; break;
case 0x21d: usage->code = KEY_PASTE; break;
 
case 0x221: usage->code = KEY_FIND; break;
case 0x223: usage->code = KEY_HOMEPAGE; break;
case 0x224: usage->code = KEY_BACK; break;
case 0x225: usage->code = KEY_FORWARD; break;
case 0x226: usage->code = KEY_STOP; break;
case 0x227: usage->code = KEY_REFRESH; break;
case 0x22a: usage->code = KEY_BOOKMARKS; break;
 
default: usage->code = KEY_UNKNOWN; break;
}
 
if (is_abs) {
usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
} else {
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
}
break;
 
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
 
set_bit(EV_REP, input->evbit);
switch (usage->hid & HID_USAGE) {
case 0x021: usage->code = KEY_PRINT; break;
case 0x070: usage->code = KEY_HP; break;
case 0x071: usage->code = KEY_CAMERA; break;
case 0x072: usage->code = KEY_SOUND; break;
case 0x073: usage->code = KEY_QUESTION; break;
 
case 0x080: usage->code = KEY_EMAIL; break;
case 0x081: usage->code = KEY_CHAT; break;
case 0x082: usage->code = KEY_SEARCH; break;
case 0x083: usage->code = KEY_CONNECT; break;
case 0x084: usage->code = KEY_FINANCE; break;
case 0x085: usage->code = KEY_SPORT; break;
case 0x086: usage->code = KEY_SHOP; break;
 
default: usage->code = KEY_UNKNOWN; break;
 
}
 
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break;
case HID_UP_PID:
 
usage->type = EV_FF; bit = input->ffbit; max = FF_MAX;
switch(usage->hid & HID_USAGE) {
case 0x26: set_bit(FF_CONSTANT, input->ffbit); break;
case 0x27: set_bit(FF_RAMP, input->ffbit); break;
case 0x28: set_bit(FF_CUSTOM, input->ffbit); break;
case 0x30: set_bit(FF_SQUARE, input->ffbit);
set_bit(FF_PERIODIC, input->ffbit); break;
case 0x31: set_bit(FF_SINE, input->ffbit);
set_bit(FF_PERIODIC, input->ffbit); break;
case 0x32: set_bit(FF_TRIANGLE, input->ffbit);
set_bit(FF_PERIODIC, input->ffbit); break;
case 0x33: set_bit(FF_SAW_UP, input->ffbit);
set_bit(FF_PERIODIC, input->ffbit); break;
case 0x34: set_bit(FF_SAW_DOWN, input->ffbit);
set_bit(FF_PERIODIC, input->ffbit); break;
case 0x40: set_bit(FF_SPRING, input->ffbit); break;
case 0x41: set_bit(FF_DAMPER, input->ffbit); break;
case 0x42: set_bit(FF_INERTIA , input->ffbit); break;
case 0x43: set_bit(FF_FRICTION, input->ffbit); break;
case 0x7e: usage->code = FF_GAIN; break;
case 0x83: /* Simultaneous Effects Max */
input->ff_effects_max = (field->value[0]);
dbg("Maximum Effects - %d",input->ff_effects_max);
break;
case 0x98: /* Device Control */
usage->code = FF_AUTOCENTER; break;
case 0xa4: /* Safety Switch */
usage->code = BTN_DEAD;
bit = input->keybit;
usage->type = EV_KEY;
max = KEY_MAX;
dbg("Safety Switch Report\n");
break;
case 0x9f: /* Device Paused */
case 0xa0: /* Actuators Enabled */
dbg("Not telling the input API about ");
resolv_usage(usage->hid);
return;
}
break;
default:
unknown:
resolv_usage(usage->hid);
 
if (field->report_size == 1) {
 
if (field->report->type == HID_OUTPUT_REPORT) {
usage->code = LED_MISC;
usage->type = EV_LED; bit = input->ledbit; max = LED_MAX;
break;
}
 
usage->code = BTN_MISC;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break;
}
 
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
usage->code = REL_MISC;
usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
break;
}
 
usage->code = ABS_MISC;
usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
break;
}
 
set_bit(usage->type, input->evbit);
if ((usage->type == EV_REL)
&& (device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK)
&& (usage->code == REL_WHEEL)) {
set_bit(REL_HWHEEL, bit);
}
 
while (usage->code <= max && test_and_set_bit(usage->code, bit)) {
usage->code = find_next_zero_bit(bit, max + 1, usage->code);
}
 
if (usage->code > max)
return;
 
if (usage->type == EV_ABS) {
int a = field->logical_minimum;
int b = field->logical_maximum;
 
if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) {
a = field->logical_minimum = 0;
b = field->logical_maximum = 255;
}
input->absmin[usage->code] = a;
input->absmax[usage->code] = b;
input->absfuzz[usage->code] = 0;
input->absflat[usage->code] = 0;
 
if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) {
input->absfuzz[usage->code] = (b - a) >> 8;
input->absflat[usage->code] = (b - a) >> 4;
}
}
 
if (usage->hat_min != usage->hat_max) {
int i;
for (i = usage->code; i < usage->code + 2 && i <= max; i++) {
input->absmax[i] = 1;
input->absmin[i] = -1;
input->absfuzz[i] = 0;
input->absflat[i] = 0;
}
set_bit(usage->code + 1, input->absbit);
}
}
 
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
{
struct input_dev *input = find_input(hid, field);
int *quirks = &hid->quirks;
 
if (!input)
return;
 
input_regs(input, regs);
 
if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK)
&& (usage->code == BTN_BACK)) {
if (value)
hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
else
hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
return;
}
if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON)
&& (usage->code == REL_WHEEL)) {
input_event(input, usage->type, REL_HWHEEL, value);
return;
}
 
if (usage->hat_min != usage->hat_max) {
value = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1;
if (value < 0 || value > 8) value = 0;
input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x);
input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y);
return;
}
 
if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
return;
}
 
if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
if (value) {
input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
return;
}
input_event(input, usage->type, usage->code, 0);
input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
return;
}
 
if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
int a = field->logical_minimum;
int b = field->logical_maximum;
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
}
 
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
input->ff_effects_max = value;
dbg("Maximum Effects - %d",input->ff_effects_max);
return;
}
if (usage->hid == (HID_UP_PID | 0x7fUL)) {
dbg("PID Pool Report\n");
return;
}
 
if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */
return;
 
input_event(input, usage->type, usage->code, value);
 
if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
input_event(input, usage->type, usage->code, 0);
}
 
void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
{
struct list_head *lh;
struct hid_input *hidinput;
 
list_for_each (lh, &hid->inputs) {
hidinput = list_entry(lh, struct hid_input, list);
input_sync(&hidinput->input);
}
}
 
static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct hid_device *hid = dev->private;
struct hid_field *field = NULL;
int offset;
 
if (type == EV_FF)
return hid_ff_event(hid, dev, type, code, value);
 
if ((offset = hid_find_field(hid, type, code, &field)) == -1) {
warn("event field not found");
return -1;
}
 
hid_set_field(field, offset, value);
hid_submit_report(hid, field->report, USB_DIR_OUT);
 
return 0;
}
 
static int hidinput_open(struct input_dev *dev)
{
struct hid_device *hid = dev->private;
return hid_open(hid);
}
 
static void hidinput_close(struct input_dev *dev)
{
struct hid_device *hid = dev->private;
hid_close(hid);
}
 
/*
* Register the input device; print a message.
* Configure the input layer interface
* Read all reports and initialize the absolute field values.
*/
 
int hidinput_connect(struct hid_device *hid)
{
struct usb_device *dev = hid->dev;
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
struct hid_input *hidinput = NULL;
int i, j, k;
 
INIT_LIST_HEAD(&hid->inputs);
 
for (i = 0; i < hid->maxcollection; i++)
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
IS_INPUT_APPLICATION(hid->collection[i].usage))
break;
 
if (i == hid->maxcollection)
return -1;
 
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
report_enum = hid->report_enum + k;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
 
if (!report->maxfield)
continue;
 
if (!hidinput) {
hidinput = kmalloc(sizeof(*hidinput), GFP_KERNEL);
if (!hidinput) {
err("Out of memory during hid input probe");
return -1;
}
memset(hidinput, 0, sizeof(*hidinput));
 
list_add_tail(&hidinput->list, &hid->inputs);
 
hidinput->input.private = hid;
hidinput->input.event = hidinput_input_event;
hidinput->input.open = hidinput_open;
hidinput->input.close = hidinput_close;
 
hidinput->input.name = hid->name;
hidinput->input.phys = hid->phys;
hidinput->input.uniq = hid->uniq;
hidinput->input.id.bustype = BUS_USB;
hidinput->input.id.vendor = dev->descriptor.idVendor;
hidinput->input.id.product = dev->descriptor.idProduct;
hidinput->input.id.version = dev->descriptor.bcdDevice;
}
 
for (i = 0; i < report->maxfield; i++)
for (j = 0; j < report->field[i]->maxusage; j++)
hidinput_configure_usage(hidinput, report->field[i],
report->field[i]->usage + j);
 
if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
/* This will leave hidinput NULL, so that it
* allocates another one if we have more inputs on
* the same interface. Some devices (e.g. Happ's
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
input_register_device(&hidinput->input);
hidinput = NULL;
}
 
list = list->next;
}
}
 
/* This only gets called when we are a single-input (most of the
* time). IOW, not a HID_QUIRK_MULTI_INPUT. The hid_ff_init() is
* only useful in this case, and not for multi-input quirks. */
if (hidinput) {
hid_ff_init(hid);
input_register_device(&hidinput->input);
}
 
return 0;
}
 
void hidinput_disconnect(struct hid_device *hid)
{
struct list_head *lh, *next;
struct hid_input *hidinput;
 
list_for_each_safe (lh, next, &hid->inputs) {
hidinput = list_entry(lh, struct hid_input, list);
input_unregister_device(&hidinput->input);
list_del(&hidinput->list);
kfree(hidinput);
}
}
/shark/trunk/drivers/usb/input/usbmouse.c
0,0 → 1,262
/*
* $Id: usbmouse.c,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Mouse support
*/
 
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
 
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
#define DRIVER_LICENSE "GPL"
 
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
 
struct usb_mouse {
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev dev;
struct urb *irq;
int open;
 
signed char *data;
dma_addr_t data_dma;
};
 
static void usb_mouse_irq(struct urb *urb, struct pt_regs *regs)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = &mouse->dev;
int status;
 
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
 
input_regs(dev, regs);
 
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
 
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
 
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
 
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
 
if (mouse->open++)
return 0;
 
mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL)) {
mouse->open--;
return -EIO;
}
 
return 0;
}
 
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
 
if (!--mouse->open)
usb_unlink_urb(mouse->irq);
}
 
static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id)
{
struct usb_device * dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
int pipe, maxp;
char path[64];
char *buf;
 
interface = &intf->altsetting[intf->act_altsetting];
 
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
 
endpoint = &interface->endpoint[0].desc;
if (!(endpoint->bEndpointAddress & 0x80))
return -ENODEV;
if ((endpoint->bmAttributes & 3) != 3)
return -ENODEV;
 
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL)))
return -ENOMEM;
memset(mouse, 0, sizeof(struct usb_mouse));
 
mouse->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &mouse->data_dma);
if (!mouse->data) {
kfree(mouse);
return -ENOMEM;
}
 
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq) {
usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
kfree(mouse);
return -ENODEV;
}
 
mouse->usbdev = dev;
 
mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
mouse->dev.relbit[0] |= BIT(REL_WHEEL);
 
mouse->dev.private = mouse;
mouse->dev.open = usb_mouse_open;
mouse->dev.close = usb_mouse_close;
 
usb_make_path(dev, path, 64);
sprintf26(mouse->phys, "%s/input0", path);
 
mouse->dev.name = mouse->name;
mouse->dev.phys = mouse->phys;
mouse->dev.id.bustype = BUS_USB;
mouse->dev.id.vendor = dev->descriptor.idVendor;
mouse->dev.id.product = dev->descriptor.idProduct;
mouse->dev.id.version = dev->descriptor.bcdDevice;
 
if (!(buf = kmalloc(63, GFP_KERNEL))) {
usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
kfree(mouse);
return -ENOMEM;
}
 
if (dev->descriptor.iManufacturer &&
usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
strcat(mouse->name, buf);
if (dev->descriptor.iProduct &&
usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
sprintf26(mouse->name, "%s %s", mouse->name, buf);
 
if (!strlen(mouse->name))
sprintf26(mouse->name, "USB HIDBP Mouse %04x:%04x",
mouse->dev.id.vendor, mouse->dev.id.product);
 
kfree(buf);
 
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
input_register_device(&mouse->dev);
printk(KERN_INFO "input: %s on %s\n", mouse->name, path);
 
usb_set_intfdata(intf, mouse);
return 0;
}
 
static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (mouse) {
usb_unlink_urb(mouse->irq);
input_unregister_device(&mouse->dev);
usb_free_urb(mouse->irq);
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
kfree(mouse);
}
}
 
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(3, 1, 2) },
{ } /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
 
static struct usb_driver usb_mouse_driver = {
.owner = THIS_MODULE,
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
 
/*static*/ int __init usb_mouse_init(void)
{
int retval = usb_register(&usb_mouse_driver);
if (retval == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return retval;
}
 
/*static*/ void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver);
}
 
module_init(usb_mouse_init);
module_exit(usb_mouse_exit);
/shark/trunk/drivers/usb/input/hid.h
0,0 → 1,469
#ifndef __HID_H
#define __HID_H
 
/*
* $Id: hid.h,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
*/
 
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
 
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
 
/*
* USB HID (Human Interface Device) interface class code
*/
 
#define USB_INTERFACE_CLASS_HID 3
 
/*
* HID class requests
*/
 
#define HID_REQ_GET_REPORT 0x01
#define HID_REQ_GET_IDLE 0x02
#define HID_REQ_GET_PROTOCOL 0x03
#define HID_REQ_SET_REPORT 0x09
#define HID_REQ_SET_IDLE 0x0A
#define HID_REQ_SET_PROTOCOL 0x0B
 
/*
* HID class descriptor types
*/
 
#define HID_DT_HID (USB_TYPE_CLASS | 0x01)
#define HID_DT_REPORT (USB_TYPE_CLASS | 0x02)
#define HID_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
 
/*
* We parse each description item into this structure. Short items data
* values are expanded to 32-bit signed int, long items contain a pointer
* into the data area.
*/
 
struct hid_item {
unsigned format;
__u8 size;
__u8 type;
__u8 tag;
union {
__u8 u8;
__s8 s8;
__u16 u16;
__s16 s16;
__u32 u32;
__s32 s32;
__u8 *longdata;
} data;
};
 
/*
* HID report item format
*/
 
#define HID_ITEM_FORMAT_SHORT 0
#define HID_ITEM_FORMAT_LONG 1
 
/*
* Special tag indicating long items
*/
 
#define HID_ITEM_TAG_LONG 15
 
/*
* HID report descriptor item type (prefix bit 2,3)
*/
 
#define HID_ITEM_TYPE_MAIN 0
#define HID_ITEM_TYPE_GLOBAL 1
#define HID_ITEM_TYPE_LOCAL 2
#define HID_ITEM_TYPE_RESERVED 3
 
/*
* HID report descriptor main item tags
*/
 
#define HID_MAIN_ITEM_TAG_INPUT 8
#define HID_MAIN_ITEM_TAG_OUTPUT 9
#define HID_MAIN_ITEM_TAG_FEATURE 11
#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10
#define HID_MAIN_ITEM_TAG_END_COLLECTION 12
 
/*
* HID report descriptor main item contents
*/
 
#define HID_MAIN_ITEM_CONSTANT 0x001
#define HID_MAIN_ITEM_VARIABLE 0x002
#define HID_MAIN_ITEM_RELATIVE 0x004
#define HID_MAIN_ITEM_WRAP 0x008
#define HID_MAIN_ITEM_NONLINEAR 0x010
#define HID_MAIN_ITEM_NO_PREFERRED 0x020
#define HID_MAIN_ITEM_NULL_STATE 0x040
#define HID_MAIN_ITEM_VOLATILE 0x080
#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100
 
/*
* HID report descriptor collection item types
*/
 
#define HID_COLLECTION_PHYSICAL 0
#define HID_COLLECTION_APPLICATION 1
#define HID_COLLECTION_LOGICAL 2
 
/*
* HID report descriptor global item tags
*/
 
#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0
#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1
#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2
#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3
#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4
#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5
#define HID_GLOBAL_ITEM_TAG_UNIT 6
#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7
#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8
#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9
#define HID_GLOBAL_ITEM_TAG_PUSH 10
#define HID_GLOBAL_ITEM_TAG_POP 11
 
/*
* HID report descriptor local item tags
*/
 
#define HID_LOCAL_ITEM_TAG_USAGE 0
#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1
#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4
#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5
#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7
#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8
#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9
#define HID_LOCAL_ITEM_TAG_DELIMITER 10
 
/*
* HID usage tables
*/
 
#define HID_USAGE_PAGE 0xffff0000
 
#define HID_UP_GENDESK 0x00010000
#define HID_UP_KEYBOARD 0x00070000
#define HID_UP_LED 0x00080000
#define HID_UP_BUTTON 0x00090000
#define HID_UP_ORDINAL 0x000a0000
#define HID_UP_CONSUMER 0x000c0000
#define HID_UP_DIGITIZER 0x000d0000
#define HID_UP_PID 0x000f0000
#define HID_UP_HPVENDOR 0xff7f0000
 
#define HID_USAGE 0x0000ffff
 
#define HID_GD_POINTER 0x00010001
#define HID_GD_MOUSE 0x00010002
#define HID_GD_JOYSTICK 0x00010004
#define HID_GD_GAMEPAD 0x00010005
#define HID_GD_HATSWITCH 0x00010039
 
/*
* HID report types --- Ouch! HID spec says 1 2 3!
*/
 
#define HID_INPUT_REPORT 0
#define HID_OUTPUT_REPORT 1
#define HID_FEATURE_REPORT 2
 
/*
* HID device quirks.
*/
 
#define HID_QUIRK_INVERT 0x001
#define HID_QUIRK_NOTOUCH 0x002
#define HID_QUIRK_IGNORE 0x004
#define HID_QUIRK_NOGET 0x008
#define HID_QUIRK_HIDDEV 0x010
#define HID_QUIRK_BADPAD 0x020
#define HID_QUIRK_MULTI_INPUT 0x040
#define HID_QUIRK_2WHEEL_MOUSE_HACK 0x080
#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x100
 
/*
* This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and
* restored with PUSH/POP statements.
*/
 
struct hid_global {
unsigned usage_page;
__s32 logical_minimum;
__s32 logical_maximum;
__s32 physical_minimum;
__s32 physical_maximum;
__s32 unit_exponent;
unsigned unit;
unsigned report_id;
unsigned report_size;
unsigned report_count;
};
 
/*
* This is the local environment. It is persistent up the next main-item.
*/
 
#define HID_MAX_DESCRIPTOR_SIZE 4096
#define HID_MAX_USAGES 1024
#define HID_DEFAULT_NUM_COLLECTIONS 16
 
struct hid_local {
unsigned usage[HID_MAX_USAGES]; /* usage array */
unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
unsigned usage_index;
unsigned usage_minimum;
unsigned delimiter_depth;
unsigned delimiter_branch;
};
 
/*
* This is the collection stack. We climb up the stack to determine
* application and function of each field.
*/
 
struct hid_collection {
unsigned type;
unsigned usage;
unsigned level;
};
 
struct hid_usage {
unsigned hid; /* hid usage code */
unsigned collection_index; /* index into collection array */
__u16 code; /* input driver code */
__u8 type; /* input driver type */
__s8 hat_min; /* hat switch fun */
__s8 hat_max; /* ditto */
};
 
struct hid_field {
unsigned physical; /* physical usage for this field */
unsigned logical; /* logical usage for this field */
unsigned application; /* application usage for this field */
struct hid_usage *usage; /* usage table for this function */
unsigned maxusage; /* maximum usage index */
unsigned flags; /* main-item flags (i.e. volatile,array,constant) */
unsigned report_offset; /* bit offset in the report */
unsigned report_size; /* size of this field in the report */
unsigned report_count; /* number of this field in the report */
unsigned report_type; /* (input,output,feature) */
__s32 *value; /* last known value(s) */
__s32 logical_minimum;
__s32 logical_maximum;
__s32 physical_minimum;
__s32 physical_maximum;
__s32 unit_exponent;
unsigned unit;
struct hid_report *report; /* associated report */
unsigned index; /* index into report->field[] */
};
 
#define HID_MAX_FIELDS 64
 
struct hid_report {
struct list_head list;
unsigned id; /* id of this report */
unsigned type; /* report type */
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
unsigned maxfield; /* maximum valid field index */
unsigned size; /* size of the report (bits) */
struct hid_device *device; /* associated device */
};
 
struct hid_report_enum {
unsigned numbered;
struct list_head report_list;
struct hid_report *report_id_hash[256];
};
 
#define HID_REPORT_TYPES 3
 
#define HID_BUFFER_SIZE 32
#define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */
#define HID_OUTPUT_FIFO_SIZE 64
 
struct hid_control_fifo {
unsigned char dir;
struct hid_report *report;
};
 
#define HID_CLAIMED_INPUT 1
#define HID_CLAIMED_HIDDEV 2
 
#define HID_CTRL_RUNNING 1
#define HID_OUT_RUNNING 2
 
struct hid_input {
struct list_head list;
struct hid_report *report;
struct input_dev input;
};
 
struct hid_device { /* device report descriptor */
__u8 *rdesc;
unsigned rsize;
struct hid_collection *collection; /* List of HID collections */
unsigned collection_size; /* Number of allocated hid_collections */
unsigned maxcollection; /* Number of parsed collections */
unsigned maxapplication; /* Number of applications */
unsigned version; /* HID version */
unsigned country; /* HID country */
struct hid_report_enum report_enum[HID_REPORT_TYPES];
 
struct usb_device *dev; /* USB device */
int ifnum; /* USB interface number */
 
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
 
struct urb *urbin; /* Input URB */
char *inbuf; /* Input buffer */
dma_addr_t inbuf_dma; /* Input buffer dma */
 
struct urb *urbctrl; /* Control URB */
struct usb_ctrlrequest *cr; /* Control request struct */
dma_addr_t cr_dma; /* Control request struct dma */
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
spinlock_t ctrllock; /* Control fifo spinlock */
 
struct urb *urbout; /* Output URB */
struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */
spinlock_t outlock; /* Output fifo spinlock */
 
unsigned claimed; /* Claimed by hidinput, hiddev? */
unsigned quirks; /* Various quirks the device can pull on us */
 
struct list_head inputs; /* The list of inputs */
void *hiddev; /* The hiddev structure */
int minor; /* Hiddev minor number */
 
wait_queue_head_t wait; /* For sleeping */
 
int open; /* is the device open by anyone? */
char name[128]; /* Device name */
char phys[64]; /* Device physical location */
char uniq[64]; /* Device unique identifier (serial #) */
 
void *ff_private; /* Private data for the force-feedback driver */
void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
int (*ff_event)(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value);
};
 
#define HID_GLOBAL_STACK_SIZE 4
#define HID_COLLECTION_STACK_SIZE 4
 
struct hid_parser {
struct hid_global global;
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
unsigned global_stack_ptr;
struct hid_local local;
unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
unsigned collection_stack_ptr;
struct hid_device *device;
};
 
struct hid_class_descriptor {
__u8 bDescriptorType;
__u16 wDescriptorLength;
} __attribute__ ((packed));
 
struct hid_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u16 bcdHID;
__u8 bCountryCode;
__u8 bNumDescriptors;
 
struct hid_class_descriptor desc[1];
} __attribute__ ((packed));
 
#ifdef DEBUG
#include "hid-debug.h"
#else
#define hid_dump_input(a,b) do { } while (0)
#define hid_dump_device(c) do { } while (0)
#define hid_dump_field(a,b) do { } while (0)
#define resolv_usage(a) do { } while (0)
#endif
 
#endif
 
#ifdef CONFIG_USB_HIDINPUT
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
/* We ignore a few input applications that are not widely used */
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || ( a == 0x00010080) || ( a == 0x000c0001))
extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32, struct pt_regs *regs);
extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
extern int hidinput_connect(struct hid_device *);
extern void hidinput_disconnect(struct hid_device *);
#else
#define IS_INPUT_APPLICATION(a) (0)
static inline void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs) { }
static inline void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { }
static inline int hidinput_connect(struct hid_device *hid) { return -ENODEV; }
static inline void hidinput_disconnect(struct hid_device *hid) { }
#endif
 
int hid_open(struct hid_device *);
void hid_close(struct hid_device *);
int hid_find_field(struct hid_device *, unsigned int, unsigned int, struct hid_field **);
int hid_set_field(struct hid_field *, unsigned, __s32);
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
void hid_init_reports(struct hid_device *hid);
int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type);
 
 
#ifdef CONFIG_HID_FF
int hid_ff_init(struct hid_device *hid);
#else
static inline int hid_ff_init(struct hid_device *hid) { return -1; }
#endif
static inline void hid_ff_exit(struct hid_device *hid)
{
if (hid->ff_exit)
hid->ff_exit(hid);
}
static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value)
{
if (hid->ff_event)
return hid->ff_event(hid, input, type, code, value);
return -ENOSYS;
}
/shark/trunk/drivers/usb/input/usbkbd.c
0,0 → 1,381
/*
* $Id: usbkbd.c,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Keyboard support
*/
 
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/usb.h>
 
/*
* Version Information
*/
#define DRIVER_VERSION ""
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
#define DRIVER_LICENSE "GPL"
 
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
 
static unsigned char usb_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189,
190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};
 
struct usb_kbd {
struct input_dev dev;
struct usb_device *usbdev;
unsigned char old[8];
struct urb *irq, *led;
unsigned char newleds;
char name[128];
char phys[64];
int open;
 
unsigned char *new;
struct usb_ctrlrequest *cr;
unsigned char *leds;
dma_addr_t cr_dma;
dma_addr_t new_dma;
dma_addr_t leds_dma;
};
 
static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)
{
struct usb_kbd *kbd = urb->context;
int i;
 
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
 
input_regs(&kbd->dev, regs);
 
for (i = 0; i < 8; i++)
input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
 
for (i = 2; i < 8; i++) {
 
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]])
input_report_key(&kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
else
info("Unknown key (scancode %#x) released.", kbd->old[i]);
}
 
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]])
input_report_key(&kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
else
info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
}
}
 
input_sync(&kbd->dev);
 
memcpy(kbd->old, kbd->new, 8);
 
resubmit:
i = usb_submit_urb (urb, SLAB_ATOMIC);
if (i)
err ("can't resubmit intr, %s-%s/input0, status %d",
kbd->usbdev->bus->bus_name,
kbd->usbdev->devpath, i);
}
 
int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct usb_kbd *kbd = dev->private;
 
if (type != EV_LED)
return -1;
 
 
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
 
if (kbd->led->status == -EINPROGRESS)
return 0;
 
if (*(kbd->leds) == kbd->newleds)
return 0;
 
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
 
return 0;
}
 
static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)
{
struct usb_kbd *kbd = urb->context;
 
if (urb->status)
warn("led urb status %d received", urb->status);
if (*(kbd->leds) == kbd->newleds)
return;
 
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
}
 
static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = dev->private;
 
if (kbd->open++)
return 0;
 
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL)) {
kbd->open--;
return -EIO;
}
 
return 0;
}
 
static void usb_kbd_close(struct input_dev *dev)
{
struct usb_kbd *kbd = dev->private;
 
if (!--kbd->open)
usb_unlink_urb(kbd->irq);
}
 
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
if (!(kbd->new = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kbd->new_dma)))
return -1;
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), SLAB_ATOMIC, &kbd->cr_dma)))
return -1;
if (!(kbd->leds = usb_buffer_alloc(dev, 1, SLAB_ATOMIC, &kbd->leds_dma)))
return -1;
 
return 0;
}
 
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (kbd->irq)
usb_free_urb(kbd->irq);
if (kbd->led)
usb_free_urb(kbd->led);
if (kbd->new)
usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
if (kbd->cr)
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
if (kbd->leds)
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
}
 
static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device * dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
int i, pipe, maxp;
char path[64];
char *buf;
 
interface = &iface->altsetting[iface->act_altsetting];
 
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
 
endpoint = &interface->endpoint[0].desc;
if (!(endpoint->bEndpointAddress & 0x80))
return -ENODEV;
if ((endpoint->bmAttributes & 3) != 3)
return -ENODEV;
 
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL)))
return -ENOMEM;
memset(kbd, 0, sizeof(struct usb_kbd));
 
if (usb_kbd_alloc_mem(dev, kbd)) {
usb_kbd_free_mem(dev, kbd);
kfree(kbd);
return -ENOMEM;
}
 
kbd->usbdev = dev;
 
kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
 
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], kbd->dev.keybit);
clear_bit(0, kbd->dev.keybit);
kbd->dev.private = kbd;
kbd->dev.event = usb_kbd_event;
kbd->dev.open = usb_kbd_open;
kbd->dev.close = usb_kbd_close;
 
usb_fill_int_urb(kbd->irq, dev, pipe,
kbd->new, (maxp > 8 ? 8 : maxp),
usb_kbd_irq, kbd, endpoint->bInterval);
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
 
usb_make_path(dev, path, 64);
sprintf26(kbd->phys, "%s/input0", path);
 
kbd->dev.name = kbd->name;
kbd->dev.phys = kbd->phys;
kbd->dev.id.bustype = BUS_USB;
kbd->dev.id.vendor = dev->descriptor.idVendor;
kbd->dev.id.product = dev->descriptor.idProduct;
kbd->dev.id.version = dev->descriptor.bcdDevice;
 
if (!(buf = kmalloc(63, GFP_KERNEL))) {
usb_free_urb(kbd->irq);
usb_kbd_free_mem(dev, kbd);
kfree(kbd);
return -ENOMEM;
}
 
if (dev->descriptor.iManufacturer &&
usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
strcat(kbd->name, buf);
if (dev->descriptor.iProduct &&
usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
sprintf26(kbd->name, "%s %s", kbd->name, buf);
 
if (!strlen(kbd->name))
sprintf26(kbd->name, "USB HIDBP Keyboard %04x:%04x",
kbd->dev.id.vendor, kbd->dev.id.product);
 
kfree(buf);
 
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);
kbd->led->setup_dma = kbd->cr_dma;
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
 
input_register_device(&kbd->dev);
 
printk(KERN_INFO "input: %s on %s\n", kbd->name, path);
 
usb_set_intfdata(iface, kbd);
return 0;
}
 
static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (kbd) {
usb_unlink_urb(kbd->irq);
input_unregister_device(&kbd->dev);
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
kfree(kbd);
}
}
 
static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(3, 1, 1) },
{ } /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
 
static struct usb_driver usb_kbd_driver = {
.owner = THIS_MODULE,
.name = "usbkbd",
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
.id_table = usb_kbd_id_table,
};
 
/*static*/ int /*__init*/ usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver);
if (result == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
 
/*static*/ void /*__exit*/ usb_kbd_exit(void)
{
usb_deregister(&usb_kbd_driver);
}
 
module_init(usb_kbd_init);
module_exit(usb_kbd_exit);
/shark/trunk/drivers/usb/input/hid-core.c
0,0 → 1,1724
/*
* USB HID support for Linux
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
*/
 
/*
* 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.
*/
 
#include <linuxcomp.h>
 
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/input.h>
 
#undef DEBUG
#undef DEBUG_DATA
 
#include <linux/usb.h>
 
#include "hid.h"
#include <linux/hiddev.h>
 
/*
* Version Information
*/
 
#define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
#define DRIVER_DESC "USB HID core driver"
#define DRIVER_LICENSE "GPL"
 
static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
 
/*
* Register a new report for a device.
*/
 
static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
{
struct hid_report_enum *report_enum = device->report_enum + type;
struct hid_report *report;
 
if (report_enum->report_id_hash[id])
return report_enum->report_id_hash[id];
 
if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
return NULL;
memset(report, 0, sizeof(struct hid_report));
 
if (id != 0)
report_enum->numbered = 1;
 
report->id = id;
report->type = type;
report->size = 0;
report->device = device;
report_enum->report_id_hash[id] = report;
 
list_add_tail(&report->list, &report_enum->report_list);
 
return report;
}
 
/*
* Register a new field for this report.
*/
 
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
{
struct hid_field *field;
 
if (report->maxfield == HID_MAX_FIELDS) {
dbg("too many fields in report");
return NULL;
}
 
if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned), GFP_KERNEL))) return NULL;
 
memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned));
 
report->field[report->maxfield++] = field;
field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages);
field->report = report;
 
return field;
}
 
/*
* Open a collection. The type/usage is pushed on the stack.
*/
 
static int open_collection(struct hid_parser *parser, unsigned type)
{
struct hid_collection *collection;
unsigned usage;
 
usage = parser->local.usage[0];
 
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
dbg("collection stack overflow");
return -1;
}
 
if (parser->device->maxcollection == parser->device->collection_size) {
collection = kmalloc(sizeof(struct hid_collection) *
parser->device->collection_size * 2,
GFP_KERNEL);
if (collection == NULL) {
dbg("failed to reallocate collection array");
return -1;
}
memcpy(collection, parser->device->collection,
sizeof(struct hid_collection) *
parser->device->collection_size);
memset(collection + parser->device->collection_size, 0,
sizeof(struct hid_collection) *
parser->device->collection_size);
kfree(parser->device->collection);
parser->device->collection = collection;
parser->device->collection_size *= 2;
}
 
parser->collection_stack[parser->collection_stack_ptr++] =
parser->device->maxcollection;
 
collection = parser->device->collection +
parser->device->maxcollection++;
collection->type = type;
collection->usage = usage;
collection->level = parser->collection_stack_ptr - 1;
if (type == HID_COLLECTION_APPLICATION)
parser->device->maxapplication++;
 
return 0;
}
 
/*
* Close a collection.
*/
 
static int close_collection(struct hid_parser *parser)
{
if (!parser->collection_stack_ptr) {
dbg("collection stack underflow");
return -1;
}
parser->collection_stack_ptr--;
return 0;
}
 
/*
* Climb up the stack, search for the specified collection type
* and return the usage.
*/
 
static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
{
int n;
for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
if (parser->device->collection[parser->collection_stack[n]].type == type)
return parser->device->collection[parser->collection_stack[n]].usage;
return 0; /* we know nothing about this usage type */
}
 
/*
* Add a usage to the temporary parser table.
*/
 
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
{
if (parser->local.usage_index >= HID_MAX_USAGES) {
dbg("usage index exceeded");
return -1;
}
parser->local.usage[parser->local.usage_index] = usage;
parser->local.collection_index[parser->local.usage_index] =
parser->collection_stack_ptr ?
parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
parser->local.usage_index++;
return 0;
}
 
/*
* Register a new field for this report.
*/
 
static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags)
{
struct hid_report *report;
struct hid_field *field;
int usages;
unsigned offset;
int i;
 
if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
dbg("hid_register_report failed");
return -1;
}
 
if (parser->global.logical_maximum < parser->global.logical_minimum) {
dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
return -1;
}
usages = parser->local.usage_index;
 
offset = report->size;
report->size += parser->global.report_size * parser->global.report_count;
 
if (usages == 0)
return 0; /* ignore padding fields */
 
if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
return 0;
 
field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
 
for (i = 0; i < usages; i++) {
field->usage[i].hid = parser->local.usage[i];
field->usage[i].collection_index =
parser->local.collection_index[i];
}
 
field->maxusage = usages;
field->flags = flags;
field->report_offset = offset;
field->report_type = report_type;
field->report_size = parser->global.report_size;
field->report_count = parser->global.report_count;
field->logical_minimum = parser->global.logical_minimum;
field->logical_maximum = parser->global.logical_maximum;
field->physical_minimum = parser->global.physical_minimum;
field->physical_maximum = parser->global.physical_maximum;
field->unit_exponent = parser->global.unit_exponent;
field->unit = parser->global.unit;
 
return 0;
}
 
/*
* Read data value from item.
*/
 
static __inline__ __u32 item_udata(struct hid_item *item)
{
switch (item->size) {
case 1: return item->data.u8;
case 2: return item->data.u16;
case 4: return item->data.u32;
}
return 0;
}
 
static __inline__ __s32 item_sdata(struct hid_item *item)
{
switch (item->size) {
case 1: return item->data.s8;
case 2: return item->data.s16;
case 4: return item->data.s32;
}
return 0;
}
 
/*
* Process a global item.
*/
 
static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
{
switch (item->tag) {
 
case HID_GLOBAL_ITEM_TAG_PUSH:
 
if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
dbg("global enviroment stack overflow");
return -1;
}
 
memcpy(parser->global_stack + parser->global_stack_ptr++,
&parser->global, sizeof(struct hid_global));
return 0;
 
case HID_GLOBAL_ITEM_TAG_POP:
 
if (!parser->global_stack_ptr) {
dbg("global enviroment stack underflow");
return -1;
}
 
memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
sizeof(struct hid_global));
return 0;
 
case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
parser->global.usage_page = item_udata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
parser->global.logical_minimum = item_sdata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
if (parser->global.logical_minimum < 0)
parser->global.logical_maximum = item_sdata(item);
else
parser->global.logical_maximum = item_udata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
parser->global.physical_minimum = item_sdata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
if (parser->global.physical_minimum < 0)
parser->global.physical_maximum = item_sdata(item);
else
parser->global.physical_maximum = item_udata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
parser->global.unit_exponent = item_sdata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_UNIT:
parser->global.unit = item_udata(item);
return 0;
 
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
if ((parser->global.report_size = item_udata(item)) > 32) {
dbg("invalid report_size %d", parser->global.report_size);
return -1;
}
return 0;
 
case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
dbg("invalid report_count %d", parser->global.report_count);
return -1;
}
return 0;
 
case HID_GLOBAL_ITEM_TAG_REPORT_ID:
if ((parser->global.report_id = item_udata(item)) == 0) {
dbg("report_id 0 is invalid");
return -1;
}
return 0;
 
default:
dbg("unknown global tag 0x%x", item->tag);
return -1;
}
}
 
/*
* Process a local item.
*/
 
static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
{
__u32 data;
unsigned n;
 
if (item->size == 0) {
dbg("item data expected for local item");
return -1;
}
 
data = item_udata(item);
 
switch (item->tag) {
 
case HID_LOCAL_ITEM_TAG_DELIMITER:
 
if (data) {
/*
* We treat items before the first delimiter
* as global to all usage sets (branch 0).
* In the moment we process only these global
* items and the first delimiter set.
*/
if (parser->local.delimiter_depth != 0) {
dbg("nested delimiters");
return -1;
}
parser->local.delimiter_depth++;
parser->local.delimiter_branch++;
} else {
if (parser->local.delimiter_depth < 1) {
dbg("bogus close delimiter");
return -1;
}
parser->local.delimiter_depth--;
}
return 1;
 
case HID_LOCAL_ITEM_TAG_USAGE:
 
if (parser->local.delimiter_branch > 1) {
dbg("alternative usage ignored");
return 0;
}
 
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
 
return hid_add_usage(parser, data);
 
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
if (parser->local.delimiter_branch > 1) {
dbg("alternative usage ignored");
return 0;
}
 
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
 
parser->local.usage_minimum = data;
return 0;
 
case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
 
if (parser->local.delimiter_branch > 1) {
dbg("alternative usage ignored");
return 0;
}
 
if (item->size <= 2)
data = (parser->global.usage_page << 16) + data;
 
for (n = parser->local.usage_minimum; n <= data; n++)
if (hid_add_usage(parser, n)) {
dbg("hid_add_usage failed\n");
return -1;
}
return 0;
 
default:
 
dbg("unknown local item tag 0x%x", item->tag);
return 0;
}
return 0;
}
 
/*
* Process a main item.
*/
 
static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
{
__u32 data;
int ret;
 
data = item_udata(item);
 
switch (item->tag) {
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
ret = open_collection(parser, data & 0xff);
break;
case HID_MAIN_ITEM_TAG_END_COLLECTION:
ret = close_collection(parser);
break;
case HID_MAIN_ITEM_TAG_INPUT:
ret = hid_add_field(parser, HID_INPUT_REPORT, data);
break;
case HID_MAIN_ITEM_TAG_OUTPUT:
ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
break;
case HID_MAIN_ITEM_TAG_FEATURE:
ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
break;
default:
dbg("unknown main item tag 0x%x", item->tag);
ret = 0;
}
 
memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */
 
return ret;
}
 
/*
* Process a reserved item.
*/
 
static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
{
dbg("reserved item type, tag 0x%x", item->tag);
return 0;
}
 
/*
* Free a report and all registered fields. The field->usage and
* field->value table's are allocated behind the field, so we need
* only to free(field) itself.
*/
 
static void hid_free_report(struct hid_report *report)
{
unsigned n;
 
for (n = 0; n < report->maxfield; n++)
kfree(report->field[n]);
kfree(report);
}
 
/*
* Free a device structure, all reports, and all fields.
*/
 
static void hid_free_device(struct hid_device *device)
{
unsigned i,j;
 
hid_ff_exit(device);
 
for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i;
 
for (j = 0; j < 256; j++) {
struct hid_report *report = report_enum->report_id_hash[j];
if (report)
hid_free_report(report);
}
}
 
if (device->rdesc)
kfree(device->rdesc);
kfree(device);
}
 
/*
* Fetch a report description item from the data stream. We support long
* items, though they are not used yet.
*/
 
static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
{
u8 b;
 
if ((end - start) <= 0)
return NULL;
 
b = *start++;
 
item->type = (b >> 2) & 3;
item->tag = (b >> 4) & 15;
 
if (item->tag == HID_ITEM_TAG_LONG) {
 
item->format = HID_ITEM_FORMAT_LONG;
 
if ((end - start) < 2)
return NULL;
 
item->size = *start++;
item->tag = *start++;
 
if ((end - start) < item->size)
return NULL;
 
item->data.longdata = start;
start += item->size;
return start;
}
 
item->format = HID_ITEM_FORMAT_SHORT;
item->size = b & 3;
 
switch (item->size) {
 
case 0:
return start;
 
case 1:
if ((end - start) < 1)
return NULL;
item->data.u8 = *start++;
return start;
 
case 2:
if ((end - start) < 2)
return NULL;
item->data.u16 = le16_to_cpu(get_unaligned(((__u16*)start)++));
return start;
 
case 3:
item->size++;
if ((end - start) < 4)
return NULL;
item->data.u32 = le32_to_cpu(get_unaligned(((__u32*)start)++));
return start;
}
 
return NULL;
}
 
/*
* Parse a report description into a hid_device structure. Reports are
* enumerated, fields are attached to these reports.
*/
 
static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
{
struct hid_device *device;
struct hid_parser *parser;
struct hid_item item;
__u8 *end;
unsigned i;
static int (*dispatch_type[])(struct hid_parser *parser,
struct hid_item *item) = {
hid_parser_main,
hid_parser_global,
hid_parser_local,
hid_parser_reserved
};
 
if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
return NULL;
memset(device, 0, sizeof(struct hid_device));
 
if (!(device->collection =kmalloc(sizeof(struct hid_collection) *
HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
kfree(device);
return NULL;
}
memset(device->collection, 0, sizeof(struct hid_collection) *
HID_DEFAULT_NUM_COLLECTIONS);
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
 
for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&device->report_enum[i].report_list);
 
if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
kfree(device->collection);
kfree(device);
return NULL;
}
memcpy(device->rdesc, start, size);
device->rsize = size;
 
if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
kfree(device->rdesc);
kfree(device->collection);
kfree(device);
return NULL;
}
memset(parser, 0, sizeof(struct hid_parser));
parser->device = device;
 
end = start + size;
while ((start = fetch_item(start, end, &item)) != 0) {
 
if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg("unexpected long global item");
kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
 
if (dispatch_type[item.type](parser, &item)) {
dbg("item %u %u %u %u parsing failed\n",
item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
 
if (start == end) {
if (parser->collection_stack_ptr) {
dbg("unbalanced collection at end of report description");
kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
if (parser->local.delimiter_depth) {
dbg("unbalanced delimiter at end of report description");
kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
kfree(parser);
return device;
}
}
 
dbg("item fetching failed at offset %d\n", (int)(end - start));
kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
 
/*
* Convert a signed n-bit integer to signed 32-bit integer. Common
* cases are done through the compiler, the screwed things has to be
* done by hand.
*/
 
static __inline__ __s32 snto32(__u32 value, unsigned n)
{
switch (n) {
case 8: return ((__s8)value);
case 16: return ((__s16)value);
case 32: return ((__s32)value);
}
return value & (1 << (n - 1)) ? value | (-1 << n) : value;
}
 
/*
* Convert a signed 32-bit integer to a signed n-bit integer.
*/
 
static __inline__ __u32 s32ton(__s32 value, unsigned n)
{
__s32 a = value >> (n - 1);
if (a && a != -1)
return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;
return value & ((1 << n) - 1);
}
 
/*
* Extract/implement a data field from/to a report.
*/
 
static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
{
report += (offset >> 5) << 2; offset &= 31;
return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1);
}
 
static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
{
report += (offset >> 5) << 2; offset &= 31;
put_unaligned((get_unaligned((__u64*)report)
& cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)))
| cpu_to_le64((__u64)value << offset), (__u64*)report);
}
 
/*
* Search an array for a value.
*/
 
static __inline__ int search(__s32 *array, __s32 value, unsigned n)
{
while (n--) {
if (*array++ == value)
return 0;
}
return -1;
}
 
static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
{
hid_dump_input(usage, value);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_hid_event(hid, field, usage, value, regs);
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_hid_event(hid, field, usage, value, regs);
}
 
/*
* Analyse a received field, and fetch the data from it. The field
* content is stored for next report processing (we do differential
* reporting to the layer).
*/
 
static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, struct pt_regs *regs)
{
unsigned n;
unsigned count = field->report_count;
unsigned offset = field->report_offset;
unsigned size = field->report_size;
__s32 min = field->logical_minimum;
__s32 max = field->logical_maximum;
__s32 value[count]; /* WARNING: gcc specific */
 
for (n = 0; n < count; n++) {
 
value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) :
extract(data, offset + n * size, size);
 
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
&& value[n] >= min && value[n] <= max
&& field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
return;
}
 
for (n = 0; n < count; n++) {
 
if (HID_MAIN_ITEM_VARIABLE & field->flags) {
 
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
if (!value[n])
continue;
} else {
if (value[n] == field->value[n])
continue;
}
hid_process_event(hid, field, &field->usage[n], value[n], regs);
continue;
}
 
if (field->value[n] >= min && field->value[n] <= max
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, regs);
 
if (value[n] >= min && value[n] <= max
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, regs);
}
 
memcpy(field->value, value, count * sizeof(__s32));
}
 
static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs)
{
struct hid_device *hid = urb->context;
struct hid_report_enum *report_enum = hid->report_enum + type;
u8 *data = urb->transfer_buffer;
int len = urb->actual_length;
struct hid_report *report;
int n, size;
 
if (!len) {
dbg("empty report");
return -1;
}
 
#ifdef DEBUG_DATA
printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", len, report_enum->numbered ? "" : "un");
#endif
 
n = 0; /* Normally report number is 0 */
if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */
n = *data++;
len--;
}
 
#ifdef DEBUG_DATA
{
int i;
printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len);
for (i = 0; i < len; i++)
printk(" %02x", data[i]);
printk("\n");
}
#endif
 
if (!(report = report_enum->report_id_hash[n])) {
dbg("undefined report_id %d received", n);
return -1;
}
 
size = ((report->size - 1) >> 3) + 1;
 
if (len < size) {
dbg("report %d is too short, (%d < %d)", report->id, len, size);
return -1;
}
 
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_report_event(hid, report);
 
for (n = 0; n < report->maxfield; n++)
hid_input_field(hid, report->field[n], data, regs);
 
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report);
 
return 0;
}
 
/*
* Input interrupt completion handler.
*/
 
static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
{
struct hid_device *hid = urb->context;
int status;
 
switch (urb->status) {
case 0: /* success */
hid_input_report(HID_INPUT_REPORT, urb, regs);
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dbg("nonzero status in input irq %d", urb->status);
}
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input%d, status %d",
hid->dev->bus->bus_name, hid->dev->devpath,
hid->ifnum, status);
}
 
/*
* Output the field into the report.
*/
 
static void hid_output_field(struct hid_field *field, __u8 *data)
{
unsigned count = field->report_count;
unsigned offset = field->report_offset;
unsigned size = field->report_size;
unsigned n;
 
for (n = 0; n < count; n++) {
if (field->logical_minimum < 0) /* signed values */
implement(data, offset + n * size, size, s32ton(field->value[n], size));
else /* unsigned values */
implement(data, offset + n * size, size, field->value[n]);
}
}
 
/*
* Create a report.
*/
 
void hid_output_report(struct hid_report *report, __u8 *data)
{
unsigned n;
 
if (report->id > 0)
*data++ = report->id;
 
for (n = 0; n < report->maxfield; n++)
hid_output_field(report->field[n], data);
}
 
/*
* Set a field value. The report this field belongs to has to be
* created and transferred to the device, to set this value in the
* device.
*/
 
int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{
unsigned size = field->report_size;
 
hid_dump_input(field->usage + offset, value);
 
if (offset >= field->report_count) {
dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count);
hid_dump_field(field, 8);
return -1;
}
if (field->logical_minimum < 0) {
if (value != snto32(s32ton(value, size), size)) {
dbg("value %d is out of range", value);
return -1;
}
}
field->value[offset] = value;
return 0;
}
 
int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
{
struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT;
struct list_head *list = report_enum->report_list.next;
int i, j;
 
while (list != &report_enum->report_list) {
struct hid_report *report = (struct hid_report *) list;
list = list->next;
for (i = 0; i < report->maxfield; i++) {
*field = report->field[i];
for (j = 0; j < (*field)->maxusage; j++)
if ((*field)->usage[j].type == type && (*field)->usage[j].code == code)
return j;
}
}
return -1;
}
 
/*
* Find a report with a specified HID usage.
*/
 
int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct list_head *list = report_enum->report_list.next;
int i, j;
 
while (list != &report_enum->report_list) {
*report = (struct hid_report *) list;
list = list->next;
for (i = 0; i < (*report)->maxfield; i++) {
struct hid_field *field = (*report)->field[i];
for (j = 0; j < field->maxusage; j++)
if (field->logical == wanted_usage)
return j;
}
}
return -1;
}
 
int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field)
{
int i, j;
 
for (i = 0; i < report->maxfield; i++) {
*field = report->field[i];
for (j = 0; j < (*field)->maxusage; j++)
if ((*field)->usage[j].hid == wanted_usage)
return j;
}
 
return -1;
}
 
static int hid_submit_out(struct hid_device *hid)
{
struct hid_report *report;
 
report = hid->out[hid->outtail];
 
hid_output_report(report, hid->outbuf);
hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
hid->urbout->dev = hid->dev;
 
dbg("submitting out urb");
 
if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) {
err("usb_submit_urb(out) failed");
return -1;
}
 
return 0;
}
 
static int hid_submit_ctrl(struct hid_device *hid)
{
struct hid_report *report;
unsigned char dir;
 
report = hid->ctrl[hid->ctrltail].report;
dir = hid->ctrl[hid->ctrltail].dir;
 
if (dir == USB_DIR_OUT)
hid_output_report(report, hid->ctrlbuf);
 
hid->urbctrl->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
hid->urbctrl->pipe = (dir == USB_DIR_OUT) ? usb_sndctrlpipe(hid->dev, 0) : usb_rcvctrlpipe(hid->dev, 0);
hid->urbctrl->dev = hid->dev;
 
hid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
hid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
hid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
hid->cr->wIndex = cpu_to_le16(hid->ifnum);
hid->cr->wLength = cpu_to_le16(hid->urbctrl->transfer_buffer_length);
 
dbg("submitting ctrl urb");
 
if (usb_submit_urb(hid->urbctrl, GFP_ATOMIC)) {
err("usb_submit_urb(ctrl) failed");
return -1;
}
 
return 0;
}
 
/*
* Output interrupt completion handler.
*/
 
static void hid_irq_out(struct urb *urb, struct pt_regs *regs)
{
struct hid_device *hid = urb->context;
unsigned long flags;
 
if (urb->status)
warn("output irq status %d received", urb->status);
 
spin_lock_irqsave(&hid->outlock, flags);
 
hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
 
if (hid->outhead != hid->outtail) {
hid_submit_out(hid);
spin_unlock_irqrestore(&hid->outlock, flags);
return;
}
 
clear_bit(HID_OUT_RUNNING, &hid->iofl);
 
spin_unlock_irqrestore(&hid->outlock, flags);
 
wake_up(&hid->wait);
}
 
/*
* Control pipe completion handler.
*/
 
static void hid_ctrl(struct urb *urb, struct pt_regs *regs)
{
struct hid_device *hid = urb->context;
unsigned long flags;
 
if (urb->status)
warn("ctrl urb status %d received", urb->status);
 
spin_lock_irqsave(&hid->ctrllock, flags);
 
if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs);
 
hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
if (hid->ctrlhead != hid->ctrltail) {
hid_submit_ctrl(hid);
spin_unlock_irqrestore(&hid->ctrllock, flags);
return;
}
 
clear_bit(HID_CTRL_RUNNING, &hid->iofl);
 
spin_unlock_irqrestore(&hid->ctrllock, flags);
 
wake_up(&hid->wait);
}
 
void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
{
int head;
unsigned long flags;
 
if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
return;
 
if (hid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
 
spin_lock_irqsave(&hid->outlock, flags);
 
if ((head = (hid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == hid->outtail) {
spin_unlock_irqrestore(&hid->outlock, flags);
warn("output queue full");
return;
}
 
hid->out[hid->outhead] = report;
hid->outhead = head;
 
if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
hid_submit_out(hid);
 
spin_unlock_irqrestore(&hid->outlock, flags);
return;
}
 
spin_lock_irqsave(&hid->ctrllock, flags);
 
if ((head = (hid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == hid->ctrltail) {
spin_unlock_irqrestore(&hid->ctrllock, flags);
warn("control queue full");
return;
}
 
hid->ctrl[hid->ctrlhead].report = report;
hid->ctrl[hid->ctrlhead].dir = dir;
hid->ctrlhead = head;
 
if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl))
hid_submit_ctrl(hid);
 
spin_unlock_irqrestore(&hid->ctrllock, flags);
}
 
int hid_wait_io(struct hid_device *hid)
{
DECLARE_WAITQUEUE(wait, current);
int timeout = 1*HZ;
 
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&hid->wait, &wait);
 
while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
test_bit(HID_OUT_RUNNING, &hid->iofl)))
timeout = schedule_timeout(timeout);
 
set_current_state(TASK_RUNNING);
remove_wait_queue(&hid->wait, &wait);
 
/* if (!timeout) {
dbg("timeout waiting for ctrl or out queue to clear");
return -1;
} */
return 0;
}
 
static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
unsigned char type, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
(type << 8), ifnum, buf, size, HZ * USB_CTRL_GET_TIMEOUT);
}
 
int hid_open(struct hid_device *hid)
{
if (hid->open++)
return 0;
 
hid->urbin->dev = hid->dev;
 
if (usb_submit_urb(hid->urbin, GFP_KERNEL))
return -EIO;
 
return 0;
}
 
void hid_close(struct hid_device *hid)
{
if (!--hid->open)
usb_unlink_urb(hid->urbin);
}
 
/*
* Initialize all reports
*/
 
void hid_init_reports(struct hid_device *hid)
{
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
int len;
int err, ret;
 
report_enum = hid->report_enum + HID_INPUT_REPORT;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
hid_submit_report(hid, report, USB_DIR_IN);
list = list->next;
}
 
report_enum = hid->report_enum + HID_FEATURE_REPORT;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
hid_submit_report(hid, report, USB_DIR_IN);
list = list->next;
}
 
err = 0;
while ((ret = hid_wait_io(hid))) {
err |= ret;
if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
usb_unlink_urb(hid->urbctrl);
if (test_bit(HID_OUT_RUNNING, &hid->iofl))
usb_unlink_urb(hid->urbout);
}
 
if (err)
warn("timeout initializing reports\n");
 
report_enum = hid->report_enum + HID_INPUT_REPORT;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
len = ((report->size - 1) >> 3) + 1 + report_enum->numbered;
if (len > hid->urbin->transfer_buffer_length)
hid->urbin->transfer_buffer_length = len < HID_BUFFER_SIZE ? len : HID_BUFFER_SIZE;
usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, report->id,
hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
list = list->next;
}
}
 
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_PENPARTNER 0x0000
#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
#define USB_DEVICE_ID_WACOM_INTUOS 0x0020
#define USB_DEVICE_ID_WACOM_PL 0x0030
#define USB_DEVICE_ID_WACOM_INTUOS2 0x0040
 
#define USB_VENDOR_ID_KBGEAR 0x084e
#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
 
 
#define USB_VENDOR_ID_AIPTEK 0x08ca
#define USB_DEVICE_ID_AIPTEK_6000 0x0020
 
#define USB_VENDOR_ID_GRIFFIN 0x077d
#define USB_DEVICE_ID_POWERMATE 0x0410
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
 
#define USB_VENDOR_ID_ATEN 0x0557
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
#define USB_DEVICE_ID_ATEN_CS124U 0x2202
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
 
#define USB_VENDOR_ID_TOPMAX 0x0663
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
 
#define USB_VENDOR_ID_HAPP 0x078b
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
 
#define USB_VENDOR_ID_MGE 0x0463
#define USB_DEVICE_ID_MGE_UPS 0xffff
#define USB_DEVICE_ID_MGE_UPS1 0x0001
 
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
 
#define USB_VENDOR_ID_TANGTOP 0x0d3d
#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001
 
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
 
#define USB_VENDOR_ID_A4TECH 0x09DA
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
 
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
unsigned quirks;
} hid_blacklist[] = {
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 4, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 5, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 4, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_6000, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
{ 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_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_HIDDEV },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_HIDDEV },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK },
{ 0, 0 }
};
 
static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
{
if (!(hid->inbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->inbuf_dma)))
return -1;
if (!(hid->outbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->outbuf_dma)))
return -1;
if (!(hid->cr = usb_buffer_alloc(dev, sizeof(*(hid->cr)), SLAB_ATOMIC, &hid->cr_dma)))
return -1;
if (!(hid->ctrlbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->ctrlbuf_dma)))
return -1;
 
return 0;
}
 
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
{
if (hid->inbuf)
usb_buffer_free(dev, HID_BUFFER_SIZE, hid->inbuf, hid->inbuf_dma);
if (hid->outbuf)
usb_buffer_free(dev, HID_BUFFER_SIZE, hid->outbuf, hid->outbuf_dma);
if (hid->cr)
usb_buffer_free(dev, sizeof(*(hid->cr)), hid->cr, hid->cr_dma);
if (hid->ctrlbuf)
usb_buffer_free(dev, HID_BUFFER_SIZE, hid->ctrlbuf, hid->ctrlbuf_dma);
}
 
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
struct usb_host_interface *interface = intf->altsetting + intf->act_altsetting;
struct usb_device *dev = interface_to_usbdev (intf);
struct hid_descriptor *hdesc;
struct hid_device *hid;
unsigned quirks = 0, rsize = 0;
char *buf, *rdesc;
int n;
 
for (n = 0; hid_blacklist[n].idVendor; n++)
if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) &&
(hid_blacklist[n].idProduct == dev->descriptor.idProduct))
quirks = hid_blacklist[n].quirks;
 
if (quirks & HID_QUIRK_IGNORE)
return NULL;
 
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && ((!interface->desc.bNumEndpoints) ||
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
dbg("class descriptor not present\n");
return NULL;
}
 
for (n = 0; n < hdesc->bNumDescriptors; n++)
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
 
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
dbg("weird size of report descriptor (%u)", rsize);
return NULL;
}
 
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
dbg("couldn't allocate rdesc memory");
return NULL;
}
 
if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg("reading report descriptor failed");
kfree(rdesc);
return NULL;
}
 
#ifdef DEBUG_DATA
printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
for (n = 0; n < rsize; n++)
printk(" %02x", (unsigned char) rdesc[n]);
printk("\n");
#endif
 
if (!(hid = hid_parse_report(rdesc, rsize))) {
dbg("parsing report descriptor failed");
kfree(rdesc);
return NULL;
}
 
kfree(rdesc);
hid->quirks = quirks;
 
if (hid_alloc_buffers(dev, hid)) {
hid_free_buffers(dev, hid);
goto fail;
}
 
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
 
struct usb_endpoint_descriptor *endpoint;
int pipe;
 
endpoint = &interface->endpoint[n].desc;
if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
continue;
 
if (endpoint->bEndpointAddress & USB_DIR_IN) {
if (hid->urbin)
continue;
if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
goto fail;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, 0,
hid_irq_in, hid, endpoint->bInterval);
hid->urbin->transfer_dma = hid->inbuf_dma;
hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
} else {
if (hid->urbout)
continue;
if (!(hid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
goto fail;
pipe = usb_sndbulkpipe(dev, endpoint->bEndpointAddress);
usb_fill_bulk_urb(hid->urbout, dev, pipe, hid->outbuf, 0,
hid_irq_out, hid);
hid->urbout->transfer_dma = hid->outbuf_dma;
hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
}
 
if (!hid->urbin) {
err("couldn't find an input interrupt endpoint");
goto fail;
}
 
init_waitqueue_head(&hid->wait);
hid->outlock = SPIN_LOCK_UNLOCKED;
hid->ctrllock = SPIN_LOCK_UNLOCKED;
 
hid->version = le16_to_cpu(hdesc->bcdHID);
hid->country = hdesc->bCountryCode;
hid->dev = dev;
hid->ifnum = interface->desc.bInterfaceNumber;
 
hid->name[0] = 0;
 
if (!(buf = kmalloc(64, GFP_KERNEL)))
goto fail;
 
if (usb_string(dev, dev->descriptor.iManufacturer, buf, 64) > 0) {
strcat(hid->name, buf);
if (usb_string(dev, dev->descriptor.iProduct, buf, 64) > 0)
snprintf26(hid->name, 64, "%s %s", hid->name, buf);
} else if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0) {
snprintf26(hid->name, 128, "%s", buf);
} else
snprintf26(hid->name, 128, "%04x:%04x", dev->descriptor.idVendor, dev->descriptor.idProduct);
 
usb_make_path(dev, buf, 64);
snprintf26(hid->phys, 64, "%s/input%d", buf,
intf->altsetting[0].desc.bInterfaceNumber);
 
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
hid->uniq[0] = 0;
 
kfree(buf);
 
hid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!hid->urbctrl)
goto fail;
usb_fill_control_urb(hid->urbctrl, dev, 0, (void *) hid->cr,
hid->ctrlbuf, 1, hid_ctrl, hid);
hid->urbctrl->setup_dma = hid->cr_dma;
hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
 
return hid;
 
fail:
 
if (hid->urbin)
usb_free_urb(hid->urbin);
if (hid->urbout)
usb_free_urb(hid->urbout);
if (hid->urbctrl)
usb_free_urb(hid->urbctrl);
hid_free_buffers(dev, hid);
hid_free_device(hid);
 
return NULL;
}
 
static void hid_disconnect(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata (intf);
 
if (!hid)
return;
 
usb_set_intfdata(intf, NULL);
usb_unlink_urb(hid->urbin);
usb_unlink_urb(hid->urbout);
usb_unlink_urb(hid->urbctrl);
 
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_disconnect(hid);
 
usb_free_urb(hid->urbin);
usb_free_urb(hid->urbctrl);
if (hid->urbout)
usb_free_urb(hid->urbout);
 
hid_free_buffers(hid->dev, hid);
hid_free_device(hid);
}
 
static int hid_probe (struct usb_interface *intf, const struct usb_device_id *id)
{
struct hid_device *hid;
char path[64];
int i;
char *c;
 
dbg("HID probe called for ifnum %d",
intf->altsetting->desc.bInterfaceNumber);
 
if (!(hid = usb_hid_configure(intf)))
return -EIO;
 
hid_init_reports(hid);
hid_dump_device(hid);
 
if (!hidinput_connect(hid))
hid->claimed |= HID_CLAIMED_INPUT;
if (!hiddev_connect(hid))
hid->claimed |= HID_CLAIMED_HIDDEV;
 
usb_set_intfdata(intf, hid);
 
if (!hid->claimed) {
printk ("HID device not claimed by input or hiddev\n");
hid_disconnect(intf);
return -EIO;
}
 
printk(KERN_INFO);
 
if (hid->claimed & HID_CLAIMED_INPUT)
printk("input");
if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
printk(",");
if (hid->claimed & HID_CLAIMED_HIDDEV)
printk("hiddev%d", hid->minor);
 
c = "Device";
for (i = 0; i < hid->maxcollection; i++) {
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
(hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
(hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
c = hid_types[hid->collection[i].usage & 0xffff];
break;
}
}
 
usb_make_path(interface_to_usbdev(intf), path, 63);
 
printk(": USB HID v%x.%02x %s [%s] on %s\n",
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
 
return 0;
}
 
static struct usb_device_id hid_usb_ids [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
{ } /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, hid_usb_ids);
 
static struct usb_driver hid_driver = {
.owner = THIS_MODULE,
.name = "hid",
.probe = hid_probe,
.disconnect = hid_disconnect,
.id_table = hid_usb_ids,
};
 
/*static*/ int __init hid_init(void)
{
int retval;
retval = hiddev_init();
if (retval)
goto hiddev_init_fail;
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
info(DRIVER_VERSION ":" DRIVER_DESC);
 
return 0;
usb_register_fail:
hiddev_exit();
hiddev_init_fail:
return retval;
}
 
/*static*/ void __exit hid_exit(void)
{
hiddev_exit();
usb_deregister(&hid_driver);
}
 
module_init(hid_init);
module_exit(hid_exit);
 
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
/shark/trunk/drivers/usb/media/pwc.h
0,0 → 1,269
/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
 
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
*/
 
#ifndef PWC_H
#define PWC_H
 
#include <linux/version.h>
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/spinlock.h>
#include <linux/videodev.h>
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include <asm/semaphore.h>
#include <asm/errno.h>
 
#include "pwc-ioctl.h"
 
/* Defines and structures for the Philips webcam */
/* Used for checking memory corruption/pointer validation */
#define PWC_MAGIC 0x89DC10ABUL
#undef PWC_MAGIC
 
/* Turn some debugging options on/off */
#define PWC_DEBUG 0
 
/* Trace certain actions in the driver */
#define TRACE_MODULE 0x0001
#define TRACE_PROBE 0x0002
#define TRACE_OPEN 0x0004
#define TRACE_READ 0x0008
#define TRACE_MEMORY 0x0010
#define TRACE_FLOW 0x0020
#define TRACE_SIZE 0x0040
#define TRACE_PWCX 0x0080
#define TRACE_SEQUENCE 0x1000
 
#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A)
#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A)
#define Info(A...) printk(KERN_INFO PWC_NAME " " A)
#define Err(A...) printk(KERN_ERR PWC_NAME " " A)
 
 
/* Defines for ToUCam cameras */
#define TOUCAM_HEADER_SIZE 8
#define TOUCAM_TRAILER_SIZE 4
 
#define FEATURE_MOTOR_PANTILT 0x0001
 
/* Version block */
#define PWC_MAJOR 9
#define PWC_MINOR 0
#define PWC_VERSION "9.0-BETA-1"
#define PWC_NAME "pwc"
 
/* Turn certain features on/off */
#define PWC_INT_PIPE 0
 
/* Ignore errors in the first N frames, to allow for startup delays */
#define FRAME_LOWMARK 5
 
/* Size and number of buffers for the ISO pipe. */
#define MAX_ISO_BUFS 2
#define ISO_FRAMES_PER_DESC 10
#define ISO_MAX_FRAME_SIZE 960
#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
 
/* Frame buffers: contains compressed or uncompressed video data. */
#define MAX_FRAMES 5
/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
 
/* Absolute maximum number of buffers available for mmap() */
#define MAX_IMAGES 10
 
/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
struct pwc_iso_buf
{
void *data;
int length;
int read;
struct urb *urb;
};
 
/* intermediate buffers with raw data from the USB cam */
struct pwc_frame_buf
{
void *data;
volatile int filled; /* number of bytes filled */
struct pwc_frame_buf *next; /* list */
#if PWC_DEBUG
int sequence; /* Sequence number */
#endif
};
 
struct pwc_device
{
struct video_device vdev;
#ifdef PWC_MAGIC
int magic;
#endif
/* Pointer to our usb_device */
struct usb_device *udev;
int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
int release; /* release number */
int features; /* feature bits */
int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */
int usb_init; /* set when the cam has been initialized over USB */
 
/*** Video data ***/
int vopen; /* flag */
int vendpoint; /* video isoc endpoint */
int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */
int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
int vpalette; /* palette: 420P, RAW or RGBBAYER */
int vframe_count; /* received frames */
int vframes_dumped; /* counter for dumped frames */
int vframes_error; /* frames received in error */
int vmax_packet_size; /* USB maxpacket size */
int vlast_packet_size; /* for frame synchronisation */
int visoc_errors; /* number of contiguous ISOC errors */
int vcompression; /* desired compression factor */
int vbandlength; /* compressed band length; 0 is uncompressed */
char vsnapshot; /* snapshot mode */
char vsync; /* used by isoc handler */
char vmirror; /* for ToUCaM series */
int cmd_len;
unsigned char cmd_buf[13];
 
/* The image acquisition requires 3 to 4 steps:
1. data is gathered in short packets from the USB controller
2. data is synchronized and packed into a frame buffer
3a. in case data is compressed, decompress it directly into image buffer
3b. in case data is uncompressed, copy into image buffer with viewport
4. data is transferred to the user process
 
Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
We have in effect a back-to-back-double-buffer system.
*/
/* 1: isoc */
struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
char iso_init;
 
/* 2: frame */
struct pwc_frame_buf *fbuf; /* all frames */
struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */
struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */
struct pwc_frame_buf *fill_frame; /* frame currently being filled */
struct pwc_frame_buf *read_frame; /* frame currently read by user process */
int frame_header_size, frame_trailer_size;
int frame_size;
int frame_total_size; /* including header & trailer */
int drop_frames;
#if PWC_DEBUG
int sequence; /* Debugging aid */
#endif
 
/* 3: decompression */
struct pwc_decompressor *decompressor; /* function block with decompression routines */
void *decompress_data; /* private data for decompression engine */
 
/* 4: image */
/* We have an 'image' and a 'view', where 'image' is the fixed-size image
as delivered by the camera, and 'view' is the size requested by the
program. The camera image is centered in this viewport, laced with
a gray or black border. view_min <= image <= view <= view_max;
*/
int image_mask; /* bitmask of supported sizes */
struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */
struct pwc_coord abs_max; /* maximum supported size with compression */
struct pwc_coord image, view; /* image and viewport size */
struct pwc_coord offset; /* offset within the viewport */
 
void *image_data; /* total buffer, which is subdivided into ... */
void *image_ptr[MAX_IMAGES]; /* ...several images... */
int fill_image; /* ...which are rotated. */
int len_per_image; /* length per image */
int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */
int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */
 
struct semaphore modlock; /* to prevent races in video_open(), etc */
spinlock_t ptrlock; /* for manipulating the buffer pointers */
 
/*** motorized pan/tilt feature */
struct pwc_mpt_range angle_range;
int pan_angle; /* in degrees * 100 */
int tilt_angle; /* absolute angle; 0,0 is home position */
 
/*** Misc. data ***/
wait_queue_head_t frameq; /* When waiting for a frame to finish... */
#if PWC_INT_PIPE
void *usb_int_handler; /* for the interrupt endpoint */
#endif
};
 
 
#ifdef __cplusplus
extern "C" {
#endif
 
/* Global variables */
extern int pwc_trace;
extern int pwc_preferred_compression;
 
/** functions in pwc-if.c */
int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot);
 
/** Functions in pwc-misc.c */
/* sizes in pixels */
extern struct pwc_coord pwc_image_sizes[PSZ_MAX];
 
int pwc_decode_size(struct pwc_device *pdev, int width, int height);
void pwc_construct(struct pwc_device *pdev);
 
/** Functions in pwc-ctrl.c */
/* Request a certain video mode. Returns < 0 if not possible */
extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
/* Calculate the number of bytes per image (not frame) */
extern void pwc_set_image_buffer_size(struct pwc_device *pdev);
 
/* Various controls; should be obvious. Value 0..65535, or < 0 on error */
extern int pwc_get_brightness(struct pwc_device *pdev);
extern int pwc_set_brightness(struct pwc_device *pdev, int value);
extern int pwc_get_contrast(struct pwc_device *pdev);
extern int pwc_set_contrast(struct pwc_device *pdev, int value);
extern int pwc_get_gamma(struct pwc_device *pdev);
extern int pwc_set_gamma(struct pwc_device *pdev, int value);
extern int pwc_get_saturation(struct pwc_device *pdev);
extern int pwc_set_saturation(struct pwc_device *pdev, int value);
extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value);
extern int pwc_get_cmos_sensor(struct pwc_device *pdev);
 
/* Power down or up the camera; not supported by all models */
extern int pwc_camera_power(struct pwc_device *pdev, int power);
 
/* Private ioctl()s; see pwc-ioctl.h */
extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
 
 
/** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
extern int pwc_decompress(struct pwc_device *pdev);
 
#ifdef __cplusplus
}
#endif
 
 
#endif
/shark/trunk/drivers/usb/media/pwc-misc.c
0,0 → 1,122
/* Linux driver for Philips webcam
Various miscellaneous functions and tables.
(C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
 
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/slab.h>
 
#include "pwc.h"
 
struct pwc_coord pwc_image_sizes[PSZ_MAX] =
{
{ 128, 96, 0 },
{ 160, 120, 0 },
{ 176, 144, 0 },
{ 320, 240, 0 },
{ 352, 288, 0 },
{ 640, 480, 0 },
};
 
/* x,y -> PSZ_ */
int pwc_decode_size(struct pwc_device *pdev, int width, int height)
{
int i, find;
 
/* Make sure we don't go beyond our max size */
if (width > pdev->view_max.x || height > pdev->view_max.y)
return -1;
/* Find the largest size supported by the camera that fits into the
requested size.
*/
find = -1;
for (i = 0; i < PSZ_MAX; i++) {
if (pdev->image_mask & (1 << i)) {
if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height)
find = i;
}
}
return find;
}
 
/* initialize variables depending on type and decompressor*/
void pwc_construct(struct pwc_device *pdev)
{
switch(pdev->type) {
case 645:
case 646:
pdev->view_min.x = 128;
pdev->view_min.y = 96;
pdev->view_max.x = 352;
pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
pdev->vcinterface = 2;
pdev->vendpoint = 4;
pdev->frame_header_size = 0;
pdev->frame_trailer_size = 0;
break;
case 675:
case 680:
case 690:
pdev->view_min.x = 128;
pdev->view_min.y = 96;
/* Anthill bug #38: PWC always reports max size, even without PWCX */
if (pdev->decompressor != NULL) {
pdev->view_max.x = 640;
pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
}
else {
pdev->view_max.x = 352;
pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF;
}
pdev->vcinterface = 3;
pdev->vendpoint = 4;
pdev->frame_header_size = 0;
pdev->frame_trailer_size = 0;
break;
case 720:
case 730:
case 740:
case 750:
pdev->view_min.x = 160;
pdev->view_min.y = 120;
/* Anthill bug #38: PWC always reports max size, even without PWCX */
if (pdev->decompressor != NULL) {
pdev->view_max.x = 640;
pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
}
else {
/* Tell CIF, even though SIF really is the maximum, but some tools really need CIF */
pdev->view_max.x = 352;
pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF;
}
pdev->vcinterface = 3;
pdev->vendpoint = 5;
pdev->frame_header_size = TOUCAM_HEADER_SIZE;
pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
break;
}
pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
/* length of image, in YUV format */
pdev->len_per_image = (pdev->view_max.size * 3) / 2;
}
 
 
/shark/trunk/drivers/usb/media/pwc_timon.h
0,0 → 1,270
/* SQCIF */
{
/* 5 fps */
{
{1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
{1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
{1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
{1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
},
/* 10 fps */
{
{2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
{2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
{2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
{2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
},
/* 15 fps */
{
{3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
{3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
{3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
{3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
},
/* 20 fps */
{
{4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
{4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
{4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
{4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
},
/* 25 fps */
{
{5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
{5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
{5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
{5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
},
/* 30 fps */
{
{7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
{7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
{7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
{7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
},
},
/* QSIF */
{
/* 5 fps */
{
{1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
{1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
{1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
{1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
},
/* 10 fps */
{
{2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}},
{1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
{1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
{1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
},
/* 15 fps */
{
{3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}},
{2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
{2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
{1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
},
/* 20 fps */
{
{4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}},
{3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
{2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
{1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
},
/* 25 fps */
{
{5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}},
{3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
{2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
{1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
},
/* 30 fps */
{
{8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}},
{5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}},
{3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}},
{2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
},
},
/* QCIF */
{
/* 5 fps */
{
{1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
{1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
{1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
{1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
},
/* 10 fps */
{
{3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}},
{2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
{2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
{1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}},
},
/* 15 fps */
{
{4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}},
{3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}},
{2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}},
{1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}},
},
/* 20 fps */
{
{6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}},
{4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}},
{3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
{2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
},
/* 25 fps */
{
{9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}},
{5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}},
{3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
{2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
},
/* 30 fps */
{
{0, },
{9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}},
{4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}},
{2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}},
},
},
/* SIF */
{
/* 5 fps */
{
{4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}},
{3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}},
{2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}},
{1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}},
},
/* 10 fps */
{
{0, },
{6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}},
{3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}},
{2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}},
},
/* 15 fps */
{
{0, },
{9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}},
{4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}},
{3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}},
},
/* 20 fps */
{
{0, },
{9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}},
{5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}},
{3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}},
},
/* 25 fps */
{
{0, },
{9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}},
{6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}},
{4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}},
},
/* 30 fps */
{
{0, },
{9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}},
{6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}},
{4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}},
},
},
/* CIF */
{
/* 5 fps */
{
{6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}},
{4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}},
{2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}},
{1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}},
},
/* 10 fps */
{
{0, },
{9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}},
{4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}},
{2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}},
},
/* 15 fps */
{
{0, },
{9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}},
{5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}},
{3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}},
},
/* 20 fps */
{
{0, },
{9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}},
{6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}},
{4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}},
},
/* 25 fps */
{
{0, },
{9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}},
{7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}},
{5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}},
},
/* 30 fps */
{
{0, },
{9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}},
{7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}},
{6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}},
},
},
/* VGA */
{
/* 5 fps */
{
{0, },
{6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}},
{4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}},
{3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}},
},
/* 10 fps */
{
{0, },
{9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}},
{6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}},
{4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}},
},
/* 15 fps */
{
{0, },
{9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
{9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
{8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}},
},
/* 20 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 25 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 30 fps */
{
{0, },
{0, },
{0, },
{0, },
},
},
/shark/trunk/drivers/usb/media/pwc_kiara.h
0,0 → 1,270
/* SQCIF */
{
/* 5 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 10 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 15 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 20 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 25 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 30 fps */
{
{0, },
{0, },
{0, },
{0, },
},
},
/* QSIF */
{
/* 5 fps */
{
{1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
{1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
{1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
{1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
},
/* 10 fps */
{
{2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}},
{1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
{1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
{1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
},
/* 15 fps */
{
{3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}},
{2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
{2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
{1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
},
/* 20 fps */
{
{4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}},
{3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}},
{2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}},
{1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
},
/* 25 fps */
{
{5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}},
{3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}},
{2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}},
{1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}},
},
/* 30 fps */
{
{8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}},
{5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}},
{3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}},
{2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}},
},
},
/* QCIF */
{
/* 5 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 10 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 15 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 20 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 25 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 30 fps */
{
{0, },
{0, },
{0, },
{0, },
},
},
/* SIF */
{
/* 5 fps */
{
{4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}},
{3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}},
{2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}},
{1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}},
},
/* 10 fps */
{
{0, },
{6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}},
{3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}},
{2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}},
},
/* 15 fps */
{
{0, },
{9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}},
{4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}},
{3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}},
},
/* 20 fps */
{
{0, },
{9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}},
{5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}},
{3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}},
},
/* 25 fps */
{
{0, },
{9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}},
{6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}},
{4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}},
},
/* 30 fps */
{
{0, },
{9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}},
{6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}},
{4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}},
},
},
/* CIF */
{
/* 5 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 10 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 15 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 20 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 25 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 30 fps */
{
{0, },
{0, },
{0, },
{0, },
},
},
/* VGA */
{
/* 5 fps */
{
{0, },
{6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}},
{4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}},
{3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}},
},
/* 10 fps */
{
{0, },
{9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}},
{6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}},
{4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}},
},
/* 15 fps */
{
{0, },
{9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
{9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
{8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}},
},
/* 20 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 25 fps */
{
{0, },
{0, },
{0, },
{0, },
},
/* 30 fps */
{
{0, },
{0, },
{0, },
{0, },
},
},
/shark/trunk/drivers/usb/media/pwc-if.c
0,0 → 1,2202
/* Linux driver for Philips webcam
USB and Video4Linux interface part.
(C) 1999-2003 Nemosoft Unv.
 
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
 
*/
 
/*
This code forms the interface between the USB layers and the Philips
specific stuff. Some adanved stuff of the driver falls under an
NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
is thus not distributed in source form. The binary pwcx.o module
contains the code that falls under the NDA.
In case you're wondering: 'pwc' stands for "Philips WebCam", but
I really didn't want to type 'philips_web_cam' every time (I'm lazy as
any Linux kernel hacker, but I don't like uncomprehensible abbreviations
without explanation).
Oh yes, convention: to disctinguish between all the various pointers to
device-structures, I use these names for the pointer variables:
udev: struct usb_device *
vdev: struct video_device *
pdev: struct pwc_devive *
*/
 
/* Contributors:
- Alvarado: adding whitebalance code
- Alistar Moire: QuickCam 3000 Pro device/product ID
- Tony Hoyle: Creative Labs Webcam 5 device/product ID
- Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
- Jk Fang: SOTEC Afina Eye ID
- Xavier Roche: QuickCam Pro 4000 ID
- Jens Knudsen: QuickCam Zoom ID
- J. Debert: QuickCam for Notebooks ID
*/
 
#include <linuxcomp.h>
 
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
 
#include "pwc.h"
#include "pwc-ioctl.h"
#include "pwc-uncompress.h"
 
/* Function prototypes and driver templates */
 
/* hotplug device table support */
static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
{ USB_DEVICE(0x0471, 0x0303) },
{ USB_DEVICE(0x0471, 0x0304) },
{ USB_DEVICE(0x0471, 0x0307) },
{ USB_DEVICE(0x0471, 0x0308) },
{ USB_DEVICE(0x0471, 0x030C) },
{ USB_DEVICE(0x0471, 0x0310) },
{ USB_DEVICE(0x0471, 0x0311) },
{ USB_DEVICE(0x0471, 0x0312) },
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
{ USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
{ USB_DEVICE(0x055D, 0x9001) },
{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
{ USB_DEVICE(0x06BE, 0x8116) }, /* AME CU-001 */
{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
{ USB_DEVICE(0x0d81, 0x1900) },
{ }
};
MODULE_DEVICE_TABLE(usb, pwc_device_table);
 
static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void usb_pwc_disconnect(struct usb_interface *intf);
 
static struct usb_driver pwc_driver = {
.owner = THIS_MODULE,
.name = "Philips webcam", /* name */
.id_table = pwc_device_table,
.probe = usb_pwc_probe, /* probe() */
.disconnect = usb_pwc_disconnect, /* disconnect() */
};
 
#define MAX_DEV_HINTS 20
#define MAX_ISOC_ERRORS 20
 
static int default_size = PSZ_QCIF;
static int default_fps = 10;
static int default_fbufs = 3; /* Default number of frame buffers */
static int default_mbufs = 2; /* Default number of mmap() buffers */
int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
static int power_save = 0;
static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
static struct {
int type;
char serial_number[30];
int device_node;
struct pwc_device *pdev;
} device_hint[MAX_DEV_HINTS];
 
/***/
 
/*static*/ int pwc_video_open(struct inode *inode, struct file *file);
static int pwc_video_close(struct inode *inode, struct file *file);
static void pwc_video_release(struct video_device *);
/*static*/ ssize_t pwc_video_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
/*static*/ int pwc_video_ioctl(struct inode *inode, struct file *file,
unsigned int ioctlnr, unsigned long arg);
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
 
static struct file_operations pwc_fops = {
.owner = THIS_MODULE,
.open = pwc_video_open,
.release = pwc_video_close,
.read = pwc_video_read,
.poll = pwc_video_poll,
.mmap = pwc_video_mmap,
.ioctl = pwc_video_ioctl,
.llseek = no_llseek,
};
static struct video_device pwc_template = {
.owner = THIS_MODULE,
.name = "Philips Webcam", /* Filled in later */
.type = VID_TYPE_CAPTURE,
.hardware = VID_HARDWARE_PWC,
.fops = &pwc_fops,
};
 
/***************************************************************************/
 
/* Okay, this is some magic that I worked out and the reasoning behind it...
 
The biggest problem with any USB device is of course: "what to do
when the user unplugs the device while it is in use by an application?"
We have several options:
1) Curse them with the 7 plagues when they do (requires divine intervention)
2) Tell them not to (won't work: they'll do it anyway)
3) Oops the kernel (this will have a negative effect on a user's uptime)
4) Do something sensible.
Of course, we go for option 4.
 
It happens that this device will be linked to two times, once from
usb_device and once from the video_device in their respective 'private'
pointers. This is done when the device is probed() and all initialization
succeeded. The pwc_device struct links back to both structures.
 
When a device is unplugged while in use it will be removed from the
list of known USB devices; I also de-register it as a V4L device, but
unfortunately I can't free the memory since the struct is still in use
by the file descriptor. This free-ing is then deferend until the first
opportunity. Crude, but it works.
A small 'advantage' is that if a user unplugs the cam and plugs it back
in, it should get assigned the same video device minor, but unfortunately
it's non-trivial to re-link the cam back to the video device... (that
would surely be magic! :))
*/
 
/***************************************************************************/
/* Private functions */
 
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the area.
*/
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
// unsigned long kva, ret;
 
// kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
// kva |= adr & (PAGE_SIZE-1); /* restore the offset */
// ret = __pa(kva); */
// return ret;
return adr;
}
 
static void * rvmalloc(unsigned long size)
{
void * mem;
unsigned long adr;
 
// size=PAGE_ALIGN(size);
// mem=vmalloc_32(size);
mem=malloc(size);
if (mem)
{
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
// adr=(unsigned long) mem;
// while (size > 0)
// {
// SetPageReserved(vmalloc_to_page((void *)adr));
// adr+=PAGE_SIZE;
// size-=PAGE_SIZE;
// }
}
return mem;
}
 
static void rvfree(void * mem, unsigned long size)
{
unsigned long adr;
 
// if (mem)
// {
// adr=(unsigned long) mem;
// while ((long) size > 0)
// {
// ClearPageReserved(vmalloc_to_page((void *)adr));
// adr+=PAGE_SIZE;
// size-=PAGE_SIZE;
// }
// vfree(mem);
// }
}
 
 
 
 
static int pwc_allocate_buffers(struct pwc_device *pdev)
{
int i;
void *kbuf;
 
Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
 
if (pdev == NULL)
return -ENXIO;
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("allocate_buffers(): magic failed.\n");
return -ENXIO;
}
#endif
/* Allocate Isochronous pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++) {
if (pdev->sbuf[i].data == NULL) {
kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate iso buffer %d.\n", i);
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
pdev->sbuf[i].data = kbuf;
memset(kbuf, 0, ISO_BUFFER_SIZE);
}
}
 
/* Allocate frame buffer structure */
if (pdev->fbuf == NULL) {
kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate frame buffer structure.\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
pdev->fbuf = kbuf;
memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
}
/* create frame buffers, and make circular ring */
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data == NULL) {
kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
if (kbuf == NULL) {
Err("Failed to allocate frame buffer %d.\n", i);
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf);
pdev->fbuf[i].data = kbuf;
memset(kbuf, 128, PWC_FRAME_SIZE);
}
}
/* Allocate decompressor table space */
kbuf = NULL;
if (pdev->decompressor != NULL) {
kbuf = kmalloc(pdev->decompressor->table_size, GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate decompress table.\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated decompress table %p.\n", kbuf);
}
pdev->decompress_data = kbuf;
/* Allocate image buffer; double buffer for mmap() */
kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
if (kbuf == NULL) {
Err("Failed to allocate image buffer(s).\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
pdev->image_data = kbuf;
for (i = 0; i < default_mbufs; i++)
pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
for (; i < MAX_IMAGES; i++)
pdev->image_ptr[i] = NULL;
 
kbuf = NULL;
Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
return 0;
}
 
static void pwc_free_buffers(struct pwc_device *pdev)
{
int i;
 
Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
 
if (pdev == NULL)
return;
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("free_buffers(): magic failed.\n");
return;
}
#endif
 
/* Release Iso-pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++)
if (pdev->sbuf[i].data != NULL) {
Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
kfree(pdev->sbuf[i].data);
pdev->sbuf[i].data = NULL;
}
 
/* The same for frame buffers */
if (pdev->fbuf != NULL) {
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data != NULL) {
Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
vfree(pdev->fbuf[i].data);
pdev->fbuf[i].data = NULL;
}
}
kfree(pdev->fbuf);
pdev->fbuf = NULL;
}
 
/* Intermediate decompression buffer & tables */
if (pdev->decompress_data != NULL) {
Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data);
kfree(pdev->decompress_data);
pdev->decompress_data = NULL;
}
pdev->decompressor = NULL;
 
/* Release image buffers */
if (pdev->image_data != NULL) {
Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
}
pdev->image_data = NULL;
Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
}
 
/* The frame & image buffer mess.
 
Yes, this is a mess. Well, it used to be simple, but alas... In this
module, 3 buffers schemes are used to get the data from the USB bus to
the user program. The first scheme involves the ISO buffers (called thus
since they transport ISO data from the USB controller), and not really
interesting. Suffices to say the data from this buffer is quickly
gathered in an interrupt handler (pwc_isoc_handler) and placed into the
frame buffer.
 
The frame buffer is the second scheme, and is the central element here.
It collects the data from a single frame from the camera (hence, the
name). Frames are delimited by the USB camera with a short USB packet,
so that's easy to detect. The frame buffers form a list that is filled
by the camera+USB controller and drained by the user process through
either read() or mmap().
 
The image buffer is the third scheme, in which frames are decompressed
and converted into planar format. For mmap() there is more than
one image buffer available.
 
The frame buffers provide the image buffering. In case the user process
is a bit slow, this introduces lag and some undesired side-effects.
The problem arises when the frame buffer is full. I used to drop the last
frame, which makes the data in the queue stale very quickly. But dropping
the frame at the head of the queue proved to be a litte bit more difficult.
I tried a circular linked scheme, but this introduced more problems than
it solved.
 
Because filling and draining are completely asynchronous processes, this
requires some fiddling with pointers and mutexes.
 
Eventually, I came up with a system with 2 lists: an 'empty' frame list
and a 'full' frame list:
* Initially, all frame buffers but one are on the 'empty' list; the one
remaining buffer is our initial fill frame.
* If a frame is needed for filling, we try to take it from the 'empty'
list, unless that list is empty, in which case we take the buffer at
the head of the 'full' list.
* When our fill buffer has been filled, it is appended to the 'full'
list.
* If a frame is needed by read() or mmap(), it is taken from the head of
the 'full' list, handled, and then appended to the 'empty' list. If no
buffer is present on the 'full' list, we wait.
The advantage is that the buffer that is currently being decompressed/
converted, is on neither list, and thus not in our way (any other scheme
I tried had the problem of old data lingering in the queue).
 
Whatever strategy you choose, it always remains a tradeoff: with more
frame buffers the chances of a missed frame are reduced. On the other
hand, on slower machines it introduces lag because the queue will
always be full.
*/
 
/**
\brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
*/
static inline int pwc_next_fill_frame(struct pwc_device *pdev)
{
int ret;
unsigned long flags;
ret = 0;
spin_lock_irqsave(&pdev->ptrlock, flags);
if (pdev->fill_frame != NULL) {
/* append to 'full' list */
if (pdev->full_frames == NULL) {
pdev->full_frames = pdev->fill_frame;
pdev->full_frames_tail = pdev->full_frames;
}
else {
pdev->full_frames_tail->next = pdev->fill_frame;
pdev->full_frames_tail = pdev->fill_frame;
}
}
if (pdev->empty_frames != NULL) {
/* We have empty frames available. That's easy */
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
}
else {
/* Hmm. Take it from the full list */
#if PWC_DEBUG
/* sanity check */
if (pdev->full_frames == NULL) {
Err("Neither empty or full frames available!\n");
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return -EINVAL;
}
#endif
pdev->fill_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
ret = 1;
}
pdev->fill_frame->next = NULL;
#if PWC_DEBUG
Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
pdev->fill_frame->sequence = pdev->sequence++;
#endif
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
 
 
/**
\brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
*/
static void pwc_reset_buffers(struct pwc_device *pdev)
{
int i;
unsigned long flags;
 
spin_lock_irqsave(&pdev->ptrlock, flags);
pdev->full_frames = NULL;
pdev->full_frames_tail = NULL;
for (i = 0; i < default_fbufs; i++) {
pdev->fbuf[i].filled = 0;
if (i > 0)
pdev->fbuf[i].next = &pdev->fbuf[i - 1];
else
pdev->fbuf->next = NULL;
}
pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
pdev->empty_frames_tail = pdev->fbuf;
pdev->read_frame = NULL;
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
 
pdev->image_read_pos = 0;
pdev->fill_image = 0;
spin_unlock_irqrestore(&pdev->ptrlock, flags);
}
 
 
/**
\brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
*/
static int pwc_handle_frame(struct pwc_device *pdev)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&pdev->ptrlock, flags);
/* First grab our read_frame; this is removed from all lists, so
we can release the lock after this without problems */
if (pdev->read_frame != NULL) {
/* This can't theoretically happen */
Err("Huh? Read frame still in use?\n");
}
else {
if (pdev->full_frames == NULL) {
Err("Woops. No frames ready.\n");
}
else {
pdev->read_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
pdev->read_frame->next = NULL;
}
 
if (pdev->read_frame != NULL) {
#if PWC_DEBUG
Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
#endif
/* Decompression is a lenghty process, so it's outside of the lock.
This gives the isoc_handler the opportunity to fill more frames
in the mean time.
*/
spin_unlock_irqrestore(&pdev->ptrlock, flags);
ret = pwc_decompress(pdev);
spin_lock_irqsave(&pdev->ptrlock, flags);
 
/* We're done with read_buffer, tack it to the end of the empty buffer list */
if (pdev->empty_frames == NULL) {
pdev->empty_frames = pdev->read_frame;
pdev->empty_frames_tail = pdev->empty_frames;
}
else {
pdev->empty_frames_tail->next = pdev->read_frame;
pdev->empty_frames_tail = pdev->read_frame;
}
pdev->read_frame = NULL;
}
}
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
 
/**
\brief Advance pointers of image buffer (after each user request)
*/
static inline void pwc_next_image(struct pwc_device *pdev)
{
pdev->image_used[pdev->fill_image] = 0;
pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
}
 
 
/* This gets called for the Isochronous pipe (video). This is done in
* interrupt time, so it has to be fast, not crash, and not stall. Neat.
*/
static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
{
struct pwc_device *pdev;
int i, fst, flen;
int awake;
struct pwc_frame_buf *fbuf;
unsigned char *fillptr = 0, *iso_buf = 0;
 
awake = 0;
pdev = (struct pwc_device *)urb->context;
if (pdev == NULL) {
Err("isoc_handler() called with NULL device?!\n");
return;
}
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("isoc_handler() called with bad magic!\n");
return;
}
#endif
if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return;
}
if (urb->status != -EINPROGRESS && urb->status != 0) {
const char *errmsg;
 
errmsg = "Unknown";
switch(urb->status) {
case -ENOSR: errmsg = "Buffer error (overrun)"; break;
case -EPIPE: errmsg = "Stalled (device not responding)"; break;
case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break;
}
Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
/* Give up after a number of contiguous errors on the USB bus.
Appearantly something is wrong so we simulate an unplug event.
*/
if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{
Info("Too many ISOC errors, bailing out.\n");
pdev->error_status = EIO;
awake = 1;
wake_up_interruptible(&pdev->frameq);
}
goto handler_end; // ugly, but practical
}
 
fbuf = pdev->fill_frame;
if (fbuf == NULL) {
Err("pwc_isoc_handler without valid fill frame.\n");
awake = 1;
goto handler_end;
}
else {
fillptr = fbuf->data + fbuf->filled;
}
/* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0;
 
/* vsync: 0 = don't copy data
1 = sync-hunt
2 = synched
*/
/* Compact data */
for (i = 0; i < urb->number_of_packets; i++) {
fst = urb->iso_frame_desc[i].status;
flen = urb->iso_frame_desc[i].actual_length;
iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (fst == 0) {
if (flen > 0) { /* if valid data... */
if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */
pdev->vsync = 2;
 
/* ...copy data to frame buffer, if possible */
if (flen + fbuf->filled > pdev->frame_total_size) {
Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
pdev->vframes_error++;
}
else {
memmove(fillptr, iso_buf, flen);
fillptr += flen;
}
}
fbuf->filled += flen;
} /* ..flen > 0 */
 
if (flen < pdev->vlast_packet_size) {
/* Shorter packet... We probably have the end of an image-frame;
wake up read() process and let select()/poll() do something.
Decompression is done in user time over there.
*/
if (pdev->vsync == 2) {
/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
frames on the USB wire after an exposure change. This conditition is
however detected in the cam and a bit is set in the header.
*/
if (pdev->type == 730) {
unsigned char *ptr = (unsigned char *)fbuf->data;
if (ptr[1] == 1 && ptr[0] & 0x10) {
#if PWC_DEBUG
Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
#endif
pdev->drop_frames += 2;
pdev->vframes_error++;
}
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
if (ptr[0] & 0x01)
Info("Snapshot button pressed.\n");
else
Info("Snapshot button released.\n");
}
if ((ptr[0] ^ pdev->vmirror) & 0x02) {
if (ptr[0] & 0x02)
Info("Image is mirrored.\n");
else
Info("Image is normal.\n");
}
pdev->vmirror = ptr[0] & 0x03;
/* Sometimes the trailer of the 730 is still sent as a 4 byte packet
after a short frame; this condition is filtered out specifically. A 4 byte
frame doesn't make sense anyway.
So we get either this sequence:
drop_bit set -> 4 byte frame -> short frame -> good frame
Or this one:
drop_bit set -> short frame -> good frame
So we drop either 3 or 2 frames in all!
*/
if (fbuf->filled == 4)
pdev->drop_frames++;
}
 
/* In case we were instructed to drop the frame, do so silently.
The buffer pointers are not updated either (but the counters are reset below).
*/
if (pdev->drop_frames > 0)
pdev->drop_frames--;
else {
/* Check for underflow first */
if (fbuf->filled < pdev->frame_total_size) {
Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled);
pdev->vframes_error++;
}
else {
/* Send only once per EOF */
awake = 1; /* delay wake_ups */
 
/* Find our next frame to fill. This will always succeed, since we
* nick a frame from either empty or full list, but if we had to
* take it from the full list, it means a frame got dropped.
*/
if (pwc_next_fill_frame(pdev)) {
pdev->vframes_dumped++;
if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
if (pdev->vframes_dumped < 20)
Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
if (pdev->vframes_dumped == 20)
Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
}
}
fbuf = pdev->fill_frame;
}
} /* !drop_frames */
pdev->vframe_count++;
}
fbuf->filled = 0;
fillptr = fbuf->data;
pdev->vsync = 1;
} /* .. flen < last_packet_size */
pdev->vlast_packet_size = flen;
} /* ..status == 0 */
#if PWC_DEBUG
/* This is normally not interesting to the user, unless you are really debugging something */
else {
static int iso_error = 0;
iso_error++;
if (iso_error < 20)
Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
}
#endif
}
 
handler_end:
if (awake)
wake_up_interruptible(&pdev->frameq);
 
urb->dev = pdev->udev;
i = usb_submit_urb(urb, GFP_ATOMIC);
if (i != 0)
Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
 
 
static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
struct urb *urb;
int i, j, ret;
 
struct usb_host_interface *idesc;
 
if (pdev == NULL)
return -EFAULT;
if (pdev->iso_init)
return 0;
pdev->vsync = 0;
udev = pdev->udev;
 
/* Get the current alternate interface, adjust packet size */
if (!udev->actconfig)
return -EFAULT;
idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate];
if (!idesc)
return -EFAULT;
 
/* Search video endpoint */
pdev->vmax_packet_size = -1;
for (i = 0; i < idesc->desc.bNumEndpoints; i++)
if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize;
break;
}
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
Err("Failed to find packet size for video endpoint in current alternate setting.\n");
return -ENFILE; /* Odd error, that should be noticeable */
}
 
/* Set alternate interface */
ret = 0;
Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
if (ret < 0)
return ret;
 
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
Err("Failed to allocate urb %d\n", i);
ret = -ENOMEM;
break;
}
pdev->sbuf[i].urb = urb;
Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
}
if (ret) {
/* De-allocate in reverse order */
while (i >= 0) {
if (pdev->sbuf[i].urb != NULL)
usb_free_urb(pdev->sbuf[i].urb);
pdev->sbuf[i].urb = NULL;
i--;
}
return ret;
}
 
/* init URB structure */
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = pdev->sbuf[i].urb;
 
urb->interval = 1; // devik
urb->dev = udev;
urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = pdev->sbuf[i].data;
urb->transfer_buffer_length = ISO_BUFFER_SIZE;
urb->complete = pwc_isoc_handler;
urb->context = pdev;
urb->start_frame = 0;
urb->number_of_packets = ISO_FRAMES_PER_DESC;
for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
}
}
 
/* link */
for (i = 0; i < MAX_ISO_BUFS; i++) {
ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
if (ret)
Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
else
Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
}
 
/* All is done... */
pdev->iso_init = 1;
Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
return 0;
}
 
static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
int i;
 
Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
if (pdev == NULL)
return;
 
/* Unlinking ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
struct urb *urb;
 
urb = pdev->sbuf[i].urb;
if (urb != 0) {
if (pdev->iso_init) {
Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
usb_unlink_urb(urb);
}
Trace(TRACE_MEMORY, "Freeing URB\n");
usb_free_urb(urb);
pdev->sbuf[i].urb = NULL;
}
}
 
/* Stop camera, but only if we are sure the camera is still there (unplug
is signalled by EPIPE)
*/
if (pdev->error_status && pdev->error_status != EPIPE) {
Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
usb_set_interface(pdev->udev, 0, 0);
}
 
pdev->iso_init = 0;
Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
}
 
int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
{
int ret, start;
 
/* Stop isoc stuff */
pwc_isoc_cleanup(pdev);
/* Reset parameters */
pwc_reset_buffers(pdev);
/* Try to set video mode... */
start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
if (ret) {
Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
/* That failed... restore old mode (we know that worked) */
start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (start) {
Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
}
}
if (start == 0)
{
if (pwc_isoc_init(pdev) < 0)
{
Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
ret = -EAGAIN; /* let's try again, who knows if it works a second time */
}
}
pdev->drop_frames++; /* try to avoid garbage during switch */
return ret; /* Return original error code */
}
 
 
/***************************************************************************/
/* Video4Linux functions */
 
extern struct video_device *video_device[];
 
/*static*/ int pwc_video_open(struct inode *inode, struct file *file)
{
int i;
struct video_device *vdev = video_device[0]; //video_devdata(file);
struct pwc_device *pdev;
 
Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev == NULL)
BUG();
if (pdev->vopen)
return -EBUSY;
down(&pdev->modlock);
if (!pdev->usb_init) {
Trace(TRACE_OPEN, "Doing first time initialization.\n");
pdev->usb_init = 1;
if (pwc_trace & TRACE_OPEN) {
/* Query sensor type */
const char *sensor_type = NULL;
 
i = pwc_get_cmos_sensor(pdev);
if (i > 0)
{
switch(i) {
case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break;
case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break;
case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break;
case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
case 0x40: sensor_type = "UPA 1021 sensor"; break;
case 0x100: sensor_type = "VGA sensor"; break;
case 0x101: sensor_type = "PAL MR sensor"; break;
default: sensor_type = "unknown type of sensor"; break;
}
}
if (sensor_type != NULL)
Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev.name, sensor_type, i);
}
}
 
/* Turn on camera */
if (power_save) {
i = pwc_camera_power(pdev, 1);
if (i < 0)
Info("Failed to restore power to the camera! (%d)\n", i);
}
/* Set LED on/off time */
if (pwc_set_leds(pdev, led_on, led_off) < 0)
Info("Failed to set LED on/off time.\n");
 
/* Find our decompressor, if any */
pdev->decompressor = pwc_find_decompressor(pdev->type);
#if PWC_DEBUG
Debug("Found decompressor for %d at 0x%p\n", pdev->type, pdev->decompressor);
#endif
pwc_construct(pdev); /* set min/max sizes correct */
 
/* So far, so good. Allocate memory. */
i = pwc_allocate_buffers(pdev);
if (i < 0) {
Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
up(&pdev->modlock);
return i;
}
/* Reset buffers & parameters */
pwc_reset_buffers(pdev);
for (i = 0; i < default_mbufs; i++)
pdev->image_used[i] = 0;
pdev->vframe_count = 0;
pdev->vframes_dumped = 0;
pdev->vframes_error = 0;
pdev->visoc_errors = 0;
pdev->error_status = 0;
#if PWC_DEBUG
pdev->sequence = 0;
#endif
pwc_construct(pdev); /* set min/max sizes correct */
 
/* Set some defaults */
pdev->vsnapshot = 0;
/* Start iso pipe for video; first try the last used video size
(or the default one); if that fails try QCIF/10 or QSIF/10;
it that fails too, give up.
*/
i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
if (i) {
Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
else
i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
}
if (i) {
Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
up(&pdev->modlock);
return i;
}
i = pwc_isoc_init(pdev);
if (i) {
Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
up(&pdev->modlock);
return i;
}
 
pdev->vopen++;
//file->private_data = vdev;
/* lock decompressor; this has a small race condition, since we
could in theory unload pwcx.o between pwc_find_decompressor()
above and this call. I doubt it's ever going to be a problem.
*/
if (pdev->decompressor != NULL)
pdev->decompressor->lock();
up(&pdev->modlock);
Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
return 0;
}
 
/* Note that all cleanup is done in the reverse order as in _open */
static int pwc_video_close(struct inode *inode, struct file *file)
{
struct video_device *vdev = video_device[0]; //file->private_data;
struct pwc_device *pdev;
int i;
 
Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
 
pdev = (struct pwc_device *)vdev->priv;
if (pdev->vopen == 0)
Info("video_close() called on closed device?\n");
 
/* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot
programs)
*/
if (pdev->vframe_count > 20)
Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
 
if (pdev->decompressor != NULL) {
pdev->decompressor->exit();
pdev->decompressor->unlock();
pdev->decompressor = NULL;
}
 
pwc_isoc_cleanup(pdev);
pwc_free_buffers(pdev);
 
/* Turn off LEDS and power down camera, but only when not unplugged */
if (pdev->error_status != EPIPE) {
/* Turn LEDs off */
if (pwc_set_leds(pdev, 0, 0) < 0)
Info("Failed to set LED on/off time.\n");
if (power_save) {
i = pwc_camera_power(pdev, 0);
if (i < 0)
Err("Failed to power down camera (%d)\n", i);
}
}
pdev->vopen = 0;
Trace(TRACE_OPEN, "<< video_close()\n");
return 0;
}
 
static void pwc_video_release(struct video_device *vfd)
{
Trace(TRACE_OPEN, "pwc_video_release() called. Now what?\n");
}
 
/*
* FIXME: what about two parallel reads ????
* ANSWER: Not supported. You can't open the device more than once,
despite what the V4L1 interface says. First, I don't see
the need, second there's no mechanism of alerting the
2nd/3rd/... process of events like changing image size.
And I don't see the point of blocking that for the
2nd/3rd/... process.
In multi-threaded environments reading parallel from any
device is tricky anyhow.
*/
 
/*static*/ ssize_t pwc_video_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
struct video_device *vdev = video_device[0]; //file->private_data;
struct pwc_device *pdev;
int noblock = 0; //file->f_flags & O_NONBLOCK;
DECLARE_WAITQUEUE(wait, current);
int bytes_to_read;
 
//printk(KERN_INFO "@0\n");
//wait_ms(1000);
// Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count);
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
if (pdev->error_status)
return -pdev->error_status; /* Something happened, report what. */
//printk(KERN_INFO "@1\n");
//wait_ms(1000);
 
/* In case we're doing partial reads, we don't have to wait for a frame */
if (pdev->image_read_pos == 0) {
/* Do wait queueing according to the (doc)book */
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
/* Check for unplugged/etc. here */
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -pdev->error_status ;
}
if (noblock) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -EWOULDBLOCK;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
//printk(KERN_INFO "@5\n");
//wait_ms(1000);
/* Decompress and release frame */
if (pwc_handle_frame(pdev))
return -EFAULT;
}
 
//printk(KERN_INFO "@6\n");
//wait_ms(1000);
// Trace(TRACE_READ, "Copying data to user space.\n");
if (pdev->vpalette == VIDEO_PALETTE_RAW) {
//printk(KERN_INFO "@7\n");
//wait_ms(1000);
bytes_to_read = pdev->frame_size;
} else
bytes_to_read = pdev->view.size;
 
/* copy bytes to user space; we allow for partial reads */
//printk(KERN_INFO "@8\n");
//wait_ms(1000);
if (count + pdev->image_read_pos > bytes_to_read)
count = bytes_to_read - pdev->image_read_pos;
if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
return -EFAULT;
pdev->image_read_pos += count;
if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
//printk(KERN_INFO "@9\n");
//wait_ms(1000);
pdev->image_read_pos = 0;
pwc_next_image(pdev);
}
return count;
}
 
static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = video_device[0]; //file->private_data;
struct pwc_device *pdev;
 
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
 
poll_wait(file, &pdev->frameq, wait);
if (pdev->error_status)
return POLLERR;
if (pdev->full_frames != NULL) /* we have frames waiting */
return (POLLIN | POLLRDNORM);
 
return 0;
}
 
/*static*/ int pwc_video_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
struct video_device *vdev = video_device[0]; //file->private_data;
struct pwc_device *pdev;
DECLARE_WAITQUEUE(wait, current);
 
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
 
switch (cmd) {
/* Query cabapilities */
case VIDIOCGCAP:
{
struct video_capability *caps = arg;
 
strcpy(caps->name, vdev->name);
caps->type = VID_TYPE_CAPTURE;
caps->channels = 1;
caps->audios = 1;
caps->minwidth = pdev->view_min.x;
caps->minheight = pdev->view_min.y;
caps->maxwidth = pdev->view_max.x;
caps->maxheight = pdev->view_max.y;
break;
}
 
/* Channel functions (simulate 1 channel) */
case VIDIOCGCHAN:
{
struct video_channel *v = arg;
 
if (v->channel != 0)
return -EINVAL;
v->flags = 0;
v->tuners = 0;
v->type = VIDEO_TYPE_CAMERA;
strcpy(v->name, "Webcam");
return 0;
}
 
case VIDIOCSCHAN:
{
/* The spec says the argument is an integer, but
the bttv driver uses a video_channel arg, which
makes sense becasue it also has the norm flag.
*/
struct video_channel *v = arg;
if (v->channel != 0)
return -EINVAL;
return 0;
}
 
 
/* Picture functions; contrast etc. */
case VIDIOCGPICT:
{
struct video_picture *p = arg;
int val;
 
val = pwc_get_brightness(pdev);
if (val >= 0)
p->brightness = val;
else
p->brightness = 0xffff;
val = pwc_get_contrast(pdev);
if (val >= 0)
p->contrast = val;
else
p->contrast = 0xffff;
/* Gamma, Whiteness, what's the difference? :) */
val = pwc_get_gamma(pdev);
if (val >= 0)
p->whiteness = val;
else
p->whiteness = 0xffff;
val = pwc_get_saturation(pdev);
if (val >= 0)
p->colour = val;
else
p->colour = 0xffff;
p->depth = 24;
p->palette = pdev->vpalette;
p->hue = 0xFFFF; /* N/A */
break;
}
 
case VIDIOCSPICT:
{
struct video_picture *p = arg;
/*
* FIXME: Suppose we are mid read
ANSWER: No problem: the firmware of the camera
can handle brightness/contrast/etc
changes at _any_ time, and the palette
is used exactly once in the uncompress
routine.
*/
pwc_set_brightness(pdev, p->brightness);
pwc_set_contrast(pdev, p->contrast);
pwc_set_gamma(pdev, p->whiteness);
pwc_set_saturation(pdev, p->colour);
if (p->palette && p->palette != pdev->vpalette) {
switch (p->palette) {
case VIDEO_PALETTE_YUV420P:
case VIDEO_PALETTE_RAW:
pdev->vpalette = p->palette;
return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
break;
default:
return -EINVAL;
break;
}
}
break;
}
 
/* Window/size parameters */
case VIDIOCGWIN:
{
struct video_window *vw = arg;
vw->x = 0;
vw->y = 0;
vw->width = pdev->view.x;
vw->height = pdev->view.y;
vw->chromakey = 0;
vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
(pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
break;
}
case VIDIOCSWIN:
{
struct video_window *vw = arg;
int fps, snapshot, ret;
 
fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
snapshot = vw->flags & PWC_FPS_SNAPSHOT;
if (fps == 0)
fps = pdev->vframes;
if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
return 0;
ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
if (ret)
return ret;
break;
}
/* We don't have overlay support (yet) */
case VIDIOCGFBUF:
{
struct video_buffer *vb = arg;
 
memset(vb,0,sizeof(*vb));
break;
}
 
/* mmap() functions */
case VIDIOCGMBUF:
{
/* Tell the user program how much memory is needed for a mmap() */
struct video_mbuf *vm = arg;
int i;
 
memset(vm, 0, sizeof(*vm));
vm->size = default_mbufs * pdev->len_per_image;
vm->frames = default_mbufs; /* double buffering should be enough for most applications */
for (i = 0; i < default_mbufs; i++)
vm->offsets[i] = i * pdev->len_per_image;
break;
}
 
case VIDIOCMCAPTURE:
{
/* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
struct video_mmap *vm = arg;
 
Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
if (vm->frame < 0 || vm->frame >= default_mbufs)
return -EINVAL;
 
/* xawtv is nasty. It probes the available palettes
by setting a very small image size and trying
various palettes... The driver doesn't support
such small images, so I'm working around it.
*/
if (vm->format)
{
switch (vm->format)
{
case VIDEO_PALETTE_YUV420P:
case VIDEO_PALETTE_RAW:
break;
default:
return -EINVAL;
break;
}
}
 
if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
(vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
int ret;
 
Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (ret)
return ret;
} /* ... size mismatch */
 
/* FIXME: should we lock here? */
if (pdev->image_used[vm->frame])
return -EBUSY; /* buffer wasn't available. Bummer */
pdev->image_used[vm->frame] = 1;
 
/* Okay, we're done here. In the SYNC call we wait until a
frame comes available, then expand image into the given
buffer.
In contrast to the CPiA cam the Philips cams deliver a
constant stream, almost like a grabber card. Also,
we have separate buffers for the rawdata and the image,
meaning we can nearly always expand into the requested buffer.
*/
Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
break;
}
 
case VIDIOCSYNC:
{
/* The doc says: "Whenever a buffer is used it should
call VIDIOCSYNC to free this frame up and continue."
The only odd thing about this whole procedure is
that MCAPTURE flags the buffer as "in use", and
SYNC immediately unmarks it, while it isn't
after SYNC that you know that the buffer actually
got filled! So you better not start a CAPTURE in
the same frame immediately (use double buffering).
This is not a problem for this cam, since it has
extra intermediate buffers, but a hardware
grabber card will then overwrite the buffer
you're working on.
*/
int *mbuf = arg;
int ret;
 
Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
 
/* bounds check */
if (*mbuf < 0 || *mbuf >= default_mbufs)
return -EINVAL;
/* check if this buffer was requested anyway */
if (pdev->image_used[*mbuf] == 0)
return -EINVAL;
 
/* Add ourselves to the frame wait-queue.
FIXME: needs auditing for safety.
QUESTION: In what respect? I think that using the
frameq is safe now.
*/
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -pdev->error_status;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
/* The frame is ready. Expand in the image buffer
requested by the user. I don't care if you
mmap() 5 buffers and request data in this order:
buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
Grabber hardware may not be so forgiving.
*/
Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
/* Decompress, etc */
ret = pwc_handle_frame(pdev);
pdev->image_used[*mbuf] = 0;
if (ret)
return -EFAULT;
break;
}
case VIDIOCGAUDIO:
{
struct video_audio *v = arg;
strcpy(v->name, "Microphone");
v->audio = -1; /* unknown audio minor */
v->flags = 0;
v->mode = VIDEO_SOUND_MONO;
v->volume = 0;
v->bass = 0;
v->treble = 0;
v->balance = 0x8000;
v->step = 1;
break;
}
case VIDIOCSAUDIO:
{
/* Dummy: nothing can be set */
break;
}
case VIDIOCGUNIT:
{
struct video_unit *vu = arg;
vu->video = pdev->vdev.minor & 0x3F;
vu->audio = -1; /* not known yet */
vu->vbi = -1;
vu->radio = -1;
vu->teletext = -1;
break;
}
default:
return pwc_ioctl(pdev, cmd, arg);
} /* ..switch */
return 0;
}
 
/*static*/ int pwc_video_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
}
 
 
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vdev = video_device[0]; //file->private_data;
struct pwc_device *pdev;
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end-vma->vm_start;
unsigned long page, pos;
Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
pdev = vdev->priv;
 
pos = (unsigned long)pdev->image_data;
while (size > 0) {
page = kvirt_to_pa(pos);
if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
 
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
 
return 0;
}
 
/***************************************************************************/
/* USB functions */
 
/* This function gets called when a new device is plugged in or the usb core
* is loaded.
*/
 
static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct pwc_device *pdev = NULL;
int vendor_id, product_id, type_id;
int i, hint;
int features = 0;
int video_nr = -1; /* default: use next available device */
char serial_number[30], *name;
 
/* Check if we can handle this device */
Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
udev->descriptor.idVendor, udev->descriptor.idProduct,
intf->altsetting->desc.bInterfaceNumber);
 
/* the interfaces are probed one by one. We are only interested in the
video interface (0) now.
Interface 1 is the Audio Control, and interface 2 Audio itself.
*/
if (intf->altsetting->desc.bInterfaceNumber > 0)
return -ENODEV;
 
vendor_id = udev->descriptor.idVendor;
product_id = udev->descriptor.idProduct;
 
if (vendor_id == 0x0471) {
switch (product_id) {
case 0x0302:
Info("Philips PCA645VC USB webcam detected.\n");
name = "Philips 645 webcam";
type_id = 645;
break;
case 0x0303:
Info("Philips PCA646VC USB webcam detected.\n");
name = "Philips 646 webcam";
type_id = 646;
break;
case 0x0304:
Info("Askey VC010 type 2 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 646;
break;
case 0x0307:
Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
name = "Philips 675 webcam";
type_id = 675;
break;
case 0x0308:
Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
name = "Philips 680 webcam";
type_id = 680;
break;
case 0x030C:
Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
name = "Philips 690 webcam";
type_id = 690;
break;
case 0x0310:
Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
name = "Philips 730 webcam";
type_id = 730;
break;
case 0x0311:
Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
name = "Philips 740 webcam";
type_id = 740;
break;
case 0x0312:
Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
name = "Philips 750 webcam";
type_id = 750;
break;
case 0x0313:
Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
name = "Philips 720K/40 webcam";
type_id = 720;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x069A) {
switch(product_id) {
case 0x0001:
Info("Askey VC010 type 1 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 645;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x046d) {
switch(product_id) {
case 0x08b0:
Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
name = "Logitech QuickCam Pro 3000";
type_id = 740; /* CCD sensor */
break;
case 0x08b1:
Info("Logitech QuickCam Notebook Pro USB webcam detected.\n");
name = "Logitech QuickCam Notebook Pro";
type_id = 740; /* CCD sensor */
break;
case 0x08b2:
Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
name = "Logitech QuickCam Pro 4000";
type_id = 740; /* CCD sensor */
break;
case 0x08b3:
Info("Logitech QuickCam Zoom USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
break;
case 0x08B4:
Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
break;
case 0x08b5:
Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
name = "Logitech QuickCam Orbit";
type_id = 740; /* CCD sensor */
features |= FEATURE_MOTOR_PANTILT;
break;
case 0x08b6:
case 0x08b7:
case 0x08b8:
Info("Logitech QuickCam detected (reserved ID).\n");
name = "Logitech QuickCam (res.)";
type_id = 730; /* Assuming CMOS */
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x055d) {
/* I don't know the difference between the C10 and the C30;
I suppose the difference is the sensor, but both cameras
work equally well with a type_id of 675
*/
switch(product_id) {
case 0x9000:
Info("Samsung MPC-C10 USB webcam detected.\n");
name = "Samsung MPC-C10";
type_id = 675;
break;
case 0x9001:
Info("Samsung MPC-C30 USB webcam detected.\n");
name = "Samsung MPC-C30";
type_id = 675;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x041e) {
switch(product_id) {
case 0x400c:
Info("Creative Labs Webcam 5 detected.\n");
name = "Creative Labs Webcam 5";
type_id = 730;
break;
case 0x4011:
Info("Creative Labs Webcam Pro Ex detected.\n");
name = "Creative Labs Webcam Pro Ex";
type_id = 740;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x04cc) {
switch(product_id) {
case 0x8116:
Info("Sotec Afina Eye USB webcam detected.\n");
name = "Sotec Afina Eye";
type_id = 730;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x06B) {
switch(product_id) {
case 0x8116:
/* Basicly the same as the Sotec Afina Eye */
Info("AME CU-001 USB webcam detected.\n");
name = "AME CU-001";
type_id = 730;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x06be) {
switch(product_id) {
case 0x8116:
/* This is essentially the same cam as the Sotec Afina Eye */
Info("AME Co. Afina Eye USB webcam detected.\n");
name = "AME Co. Afina Eye";
type_id = 750;
break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x0d81) {
switch(product_id) {
case 0x1900:
Info("Visionite VCS-UC300 USB webcam detected.\n");
name = "Visionite VCS-UC300";
type_id = 740; /* CCD sensor */
break;
case 0x1910:
Info("Visionite VCS-UM100 USB webcam detected.\n");
name = "Visionite VCS-UM100";
type_id = 730; /* CMOS sensor */
break;
default:
return -ENODEV;
break;
}
}
else
return -ENODEV; /* Not any of the know types; but the list keeps growing. */
 
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
 
if (udev->descriptor.bNumConfigurations > 1)
Info("Warning: more than 1 configuration available.\n");
 
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL);
if (pdev == NULL) {
Err("Oops, could not allocate memory for pwc_device.\n");
return -ENOMEM;
}
memset(pdev, 0, sizeof(struct pwc_device));
pdev->type = type_id;
pdev->vsize = default_size;
pdev->vframes = default_fps;
pdev->features = features;
if (vendor_id == 0x046D && product_id == 0x08B5)
{
/* Logitech QuickCam Orbit
The ranges have been determined experimentally; they may differ from cam to cam.
Also, the exact ranges left-right and up-down are different for my cam
*/
pdev->angle_range.pan_min = -7000;
pdev->angle_range.pan_max = 7000;
pdev->angle_range.tilt_min = -3000;
pdev->angle_range.tilt_max = 2500;
pdev->angle_range.zoom_min = -1;
pdev->angle_range.zoom_max = -1;
}
 
init_MUTEX(&pdev->modlock);
pdev->ptrlock = SPIN_LOCK_UNLOCKED;
 
pdev->udev = udev;
init_waitqueue_head(&pdev->frameq);
pdev->vcompression = pwc_preferred_compression;
 
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
strcpy(pdev->vdev.name, name);
pdev->vdev.owner = THIS_MODULE;
pdev->vdev.priv = pdev;
pdev->release = udev->descriptor.bcdDevice;
Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
 
/* Now search device_hint[] table for a match, so we can hint a node number. */
for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
(device_hint[hint].pdev == NULL)) {
/* so far, so good... try serial number */
if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
/* match! */
video_nr = device_hint[hint].device_node;
Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr);
break;
}
}
}
 
pdev->vdev.release = pwc_video_release;
i = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
if (i < 0) {
Err("Failed to register as video device (%d).\n", i);
kfree(pdev); /* Oops, no memory leaks please */
return -EIO;
}
else {
Info("Registered as /dev/video%d.\n", pdev->vdev.minor & 0x3F);
}
/* occupy slot */
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = pdev;
 
Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
usb_set_intfdata (intf, pdev);
return 0;
}
 
/* The user janked out the cable... */
static void usb_pwc_disconnect(struct usb_interface *intf)
{
struct pwc_device *pdev;
int hint;
 
lock_kernel();
pdev = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
if (pdev == NULL) {
Err("pwc_disconnect() Called without private pointer.\n");
goto disconnect_out;
}
if (pdev->udev == NULL) {
Err("pwc_disconnect() already called for %p\n", pdev);
goto disconnect_out;
}
if (pdev->udev != interface_to_usbdev(intf)) {
Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
goto disconnect_out;
}
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
goto disconnect_out;
}
#endif
/* We got unplugged; this is signalled by an EPIPE error code */
if (pdev->vopen) {
Info("Disconnected while webcam is in use!\n");
pdev->error_status = EPIPE;
}
 
/* Alert waiting processes */
wake_up_interruptible(&pdev->frameq);
/* Wait until device is closed */
while (pdev->vopen)
schedule();
/* Device is now closed, so we can safely unregister it */
Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
video_unregister_device(&pdev->vdev);
 
/* Free memory (don't set pdev to 0 just yet) */
kfree(pdev);
 
disconnect_out:
/* search device_hint[] table if we occupy a slot, by any chance */
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
if (device_hint[hint].pdev == pdev)
device_hint[hint].pdev = NULL;
 
unlock_kernel();
}
 
 
/* *grunt* We have to do atoi ourselves :-( */
static int pwc_atoi(const char *s)
{
int k = 0;
 
k = 0;
while (*s != '\0' && *s >= '0' && *s <= '9') {
k = 10 * k + (*s - '0');
s++;
}
return k;
}
 
 
/*
* Initialization code & module stuff
*/
 
static char *size = NULL;
static int fps = 0;
static int fbufs = 0;
static int mbufs = 0;
static int trace = -1;
static int compression = -1;
static int leds[2] = { -1, -1 };
static char *dev_hint[MAX_DEV_HINTS] = { };
 
MODULE_PARM(size, "s");
MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
MODULE_PARM(fps, "i");
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
MODULE_PARM(fbufs, "i");
MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
MODULE_PARM(mbufs, "i");
MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
MODULE_PARM(trace, "i");
MODULE_PARM_DESC(trace, "For debugging purposes");
MODULE_PARM(power_save, "i");
MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
MODULE_PARM(compression, "i");
MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
MODULE_PARM(leds, "2i");
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
MODULE_PARM(dev_hint, "0-20s");
MODULE_PARM_DESC(dev_hint, "Device node hints");
 
MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
MODULE_AUTHOR("Nemosoft Unv. <webcamt@smcc.demon.nl>");
MODULE_LICENSE("GPL");
 
/*static*/ int __init usb_pwc_init(void)
{
int i, sz;
char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
 
Info("Philips webcam module version " PWC_VERSION " loaded.\n");
Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
 
if (fps) {
if (fps < 4 || fps > 30) {
Err("Framerate out of bounds (4-30).\n");
return -EINVAL;
}
default_fps = fps;
Info("Default framerate set to %d.\n", default_fps);
}
 
if (size) {
/* string; try matching with array */
for (sz = 0; sz < PSZ_MAX; sz++) {
if (!strcmp(sizenames[sz], size)) { /* Found! */
default_size = sz;
break;
}
}
if (sz == PSZ_MAX) {
Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
return -EINVAL;
}
Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
}
if (mbufs) {
if (mbufs < 1 || mbufs > MAX_IMAGES) {
Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
return -EINVAL;
}
default_mbufs = mbufs;
Info("Number of image buffers set to %d.\n", default_mbufs);
}
if (fbufs) {
if (fbufs < 2 || fbufs > MAX_FRAMES) {
Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
return -EINVAL;
}
default_fbufs = fbufs;
Info("Number of frame buffers set to %d.\n", default_fbufs);
}
if (trace >= 0) {
Info("Trace options: 0x%04x\n", trace);
pwc_trace = trace;
}
if (compression >= 0) {
if (compression > 3) {
Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
return -EINVAL;
}
pwc_preferred_compression = compression;
Info("Preferred compression set to %d.\n", pwc_preferred_compression);
}
if (power_save)
Info("Enabling power save on open/close.\n");
if (leds[0] >= 0)
led_on = leds[0];
if (leds[1] >= 0)
led_off = leds[1];
 
/* Big device node whoopla. Basically, it allows you to assign a
device node (/dev/videoX) to a camera, based on its type
& serial number. The format is [type[.serialnumber]:]node.
 
Any camera that isn't matched by these rules gets the next
available free device node.
*/
for (i = 0; i < MAX_DEV_HINTS; i++) {
char *s, *colon, *dot;
 
/* This loop also initializes the array */
device_hint[i].pdev = NULL;
s = dev_hint[i];
if (s != NULL && *s != '\0') {
device_hint[i].type = -1; /* wildcard */
strcpy(device_hint[i].serial_number, "*");
 
/* parse string: chop at ':' & '/' */
colon = dot = s;
while (*colon != '\0' && *colon != ':')
colon++;
while (*dot != '\0' && *dot != '.')
dot++;
/* Few sanity checks */
if (*dot != '\0' && dot > colon) {
Err("Malformed camera hint: the colon must be after the dot.\n");
return -EINVAL;
}
 
if (*colon == '\0') {
/* No colon */
if (*dot != '\0') {
Err("Malformed camera hint: no colon + device node given.\n");
return -EINVAL;
}
else {
/* No type or serial number specified, just a number. */
device_hint[i].device_node = pwc_atoi(s);
}
}
else {
/* There's a colon, so we have at least a type and a device node */
device_hint[i].type = pwc_atoi(s);
device_hint[i].device_node = pwc_atoi(colon + 1);
if (*dot != '\0') {
/* There's a serial number as well */
int k;
dot++;
k = 0;
while (*dot != ':' && k < 29) {
device_hint[i].serial_number[k++] = *dot;
dot++;
}
device_hint[i].serial_number[k] = '\0';
}
}
#if PWC_DEBUG
Debug("device_hint[%d]:\n", i);
Debug(" type : %d\n", device_hint[i].type);
Debug(" serial# : %s\n", device_hint[i].serial_number);
Debug(" node : %d\n", device_hint[i].device_node);
#endif
}
else
device_hint[i].type = 0; /* not filled */
} /* ..for MAX_DEV_HINTS */
 
Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
return usb_register(&pwc_driver);
}
 
static void __exit usb_pwc_exit(void)
{
Trace(TRACE_MODULE, "Deregistering driver.\n");
usb_deregister(&pwc_driver);
Info("Philips webcam module removed.\n");
}
 
module_init(usb_pwc_init);
module_exit(usb_pwc_exit);
 
/shark/trunk/drivers/usb/media/pwc-uncompress.c
0,0 → 1,180
/* Linux driver for Philips webcam
Decompression frontend.
(C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
 
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
*/
/*
This is where the decompression routines register and unregister
themselves. It also has a decompressor wrapper function.
*/
 
#include <asm/current.h>
#include <asm/types.h>
// #include <linux/sched.h>
 
#include "pwc.h"
#include "pwc-uncompress.h"
 
 
/* This contains a list of all registered decompressors */
static LIST_HEAD(pwc_decompressor_list);
 
/* Should the pwc_decompress structure ever change, we increase the
version number so that we don't get nasty surprises, or can
dynamically adjust our structure.
*/
const int pwc_decompressor_version = PWC_MAJOR;
 
/* Add decompressor to list, ignoring duplicates */
void pwc_register_decompressor(struct pwc_decompressor *pwcd)
{
if (pwc_find_decompressor(pwcd->type) == NULL) {
Trace(TRACE_PWCX, "Adding decompressor for model %d.\n", pwcd->type);
list_add_tail(&pwcd->pwcd_list, &pwc_decompressor_list);
}
}
 
/* Remove decompressor from list */
void pwc_unregister_decompressor(int type)
{
struct pwc_decompressor *find;
find = pwc_find_decompressor(type);
if (find != NULL) {
Trace(TRACE_PWCX, "Removing decompressor for model %d.\n", type);
list_del(&find->pwcd_list);
}
}
 
/* Find decompressor in list */
struct pwc_decompressor *pwc_find_decompressor(int type)
{
struct list_head *tmp;
struct pwc_decompressor *pwcd;
 
list_for_each(tmp, &pwc_decompressor_list) {
pwcd = list_entry(tmp, struct pwc_decompressor, pwcd_list);
if (pwcd->type == type)
return pwcd;
}
return NULL;
}
 
 
 
int pwc_decompress(struct pwc_device *pdev)
{
struct pwc_frame_buf *fbuf;
int n, line, col, stride;
void *yuv, *image;
u16 *src;
u16 *dsty, *dstu, *dstv;
 
if (pdev == NULL)
return -EFAULT;
#if defined(__KERNEL__) && defined(PWC_MAGIC)
if (pdev->magic != PWC_MAGIC) {
Err("pwc_decompress(): magic failed.\n");
return -EFAULT;
}
#endif
 
fbuf = pdev->read_frame;
if (fbuf == NULL)
return -EFAULT;
image = pdev->image_ptr[pdev->fill_image];
if (!image)
return -EFAULT;
 
yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
 
/* Raw format; that's easy... */
if (pdev->vpalette == VIDEO_PALETTE_RAW)
{
memcpy(image, yuv, pdev->frame_size);
return 0;
}
 
if (pdev->vbandlength == 0) {
/* Uncompressed mode. We copy the data into the output buffer,
using the viewport size (which may be larger than the image
size). Unfortunately we have to do a bit of byte stuffing
to get the desired output format/size.
*/
/*
* We do some byte shuffling here to go from the
* native format to YUV420P.
*/
src = (u16 *)yuv;
n = pdev->view.x * pdev->view.y;
 
/* offset in Y plane */
stride = pdev->view.x * pdev->offset.y + pdev->offset.x;
dsty = (u16 *)(image + stride);
 
/* offsets in U/V planes */
stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2;
dstu = (u16 *)(image + n + stride);
dstv = (u16 *)(image + n + n / 4 + stride);
 
/* increment after each line */
stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */
 
for (line = 0; line < pdev->image.y; line++) {
for (col = 0; col < pdev->image.x; col += 4) {
*dsty++ = *src++;
*dsty++ = *src++;
if (line & 1)
*dstv++ = *src++;
else
*dstu++ = *src++;
}
dsty += stride;
if (line & 1)
dstv += (stride >> 1);
else
dstu += (stride >> 1);
}
}
else {
/* Compressed; the decompressor routines will write the data
in planar format immediately.
*/
int flags;
flags = PWCX_FLAG_PLANAR;
if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot)
flags |= PWCX_FLAG_BAYER;
 
if (pdev->decompressor)
pdev->decompressor->decompress(
&pdev->image, &pdev->view, &pdev->offset,
yuv, image,
flags,
pdev->decompress_data, pdev->vbandlength);
else
return -ENXIO; /* No such device or address: missing decompressor */
}
return 0;
}
 
/* Make sure these functions are available for the decompressor plugin
both when this code is compiled into the kernel or as as module.
*/
 
EXPORT_SYMBOL_NOVERS(pwc_decompressor_version);
EXPORT_SYMBOL(pwc_register_decompressor);
EXPORT_SYMBOL(pwc_unregister_decompressor);
/shark/trunk/drivers/usb/media/pwc-ioctl.h
0,0 → 1,276
#ifndef PWC_IOCTL_H
#define PWC_IOCTL_H
 
/* (C) 2001-2003 Nemosoft Unv. webcam@smcc.demon.nl
 
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
*/
 
/* This is pwc-ioctl.h belonging to PWC 8.12.1
It contains structures and defines to communicate from user space
directly to the driver.
*/
 
/*
Changes
2001/08/03 Alvarado Added ioctl constants to access methods for
changing white balance and red/blue gains
2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
2003/12/13 Nemosft Unv. Some modifications to make interfacing to
PWCX easier
*/
 
/* These are private ioctl() commands, specific for the Philips webcams.
They contain functions not found in other webcams, and settings not
specified in the Video4Linux API.
 
The #define names are built up like follows:
VIDIOC VIDeo IOCtl prefix
PWC Philps WebCam
G optional: Get
S optional: Set
... the function
*/
 
 
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
#define PSZ_QSIF 0x01
#define PSZ_QCIF 0x02
#define PSZ_SIF 0x03
#define PSZ_CIF 0x04
#define PSZ_VGA 0x05
#define PSZ_MAX 6
 
 
/* The frame rate is encoded in the video_window.flags parameter using
the upper 16 bits, since some flags are defined nowadays. The following
defines provide a mask and shift to filter out this value.
 
In 'Snapshot' mode the camera freezes its automatic exposure and colour
balance controls.
*/
#define PWC_FPS_SHIFT 16
#define PWC_FPS_MASK 0x00FF0000
#define PWC_FPS_FRMASK 0x003F0000
#define PWC_FPS_SNAPSHOT 0x00400000
 
 
/* structure for transfering x & y coordinates */
struct pwc_coord
{
int x, y; /* guess what */
int size; /* size, or offset */
};
 
 
/* Used with VIDIOCPWCPROBE */
struct pwc_probe
{
char name[32];
int type;
};
 
 
/* pwc_whitebalance.mode values */
#define PWC_WB_INDOOR 0
#define PWC_WB_OUTDOOR 1
#define PWC_WB_FL 2
#define PWC_WB_MANUAL 3
#define PWC_WB_AUTO 4
 
/* Used with VIDIOCPWC[SG]AWB (Auto White Balance).
Set mode to one of the PWC_WB_* values above.
*red and *blue are the respective gains of these colour components inside
the camera; range 0..65535
When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read;
otherwise undefined.
'read_red' and 'read_blue' are read-only.
*/
struct pwc_whitebalance
{
int mode;
int manual_red, manual_blue; /* R/W */
int read_red, read_blue; /* R/O */
};
 
/*
'control_speed' and 'control_delay' are used in automatic whitebalance mode,
and tell the camera how fast it should react to changes in lighting, and
with how much delay. Valid values are 0..65535.
*/
struct pwc_wb_speed
{
int control_speed;
int control_delay;
 
};
 
/* Used with VIDIOCPWC[SG]LED */
struct pwc_leds
{
int led_on; /* Led on-time; range = 0..25000 */
int led_off; /* Led off-time; range = 0..25000 */
};
 
/* Image size (used with GREALSIZE) */
struct pwc_imagesize
{
int width;
int height;
};
 
/* Defines and structures for Motorized Pan & Tilt */
#define PWC_MPT_PAN 0x01
#define PWC_MPT_TILT 0x02
#define PWC_MPT_TIMEOUT 0x04 /* for status */
 
/* Set angles; when absolute = 1, the angle is absolute and the
driver calculates the relative offset for you. This can only
be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
absolute angles.
*/
struct pwc_mpt_angles
{
int absolute; /* write-only */
int pan; /* degrees * 100 */
int tilt; /* degress * 100 */
int zoom; /* N/A, set to -1 */
};
 
/* Range of angles of the camera, both horizontally and vertically.
The zoom is not used, maybe in the future...
 
*/
struct pwc_mpt_range
{
int pan_min, pan_max; /* degrees * 100 */
int tilt_min, tilt_max;
int zoom_min, zoom_max; /* -1, -1 */
};
 
struct pwc_mpt_status
{
int status;
int time_pan;
int time_tilt;
};
 
 
/* This is used for out-of-kernel decompression. With it, you can get
all the necessary information to initialize and use the decompressor
routines in standalone applications.
*/
struct pwc_video_command
{
int type; /* camera type (645, 675, 730, etc.) */
int release; /* release number */
 
int size; /* one of PSZ_* */
int alternate;
int command_len; /* length of USB video command */
unsigned char command_buf[13]; /* Actual USB video command */
int bandlength; /* >0 = compressed */
int frame_size; /* Size of one (un)compressed frame */
};
 
/* Flags for PWCX subroutines. Not all modules honour all flags. */
#define PWCX_FLAG_PLANAR 0x0001
#define PWCX_FLAG_BAYER 0x0008
 
 
/* IOCTL definitions */
 
/* Restore user settings */
#define VIDIOCPWCRUSER _IO('v', 192)
/* Save user settings */
#define VIDIOCPWCSUSER _IO('v', 193)
/* Restore factory settings */
#define VIDIOCPWCFACTORY _IO('v', 194)
 
/* You can manipulate the compression factor. A compression preference of 0
means use uncompressed modes when available; 1 is low compression, 2 is
medium and 3 is high compression preferred. Of course, the higher the
compression, the lower the bandwidth used but more chance of artefacts
in the image. The driver automatically chooses a higher compression when
the preferred mode is not available.
*/
/* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
#define VIDIOCPWCSCQUAL _IOW('v', 195, int)
/* Get preferred compression quality */
#define VIDIOCPWCGCQUAL _IOR('v', 195, int)
 
 
/* This is a probe function; since so many devices are supported, it
becomes difficult to include all the names in programs that want to
check for the enhanced Philips stuff. So in stead, try this PROBE;
it returns a structure with the original name, and the corresponding
Philips type.
To use, fill the structure with zeroes, call PROBE and if that succeeds,
compare the name with that returned from VIDIOCGCAP; they should be the
same. If so, you can be assured it is a Philips (OEM) cam and the type
is valid.
*/
#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe)
 
/* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
#define VIDIOCPWCSAGC _IOW('v', 200, int)
/* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
#define VIDIOCPWCGAGC _IOR('v', 200, int)
/* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
#define VIDIOCPWCSSHUTTER _IOW('v', 201, int)
 
/* Color compensation (Auto White Balance) */
#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance)
#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance)
 
/* Auto WB speed */
#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed)
#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed)
 
/* LEDs on/off/blink; int range 0..65535 */
#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds)
#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds)
 
/* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
#define VIDIOCPWCSCONTOUR _IOW('v', 206, int)
#define VIDIOCPWCGCONTOUR _IOR('v', 206, int)
 
/* Backlight compensation; 0 = off, otherwise on */
#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int)
#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int)
 
/* Flickerless mode; = 0 off, otherwise on */
#define VIDIOCPWCSFLICKER _IOW('v', 208, int)
#define VIDIOCPWCGFLICKER _IOR('v', 208, int)
 
/* Dynamic noise reduction; 0 off, 3 = high noise reduction */
#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int)
#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int)
 
/* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize)
 
/* Motorized pan & tilt functions */
#define VIDIOCPWCMPTRESET _IOW('v', 211, int)
#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range)
#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles)
#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
 
/* Get the USB set-video command; needed for initializing libpwcx */
#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
 
#endif
/shark/trunk/drivers/usb/media/pwc_nala.h
0,0 → 1,66
/* SQCIF */
{
{0, 0, {0x04, 0x01, 0x03}},
{8, 0, {0x05, 0x01, 0x03}},
{7, 0, {0x08, 0x01, 0x03}},
{7, 0, {0x0A, 0x01, 0x03}},
{6, 0, {0x0C, 0x01, 0x03}},
{5, 0, {0x0F, 0x01, 0x03}},
{4, 0, {0x14, 0x01, 0x03}},
{3, 0, {0x18, 0x01, 0x03}},
},
/* QSIF */
{
{0},
{0},
{0},
{0},
{0},
{0},
{0},
{0},
},
/* QCIF */
{
{0, 0, {0x04, 0x01, 0x02}},
{8, 0, {0x05, 0x01, 0x02}},
{7, 0, {0x08, 0x01, 0x02}},
{6, 0, {0x0A, 0x01, 0x02}},
{5, 0, {0x0C, 0x01, 0x02}},
{4, 0, {0x0F, 0x01, 0x02}},
{1, 0, {0x14, 0x01, 0x02}},
{1, 0, {0x18, 0x01, 0x02}},
},
/* SIF */
{
{0},
{0},
{0},
{0},
{0},
{0},
{0},
{0},
},
/* CIF */
{
{4, 0, {0x04, 0x01, 0x01}},
{7, 1, {0x05, 0x03, 0x01}},
{6, 1, {0x08, 0x03, 0x01}},
{4, 1, {0x0A, 0x03, 0x01}},
{3, 1, {0x0C, 0x03, 0x01}},
{2, 1, {0x0F, 0x03, 0x01}},
{0},
{0},
},
/* VGA */
{
{0},
{0},
{0},
{0},
{0},
{0},
{0},
{0},
},
/shark/trunk/drivers/usb/media/pwc-uncompress.h
0,0 → 1,82
/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
 
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
*/
 
/* This file is the bridge between the kernel module and the plugin; it
describes the structures and datatypes used in both modules. Any
significant change should be reflected by increasing the
pwc_decompressor_version major number.
*/
#ifndef PWC_UNCOMPRESS_H
#define PWC_UNCOMPRESS_H
 
#include <linux/config.h>
#include <linux/list.h>
 
#include "pwc.h"
 
/* from pwc-dec.h */
#define PWCX_FLAG_PLANAR 0x0001
/* */
 
 
#ifdef __cplusplus
extern "C" {
#endif
 
/* The decompressor structure.
Every type of decompressor registers itself with the main module.
When a device is opened, it looks up the correct compressor, and
uses that when a compressed video mode is requested.
*/
struct pwc_decompressor
{
int type; /* type of camera (645, 680, etc) */
int table_size; /* memory needed */
 
void (* init)(int type, int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */
void (* exit)(void); /* Cleanup routine */
void (* decompress)(struct pwc_coord *image, struct pwc_coord *view, struct pwc_coord *offset,
void *src, void *dst, int flags,
void *table, int bandlength);
void (* lock)(void); /* make sure module cannot be unloaded */
void (* unlock)(void); /* release lock on module */
 
struct list_head pwcd_list;
};
 
 
/* Our structure version number. Is set to the version number major */
extern const int pwc_decompressor_version;
 
/* Adds decompressor to list, based on its 'type' field (which matches the 'type' field in pwc_device; ignores any double requests */
extern void pwc_register_decompressor(struct pwc_decompressor *pwcd);
/* Removes decompressor, based on the type number */
extern void pwc_unregister_decompressor(int type);
/* Returns pointer to decompressor struct, or NULL if it doesn't exist */
extern struct pwc_decompressor *pwc_find_decompressor(int type);
 
#ifdef CONFIG_USB_PWCX
/* If the decompressor is compiled in, we must call these manually */
extern int usb_pwcx_init(void);
extern void usb_pwcx_exit(void);
#endif
 
#ifdef __cplusplus
}
#endif
 
#endif
/shark/trunk/drivers/usb/media/pwc-ctrl.c
0,0 → 1,1747
/* Driver for Philips webcam
Functions that send various control messages to the webcam, including
video modes.
(C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
 
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
*/
 
/*
Changes
2001/08/03 Alvarado Added methods for changing white balance and
red/green gains
*/
 
/* Control functions for the cam; brightness, contrast, video mode, etc. */
 
#ifdef __KERNEL__
#include <asm/uaccess.h>
#endif
#include <asm/errno.h>
#include "pwc.h"
#include "pwc-ioctl.h"
#include "pwc-uncompress.h"
 
/* Request types: video */
#define SET_LUM_CTL 0x01
#define GET_LUM_CTL 0x02
#define SET_CHROM_CTL 0x03
#define GET_CHROM_CTL 0x04
#define SET_STATUS_CTL 0x05
#define GET_STATUS_CTL 0x06
#define SET_EP_STREAM_CTL 0x07
#define GET_EP_STREAM_CTL 0x08
#define SET_MPT_CTL 0x0D
#define GET_MPT_CTL 0x0E
 
/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
#define AGC_MODE_FORMATTER 0x2000
#define PRESET_AGC_FORMATTER 0x2100
#define SHUTTER_MODE_FORMATTER 0x2200
#define PRESET_SHUTTER_FORMATTER 0x2300
#define PRESET_CONTOUR_FORMATTER 0x2400
#define AUTO_CONTOUR_FORMATTER 0x2500
#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
#define CONTRAST_FORMATTER 0x2700
#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
#define FLICKERLESS_MODE_FORMATTER 0x2900
#define AE_CONTROL_SPEED 0x2A00
#define BRIGHTNESS_FORMATTER 0x2B00
#define GAMMA_FORMATTER 0x2C00
 
/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
#define WB_MODE_FORMATTER 0x1000
#define AWB_CONTROL_SPEED_FORMATTER 0x1100
#define AWB_CONTROL_DELAY_FORMATTER 0x1200
#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
#define COLOUR_MODE_FORMATTER 0x1500
#define SATURATION_MODE_FORMATTER1 0x1600
#define SATURATION_MODE_FORMATTER2 0x1700
 
/* Selectors for the Status controls [GS]ET_STATUS_CTL */
#define SAVE_USER_DEFAULTS_FORMATTER 0x0200
#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
#define READ_AGC_FORMATTER 0x0500
#define READ_SHUTTER_FORMATTER 0x0600
#define READ_RED_GAIN_FORMATTER 0x0700
#define READ_BLUE_GAIN_FORMATTER 0x0800
#define SENSOR_TYPE_FORMATTER1 0x0C00
#define READ_RAW_Y_MEAN_FORMATTER 0x3100
#define SET_POWER_SAVE_MODE_FORMATTER 0x3200
#define MIRROR_IMAGE_FORMATTER 0x3300
#define LED_FORMATTER 0x3400
#define SENSOR_TYPE_FORMATTER2 0x3700
 
/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
 
/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
#define PT_RELATIVE_CONTROL_FORMATTER 0x01
#define PT_RESET_CONTROL_FORMATTER 0x02
#define PT_STATUS_FORMATTER 0x03
 
static char *size2name[PSZ_MAX] =
{
"subQCIF",
"QSIF",
"QCIF",
"SIF",
"CIF",
"VGA",
};
 
/********/
 
/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
preferences, so you either get compressed or non-compressed streams.
An alternate value of 0 means this mode is not available at all.
*/
 
struct Nala_table_entry {
char alternate; /* USB alternate setting */
int compressed; /* Compressed yes/no */
 
unsigned char mode[3]; /* precomputed mode table */
};
 
static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
{
#include "pwc_nala.h"
};
 
/* This tables contains entries for the 675/680/690 (Timon) camera, with
4 different qualities (no compression, low, medium, high).
It lists the bandwidth requirements for said mode by its alternate interface
number. An alternate of 0 means that the mode is unavailable.
 
There are 6 * 4 * 4 entries:
6 different resolutions subqcif, qsif, qcif, sif, cif, vga
6 framerates: 5, 10, 15, 20, 25, 30
4 compression modi: none, low, medium, high
 
When an uncompressed mode is not available, the next available compressed mode
will be chosen (unless the decompressor is absent). Sometimes there are only
1 or 2 compressed modes available; in that case entries are duplicated.
*/
struct Timon_table_entry
{
char alternate; /* USB alternate interface */
unsigned short packetsize; /* Normal packet size */
unsigned short bandlength; /* Bandlength when decompressing */
unsigned char mode[13]; /* precomputed mode settings for cam */
};
 
static struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
{
#include "pwc_timon.h"
};
 
/* Entries for the Kiara (730/740/750) camera */
 
struct Kiara_table_entry
{
char alternate; /* USB alternate interface */
unsigned short packetsize; /* Normal packet size */
unsigned short bandlength; /* Bandlength when decompressing */
unsigned char mode[12]; /* precomputed mode settings for cam */
};
 
static struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
{
#include "pwc_kiara.h"
};
 
 
/****************************************************************************/
 
 
#define SendControlMsg(request, value, buflen) \
usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \
request, \
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
value, \
pdev->vcinterface, \
&buf, buflen, HZ / 2)
 
#define RecvControlMsg(request, value, buflen) \
usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \
request, \
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
value, \
pdev->vcinterface, \
&buf, buflen, HZ / 2)
 
 
#if PWC_DEBUG
void pwc_hexdump(void *p, int len)
{
int i;
unsigned char *s;
char buf[100], *d;
 
s = (unsigned char *)p;
d = buf;
*d = '\0';
Debug("Doing hexdump @ %p, %d bytes.\n", p, len);
for (i = 0; i < len; i++) {
d += sprintf26(d, "%02X ", *s++);
if ((i & 0xF) == 0xF) {
Debug("%s\n", buf);
d = buf;
*d = '\0';
}
}
if ((i & 0xF) != 0)
Debug("%s\n", buf);
}
#endif
 
static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen)
{
return usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
SET_EP_STREAM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
VIDEO_OUTPUT_CONTROL_FORMATTER,
index,
buf, buflen, HZ);
}
 
 
 
static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
{
unsigned char buf[3];
int ret, fps;
struct Nala_table_entry *pEntry;
int frames2frames[31] =
{ /* closest match of framerate */
0, 0, 0, 0, 4, /* 0-4 */
5, 5, 7, 7, 10, /* 5-9 */
10, 10, 12, 12, 15, /* 10-14 */
15, 15, 15, 20, 20, /* 15-19 */
20, 20, 20, 24, 24, /* 20-24 */
24, 24, 24, 24, 24, /* 25-29 */
24 /* 30 */
};
int frames2table[31] =
{ 0, 0, 0, 0, 0, /* 0-4 */
1, 1, 1, 2, 2, /* 5-9 */
3, 3, 4, 4, 4, /* 10-14 */
5, 5, 5, 5, 5, /* 15-19 */
6, 6, 6, 6, 7, /* 20-24 */
7, 7, 7, 7, 7, /* 25-29 */
7 /* 30 */
};
if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25)
return -EINVAL;
frames = frames2frames[frames];
fps = frames2table[frames];
pEntry = &Nala_table[size][fps];
if (pEntry->alternate == 0)
return -EINVAL;
 
if (pEntry->compressed && pdev->decompressor == NULL)
return -ENOENT; /* Not supported. */
 
memcpy(buf, pEntry->mode, 3);
ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
if (ret < 0) {
Debug("Failed to send video command... %d\n", ret);
return ret;
}
if (pEntry->compressed && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
 
pdev->cmd_len = 3;
memcpy(pdev->cmd_buf, buf, 3);
 
/* Set various parameters */
pdev->vframes = frames;
pdev->vsize = size;
pdev->valternate = pEntry->alternate;
pdev->image = pwc_image_sizes[size];
pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2;
if (pEntry->compressed) {
if (pdev->release < 5) { /* 4 fold compression */
pdev->vbandlength = 528;
pdev->frame_size /= 4;
}
else {
pdev->vbandlength = 704;
pdev->frame_size /= 3;
}
}
else
pdev->vbandlength = 0;
return 0;
}
 
 
static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
{
unsigned char buf[13];
struct Timon_table_entry *pChoose;
int ret, fps;
 
if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
return -EINVAL;
if (size == PSZ_VGA && frames > 15)
return -EINVAL;
fps = (frames / 5) - 1;
 
/* Find a supported framerate with progressively higher compression ratios
if the preferred ratio is not available.
*/
pChoose = NULL;
if (pdev->decompressor == NULL) {
#if PWC_DEBUG
Debug("Trying to find uncompressed mode.\n");
#endif
pChoose = &Timon_table[size][fps][0];
}
else {
while (compression <= 3) {
pChoose = &Timon_table[size][fps][compression];
if (pChoose->alternate != 0)
break;
compression++;
}
}
if (pChoose == NULL || pChoose->alternate == 0)
return -ENOENT; /* Not supported. */
 
memcpy(buf, pChoose->mode, 13);
if (snapshot)
buf[0] |= 0x80;
ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13);
if (ret < 0)
return ret;
 
if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
 
pdev->cmd_len = 13;
memcpy(pdev->cmd_buf, buf, 13);
 
/* Set various parameters */
pdev->vframes = frames;
pdev->vsize = size;
pdev->vsnapshot = snapshot;
pdev->valternate = pChoose->alternate;
pdev->image = pwc_image_sizes[size];
pdev->vbandlength = pChoose->bandlength;
if (pChoose->bandlength > 0)
pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
else
pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
return 0;
}
 
 
static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
{
struct Kiara_table_entry *pChoose = 0;
int fps, ret;
unsigned char buf[12];
struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}};
 
if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
return -EINVAL;
if (size == PSZ_VGA && frames > 15)
return -EINVAL;
fps = (frames / 5) - 1;
 
 
/* special case: VGA @ 5 fps and snapshot is raw bayer mode */
if (size == PSZ_VGA && frames == 5 && snapshot)
{
Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette);
pChoose = &RawEntry;
}
else
{
/* Find a supported framerate with progressively higher compression ratios
if the preferred ratio is not available.
Skip this step when using RAW modes.
*/
if (pdev->decompressor == NULL && pdev->vpalette != VIDEO_PALETTE_RAW) {
#if PWC_DEBUG
Debug("Trying to find uncompressed mode.\n");
#endif
pChoose = &Kiara_table[size][fps][0];
}
else {
while (compression <= 3) {
pChoose = &Kiara_table[size][fps][compression];
if (pChoose->alternate != 0)
break;
compression++;
}
}
}
if (pChoose == NULL || pChoose->alternate == 0)
return -ENOENT; /* Not supported. */
 
/* usb_control_msg won't take staticly allocated arrays as argument?? */
memcpy(buf, pChoose->mode, 12);
if (snapshot)
buf[0] |= 0x80;
 
/* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12);
if (ret < 0)
return ret;
 
if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
 
pdev->cmd_len = 12;
memcpy(pdev->cmd_buf, buf, 12);
/* All set and go */
pdev->vframes = frames;
pdev->vsize = size;
pdev->vsnapshot = snapshot;
pdev->valternate = pChoose->alternate;
pdev->image = pwc_image_sizes[size];
pdev->vbandlength = pChoose->bandlength;
if (pdev->vbandlength > 0)
pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
else
pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
return 0;
}
 
 
 
/**
@pdev: device structure
@width: viewport width
@height: viewport height
@frame: framerate, in fps
@compression: preferred compression ratio
@snapshot: snapshot mode or streaming
*/
int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot)
{
 
int ret, size;
 
Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette);
size = pwc_decode_size(pdev, width, height);
if (size < 0) {
Debug("Could not find suitable size.\n");
return -ERANGE;
}
Debug("decode_size = %d.\n", size);
 
ret = -EINVAL;
switch(pdev->type) {
case 645:
case 646:
ret = set_video_mode_Nala(pdev, size, frames);
break;
 
case 675:
case 680:
case 690:
ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot);
break;
case 720:
case 730:
case 740:
case 750:
ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot);
break;
}
if (ret < 0) {
if (ret == -ENOENT)
Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames);
else {
Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
}
return ret;
}
pdev->view.x = width;
pdev->view.y = height;
pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
pwc_set_image_buffer_size(pdev);
Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
return 0;
}
 
 
void pwc_set_image_buffer_size(struct pwc_device *pdev)
{
int i, factor = 0, filler = 0;
 
/* for PALETTE_YUV420P */
switch(pdev->vpalette)
{
case VIDEO_PALETTE_YUV420P:
factor = 6;
filler = 128;
break;
case VIDEO_PALETTE_RAW:
factor = 6; /* can be uncompressed YUV420P */
filler = 0;
break;
}
 
/* Set sizes in bytes */
pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
pdev->view.size = pdev->view.x * pdev->view.y * factor / 4;
 
/* Align offset, or you'll get some very weird results in
YUV420 mode... x must be multiple of 4 (to get the Y's in
place), and y even (or you'll mixup U & V). This is less of a
problem for YUV420P.
*/
pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
 
/* Fill buffers with gray or black */
for (i = 0; i < MAX_IMAGES; i++) {
if (pdev->image_ptr[i] != NULL)
memset(pdev->image_ptr[i], filler, pdev->view.size);
}
}
 
 
 
/* BRIGHTNESS */
 
int pwc_get_brightness(struct pwc_device *pdev)
{
char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BRIGHTNESS_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return buf << 9;
}
 
int pwc_set_brightness(struct pwc_device *pdev, int value)
{
char buf;
 
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
buf = (value >> 9) & 0x7f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BRIGHTNESS_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
/* CONTRAST */
 
int pwc_get_contrast(struct pwc_device *pdev)
{
char buf;
int ret;
 
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
CONTRAST_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return buf << 10;
}
 
int pwc_set_contrast(struct pwc_device *pdev, int value)
{
char buf;
 
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
buf = (value >> 10) & 0x3f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
CONTRAST_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
/* GAMMA */
 
int pwc_get_gamma(struct pwc_device *pdev)
{
char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
GAMMA_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return buf << 11;
}
 
int pwc_set_gamma(struct pwc_device *pdev, int value)
{
char buf;
 
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
buf = (value >> 11) & 0x1f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
GAMMA_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
 
/* SATURATION */
 
int pwc_get_saturation(struct pwc_device *pdev)
{
char buf;
int ret;
 
if (pdev->type < 675)
return -1;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return 32768 + buf * 327;
}
 
int pwc_set_saturation(struct pwc_device *pdev, int value)
{
char buf;
 
if (pdev->type < 675)
return -EINVAL;
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
/* saturation ranges from -100 to +100 */
buf = (value - 32768) / 327;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
/* AGC */
 
static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
{
char buf;
int ret;
if (mode)
buf = 0x0; /* auto */
else
buf = 0xff; /* fixed */
 
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AGC_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (!mode && ret >= 0) {
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
buf = (value >> 10) & 0x3F;
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
if (ret < 0)
return ret;
return 0;
}
 
static inline int pwc_get_agc(struct pwc_device *pdev, int *value)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AGC_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
 
if (buf != 0) { /* fixed */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
if (buf > 0x3F)
buf = 0x3F;
*value = (buf << 10);
}
else { /* auto */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
/* Gah... this value ranges from 0x00 ... 0x9F */
if (buf > 0x9F)
buf = 0x9F;
*value = -(48 + buf * 409);
}
 
return 0;
}
 
static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
{
char buf[2];
int speed, ret;
 
 
if (mode)
buf[0] = 0x0; /* auto */
else
buf[0] = 0xff; /* fixed */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SHUTTER_MODE_FORMATTER,
pdev->vcinterface,
buf, 1, HZ / 2);
 
if (!mode && ret >= 0) {
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
switch(pdev->type) {
case 675:
case 680:
case 690:
/* speed ranges from 0x0 to 0x290 (656) */
speed = (value / 100);
buf[1] = speed >> 8;
buf[0] = speed & 0xff;
break;
case 720:
case 730:
case 740:
case 750:
/* speed seems to range from 0x0 to 0xff */
buf[1] = 0;
buf[0] = value >> 8;
break;
}
 
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_SHUTTER_FORMATTER,
pdev->vcinterface,
&buf, 2, HZ / 2);
}
return ret;
}
 
 
/* POWER */
 
int pwc_camera_power(struct pwc_device *pdev, int power)
{
char buf;
 
if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
return 0; /* Not supported by Nala or Timon < release 6 */
 
if (power)
buf = 0x00; /* active */
else
buf = 0xFF; /* power save */
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_STATUS_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SET_POWER_SAVE_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
 
 
/* private calls */
 
static inline int pwc_restore_user(struct pwc_device *pdev)
{
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_STATUS_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
RESTORE_USER_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
}
 
static inline int pwc_save_user(struct pwc_device *pdev)
{
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_STATUS_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SAVE_USER_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
}
 
static inline int pwc_restore_factory(struct pwc_device *pdev)
{
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_STATUS_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
RESTORE_FACTORY_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
}
 
/* ************************************************* */
/* Patch by Alvarado: (not in the original version */
 
/*
* the camera recognizes modes from 0 to 4:
*
* 00: indoor (incandescant lighting)
* 01: outdoor (sunlight)
* 02: fluorescent lighting
* 03: manual
* 04: auto
*/
static inline int pwc_set_awb(struct pwc_device *pdev, int mode)
{
char buf;
int ret;
if (mode < 0)
mode = 0;
if (mode > 4)
mode = 4;
buf = mode & 0x07; /* just the lowest three bits */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
WB_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return 0;
}
 
static inline int pwc_get_awb(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
WB_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
 
if (ret < 0)
return ret;
return buf;
}
 
static inline int pwc_set_red_gain(struct pwc_device *pdev, int value)
{
unsigned char buf;
 
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
 
/* only the msb are considered */
buf = value >> 8;
 
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
static inline int pwc_get_red_gain(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
 
if (ret < 0)
return ret;
return (buf << 8);
}
 
 
static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value)
{
unsigned char buf;
 
if (value < 0)
value = 0;
if (value > 0xffff)
value = 0xffff;
 
/* linear mapping of 0..0xffff to -0x80..0x7f */
buf = (value >> 8);
 
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
static inline int pwc_get_blue_gain(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
 
if (ret < 0)
return ret;
return (buf << 8);
}
 
 
/* The following two functions are different, since they only read the
internal red/blue gains, which may be different from the manual
gains set or read above.
*/
static inline int pwc_read_red_gain(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
 
if (ret < 0)
return ret;
return (buf << 8);
}
 
static inline int pwc_read_blue_gain(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
 
if (ret < 0)
return ret;
return (buf << 8);
}
 
 
static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
{
unsigned char buf;
/* useful range is 0x01..0x20 */
buf = speed / 0x7f0;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_SPEED_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
static inline int pwc_get_wb_speed(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_SPEED_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return (buf * 0x7f0);
}
 
 
static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay)
{
unsigned char buf;
/* useful range is 0x01..0x3F */
buf = (delay >> 10);
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_DELAY_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
static inline int pwc_get_wb_delay(struct pwc_device *pdev)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_DELAY_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return (buf << 10);
}
 
 
int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
{
unsigned char buf[2];
 
if (pdev->type < 730)
return 0;
on_value /= 100;
off_value /= 100;
if (on_value < 0)
on_value = 0;
if (on_value > 0xff)
on_value = 0xff;
if (off_value < 0)
off_value = 0;
if (off_value > 0xff)
off_value = 0xff;
 
buf[0] = on_value;
buf[1] = off_value;
 
return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2);
}
 
int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
{
unsigned char buf[2];
int ret;
if (pdev->type < 730) {
*on_value = -1;
*off_value = -1;
return 0;
}
 
ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2);
if (ret < 0)
return ret;
*on_value = buf[0] * 100;
*off_value = buf[1] * 100;
return 0;
}
 
static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
{
unsigned char buf;
int ret;
if (contour < 0)
buf = 0xff; /* auto contour on */
else
buf = 0x0; /* auto contour off */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AUTO_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
if (contour < 0)
return 0;
if (contour > 0xffff)
contour = 0xffff;
buf = (contour >> 10); /* contour preset is [0..3f] */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return 0;
}
 
static inline int pwc_get_contour(struct pwc_device *pdev, int *contour)
{
unsigned char buf;
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AUTO_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
 
if (buf == 0) {
/* auto mode off, query current preset value */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
*contour = (buf << 10);
}
else
*contour = -1;
return 0;
}
 
 
static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight)
{
unsigned char buf;
if (backlight)
buf = 0xff;
else
buf = 0x0;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BACK_LIGHT_COMPENSATION_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
}
 
static inline int pwc_get_backlight(struct pwc_device *pdev)
{
int ret;
unsigned char buf;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BACK_LIGHT_COMPENSATION_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
return buf;
}
 
 
static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker)
{
unsigned char buf;
if (flicker)
buf = 0xff;
else
buf = 0x0;
return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
}
 
static inline int pwc_get_flicker(struct pwc_device *pdev)
{
int ret;
unsigned char buf;
ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
if (ret < 0)
return ret;
return buf;
}
 
 
static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
{
unsigned char buf;
 
if (noise < 0)
noise = 0;
if (noise > 3)
noise = 3;
buf = noise;
return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
}
 
static inline int pwc_get_dynamic_noise(struct pwc_device *pdev)
{
int ret;
unsigned char buf;
ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
if (ret < 0)
return ret;
return buf;
}
 
int pwc_mpt_reset(struct pwc_device *pdev, int flags)
{
unsigned char buf;
buf = flags & 0x03; // only lower two bits are currently used
return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1);
}
 
static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
{
unsigned char buf[4];
/* set new relative angle; angles are expressed in degrees * 100,
but cam as .5 degree resolution, hence devide by 200. Also
the angle must be multiplied by 64 before it's send to
the cam (??)
*/
pan = 64 * pan / 100;
tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */
buf[0] = pan & 0xFF;
buf[1] = (pan >> 8) & 0xFF;
buf[2] = tilt & 0xFF;
buf[3] = (tilt >> 8) & 0xFF;
return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4);
}
 
static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status)
{
int ret;
unsigned char buf[5];
ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5);
if (ret < 0)
return ret;
status->status = buf[0] & 0x7; // 3 bits are used for reporting
status->time_pan = (buf[1] << 8) + buf[2];
status->time_tilt = (buf[3] << 8) + buf[4];
return 0;
}
 
 
int pwc_get_cmos_sensor(struct pwc_device *pdev)
{
unsigned char buf;
int ret = -1, request;
if (pdev->type < 675)
request = SENSOR_TYPE_FORMATTER1;
else if (pdev->type < 730)
return -1; /* The Vesta series doesn't have this call */
else
request = SENSOR_TYPE_FORMATTER2;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
request,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0)
return ret;
if (pdev->type < 675)
return buf | 0x100;
else
return buf;
}
 
 
/* End of Add-Ons */
/* ************************************************* */
 
int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
{
int ret = 0;
 
switch(cmd) {
case VIDIOCPWCRUSER:
{
if (pwc_restore_user(pdev))
ret = -EINVAL;
break;
}
case VIDIOCPWCSUSER:
{
if (pwc_save_user(pdev))
ret = -EINVAL;
break;
}
case VIDIOCPWCFACTORY:
{
if (pwc_restore_factory(pdev))
ret = -EINVAL;
break;
}
case VIDIOCPWCSCQUAL:
{
int *qual = arg;
 
if (*qual < 0 || *qual > 3)
ret = -EINVAL;
else
ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot);
if (ret >= 0)
pdev->vcompression = *qual;
break;
}
case VIDIOCPWCGCQUAL:
{
int *qual = arg;
*qual = pdev->vcompression;
break;
}
 
case VIDIOCPWCPROBE:
{
struct pwc_probe *probe = arg;
strcpy(probe->name, pdev->vdev.name);
probe->type = pdev->type;
break;
}
 
case VIDIOCPWCSAGC:
{
int *agc = arg;
 
if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc))
ret = -EINVAL;
break;
}
case VIDIOCPWCGAGC:
{
int *agc = arg;
if (pwc_get_agc(pdev, agc))
ret = -EINVAL;
break;
}
case VIDIOCPWCSSHUTTER:
{
int *shutter_speed = arg;
 
ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed);
break;
}
case VIDIOCPWCSAWB:
{
struct pwc_whitebalance *wb = arg;
ret = pwc_set_awb(pdev, wb->mode);
if (ret >= 0 && wb->mode == PWC_WB_MANUAL) {
pwc_set_red_gain(pdev, wb->manual_red);
pwc_set_blue_gain(pdev, wb->manual_blue);
}
break;
}
 
case VIDIOCPWCGAWB:
{
struct pwc_whitebalance *wb = arg;
 
memset(wb, 0, sizeof(*wb));
wb->mode = pwc_get_awb(pdev);
if (wb->mode < 0)
ret = -EINVAL;
else {
if (wb->mode == PWC_WB_MANUAL) {
wb->manual_red = pwc_get_red_gain(pdev);
wb->manual_blue = pwc_get_blue_gain(pdev);
}
if (wb->mode == PWC_WB_AUTO) {
wb->read_red = pwc_read_red_gain(pdev);
wb->read_blue = pwc_read_blue_gain(pdev);
}
}
break;
}
case VIDIOCPWCSAWBSPEED:
{
struct pwc_wb_speed *wbs = arg;
if (wbs->control_speed > 0) {
ret = pwc_set_wb_speed(pdev, wbs->control_speed);
}
if (wbs->control_delay > 0) {
ret = pwc_set_wb_delay(pdev, wbs->control_delay);
}
break;
}
case VIDIOCPWCGAWBSPEED:
{
struct pwc_wb_speed *wbs = arg;
ret = pwc_get_wb_speed(pdev);
if (ret < 0)
break;
wbs->control_speed = ret;
ret = pwc_get_wb_delay(pdev);
if (ret < 0)
break;
wbs->control_delay = ret;
break;
}
 
case VIDIOCPWCSLED:
{
struct pwc_leds *leds = arg;
 
ret = pwc_set_leds(pdev, leds->led_on, leds->led_off);
break;
}
 
 
case VIDIOCPWCGLED:
{
struct pwc_leds *leds = arg;
ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off);
break;
}
 
case VIDIOCPWCSCONTOUR:
{
int *contour = arg;
 
ret = pwc_set_contour(pdev, *contour);
break;
}
case VIDIOCPWCGCONTOUR:
{
int *contour = arg;
ret = pwc_get_contour(pdev, contour);
break;
}
case VIDIOCPWCSBACKLIGHT:
{
int *backlight = arg;
ret = pwc_set_backlight(pdev, *backlight);
break;
}
 
case VIDIOCPWCGBACKLIGHT:
{
int *backlight = arg;
ret = pwc_get_backlight(pdev);
if (ret >= 0)
*backlight = ret;
break;
}
case VIDIOCPWCSFLICKER:
{
int *flicker = arg;
ret = pwc_set_flicker(pdev, *flicker);
break;
}
 
case VIDIOCPWCGFLICKER:
{
int *flicker = arg;
ret = pwc_get_flicker(pdev);
if (ret >= 0)
*flicker = ret;
break;
}
case VIDIOCPWCSDYNNOISE:
{
int *dynnoise = arg;
ret = pwc_set_dynamic_noise(pdev, *dynnoise);
break;
}
case VIDIOCPWCGDYNNOISE:
{
int *dynnoise = arg;
 
ret = pwc_get_dynamic_noise(pdev);
if (ret < 0)
break;
*dynnoise = ret;
break;
}
 
case VIDIOCPWCGREALSIZE:
{
struct pwc_imagesize *size = arg;
size->width = pdev->image.x;
size->height = pdev->image.y;
break;
}
case VIDIOCPWCMPTRESET:
{
int *flags = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT)
{
ret = pwc_mpt_reset(pdev, *flags);
if (ret >= 0)
{
pdev->pan_angle = 0;
pdev->tilt_angle = 0;
}
}
else
{
ret = -ENXIO;
}
break;
}
case VIDIOCPWCMPTGRANGE:
{
if (pdev->features & FEATURE_MOTOR_PANTILT)
{
memcpy(arg, &pdev->angle_range, sizeof(struct pwc_mpt_range));
}
else
{
ret = -ENXIO;
}
break;
}
case VIDIOCPWCMPTSANGLE:
{
struct pwc_mpt_angles *angles = arg;
int new_pan, new_tilt;
if (pdev->features & FEATURE_MOTOR_PANTILT)
{
/* The camera can only set relative angles, so
do some calculations when getting an absolute angle .
*/
if (angles->absolute)
{
new_pan = angles->pan;
new_tilt = angles->tilt;
}
else
{
new_pan = pdev->pan_angle + angles->pan;
new_tilt = pdev->tilt_angle + angles->tilt;
}
/* check absolute ranges */
if (new_pan < pdev->angle_range.pan_min ||
new_pan > pdev->angle_range.pan_max ||
new_tilt < pdev->angle_range.tilt_min ||
new_tilt > pdev->angle_range.tilt_max)
{
ret = -ERANGE;
}
else
{
/* go to relative range, check again */
new_pan -= pdev->pan_angle;
new_tilt -= pdev->tilt_angle;
/* angles are specified in degrees * 100, thus the limit = 36000 */
if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000)
ret = -ERANGE;
}
if (ret == 0) /* no errors so far */
{
ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt);
if (ret >= 0)
{
pdev->pan_angle += new_pan;
pdev->tilt_angle += new_tilt;
}
if (ret == -EPIPE) /* stall -> out of range */
ret = -ERANGE;
}
}
else
{
ret = -ENXIO;
}
break;
}
 
case VIDIOCPWCMPTGANGLE:
{
struct pwc_mpt_angles *angles = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT)
{
angles->absolute = 1;
angles->pan = pdev->pan_angle;
angles->tilt = pdev->tilt_angle;
}
else
{
ret = -ENXIO;
}
break;
}
case VIDIOCPWCMPTSTATUS:
{
struct pwc_mpt_status *status = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT)
{
ret = pwc_mpt_get_status(pdev, status);
}
else
{
ret = -ENXIO;
}
break;
}
 
case VIDIOCPWCGVIDCMD:
{
struct pwc_video_command cmd;
cmd.type = pdev->type;
cmd.release = pdev->release;
cmd.command_len = pdev->cmd_len;
memcpy(cmd.command_buf, pdev->cmd_buf, pdev->cmd_len);
cmd.bandlength = pdev->vbandlength;
cmd.frame_size = pdev->frame_size;
 
if (copy_to_user(arg, &cmd, sizeof(cmd)))
ret = -EFAULT;
break;
}
 
default:
ret = -ENOIOCTLCMD;
break;
}
if (ret > 0)
return 0;
return ret;
}
 
 
 
/shark/trunk/drivers/usb/serial/console.c
0,0 → 1,265
/*
* USB Serial Console driver
*
* Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* Thanks to Randy Dunlap for the original version of this code.
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/usb.h>
 
static int debug;
 
#include "usb-serial.h"
 
struct usbcons_info {
int magic;
int break_flag;
struct usb_serial_port *port;
};
 
static struct usbcons_info usbcons_info;
static struct console usbcons;
 
/*
* ------------------------------------------------------------
* USB Serial console driver
*
* Much of the code here is copied from drivers/char/serial.c
* and implements a phony serial console in the same way that
* serial.c does so that in case some software queries it,
* it will get the same results.
*
* Things that are different from the way the serial port code
* does things, is that we call the lower level usb-serial
* driver code to initialize the device, and we set the initial
* console speeds based on the command line arguments.
* ------------------------------------------------------------
*/
 
 
/*
* The parsing of the command line works exactly like the
* serial.c code, except that the specifier is "ttyUSB" instead
* of "ttyS".
*/
static int __init usb_console_setup(struct console *co, char *options)
{
struct usbcons_info *info = &usbcons_info;
int baud = 9600;
int bits = 8;
int parity = 'n';
int doflow = 0;
int cflag = CREAD | HUPCL | CLOCAL;
char *s;
struct usb_serial *serial;
struct usb_serial_port *port;
int retval = 0;
struct tty_struct *tty;
struct termios *termios;
 
dbg ("%s", __FUNCTION__);
 
if (options) {
baud = simple_strtoul(options, NULL, 10);
s = options;
while (*s >= '0' && *s <= '9')
s++;
if (*s)
parity = *s++;
if (*s)
bits = *s++ - '0';
if (*s)
doflow = (*s++ == 'r');
}
 
/* build a cflag setting */
switch (baud) {
case 1200:
cflag |= B1200;
break;
case 2400:
cflag |= B2400;
break;
case 4800:
cflag |= B4800;
break;
case 19200:
cflag |= B19200;
break;
case 38400:
cflag |= B38400;
break;
case 57600:
cflag |= B57600;
break;
case 115200:
cflag |= B115200;
break;
case 9600:
default:
cflag |= B9600;
/*
* Set this to a sane value to prevent a divide error
*/
baud = 9600;
break;
}
switch (bits) {
case 7:
cflag |= CS7;
break;
default:
case 8:
cflag |= CS8;
break;
}
switch (parity) {
case 'o': case 'O':
cflag |= PARODD;
break;
case 'e': case 'E':
cflag |= PARENB;
break;
}
co->cflag = cflag;
 
/* grab the first serial port that happens to be connected */
serial = usb_serial_get_by_index(0);
if (serial_paranoia_check (serial, __FUNCTION__)) {
/* no device is connected yet, sorry :( */
err ("No USB device connected to ttyUSB0");
return -ENODEV;
}
 
port = serial->port[0];
port->tty = NULL;
 
info->port = port;
++port->open_count;
if (port->open_count == 1) {
/* only call the device specific open if this
* is the first time the port is opened */
if (serial->type->open)
retval = serial->type->open(port, NULL);
else
retval = usb_serial_generic_open(port, NULL);
if (retval)
port->open_count = 0;
}
 
if (retval) {
err ("could not open USB console port");
return retval;
}
 
if (serial->type->set_termios) {
/* build up a fake tty structure so that the open call has something
* to look at to get the cflag value */
tty = kmalloc (sizeof (*tty), GFP_KERNEL);
if (!tty) {
err ("no more memory");
return -ENOMEM;
}
termios = kmalloc (sizeof (*termios), GFP_KERNEL);
if (!termios) {
err ("no more memory");
kfree (tty);
return -ENOMEM;
}
memset (tty, 0x00, sizeof(*tty));
memset (termios, 0x00, sizeof(*termios));
termios->c_cflag = cflag;
tty->termios = termios;
port->tty = tty;
 
/* set up the initial termios settings */
serial->type->set_termios(port, NULL);
port->tty = NULL;
kfree (termios);
kfree (tty);
}
 
return retval;
}
 
static void usb_console_write(struct console *co, const char *buf, unsigned count)
{
static struct usbcons_info *info = &usbcons_info;
struct usb_serial_port *port = info->port;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -ENODEV;
 
if (!serial || !port)
return;
 
if (count == 0)
return;
 
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
 
if (!port->open_count) {
dbg ("%s - port not opened", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function if it is available */
if (serial->type->write)
retval = serial->type->write(port, 0, buf, count);
else
retval = usb_serial_generic_write(port, 0, buf, count);
 
exit:
dbg("%s - return value (if we had one): %d", __FUNCTION__, retval);
}
 
static struct console usbcons = {
.name = "ttyUSB",
.write = usb_console_write,
.setup = usb_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
 
void usb_serial_console_init (int serial_debug, int minor)
{
debug = serial_debug;
 
if (minor == 0) {
/*
* Call register_console() if this is the first device plugged
* in. If we call it earlier, then the callback to
* console_setup() will fail, as there is not a device seen by
* the USB subsystem yet.
*/
/*
* Register console.
* NOTES:
* console_setup() is called (back) immediately (from register_console).
* console_write() is called immediately from register_console iff
* CON_PRINTBUFFER is set in flags.
*/
dbg ("registering the USB serial console.");
register_console(&usbcons);
}
}
 
void usb_serial_console_exit (void)
{
unregister_console(&usbcons);
}
 
/shark/trunk/drivers/usb/serial/pl2303.c
0,0 → 1,818
/*
* Prolific PL2303 USB to serial adaptor driver
*
* Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2003 IBM Corp.
*
* Original driver for 2.2.x by anonymous
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
* 2002_Mar_26 gkh
* allowed driver to work properly if there is no tty assigned to a port
* (this happens for serial console devices.)
*
* 2001_Oct_06 gkh
* Added RTS and DTR line control. Thanks to joe@bndlg.de for parts of it.
*
* 2001_Sep_19 gkh
* Added break support.
*
* 2001_Aug_30 gkh
* fixed oops in write_bulk_callback.
*
* 2001_Aug_28 gkh
* reworked buffer logic to be like other usb-serial drivers. Hopefully
* removing some reported problems.
*
* 2001_Jun_06 gkh
* finished porting to 2.4 format.
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
 
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
static int debug;
#endif
 
#include "usb-serial.h"
#include "pl2303.h"
 
/*
* Version Information
*/
#define DRIVER_VERSION "v0.10"
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
 
 
 
static struct usb_device_id id_table [] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
{ } /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, id_table);
 
static struct usb_driver pl2303_driver = {
.owner = THIS_MODULE,
.name = "pl2303",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
};
 
#define SET_LINE_REQUEST_TYPE 0x21
#define SET_LINE_REQUEST 0x20
 
#define SET_CONTROL_REQUEST_TYPE 0x21
#define SET_CONTROL_REQUEST 0x22
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
 
#define BREAK_REQUEST_TYPE 0x21
#define BREAK_REQUEST 0x23
#define BREAK_ON 0xffff
#define BREAK_OFF 0x0000
 
#define GET_LINE_REQUEST_TYPE 0xa1
#define GET_LINE_REQUEST 0x21
 
#define VENDOR_WRITE_REQUEST_TYPE 0x40
#define VENDOR_WRITE_REQUEST 0x01
 
#define VENDOR_READ_REQUEST_TYPE 0xc0
#define VENDOR_READ_REQUEST 0x01
 
#define UART_STATE 0x08
#define UART_DCD 0x01
#define UART_DSR 0x02
#define UART_BREAK_ERROR 0x04
#define UART_RING 0x08
#define UART_FRAME_ERROR 0x10
#define UART_PARITY_ERROR 0x20
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
 
/* function prototypes for a PL2303 serial converter */
static int pl2303_open (struct usb_serial_port *port, struct file *filp);
static void pl2303_close (struct usb_serial_port *port, struct file *filp);
static void pl2303_set_termios (struct usb_serial_port *port,
struct termios *old);
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file,
unsigned int cmd, unsigned long arg);
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static int pl2303_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count);
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear);
static int pl2303_startup (struct usb_serial *serial);
static void pl2303_shutdown (struct usb_serial *serial);
 
 
/* All of the device info needed for the PL2303 SIO serial converter */
static struct usb_serial_device_type pl2303_device = {
.owner = THIS_MODULE,
.name = "PL-2303",
.id_table = id_table,
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = 1,
.num_bulk_out = 1,
.num_ports = 1,
.open = pl2303_open,
.close = pl2303_close,
.write = pl2303_write,
.ioctl = pl2303_ioctl,
.break_ctl = pl2303_break_ctl,
.set_termios = pl2303_set_termios,
.tiocmget = pl2303_tiocmget,
.tiocmset = pl2303_tiocmset,
.read_bulk_callback = pl2303_read_bulk_callback,
.read_int_callback = pl2303_read_int_callback,
.write_bulk_callback = pl2303_write_bulk_callback,
.attach = pl2303_startup,
.shutdown = pl2303_shutdown,
};
 
struct pl2303_private {
spinlock_t lock;
u8 line_control;
u8 line_status;
u8 termios_initialized;
};
 
 
static int pl2303_startup (struct usb_serial *serial)
{
struct pl2303_private *priv;
int i;
 
for (i = 0; i < serial->num_ports; ++i) {
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock);
usb_set_serial_port_data(serial->port[i], priv);
}
return 0;
}
 
static int set_control_lines (struct usb_device *dev, u8 value)
{
int retval;
retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
value, 0, NULL, 0, 100);
dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval);
return retval;
}
 
static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
int result;
 
dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
 
if (port->write_urb->status == -EINPROGRESS) {
dbg("%s - already writing", __FUNCTION__);
return 0;
}
 
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
if (from_user) {
if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
return -EFAULT;
} else {
memcpy (port->write_urb->transfer_buffer, buf, count);
}
usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
port->write_urb->transfer_buffer_length = count;
port->write_urb->dev = port->serial->dev;
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
else
result = count;
 
return result;
}
 
 
 
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
struct usb_serial *serial = port->serial;
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int cflag;
unsigned char *buf;
int baud;
int i;
u8 control;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if ((!port->tty) || (!port->tty->termios)) {
dbg("%s - no tty structures", __FUNCTION__);
return;
}
 
spin_lock_irqsave(&priv->lock, flags);
 
if (!priv->termios_initialized) {
printk(KERN_INFO "@####### termios initializated\n");
*(port->tty->termios) = tty_std_termios;
port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
priv->termios_initialized = 1;
}
 
spin_unlock_irqrestore(&priv->lock, flags);
 
cflag = port->tty->termios->c_cflag;
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
dbg("%s - nothing to change...", __FUNCTION__);
return;
}
}
 
buf = kmalloc (7, GFP_KERNEL);
if (!buf) {
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
return;
}
memset (buf, 0x00, 0x07);
i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
 
if (cflag & CSIZE) {
switch (cflag & CSIZE) {
case CS5: buf[6] = 5; break;
case CS6: buf[6] = 6; break;
case CS7: buf[6] = 7; break;
default:
case CS8: buf[6] = 8; break;
}
dbg("%s - data bits = %d", __FUNCTION__, buf[6]);
}
 
baud = 0;
switch (cflag & CBAUD) {
case B0: baud = 0; break;
case B75: baud = 75; break;
case B150: baud = 150; break;
case B300: baud = 300; break;
case B600: baud = 600; break;
case B1200: baud = 1200; break;
case B1800: baud = 1800; break;
case B2400: baud = 2400; break;
case B4800: baud = 4800; break;
case B9600: baud = 9600; break;
case B19200: baud = 19200; break;
case B38400: baud = 38400; break;
case B57600: baud = 57600; break;
case B115200: baud = 115200; break;
case B230400: baud = 230400; break;
case B460800: baud = 460800; break;
default:
dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n");
break;
}
dbg("%s - baud = %d", __FUNCTION__, baud);
if (baud) {
buf[0] = baud & 0xff;
buf[1] = (baud >> 8) & 0xff;
buf[2] = (baud >> 16) & 0xff;
buf[3] = (baud >> 24) & 0xff;
}
 
/* For reference buf[4]=0 is 1 stop bits */
/* For reference buf[4]=1 is 1.5 stop bits */
/* For reference buf[4]=2 is 2 stop bits */
if (cflag & CSTOPB) {
buf[4] = 2;
dbg("%s - stop bits = 2", __FUNCTION__);
} else {
buf[4] = 0;
dbg("%s - stop bits = 1", __FUNCTION__);
}
 
if (cflag & PARENB) {
/* For reference buf[5]=0 is none parity */
/* For reference buf[5]=1 is odd parity */
/* For reference buf[5]=2 is even parity */
/* For reference buf[5]=3 is mark parity */
/* For reference buf[5]=4 is space parity */
if (cflag & PARODD) {
buf[5] = 1;
dbg("%s - parity = odd", __FUNCTION__);
} else {
buf[5] = 2;
dbg("%s - parity = even", __FUNCTION__);
}
} else {
buf[5] = 0;
dbg("%s - parity = none", __FUNCTION__);
}
 
i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
dbg ("0x21:0x20:0:0 %d", i);
 
/* change control lines if we are switching to or from B0 */
spin_lock_irqsave(&priv->lock, flags);
control = priv->line_control;
if ((cflag & CBAUD) == B0)
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
else
priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
if (control != priv->line_control) {
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
set_control_lines(serial->dev, control);
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
 
i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
if (cflag & CRTSCTS) {
i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
0x0, 0x41, NULL, 0, 100);
dbg ("0x40:0x1:0x0:0x41 %d", i);
}
 
kfree (buf);
}
 
 
static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{
struct termios tmp_termios;
struct usb_serial *serial = port->serial;
unsigned char buf[10];
int result;
 
if (port_paranoia_check (port, __FUNCTION__))
return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
 
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
 
#define FISH(a,b,c,d) \
result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \
b, a, c, d, buf, 1, 100); \
dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]);
 
#define SOUP(a,b,c,d) \
result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \
b, a, c, d, NULL, 0, 100); \
dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result);
 
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
 
/* Setup termios */
if (port->tty) {
printk(KERN_INFO "#####@ set termios");
pl2303_set_termios (port, &tmp_termios);
}
 
//FIXME: need to assert RTS and DTR if CRTSCTS off
 
dbg("%s - submitting read urb", __FUNCTION__);
port->read_urb->dev = serial->dev;
result = usb_submit_urb (port->read_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
pl2303_close (port, NULL);
return -EPROTO;
}
 
dbg("%s - submitting interrupt urb", __FUNCTION__);
port->interrupt_in_urb->dev = serial->dev;
result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result);
pl2303_close (port, NULL);
return -EPROTO;
}
return 0;
}
 
 
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial;
struct pl2303_private *priv;
unsigned long flags;
unsigned int c_cflag;
int result;
 
if (port_paranoia_check (port, __FUNCTION__))
return;
serial = get_usb_serial (port, __FUNCTION__);
if (!serial)
return;
dbg("%s - port %d", __FUNCTION__, port->number);
 
/* shutdown our urbs */
dbg("%s - shutting down urbs", __FUNCTION__);
result = usb_unlink_urb (port->write_urb);
if (result)
dbg("%s - usb_unlink_urb (write_urb)"
" failed with reason: %d", __FUNCTION__,
result);
 
result = usb_unlink_urb (port->read_urb);
if (result)
dbg("%s - usb_unlink_urb (read_urb) "
"failed with reason: %d", __FUNCTION__,
result);
 
result = usb_unlink_urb (port->interrupt_in_urb);
if (result)
dbg("%s - usb_unlink_urb (interrupt_in_urb)"
" failed with reason: %d", __FUNCTION__,
result);
 
if (port->tty) {
c_cflag = port->tty->termios->c_cflag;
if (c_cflag & HUPCL) {
/* drop DTR and RTS */
priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
priv->line_control = 0;
spin_unlock_irqrestore (&priv->lock, flags);
set_control_lines (port->serial->dev, 0);
}
}
 
}
 
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
 
spin_lock_irqsave (&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= CONTROL_RTS;
if (set & TIOCM_DTR)
priv->line_control |= CONTROL_DTR;
if (clear & TIOCM_RTS)
priv->line_control &= ~CONTROL_RTS;
if (clear & TIOCM_DTR)
priv->line_control &= ~CONTROL_DTR;
control = priv->line_control;
spin_unlock_irqrestore (&priv->lock, flags);
 
return set_control_lines (port->serial->dev, control);
}
 
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int mcr;
unsigned int status;
unsigned int result;
 
dbg("%s (%d)", __FUNCTION__, port->number);
 
spin_lock_irqsave (&priv->lock, flags);
mcr = priv->line_control;
status = priv->line_status;
spin_unlock_irqrestore (&priv->lock, flags);
 
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
| ((status & UART_CTS) ? TIOCM_CTS : 0)
| ((status & UART_DSR) ? TIOCM_DSR : 0)
| ((status & UART_RING) ? TIOCM_RI : 0)
| ((status & UART_DCD) ? TIOCM_CD : 0);
 
dbg("%s - result = %x", __FUNCTION__, result);
 
return result;
}
 
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
{
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
 
switch (cmd) {
default:
dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
break;
}
 
return -ENOIOCTLCMD;
}
 
static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
{
struct usb_serial *serial = port->serial;
u16 state;
int result;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (break_state == 0)
state = BREAK_OFF;
else
state = BREAK_ON;
dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__);
 
result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
0, NULL, 0, 100);
if (result)
dbg("%s - error sending break = %d", __FUNCTION__, result);
}
 
 
static void pl2303_shutdown (struct usb_serial *serial)
{
int i;
 
dbg("%s", __FUNCTION__);
 
for (i = 0; i < serial->num_ports; ++i) {
kfree (usb_get_serial_port_data(serial->port[i]));
usb_set_serial_port_data(serial->port[i], NULL);
}
}
 
 
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
int status;
 
dbg("%s (%d)", __FUNCTION__, port->number);
 
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
goto exit;
}
 
if (!serial) {
return;
}
 
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
 
if (urb->actual_length < UART_STATE)
goto exit;
 
/* Save off the uart status for others to look at */
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = data[UART_STATE];
spin_unlock_irqrestore(&priv->lock, flags);
exit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
__FUNCTION__, status);
}
 
 
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
struct pl2303_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
int i;
int result;
u8 status;
char tty_flag;
 
if (port_paranoia_check (port, __FUNCTION__))
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
}
 
 
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
if (!port->open_count) {
dbg("%s - port is closed, exiting.", __FUNCTION__);
return;
}
if (urb->status == -EPROTO) {
/* PL2303 mysteriously fails with -EPROTO reschedule the read */
dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
urb->status = 0;
urb->dev = serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
return;
}
dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
return;
}
 
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
 
/* get tty_flag from status */
tty_flag = TTY_NORMAL;
 
spin_lock_irqsave(&priv->lock, flags);
status = priv->line_status;
spin_unlock_irqrestore(&priv->lock, flags);
 
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
if (status & UART_BREAK_ERROR )
tty_flag = TTY_BREAK;
else if (status & UART_PARITY_ERROR)
tty_flag = TTY_PARITY;
else if (status & UART_FRAME_ERROR)
tty_flag = TTY_FRAME;
dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
 
tty = port->tty;
if (tty && urb->actual_length) {
/* overrun is special, not associated with a char */
if (status & UART_OVERRUN_ERROR)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
for (i = 0; i < urb->actual_length; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
}
//**#ifdef DEBUG_ME
tty_insert_flip_char (tty, data[i], tty_flag);
//**#endif /* DEBUG_ME */
}
tty_flip_buffer_push (tty);
}
 
 
/* Schedule the next read _if_ we are still open */
if (port->open_count) {
urb->dev = serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
}
 
 
return;
}
 
 
 
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
int result;
 
if (port_paranoia_check (port, __FUNCTION__))
return;
dbg("%s - port %d", __FUNCTION__, port->number);
if (urb->status) {
/* error in the urb, so we have to resubmit it */
if (serial_paranoia_check (port->serial, __FUNCTION__)) {
return;
}
dbg("%s - Overflow in write", __FUNCTION__);
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
port->write_urb->transfer_buffer_length = 1;
port->write_urb->dev = port->serial->dev;
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
 
return;
}
 
schedule_work(&port->work);
}
 
 
/*static*/ int __init pl2303_init (void)
{
int retval;
retval = usb_serial_register(&pl2303_device);
if (retval)
goto failed_usb_serial_register;
retval = usb_register(&pl2303_driver);
if (retval)
goto failed_usb_register;
info(DRIVER_DESC " " DRIVER_VERSION);
return 0;
failed_usb_register:
usb_serial_deregister(&pl2303_device);
failed_usb_serial_register:
return retval;
}
 
 
/*static*/ void __exit pl2303_exit (void)
{
usb_deregister (&pl2303_driver);
usb_serial_deregister (&pl2303_device);
}
 
 
module_init(pl2303_init);
module_exit(pl2303_exit);
 
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
 
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
 
/shark/trunk/drivers/usb/serial/usb-serial.c
0,0 → 1,1476
/*
* USB Serial Converter driver
*
* Copyright (C) 1999 - 2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2000 Peter Berger (pberger@brimson.com)
* Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This driver was originally based on the ACM driver by Armin Fuerst (which was
* based on a driver by Brad Keryan)
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
* (12/10/2002) gkh
* Split the ports off into their own struct device, and added a
* usb-serial bus driver.
*
* (11/19/2002) gkh
* removed a few #ifdefs for the generic code and cleaned up the failure
* logic in initialization.
*
* (10/02/2002) gkh
* moved the console code to console.c and out of this file.
*
* (06/05/2002) gkh
* moved location of startup() call in serial_probe() until after all
* of the port information and endpoints are initialized. This makes
* things easier for some drivers.
*
* (04/10/2002) gkh
* added serial_read_proc function which creates a
* /proc/tty/driver/usb-serial file.
*
* (03/27/2002) gkh
* Got USB serial console code working properly and merged into the main
* version of the tree. Thanks to Randy Dunlap for the initial version
* of this code, and for pushing me to finish it up.
* The USB serial console works with any usb serial driver device.
*
* (03/21/2002) gkh
* Moved all manipulation of port->open_count into the core. Now the
* individual driver's open and close functions are called only when the
* first open() and last close() is called. Making the drivers a bit
* smaller and simpler.
* Fixed a bug if a driver didn't have the owner field set.
*
* (02/26/2002) gkh
* Moved all locking into the main serial_* functions, instead of having
* the individual drivers have to grab the port semaphore. This should
* reduce races.
* Reworked the MOD_INC logic a bit to always increment and decrement, even
* if the generic driver is being used.
*
* (10/10/2001) gkh
* usb_serial_disconnect() now sets the serial->dev pointer is to NULL to
* help prevent child drivers from accessing the device since it is now
* gone.
*
* (09/13/2001) gkh
* Moved generic driver initialize after we have registered with the USB
* core. Thanks to Randy Dunlap for pointing this problem out.
*
* (07/03/2001) gkh
* Fixed module paramater size. Thanks to John Brockmeyer for the pointer.
* Fixed vendor and product getting defined through the MODULE_PARM macro
* if the Generic driver wasn't compiled in.
* Fixed problem with generic_shutdown() not being called for drivers that
* don't have a shutdown() function.
*
* (06/06/2001) gkh
* added evil hack that is needed for the prolific pl2303 device due to the
* crazy way its endpoints are set up.
*
* (05/30/2001) gkh
* switched from using spinlock to a semaphore, which fixes lots of problems.
*
* (04/08/2001) gb
* Identify version on module load.
*
* 2001_02_05 gkh
* Fixed buffer overflows bug with the generic serial driver. Thanks to
* Todd Squires <squirest@ct0.com> for fixing this.
*
* (01/10/2001) gkh
* Fixed bug where the generic serial adaptor grabbed _any_ device that was
* offered to it.
*
* (12/12/2000) gkh
* Removed MOD_INC and MOD_DEC from poll and disconnect functions, and
* moved them to the serial_open and serial_close functions.
* Also fixed bug with there not being a MOD_DEC for the generic driver
* (thanks to Gary Brubaker for finding this.)
*
* (11/29/2000) gkh
* Small NULL pointer initialization cleanup which saves a bit of disk image
*
* (11/01/2000) Adam J. Richter
* instead of using idVendor/idProduct pairs, usb serial drivers
* now identify their hardware interest with usb_device_id tables,
* which they usually have anyhow for use with MODULE_DEVICE_TABLE.
*
* (10/05/2000) gkh
* Fixed bug with urb->dev not being set properly, now that the usb
* core needs it.
*
* (09/11/2000) gkh
* Removed DEBUG #ifdefs with call to usb_serial_debug_data
*
* (08/28/2000) gkh
* Added port_lock to port structure.
* Added locks for SMP safeness to generic driver
* Fixed the ability to open a generic device's port more than once.
*
* (07/23/2000) gkh
* Added bulk_out_endpointAddress to port structure.
*
* (07/19/2000) gkh, pberger, and borchers
* Modifications to allow usb-serial drivers to be modules.
*
* (07/03/2000) gkh
* Added more debugging to serial_ioctl call
*
* (06/25/2000) gkh
* Changed generic_write_bulk_callback to not call wake_up_interruptible
* directly, but to have port_softint do it at a safer time.
*
* (06/23/2000) gkh
* Cleaned up debugging statements in a quest to find UHCI timeout bug.
*
* (05/22/2000) gkh
* Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be
* removed from the individual device source files.
*
* (05/03/2000) gkh
* Added the Digi Acceleport driver from Al Borchers and Peter Berger.
*
* (05/02/2000) gkh
* Changed devfs and tty register code to work properly now. This was based on
* the ACM driver changes by Vojtech Pavlik.
*
* (04/27/2000) Ryan VanderBijl
* Put calls to *_paranoia_checks into one function.
*
* (04/23/2000) gkh
* Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports.
* Moved when the startup code printed out the devices that are supported.
*
* (04/19/2000) gkh
* Added driver for ZyXEL omni.net lcd plus ISDN TA
* Made startup info message specify which drivers were compiled in.
*
* (04/03/2000) gkh
* Changed the probe process to remove the module unload races.
* Changed where the tty layer gets initialized to have devfs work nicer.
* Added initial devfs support.
*
* (03/26/2000) gkh
* Split driver up into device specific pieces.
*
* (03/19/2000) gkh
* Fixed oops that could happen when device was removed while a program
* was talking to the device.
* Removed the static urbs and now all urbs are created and destroyed
* dynamically.
* Reworked the internal interface. Now everything is based on the
* usb_serial_port structure instead of the larger usb_serial structure.
* This fixes the bug that a multiport device could not have more than
* one port open at one time.
*
* (03/17/2000) gkh
* Added config option for debugging messages.
* Added patch for keyspan pda from Brian Warner.
*
* (03/06/2000) gkh
* Added the keyspan pda code from Brian Warner <warner@lothar.com>
* Moved a bunch of the port specific stuff into its own structure. This
* is in anticipation of the true multiport devices (there's a bug if you
* try to access more than one port of any multiport device right now)
*
* (02/21/2000) gkh
* Made it so that any serial devices only have to specify which functions
* they want to overload from the generic function calls (great,
* inheritance in C, in a driver, just what I wanted...)
* Added support for set_termios and ioctl function calls. No drivers take
* advantage of this yet.
* Removed the #ifdef MODULE, now there is no module specific code.
* Cleaned up a few comments in usb-serial.h that were wrong (thanks again
* to Miles Lott).
* Small fix to get_free_serial.
*
* (02/14/2000) gkh
* Removed the Belkin and Peracom functionality from the driver due to
* the lack of support from the vendor, and me not wanting people to
* accidenatly buy the device, expecting it to work with Linux.
* Added read_bulk_callback and write_bulk_callback to the type structure
* for the needs of the FTDI and WhiteHEAT driver.
* Changed all reverences to FTDI to FTDI_SIO at the request of Bill
* Ryder.
* Changed the output urb size back to the max endpoint size to make
* the ftdi_sio driver have it easier, and due to the fact that it didn't
* really increase the speed any.
*
* (02/11/2000) gkh
* Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
* patch from Miles Lott (milos@insync.net).
* Fixed bug with not restoring the minor range that a device grabs, if
* the startup function fails (thanks Miles for finding this).
*
* (02/05/2000) gkh
* Added initial framework for the Keyspan PDA serial converter so that
* Brian Warner has a place to put his code.
* Made the ezusb specific functions generic enough that different
* devices can use them (whiteheat and keyspan_pda both need them).
* Split out a whole bunch of structure and other stuff to a separate
* usb-serial.h file.
* Made the Visor connection messages a little more understandable, now
* that Miles Lott (milos@insync.net) has gotten the Generic channel to
* work. Also made them always show up in the log file.
*
* (01/25/2000) gkh
* Added initial framework for FTDI serial converter so that Bill Ryder
* has a place to put his code.
* Added the vendor specific info from Handspring. Now we can print out
* informational debug messages as well as understand what is happening.
*
* (01/23/2000) gkh
* Fixed problem of crash when trying to open a port that didn't have a
* device assigned to it. Made the minor node finding a little smarter,
* now it looks to find a continuous space for the new device.
*
* (01/21/2000) gkh
* Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
* Fixed get_serial_by_minor which was all messed up for multi port
* devices. Fixed multi port problem for generic devices. Now the number
* of ports is determined by the number of bulk out endpoints for the
* generic device.
*
* (01/19/2000) gkh
* Removed lots of cruft that was around from the old (pre urb) driver
* interface.
* Made the serial_table dynamic. This should save lots of memory when
* the number of minor nodes goes up to 256.
* Added initial support for devices that have more than one port.
* Added more debugging comments for the Visor, and added a needed
* set_configuration call.
*
* (01/17/2000) gkh
* Fixed the WhiteHEAT firmware (my processing tool had a bug)
* and added new debug loader firmware for it.
* Removed the put_char function as it isn't really needed.
* Added visor startup commands as found by the Win98 dump.
*
* (01/13/2000) gkh
* Fixed the vendor id for the generic driver to the one I meant it to be.
*
* (01/12/2000) gkh
* Forget the version numbering...that's pretty useless...
* Made the driver able to be compiled so that the user can select which
* converter they want to use. This allows people who only want the Visor
* support to not pay the memory size price of the WhiteHEAT.
* Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
* grabbed the root hub. Not good.
*
* version 0.4.0 (01/10/2000) gkh
* Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
* device. Added startup function to allow firmware to be downloaded to
* a device if it needs to be.
* Added firmware download logic to the WhiteHEAT device.
* Started to add #defines to split up the different drivers for potential
* configuration option.
*
* version 0.3.1 (12/30/99) gkh
* Fixed problems with urb for bulk out.
* Added initial support for multiple sets of endpoints. This enables
* the Handspring Visor to be attached successfully. Only the first
* bulk in / bulk out endpoint pair is being used right now.
*
* version 0.3.0 (12/27/99) gkh
* Added initial support for the Handspring Visor based on a patch from
* Miles Lott (milos@sneety.insync.net)
* Cleaned up the code a bunch and converted over to using urbs only.
*
* version 0.2.3 (12/21/99) gkh
* Added initial support for the Connect Tech WhiteHEAT converter.
* Incremented the number of ports in expectation of getting the
* WhiteHEAT to work properly (4 ports per connection).
* Added notification on insertion and removal of what port the
* device is/was connected to (and what kind of device it was).
*
* version 0.2.2 (12/16/99) gkh
* Changed major number to the new allocated number. We're legal now!
*
* version 0.2.1 (12/14/99) gkh
* Fixed bug that happens when device node is opened when there isn't a
* device attached to it. Thanks to marek@webdesign.no for noticing this.
*
* version 0.2.0 (11/10/99) gkh
* Split up internals to make it easier to add different types of serial
* converters to the code.
* Added a "generic" driver that gets it's vendor and product id
* from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
* for the idea and sample code (from the usb scanner driver.)
* Cleared up any licensing questions by releasing it under the GNU GPL.
*
* version 0.1.2 (10/25/99) gkh
* Fixed bug in detecting device.
*
* version 0.1.1 (10/05/99) gkh
* Changed the major number to not conflict with anything else.
*
* version 0.1 (09/28/99) gkh
* Can recognize the two different devices and start up a read from
* device when asked to. Writes also work. No control signals yet, this
* all is vendor specific data (i.e. no spec), also no control for
* different baud rates or other bit settings.
* Currently we are using the same devid as the acm driver. This needs
* to change.
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
 
 
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
static int debug;
#endif
 
#include "usb-serial.h"
#include "pl2303.h"
 
/*
* Version Information
*/
#define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
#define DRIVER_DESC "USB Serial Driver core"
 
 
#ifdef CONFIG_USB_SERIAL_GENERIC
/* we want to look at all devices, as the vendor/product id can change
* depending on the command line argument */
static struct usb_device_id generic_serial_ids[] = {
{.driver_info = 42},
{}
};
 
#endif /* CONFIG_USB_SERIAL_GENERIC */
 
/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
.owner = THIS_MODULE,
.name = "usbserial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
#ifdef CONFIG_USB_SERIAL_GENERIC
.id_table = generic_serial_ids,
#endif
};
 
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead
the MODULE_DEVICE_TABLE declarations in each serial driver
cause the "hotplug" program to pull in whatever module is necessary
via modprobe, and modprobe will load usbserial because the serial
drivers depend on it.
*/
 
static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */
static LIST_HEAD(usb_serial_driver_list);
 
 
struct usb_serial *usb_serial_get_by_index(unsigned index)
{
struct usb_serial *serial = serial_table[index];
 
if (serial)
kobject_get (&serial->kobj);
return serial;
}
 
static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor)
{
unsigned int i, j;
int good_spot;
 
dbg("%s %d", __FUNCTION__, num_ports);
 
*minor = 0;
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
if (serial_table[i])
continue;
 
good_spot = 1;
for (j = 1; j <= num_ports-1; ++j)
if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) {
good_spot = 0;
i += j;
break;
}
if (good_spot == 0)
continue;
serial->magic = USB_SERIAL_MAGIC;
*minor = i;
dbg("%s - minor base = %d", __FUNCTION__, *minor);
for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
serial_table[i] = serial;
return serial;
}
return NULL;
}
 
static void return_serial (struct usb_serial *serial)
{
int i;
 
dbg("%s", __FUNCTION__);
 
if (serial == NULL)
return;
 
for (i = 0; i < serial->num_ports; ++i) {
serial_table[serial->minor + i] = NULL;
}
 
return;
}
 
/*****************************************************************************
* Driver tty interface functions
*****************************************************************************/
/*static*/ int serial_open (struct tty_struct *tty, struct file * filp)
{
struct usb_serial *serial;
struct usb_serial_port *port;
unsigned int portNumber;
int retval = 0;
dbg("%s", __FUNCTION__);
 
/* initialize the pointer incase something fails */
tty->driver_data = NULL;
 
/* get the serial object associated with this tty pointer */
serial = usb_serial_get_by_index(tty->index);
 
if (serial_paranoia_check (serial, __FUNCTION__))
return -ENODEV;
 
/* set up our port structure making the tty driver remember our port object, and us it */
portNumber = tty->index - serial->minor;
port = serial->port[portNumber];
tty->driver_data = port;
 
port->tty = tty;
/* lock this module before we call it,
this may, which means we must bail out, safe because we are called with BKL held */
if (!try_module_get(serial->type->owner)) {
retval = -ENODEV;
goto bailout;
}
 
++port->open_count;
if (port->open_count == 1) {
/* only call the device specific open if this
* is the first time the port is opened */
retval = serial->type->open(port, filp);
if (retval) {
port->open_count = 0;
module_put(serial->type->owner);
kobject_put(&serial->kobj);
}
}
bailout:
return retval;
}
 
static void serial_close(struct tty_struct *tty, struct file * filp)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
--port->open_count;
if (port->open_count <= 0) {
/* only call the device specific close if this
* port is being closed by the last owner */
port->serial->type->close(port, filp);
port->open_count = 0;
 
if (port->tty) {
if (port->tty->driver_data)
port->tty->driver_data = NULL;
port->tty = NULL;
}
}
 
module_put(port->serial->type->owner);
kobject_put(&port->serial->kobj);
}
 
/*static*/ int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -EINVAL;
 
if (!serial)
return -ENODEV;
 
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
 
if (!port->open_count) {
dbg("%s - port not opened", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function */
retval = serial->type->write(port, from_user, buf, count);
 
exit:
return retval;
}
 
static int serial_write_room (struct tty_struct *tty)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -EINVAL;
 
if (!serial)
return -ENODEV;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function */
retval = serial->type->write_room(port);
 
exit:
return retval;
}
 
static int serial_chars_in_buffer (struct tty_struct *tty)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -EINVAL;
 
if (!serial)
return -ENODEV;
 
dbg("%s = port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function */
retval = serial->type->chars_in_buffer(port);
 
exit:
return retval;
}
 
static void serial_throttle (struct tty_struct * tty)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg ("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function */
if (serial->type->throttle)
serial->type->throttle(port);
 
exit:
;
}
 
static void serial_unthrottle (struct tty_struct * tty)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function */
if (serial->type->unthrottle)
serial->type->unthrottle(port);
 
exit:
;
}
 
static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -ENODEV;
 
if (!serial)
return -ENODEV;
 
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
 
if (!port->open_count) {
dbg ("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function if it is available */
if (serial->type->ioctl)
retval = serial->type->ioctl(port, file, cmd, arg);
else
retval = -ENOIOCTLCMD;
 
exit:
return retval;
}
 
static void serial_set_termios (struct tty_struct *tty, struct termios * old)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function if it is available */
if (serial->type->set_termios)
serial->type->set_termios(port, old);
 
exit:
;
}
 
static void serial_break (struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
return;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
/* pass on to the driver specific version of this function if it is available */
if (serial->type->break_ctl)
serial->type->break_ctl(port, break_state);
 
exit:
;
}
 
static void serial_shutdown (struct usb_serial *serial)
{
dbg ("%s", __FUNCTION__);
 
serial->type->shutdown(serial);
}
 
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
{
struct usb_serial *serial;
int length = 0;
int i;
off_t begin = 0;
char tmp[40];
 
dbg("%s", __FUNCTION__);
length += sprintf26 (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION);
for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) {
serial = usb_serial_get_by_index(i);
if (serial == NULL)
continue;
 
length += sprintf26 (page+length, "%d:", i);
if (serial->type->owner)
length += sprintf26 (page+length, " module:%s", module_name(serial->type->owner));
length += sprintf26 (page+length, " name:\"%s\"", serial->type->name);
length += sprintf26 (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product);
length += sprintf26 (page+length, " num_ports:%d", serial->num_ports);
length += sprintf26 (page+length, " port:%d", i - serial->minor + 1);
 
usb_make_path(serial->dev, tmp, sizeof(tmp));
length += sprintf26 (page+length, " path:%s", tmp);
length += sprintf26 (page+length, "\n");
if ((length + begin) > (off + count))
goto done;
if ((length + begin) < off) {
begin += length;
length = 0;
}
kobject_put(&serial->kobj);
}
*eof = 1;
done:
if (off >= (length + begin))
return 0;
*start = page + (off-begin);
return ((count < begin+length-off) ? count : begin+length-off);
}
 
static int serial_tiocmget (struct tty_struct *tty, struct file *file)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
goto exit;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
if (serial->type->tiocmget)
return serial->type->tiocmget(port, file);
 
exit:
return -EINVAL;
}
 
static int serial_tiocmset (struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
if (!serial)
goto exit;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
goto exit;
}
 
if (serial->type->tiocmset)
return serial->type->tiocmset(port, file, set, clear);
 
exit:
return -EINVAL;
}
 
void usb_serial_port_softint(void *private)
{
struct usb_serial_port *port = (struct usb_serial_port *)private;
struct usb_serial *serial;
struct tty_struct *tty;
 
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port)
return;
 
serial = get_usb_serial (port, __FUNCTION__);
if (!serial)
return;
 
tty = port->tty;
if (!tty)
return;
 
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
dbg("%s - write wakeup call.", __FUNCTION__);
(tty->ldisc.write_wakeup)(tty);
}
 
wake_up_interruptible(&tty->write_wait);
}
 
static void destroy_serial (struct kobject *kobj)
{
struct usb_serial *serial;
struct usb_serial_port *port;
int i;
 
dbg ("%s - %s", __FUNCTION__, kobj->name);
 
serial = to_usb_serial(kobj);
serial_shutdown (serial);
 
/* return the minor range that this device had */
return_serial(serial);
 
for (i = 0; i < serial->num_ports; ++i)
serial->port[i]->open_count = 0;
 
/* the ports are cleaned up and released in port_release() */
for (i = 0; i < serial->num_ports; ++i)
if (serial->port[i]->dev.parent != NULL) {
device_unregister(&serial->port[i]->dev);
serial->port[i] = NULL;
}
 
/* If this is a "fake" port, we have to clean it up here, as it will
* not get cleaned up in port_release() as it was never registered with
* the driver core */
if (serial->num_ports < serial->num_port_pointers) {
for (i = serial->num_ports; i < serial->num_port_pointers; ++i) {
port = serial->port[i];
if (!port)
continue;
if (port->read_urb) {
usb_unlink_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
usb_unlink_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
usb_unlink_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
kfree(port->bulk_out_buffer);
kfree(port->interrupt_in_buffer);
}
}
 
usb_put_dev(serial->dev);
 
/* free up any memory that we allocated */
kfree (serial);
}
 
static struct kobj_type usb_serial_kobj_type = {
.release = destroy_serial,
};
 
static void port_release(struct device *dev)
{
struct usb_serial_port *port = to_usb_serial_port(dev);
 
dbg ("%s - %s", __FUNCTION__, dev->bus_id);
if (port->read_urb) {
usb_unlink_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
usb_unlink_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
usb_unlink_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
kfree(port->bulk_out_buffer);
kfree(port->interrupt_in_buffer);
kfree(port);
}
 
static struct usb_serial * create_serial (struct usb_device *dev,
struct usb_interface *interface,
struct usb_serial_device_type *type)
{
struct usb_serial *serial;
 
serial = kmalloc (sizeof (*serial), GFP_KERNEL);
if (!serial) {
dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
return NULL;
}
memset (serial, 0, sizeof(*serial));
serial->dev = usb_get_dev(dev);
serial->type = type;
serial->interface = interface;
serial->vendor = dev->descriptor.idVendor;
serial->product = dev->descriptor.idProduct;
 
/* initialize the kobject portion of the usb_device */
kobject_init(&serial->kobj);
serial->kobj.ktype = &usb_serial_kobj_type;
 
return serial;
}
 
int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev (interface);
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
struct usb_serial_device_type *type = NULL;
struct list_head *tmp;
int retval;
int found;
int minor;
int buffer_size;
int i;
int num_interrupt_in = 0;
int num_bulk_in = 0;
int num_bulk_out = 0;
int num_ports = 0;
int max_endpoints;
const struct usb_device_id *id_pattern = NULL;
 
/* loop through our list of known serial converters, and see if this
device matches. */
found = 0;
list_for_each (tmp, &usb_serial_driver_list) {
type = list_entry(tmp, struct usb_serial_device_type, driver_list);
id_pattern = usb_match_id(interface, type->id_table);
if (id_pattern != NULL) {
dbg("descriptor matches");
found = 1;
break;
}
}
if (!found) {
/* no match */
dbg("none matched");
return -ENODEV;
}
serial = create_serial (dev, interface, type);
if (!serial) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
return -ENODEV;
}
 
/* if this device type has a probe function, call it */
if (type->probe) {
if (!try_module_get(type->owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
kfree (serial);
return -EIO;
}
retval = type->probe (serial, id_pattern);
module_put(type->owner);
 
if (retval) {
dbg ("sub driver rejected device");
kfree (serial);
return retval;
}
}
 
/* descriptor matches, let's find the endpoints needed */
/* check out the endpoints */
iface_desc = &interface->altsetting[0];
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk in endpoint */
dbg("found bulk in");
bulk_in_endpoint[num_bulk_in] = endpoint;
++num_bulk_in;
}
 
if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk out endpoint */
dbg("found bulk out");
bulk_out_endpoint[num_bulk_out] = endpoint;
++num_bulk_out;
}
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
}
 
#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
/* BEGIN HORRIBLE HACK FOR PL2303 */
/* this is needed due to the looney way its endpoints are set up */
if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) &&
(dev->descriptor.idProduct == PL2303_PRODUCT_ID)) ||
((dev->descriptor.idVendor == ATEN_VENDOR_ID) &&
(dev->descriptor.idProduct == ATEN_PRODUCT_ID))) {
if (interface != dev->actconfig->interface[0]) {
/* check out the endpoints of the other interface*/
iface_desc = &dev->actconfig->interface[0]->altsetting[0];
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in for Prolific device on separate interface");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
}
}
 
/* Now make sure the PL-2303 is configured correctly.
* If not, give up now and hope this hack will work
* properly during a later invocation of usb_serial_probe
*/
if (num_bulk_in == 0 || num_bulk_out == 0) {
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
kfree (serial);
return -ENODEV;
}
}
/* END HORRIBLE HACK FOR PL2303 */
#endif
 
/* found all that we need */
dev_info(&interface->dev, "%s converter detected\n", type->name);
 
#ifdef CONFIG_USB_SERIAL_GENERIC
if (type == &usb_serial_generic_device) {
num_ports = num_bulk_out;
if (num_ports == 0) {
dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n");
kfree (serial);
return -EIO;
}
}
#endif
if (!num_ports) {
/* if this device type has a calc_num_ports function, call it */
if (type->calc_num_ports) {
if (!try_module_get(type->owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
kfree (serial);
return -EIO;
}
num_ports = type->calc_num_ports (serial);
module_put(type->owner);
}
if (!num_ports)
num_ports = type->num_ports;
}
 
if (get_free_serial (serial, num_ports, &minor) == NULL) {
dev_err(&interface->dev, "No more free serial devices\n");
kfree (serial);
return -ENOMEM;
}
 
serial->minor = minor;
serial->num_ports = num_ports;
serial->num_bulk_in = num_bulk_in;
serial->num_bulk_out = num_bulk_out;
serial->num_interrupt_in = num_interrupt_in;
 
/* create our ports, we need as many as the max endpoints */
/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
max_endpoints = max(num_bulk_in, num_bulk_out);
max_endpoints = max(max_endpoints, num_interrupt_in);
max_endpoints = max(max_endpoints, (int)serial->num_ports);
serial->num_port_pointers = max_endpoints;
dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
for (i = 0; i < max_endpoints; ++i) {
port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
if (!port)
goto probe_error;
memset(port, 0x00, sizeof(struct usb_serial_port));
port->number = i + serial->minor;
port->serial = serial;
port->magic = USB_SERIAL_PORT_MAGIC;
INIT_WORK(&port->work, usb_serial_port_softint, port);
serial->port[i] = port;
}
 
/* set up the endpoint information */
for (i = 0; i < num_bulk_in; ++i) {
endpoint = bulk_in_endpoint[i];
port = serial->port[i];
port->read_urb = usb_alloc_urb (0, GFP_KERNEL);
if (!port->read_urb) {
dev_err(&interface->dev, "No free urbs available\n");
goto probe_error;
}
buffer_size = endpoint->wMaxPacketSize;
 
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_in_buffer) {
dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n");
goto probe_error;
}
usb_fill_bulk_urb (port->read_urb, dev,
usb_rcvbulkpipe (dev,
endpoint->bEndpointAddress),
port->bulk_in_buffer, buffer_size,
serial->type->read_bulk_callback,
port);
}
 
for (i = 0; i < num_bulk_out; ++i) {
endpoint = bulk_out_endpoint[i];
port = serial->port[i];
port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->write_urb) {
dev_err(&interface->dev, "No free urbs available\n");
goto probe_error;
}
buffer_size = endpoint->wMaxPacketSize;
port->bulk_out_size = buffer_size;
port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_out_buffer) {
dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n");
goto probe_error;
}
usb_fill_bulk_urb (port->write_urb, dev,
usb_sndbulkpipe (dev,
endpoint->bEndpointAddress),
port->bulk_out_buffer, buffer_size,
serial->type->write_bulk_callback,
port);
}
 
for (i = 0; i < num_interrupt_in; ++i) {
endpoint = interrupt_in_endpoint[i];
port = serial->port[i];
port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_in_urb) {
dev_err(&interface->dev, "No free urbs available\n");
goto probe_error;
}
buffer_size = endpoint->wMaxPacketSize;
port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->interrupt_in_buffer) {
dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
goto probe_error;
}
usb_fill_int_urb (port->interrupt_in_urb, dev,
usb_rcvintpipe (dev,
endpoint->bEndpointAddress),
port->interrupt_in_buffer, buffer_size,
serial->type->read_int_callback, port,
endpoint->bInterval);
}
 
/* if this device type has an attach function, call it */
if (type->attach) {
if (!try_module_get(type->owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
goto probe_error;
}
retval = type->attach (serial);
module_put(type->owner);
if (retval < 0)
goto probe_error;
if (retval > 0) {
/* quietly accept this device, but don't bind to a serial port
* as it's about to disappear */
goto exit;
}
}
 
/* register all of the individual ports with the driver core */
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
port->dev.parent = &interface->dev;
port->dev.driver = NULL;
port->dev.bus = &usb_serial_bus_type;
port->dev.release = &port_release;
 
snprintf26(&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);
device_register (&port->dev);
}
 
usb_serial_console_init (debug, minor);
 
exit:
/* success */
usb_set_intfdata (interface, serial);
return 0;
 
probe_error:
for (i = 0; i < num_bulk_in; ++i) {
port = serial->port[i];
if (!port)
continue;
if (port->read_urb)
usb_free_urb (port->read_urb);
kfree(port->bulk_in_buffer);
}
for (i = 0; i < num_bulk_out; ++i) {
port = serial->port[i];
if (!port)
continue;
if (port->write_urb)
usb_free_urb (port->write_urb);
kfree(port->bulk_out_buffer);
}
for (i = 0; i < num_interrupt_in; ++i) {
port = serial->port[i];
if (!port)
continue;
if (port->interrupt_in_urb)
usb_free_urb (port->interrupt_in_urb);
kfree(port->interrupt_in_buffer);
}
 
/* return the minor range that this device had */
return_serial (serial);
 
/* free up any memory that we allocated */
for (i = 0; i < serial->num_port_pointers; ++i)
kfree(serial->port[i]);
kfree (serial);
return -EIO;
}
 
void usb_serial_disconnect(struct usb_interface *interface)
{
struct usb_serial *serial = usb_get_intfdata (interface);
struct device *dev = &interface->dev;
 
dbg ("%s", __FUNCTION__);
 
usb_set_intfdata (interface, NULL);
if (serial) {
/* let the last holder of this object
* cause it to be cleaned up */
kobject_put (&serial->kobj);
}
dev_info(dev, "device disconnected\n");
}
 
static struct tty_operations serial_ops = {
.open = serial_open,
.close = serial_close,
.write = serial_write,
.write_room = serial_write_room,
.ioctl = serial_ioctl,
.set_termios = serial_set_termios,
.throttle = serial_throttle,
.unthrottle = serial_unthrottle,
.break_ctl = serial_break,
.chars_in_buffer = serial_chars_in_buffer,
.read_proc = serial_read_proc,
.tiocmget = serial_tiocmget,
.tiocmset = serial_tiocmset,
};
 
struct tty_driver *usb_serial_tty_driver;
 
/*static*/ int __init usb_serial_init(void)
{
int i;
int result = 0;
 
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
if (!usb_serial_tty_driver)
return -ENOMEM;
 
/* Initialize our global data */
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
 
bus_register(&usb_serial_bus_type);
 
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit;
}
 
usb_serial_tty_driver->owner = THIS_MODULE;
usb_serial_tty_driver->driver_name = "usbserial";
usb_serial_tty_driver->devfs_name = "usb/tts/";
usb_serial_tty_driver->name = "ttyUSB";
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
usb_serial_tty_driver->minor_start = 0;
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
usb_serial_tty_driver->init_termios = tty_std_termios;
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(usb_serial_tty_driver, &serial_ops);
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_generic;
}
 
/* register the USB driver */
result = usb_register(&usb_serial_driver);
if (result < 0) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
 
info(DRIVER_DESC " " DRIVER_VERSION);
 
return result;
 
exit_tty:
tty_unregister_driver(usb_serial_tty_driver);
 
exit_generic:
usb_serial_generic_deregister();
 
exit:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}
 
 
/*static*/ void __exit usb_serial_exit(void)
{
usb_serial_console_exit();
 
usb_serial_generic_deregister();
 
usb_deregister(&usb_serial_driver);
tty_unregister_driver(usb_serial_tty_driver);
put_tty_driver(usb_serial_tty_driver);
bus_unregister(&usb_serial_bus_type);
}
 
 
module_init(usb_serial_init);
module_exit(usb_serial_exit);
 
#define set_to_generic_if_null(type, function) \
do { \
if (!type->function) { \
type->function = usb_serial_generic_##function; \
dbg("Had to override the " #function \
" usb serial operation with the generic one.");\
} \
} while (0)
 
static void fixup_generic(struct usb_serial_device_type *device)
{
set_to_generic_if_null(device, open);
set_to_generic_if_null(device, write);
set_to_generic_if_null(device, close);
set_to_generic_if_null(device, write_room);
set_to_generic_if_null(device, chars_in_buffer);
set_to_generic_if_null(device, read_bulk_callback);
set_to_generic_if_null(device, write_bulk_callback);
set_to_generic_if_null(device, shutdown);
}
 
int usb_serial_register(struct usb_serial_device_type *new_device)
{
int retval;
 
fixup_generic(new_device);
 
/* Add this device to our list of devices */
list_add(&new_device->driver_list, &usb_serial_driver_list);
 
retval = usb_serial_bus_register (new_device);
 
if (retval)
goto error;
 
info("USB Serial support registered for %s", new_device->name);
 
return retval;
error:
err("problem %d when registering driver %s", retval, new_device->name);
list_del(&new_device->driver_list);
 
return retval;
}
 
 
void usb_serial_deregister(struct usb_serial_device_type *device)
{
struct usb_serial *serial;
int i;
 
info("USB Serial deregistering driver %s", device->name);
 
/* clear out the serial_table if the device is attached to a port */
for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial = serial_table[i];
if ((serial != NULL) && (serial->type == device)) {
usb_driver_release_interface (&usb_serial_driver, serial->interface);
usb_serial_disconnect (serial->interface);
}
}
 
list_del(&device->driver_list);
usb_serial_bus_deregister (device);
}
 
 
 
/* If the usb-serial core is built into the core, the usb-serial drivers
need these symbols to load properly as modules. */
EXPORT_SYMBOL(usb_serial_register);
EXPORT_SYMBOL(usb_serial_deregister);
EXPORT_SYMBOL(usb_serial_probe);
EXPORT_SYMBOL(usb_serial_disconnect);
EXPORT_SYMBOL(usb_serial_port_softint);
 
 
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
 
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
/shark/trunk/drivers/usb/serial/tty_io.c
0,0 → 1,210
/*
* linux/drivers/char/tty_io.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/devpts_fs.h>
#include <linux/file.h>
#include <linux/console.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/kd.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/device.h>
 
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
 
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/devfs_fs_kernel.h>
 
#include <linux/kmod.h>
 
#undef TTY_DEBUG_HANGUP
 
#define TTY_PARANOIA_CHECK 1
#define CHECK_TTY_COUNT 1
 
struct tty_ldisc ldiscs[NR_LDISCS];
 
struct termios tty_std_termios = { /* for the benefit of tty drivers */
.c_iflag = ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN,
.c_cc = INIT_C_CC
};
 
struct tty_driver *alloc_tty_driver(int lines)
{
struct tty_driver *driver;
 
driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
memset(driver, 0, sizeof(struct tty_driver));
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
/* later we'll move allocation of tables here */
}
return driver;
}
 
void put_tty_driver(struct tty_driver *driver)
{
kfree(driver);
}
 
void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
{
driver->open = op->open;
driver->close = op->close;
driver->write = op->write;
driver->put_char = op->put_char;
driver->flush_chars = op->flush_chars;
driver->write_room = op->write_room;
driver->chars_in_buffer = op->chars_in_buffer;
driver->ioctl = op->ioctl;
driver->set_termios = op->set_termios;
driver->throttle = op->throttle;
driver->unthrottle = op->unthrottle;
driver->stop = op->stop;
driver->start = op->start;
driver->hangup = op->hangup;
driver->break_ctl = op->break_ctl;
driver->flush_buffer = op->flush_buffer;
driver->set_ldisc = op->set_ldisc;
driver->wait_until_sent = op->wait_until_sent;
driver->send_xchar = op->send_xchar;
driver->read_proc = op->read_proc;
driver->write_proc = op->write_proc;
driver->tiocmget = op->tiocmget;
driver->tiocmset = op->tiocmset;
}
 
int tty_register_driver(struct tty_driver *driver)
{
return 0;
}
 
int tty_unregister_driver(struct tty_driver *driver)
{
return 0;
}
 
void tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
 
}
 
void tty_unregister_device(struct tty_driver *driver, unsigned index)
{
 
}
 
void tty_flip_buffer_push(struct tty_struct *tty)
{
printk(KERN_INFO "%c\n", *(tty->flip.char_buf_ptr -1));
}
 
static void flush_to_ldisc(void *private_)
{
 
}
 
static struct tty_struct *alloc_tty_struct(void)
{
struct tty_struct *tty;
 
tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
// if (tty)
// memset(tty, 0, sizeof(struct tty_struct));
return tty;
}
 
void do_tty_hangup(void *data)
{
 
}
 
/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
{
memset(tty, 0, sizeof(struct tty_struct));
tty->magic = TTY_MAGIC;
tty->ldisc = ldiscs[N_TTY];
tty->pgrp = -1;
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
INIT_WORK(&tty->flip.work, flush_to_ldisc, tty);
init_MUTEX(&tty->flip.pty_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
//*sema_init(&tty->atomic_read, 1);
//*sema_init(&tty->atomic_write, 1);
spin_lock_init(&tty->read_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, NULL, NULL);
}
 
extern int serial_open (struct tty_struct *tty, struct file * filp);
extern int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
 
struct tty_struct *tty;
int serial_usbport_open(int port_number)
{
int retval;
tty = alloc_tty_struct();
if(!tty)
goto fail_no_mem;
 
initialize_tty_struct (tty);
 
tty->termios = malloc (sizeof(struct termios));
*(tty->termios) = tty_std_termios;
 
retval = serial_open(tty, NULL);
return retval;
 
fail_no_mem:
return -ENOMEM;
}
 
int serial_usbport_write(const unsigned char *buf, int count)
{
int retval;
retval = serial_write(tty, 0, buf, count);
return retval;
 
// printk(KERN_DEBUG "######### retval=%d\n", retval);
}
/shark/trunk/drivers/usb/serial/pl2303.h
0,0 → 1,39
/*
* Prolific PL2303 USB to serial adaptor driver header file
*
* 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.
*
*/
#define PL2303_VENDOR_ID 0x067b
#define PL2303_PRODUCT_ID 0x2303
#define PL2303_PRODUCT_ID_RSAQ2 0x04bb
 
#define ATEN_VENDOR_ID 0x0557
#define ATEN_PRODUCT_ID 0x2008
 
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
 
#define ELCOM_VENDOR_ID 0x056e
#define ELCOM_PRODUCT_ID 0x5003
 
#define ITEGNO_VENDOR_ID 0x0eba
#define ITEGNO_PRODUCT_ID 0x1080
 
#define MA620_VENDOR_ID 0x0df7
#define MA620_PRODUCT_ID 0x0620
 
#define RATOC_VENDOR_ID 0x0584
#define RATOC_PRODUCT_ID 0xb000
 
#define TRIPP_VENDOR_ID 0x2478
#define TRIPP_PRODUCT_ID 0x2008
 
#define RADIOSHACK_VENDOR_ID 0x1453
#define RADIOSHACK_PRODUCT_ID 0x4026
 
#define DCU10_VENDOR_ID 0x0731
#define DCU10_PRODUCT_ID 0x0528
/shark/trunk/drivers/usb/serial/bus.c
0,0 → 1,142
/*
* USB Serial Converter Bus specific functions
*
* Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
 
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
static int debug;
#endif
 
#include "usb-serial.h"
 
static int usb_serial_device_match (struct device *dev, struct device_driver *drv)
{
struct usb_serial_device_type *driver;
const struct usb_serial_port *port;
 
/*
* drivers are already assigned to ports in serial_probe so it's
* a simple check here.
*/
port = to_usb_serial_port(dev);
if (!port)
return 0;
 
driver = to_usb_serial_driver(drv);
 
if (driver == port->serial->type)
return 1;
 
return 0;
}
 
struct bus_type usb_serial_bus_type = {
.name = "usb-serial",
.match = usb_serial_device_match,
};
 
static int usb_serial_device_probe (struct device *dev)
{
struct usb_serial_device_type *driver;
struct usb_serial_port *port;
int retval = 0;
int minor;
 
port = to_usb_serial_port(dev);
if (!port) {
retval = -ENODEV;
goto exit;
}
 
driver = port->serial->type;
if (driver->port_probe) {
if (!try_module_get(driver->owner)) {
dev_err(dev, "module get failed, exiting\n");
retval = -EIO;
goto exit;
}
retval = driver->port_probe (port);
module_put(driver->owner);
if (retval)
goto exit;
}
 
minor = port->number;
tty_register_device (usb_serial_tty_driver, minor, dev);
dev_info(&port->serial->dev->dev,
"%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)\n",
driver->name, minor, minor);
 
exit:
return retval;
}
 
static int usb_serial_device_remove (struct device *dev)
{
struct usb_serial_device_type *driver;
struct usb_serial_port *port;
int retval = 0;
int minor;
 
port = to_usb_serial_port(dev);
if (!port) {
return -ENODEV;
}
 
driver = port->serial->type;
if (driver->port_remove) {
if (!try_module_get(driver->owner)) {
dev_err(dev, "module get failed, exiting\n");
retval = -EIO;
goto exit;
}
retval = driver->port_remove (port);
module_put(driver->owner);
}
exit:
minor = port->number;
tty_unregister_device (usb_serial_tty_driver, minor);
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
driver->name, minor);
 
return retval;
}
 
int usb_serial_bus_register(struct usb_serial_device_type *device)
{
int retval;
 
if (device->short_name)
device->driver.name = (char *)device->short_name;
else
device->driver.name = (char *)device->name;
device->driver.bus = &usb_serial_bus_type;
device->driver.probe = usb_serial_device_probe;
device->driver.remove = usb_serial_device_remove;
 
retval = driver_register(&device->driver);
 
return retval;
}
 
void usb_serial_bus_deregister(struct usb_serial_device_type *device)
{
driver_unregister (&device->driver);
}
 
/shark/trunk/drivers/usb/serial/usb-serial.h
0,0 → 1,379
/*
* USB Serial Converter driver
*
* Copyright (C) 1999 - 2003
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
* (03/26/2002) gkh
* removed the port->tty check from port_paranoia_check() due to serial
* consoles not having a tty device assigned to them.
*
* (12/03/2001) gkh
* removed active from the port structure.
* added documentation to the usb_serial_device_type structure
*
* (10/10/2001) gkh
* added vendor and product to serial structure. Needed to determine device
* owner when the device is disconnected.
*
* (05/30/2001) gkh
* added sem to port structure and removed port_lock
*
* (10/05/2000) gkh
* Added interrupt_in_endpointAddress and bulk_in_endpointAddress to help
* fix bug with urb->dev not being set properly, now that the usb core
* needs it.
*
* (09/11/2000) gkh
* Added usb_serial_debug_data function to help get rid of #DEBUG in the
* drivers.
*
* (08/28/2000) gkh
* Added port_lock to port structure.
*
* (08/08/2000) gkh
* Added open_count to port structure.
*
* (07/23/2000) gkh
* Added bulk_out_endpointAddress to port structure.
*
* (07/19/2000) gkh, pberger, and borchers
* Modifications to allow usb-serial drivers to be modules.
*
*
*/
 
 
#ifndef __LINUX_USB_SERIAL_H
#define __LINUX_USB_SERIAL_H
 
#include <linux/config.h>
 
#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
#define SERIAL_TTY_MINORS 255 /* loads of devices :) */
 
#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */
 
#define USB_SERIAL_MAGIC 0x6702 /* magic number for usb_serial struct */
#define USB_SERIAL_PORT_MAGIC 0x7301 /* magic number for usb_serial_port struct */
 
/* parity check flag */
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
/**
* usb_serial_port: structure for the specific ports of a device.
* @magic: magic number for internal validity of this pointer.
* @serial: pointer back to the struct usb_serial owner of this port.
* @tty: pointer to the corresponding tty for this port.
* @number: the number of the port (the minor number).
* @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
* @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
* @interrupt_in_endpointAddress: endpoint address for the interrupt in pipe
* for this port.
* @bulk_in_buffer: pointer to the bulk in buffer for this port.
* @read_urb: pointer to the bulk in struct urb for this port.
* @bulk_in_endpointAddress: endpoint address for the bulk in pipe for this
* port.
* @bulk_out_buffer: pointer to the bulk out buffer for this port.
* @bulk_out_size: the size of the bulk_out_buffer, in bytes.
* @write_urb: pointer to the bulk out struct urb for this port.
* @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this
* port.
* @write_wait: a wait_queue_head_t used by the port.
* @work: work queue entry for the line discipline waking up.
* @open_count: number of times this port has been opened.
*
* This structure is used by the usb-serial core and drivers for the specific
* ports of a device.
*/
struct usb_serial_port {
int magic;
struct usb_serial *serial;
struct tty_struct * tty;
unsigned char number;
 
unsigned char * interrupt_in_buffer;
struct urb * interrupt_in_urb;
__u8 interrupt_in_endpointAddress;
 
unsigned char * bulk_in_buffer;
struct urb * read_urb;
__u8 bulk_in_endpointAddress;
 
unsigned char * bulk_out_buffer;
int bulk_out_size;
struct urb * write_urb;
__u8 bulk_out_endpointAddress;
 
wait_queue_head_t write_wait;
struct work_struct work;
int open_count;
struct device dev;
};
#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)
 
/* get and set the port private data pointer helper functions */
static inline void *usb_get_serial_port_data (struct usb_serial_port *port)
{
return dev_get_drvdata(&port->dev);
}
 
static inline void usb_set_serial_port_data (struct usb_serial_port *port, void *data)
{
dev_set_drvdata(&port->dev, data);
}
 
/**
* usb_serial - structure used by the usb-serial core for a device
* @magic: magic number for internal validity of this pointer.
* @dev: pointer to the struct usb_device for this device
* @type: pointer to the struct usb_serial_device_type for this device
* @interface: pointer to the struct usb_interface for this device
* @minor: the starting minor number for this device
* @num_ports: the number of ports this device has
* @num_interrupt_in: number of interrupt in endpoints we have
* @num_bulk_in: number of bulk in endpoints we have
* @num_bulk_out: number of bulk out endpoints we have
* @vendor: vendor id of this device
* @product: product id of this device
* @port: array of struct usb_serial_port structures for the different ports.
* @private: place to put any driver specific information that is needed. The
* usb-serial driver is required to manage this data, the usb-serial core
* will not touch this. Use usb_get_serial_data() and
* usb_set_serial_data() to access this.
*/
struct usb_serial {
int magic;
struct usb_device * dev;
struct usb_serial_device_type * type;
struct usb_interface * interface;
unsigned char minor;
unsigned char num_ports;
unsigned char num_port_pointers;
char num_interrupt_in;
char num_bulk_in;
char num_bulk_out;
__u16 vendor;
__u16 product;
struct usb_serial_port * port[MAX_NUM_PORTS];
struct kobject kobj;
void * private;
};
#define to_usb_serial(d) container_of(d, struct usb_serial, kobj)
 
#define NUM_DONT_CARE (-1)
 
/* get and set the serial private data pointer helper functions */
static inline void *usb_get_serial_data (struct usb_serial *serial)
{
return serial->private;
}
 
static inline void usb_set_serial_data (struct usb_serial *serial, void *data)
{
serial->private = data;
}
 
/**
* usb_serial_device_type - a structure that defines a usb serial device
* @owner: pointer to the module that owns this device.
* @name: pointer to a string that describes this device. This string used
* in the syslog messages when a device is inserted or removed.
* @short_name: a pointer to a string that describes this device in
* KOBJ_NAME_LEN characters or less. This is used for the sysfs interface
* to describe the driver.
* @id_table: pointer to a list of usb_device_id structures that define all
* of the devices this structure can support.
* @num_interrupt_in: the number of interrupt in endpoints this device will
* have.
* @num_bulk_in: the number of bulk in endpoints this device will have.
* @num_bulk_out: the number of bulk out endpoints this device will have.
* @num_ports: the number of different ports this device will have.
* @calc_num_ports: pointer to a function to determine how many ports this
* device has dynamically. It will be called after the probe()
* callback is called, but before attach()
* @probe: pointer to the driver's probe function.
* This will be called when the device is inserted into the system,
* but before the device has been fully initialized by the usb_serial
* subsystem. Use this function to download any firmware to the device,
* or any other early initialization that might be needed.
* Return 0 to continue on with the initialization sequence. Anything
* else will abort it.
* @attach: pointer to the driver's attach function.
* This will be called when the struct usb_serial structure is fully set
* set up. Do any local initialization of the device, or any private
* memory structure allocation at this point in time.
* @shutdown: pointer to the driver's shutdown function. This will be
* called when the device is removed from the system.
*
* This structure is defines a USB Serial device. It provides all of
* the information that the USB serial core code needs. If the function
* pointers are defined, then the USB serial core code will call them when
* the corresponding tty port functions are called. If they are not
* called, the generic serial function will be used instead.
*/
struct usb_serial_device_type {
struct module *owner;
char *name;
char *short_name;
const struct usb_device_id *id_table;
char num_interrupt_in;
char num_bulk_in;
char num_bulk_out;
char num_ports;
 
struct list_head driver_list;
struct device_driver driver;
 
int (*probe) (struct usb_serial *serial, const struct usb_device_id *id);
int (*attach) (struct usb_serial *serial);
int (*calc_num_ports) (struct usb_serial *serial);
 
void (*shutdown) (struct usb_serial *serial);
 
int (*port_probe) (struct usb_serial_port *port);
int (*port_remove) (struct usb_serial_port *port);
 
/* serial function calls */
int (*open) (struct usb_serial_port *port, struct file * filp);
void (*close) (struct usb_serial_port *port, struct file * filp);
int (*write) (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
int (*write_room) (struct usb_serial_port *port);
int (*ioctl) (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
void (*set_termios) (struct usb_serial_port *port, struct termios * old);
void (*break_ctl) (struct usb_serial_port *port, int break_state);
int (*chars_in_buffer) (struct usb_serial_port *port);
void (*throttle) (struct usb_serial_port *port);
void (*unthrottle) (struct usb_serial_port *port);
int (*tiocmget) (struct usb_serial_port *port, struct file *file);
int (*tiocmset) (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);
 
void (*read_int_callback)(struct urb *urb, struct pt_regs *regs);
void (*read_bulk_callback)(struct urb *urb, struct pt_regs *regs);
void (*write_bulk_callback)(struct urb *urb, struct pt_regs *regs);
};
#define to_usb_serial_driver(d) container_of(d, struct usb_serial_device_type, driver)
 
extern int usb_serial_register(struct usb_serial_device_type *new_device);
extern void usb_serial_deregister(struct usb_serial_device_type *device);
extern void usb_serial_port_softint(void *private);
 
extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id);
extern void usb_serial_disconnect(struct usb_interface *iface);
 
extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
 
/* USB Serial console functions */
#ifdef CONFIG_USB_SERIAL_CONSOLE
extern void usb_serial_console_init (int debug, int minor);
extern void usb_serial_console_exit (void);
#else
static inline void usb_serial_console_init (int debug, int minor) { }
static inline void usb_serial_console_exit (void) { }
#endif
 
/* Functions needed by other parts of the usbserial core */
extern struct usb_serial *usb_serial_get_by_index (unsigned int minor);
extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
extern int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp);
extern int usb_serial_generic_write_room (struct usb_serial_port *port);
extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port);
extern void usb_serial_generic_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
extern void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
extern void usb_serial_generic_shutdown (struct usb_serial *serial);
extern int usb_serial_generic_register (int debug);
extern void usb_serial_generic_deregister (void);
 
extern int usb_serial_bus_register (struct usb_serial_device_type *device);
extern void usb_serial_bus_deregister (struct usb_serial_device_type *device);
 
extern struct usb_serial_device_type usb_serial_generic_device;
extern struct bus_type usb_serial_bus_type;
extern struct tty_driver *usb_serial_tty_driver;
 
/* Inline functions to check the sanity of a pointer that is passed to us */
static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
{
if (!serial) {
dbg("%s - serial == NULL", function);
return -1;
}
if (serial->magic != USB_SERIAL_MAGIC) {
dbg("%s - bad magic number for serial", function);
return -1;
}
if (!serial->type) {
dbg("%s - serial->type == NULL!", function);
return -1;
}
 
return 0;
}
 
 
static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
{
if (!port) {
dbg("%s - port == NULL", function);
return -1;
}
if (port->magic != USB_SERIAL_PORT_MAGIC) {
dbg("%s - bad magic number for port", function);
return -1;
}
if (!port->serial) {
dbg("%s - port->serial == NULL", function);
return -1;
}
 
return 0;
}
 
 
static inline struct usb_serial* get_usb_serial (struct usb_serial_port *port, const char *function)
{
/* if no port was specified, or it fails a paranoia check */
if (!port ||
port_paranoia_check (port, function) ||
serial_paranoia_check (port->serial, function)) {
/* then say that we don't have a valid usb_serial thing, which will
* end up genrating -ENODEV return values */
return NULL;
}
 
return port->serial;
}
 
 
static inline void usb_serial_debug_data (const char *file, const char *function, int size, const unsigned char *data)
{
int i;
 
if (!debug)
return;
printk (KERN_DEBUG "%s: %s - length = %d, data = ", file, function, size);
for (i = 0; i < size; ++i) {
printk ("%.2x ", data[i]);
}
printk ("\n");
}
 
 
/* Use our own dbg macro */
#undef dbg
#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg); } while (0)
 
 
 
#endif /* ifdef __LINUX_USB_SERIAL_H */
 
/shark/trunk/drivers/usb/serial/generic.c
0,0 → 1,308
/*
* USB Serial Converter Generic functions
*
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <asm/uaccess.h>
 
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
static int debug;
#endif
 
#include "usb-serial.h"
 
#ifdef CONFIG_USB_SERIAL_GENERIC
static __u16 vendor = 0x05f9;
static __u16 product = 0xffff;
 
MODULE_PARM(vendor, "h");
MODULE_PARM_DESC(vendor, "User specified USB idVendor");
 
MODULE_PARM(product, "h");
MODULE_PARM_DESC(product, "User specified USB idProduct");
 
static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
 
/* All of the device info needed for the Generic Serial Converter */
struct usb_serial_device_type usb_serial_generic_device = {
.owner = THIS_MODULE,
.name = "Generic",
.short_name = "generic",
.id_table = generic_device_ids,
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
.num_ports = 1,
.shutdown = usb_serial_generic_shutdown,
};
#endif
 
int usb_serial_generic_register (int _debug)
{
int retval = 0;
 
debug = _debug;
#ifdef CONFIG_USB_SERIAL_GENERIC
generic_device_ids[0].idVendor = vendor;
generic_device_ids[0].idProduct = product;
generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
 
/* register our generic driver with ourselves */
retval = usb_serial_register (&usb_serial_generic_device);
#endif
return retval;
}
 
void usb_serial_generic_deregister (void)
{
#ifdef CONFIG_USB_SERIAL_GENERIC
/* remove our generic driver */
usb_serial_deregister (&usb_serial_generic_device);
#endif
}
 
int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
int result = 0;
 
if (port_paranoia_check (port, __FUNCTION__))
return -ENODEV;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
/* force low_latency on so that our tty_push actually forces the data through,
otherwise it is scheduled, and with high data rates (like with OHCI) data
can get lost. */
if (port->tty)
port->tty->low_latency = 1;
 
/* if we have a bulk interrupt, start reading from it */
if (serial->num_bulk_in) {
/* Start reading from the device */
usb_fill_bulk_urb (port->read_urb, serial->dev,
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer,
port->read_urb->transfer_buffer_length,
((serial->type->read_bulk_callback) ?
serial->type->read_bulk_callback :
usb_serial_generic_read_bulk_callback),
port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
}
 
return result;
}
 
static void generic_cleanup (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (serial->dev) {
/* shutdown any bulk reads that might be going on */
if (serial->num_bulk_out)
usb_unlink_urb (port->write_urb);
if (serial->num_bulk_in)
usb_unlink_urb (port->read_urb);
}
}
 
void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp)
{
dbg("%s - port %d", __FUNCTION__, port->number);
generic_cleanup (port);
}
 
int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
struct usb_serial *serial = port->serial;
int result;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (count == 0) {
dbg("%s - write request of 0 bytes", __FUNCTION__);
return (0);
}
 
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
if (port->write_urb->status == -EINPROGRESS) {
dbg("%s - already writing", __FUNCTION__);
return (0);
}
 
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
if (from_user) {
if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
return -EFAULT;
}
else {
memcpy (port->write_urb->transfer_buffer, buf, count);
}
 
usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
/* set up our urb */
usb_fill_bulk_urb (port->write_urb, serial->dev,
usb_sndbulkpipe (serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, count,
((serial->type->write_bulk_callback) ?
serial->type->write_bulk_callback :
usb_serial_generic_write_bulk_callback), port);
 
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
else
result = count;
 
return result;
}
 
/* no bulk out, so return 0 bytes written */
return (0);
}
 
int usb_serial_generic_write_room (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
int room = 0;
 
dbg("%s - port %d", __FUNCTION__, port->number);
if (serial->num_bulk_out) {
if (port->write_urb->status != -EINPROGRESS)
room = port->bulk_out_size;
}
 
dbg("%s - returns %d", __FUNCTION__, room);
return (room);
}
 
int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
int chars = 0;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (serial->num_bulk_out) {
if (port->write_urb->status == -EINPROGRESS)
chars = port->write_urb->transfer_buffer_length;
}
 
dbg("%s - returns %d", __FUNCTION__, chars);
return (chars);
}
 
void usb_serial_generic_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int i;
int result;
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
}
 
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
return;
}
 
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
 
tty = port->tty;
if (tty && urb->actual_length) {
for (i = 0; i < urb->actual_length ; ++i) {
/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
}
/* this doesn't actually push the data through unless tty->low_latency is set */
tty_insert_flip_char(tty, data[i], 0);
}
tty_flip_buffer_push(tty);
}
 
/* Continue trying to always read */
usb_fill_bulk_urb (port->read_urb, serial->dev,
usb_rcvbulkpipe (serial->dev,
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer,
port->read_urb->transfer_buffer_length,
((serial->type->read_bulk_callback) ?
serial->type->read_bulk_callback :
usb_serial_generic_read_bulk_callback), port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
}
 
void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
dbg("%s - port %d", __FUNCTION__, port->number);
 
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
}
 
if (urb->status) {
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
return;
}
 
usb_serial_port_softint((void *)port);
 
schedule_work(&port->work);
}
 
void usb_serial_generic_shutdown (struct usb_serial *serial)
{
int i;
 
dbg("%s", __FUNCTION__);
 
/* stop reads and writes on all ports */
for (i=0; i < serial->num_ports; ++i) {
generic_cleanup(serial->port[i]);
}
}
 
/shark/trunk/drivers/usb/include/drivers/shark_usb26.h
0,0 → 1,7
#ifndef __SHARK_USB26_H__
#define __SHARK_USB26_H__
 
int USB26_init(void);
int USB26_close(void);
 
#endif
/shark/trunk/drivers/usb/shark_glue/shark_usb.c
0,0 → 1,56
#include <kernel/kern.h>
 
extern int usb_init(void);
extern void usb_exit(void);
 
extern int ohci_hcd_pci_init (void);
extern void ohci_hcd_pci_cleanup (void);
 
extern int uhci_hcd_init(void);
extern void uhci_hcd_cleanup(void);
 
extern int usb_mouse_init(void);
extern void usb_mouse_exit(void);
 
extern int usb_kbd_init(void);
extern void usb_kbd_exit(void);
 
extern int hid_init(void);
extern void hid_exit(void);
 
 
static int usb_installed = FALSE;
 
/* to do: return error code */
int USB26_init()
{
if (usb_installed == TRUE)
return 0;
 
usb_init();
ohci_hcd_pci_init();
uhci_hcd_init();
usb_mouse_init();
usb_kbd_init();
hid_init();
usb_installed = TRUE;
 
return 0;
}
 
/* to do : add all usb closing functions ?*/
int USB26_close()
{
if (usb_installed == FALSE)
return -1;
ohci_hcd_pci_cleanup();
uhci_hcd_cleanup();
usb_mouse_exit();
usb_kbd_exit();
hid_exit();
usb_exit();
usb_installed = FALSE;
return 0;
}
/shark/trunk/drivers/usb/core/usb.h
0,0 → 1,16
/* Functions local to drivers/usb/core/ */
 
extern void usb_create_driverfs_dev_files (struct usb_device *dev);
extern void usb_create_driverfs_intf_files (struct usb_interface *intf);
extern int usb_probe_interface (struct device *dev);
extern int usb_unbind_interface (struct device *dev);
 
extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_interface (struct usb_device *dev,
struct usb_interface *intf);
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
 
extern void usb_enable_endpoint (struct usb_device *dev,
struct usb_endpoint_descriptor *epd);
extern void usb_enable_interface (struct usb_device *dev,
struct usb_interface *intf);
/shark/trunk/drivers/usb/core/hcd.c
0,0 → 1,1551
/*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2002
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#endif
 
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/uts.h> /* for UTS_SYSNAME */
#include <linux/pci.h> /* for hcd->pdev and dma addressing */
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
 
#include <linux/usb.h>
#include "hcd.h"
 
 
// #define USB_BANDWIDTH_MESSAGES
 
/*-------------------------------------------------------------------------*/
 
/*
* USB Host Controller Driver framework
*
* Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
* HCD-specific behaviors/bugs.
*
* This does error checks, tracks devices and urbs, and delegates to a
* "hc_driver" only for code (and data) that really needs to know about
* hardware differences. That includes root hub registers, i/o queues,
* and so on ... but as little else as possible.
*
* Shared code includes most of the "root hub" code (these are emulated,
* though each HC's hardware works differently) and PCI glue, plus request
* tracking overhead. The HCD code should only block on spinlocks or on
* hardware handshaking; blocking on software events (such as other kernel
* threads releasing resources, or completing actions) is all generic.
*
* Happens the USB 2.0 spec says this would be invisible inside the "USBD",
* and includes mostly a "HCDI" (HCD Interface) along with some APIs used
* only by the hub driver ... and that neither should be seen or used by
* usb client device drivers.
*
* Contributors of ideas or unattributed patches include: David Brownell,
* Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...
*
* HISTORY:
* 2002-02-21 Pull in most of the usb_bus support from usb.c; some
* associated cleanup. "usb_hcd" still != "usb_bus".
* 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
*/
 
/*-------------------------------------------------------------------------*/
 
/* host controllers we manage */
LIST_HEAD (usb_bus_list);
EXPORT_SYMBOL_GPL (usb_bus_list);
 
/* used when allocating bus numbers */
#define USB_MAXBUS 64
struct usb_busmap {
unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
};
static struct usb_busmap busmap;
 
/* used when updating list of hcds */
DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
EXPORT_SYMBOL_GPL (usb_bus_list_lock);
 
/* used when updating hcd data */
static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
 
/*-------------------------------------------------------------------------*/
 
/*
* Sharable chunks of root hub code.
*/
 
/*-------------------------------------------------------------------------*/
 
#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
 
/* usb 2.0 root hub device descriptor */
static const u8 usb2_rh_dev_descriptor [18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x00, 0x02, /* __u16 bcdUSB; v2.0 */
 
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
 
0x00, 0x00, /* __u16 idVendor; */
0x00, 0x00, /* __u16 idProduct; */
KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
 
0x03, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
 
/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
 
/* usb 1.1 root hub device descriptor */
static const u8 usb11_rh_dev_descriptor [18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, 0x01, /* __u16 bcdUSB; v1.1 */
 
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
 
0x00, 0x00, /* __u16 idVendor; */
0x00, 0x00, /* __u16 idProduct; */
KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
 
0x03, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
 
 
/*-------------------------------------------------------------------------*/
 
/* Configuration descriptors for our root hubs */
 
static const u8 fs_rh_config_descriptor [] = {
 
/* one configuration */
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, 0x00, /* __u16 wTotalLength; */
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
Bit 7: Bus-powered,
6: Self-powered,
5 Remote-wakwup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
*
* USB 2.0, multiple TT organization (optional):
* two interfaces, protocols 1 (like single TT)
* and 2 (multiple TT mode) ... config is
* sometimes settable
* NOT IMPLEMENTED
*/
 
/* one interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
 
static const u8 hs_rh_config_descriptor [] = {
 
/* one configuration */
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, 0x00, /* __u16 wTotalLength; */
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
Bit 7: Bus-powered,
6: Self-powered,
5 Remote-wakwup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
*
* USB 2.0, multiple TT organization (optional):
* two interfaces, protocols 1 (like single TT)
* and 2 (multiple TT mode) ... config is
* sometimes settable
* NOT IMPLEMENTED
*/
 
/* one interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
};
 
/*-------------------------------------------------------------------------*/
 
/*
* helper routine for returning string descriptors in UTF-16LE
* input can actually be ISO-8859-1; ASCII is its 7-bit subset
*/
static int ascii2utf (char *s, u8 *utf, int utfmax)
{
int retval;
 
for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
*utf++ = *s++;
*utf++ = 0;
}
return retval;
}
 
/*
* rh_string - provides manufacturer, product and serial strings for root hub
* @id: the string ID number (1: serial number, 2: product, 3: vendor)
* @hcd: the host controller for this root hub
* @type: string describing our driver
* @data: return packet in UTF-16 LE
* @len: length of the return packet
*
* Produces either a manufacturer, product or serial number string for the
* virtual root hub device.
*/
static int rh_string (
int id,
struct usb_hcd *hcd,
u8 *data,
int len
) {
char buf [100];
 
// language ids
if (id == 0) {
*data++ = 4; *data++ = 3; /* 4 bytes string data */
*data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */
return 4;
 
// serial number
} else if (id == 1) {
strcpy (buf, hcd->self.bus_name);
 
// product description
} else if (id == 2) {
strcpy (buf, hcd->product_desc);
 
// id 3 == vendor description
} else if (id == 3) {
sprintf26 (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE,
hcd->description);
 
// unsupported IDs --> "protocol stall"
} else
return 0;
 
data [0] = 2 * (strlen (buf) + 1);
data [1] = 3; /* type == string */
return 2 + ascii2utf (buf, data + 2, len - 2);
}
 
 
/* Root hub control transfers execute synchronously */
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
u16 typeReq, wValue, wIndex, wLength;
const u8 *bufp = 0;
u8 *ubuf = urb->transfer_buffer;
int len = 0;
unsigned long flags;
 
typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
wValue = le16_to_cpu (cmd->wValue);
wIndex = le16_to_cpu (cmd->wIndex);
wLength = le16_to_cpu (cmd->wLength);
 
if (wLength > urb->transfer_buffer_length)
goto error;
/* set up for success */
urb->status = 0;
urb->actual_length = wLength;
 
switch (typeReq) {
 
/* DEVICE REQUESTS */
 
case DeviceRequest | USB_REQ_GET_STATUS:
// DEVICE_REMOTE_WAKEUP
ubuf [0] = 1; // selfpowered
ubuf [1] = 0;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
case DeviceOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->controller, "no device features yet yet\n");
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
ubuf [0] = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (wValue & 0xff00) {
case USB_DT_DEVICE << 8:
if (hcd->driver->flags & HCD_USB2)
bufp = usb2_rh_dev_descriptor;
else if (hcd->driver->flags & HCD_USB11)
bufp = usb11_rh_dev_descriptor;
else
goto error;
len = 18;
break;
case USB_DT_CONFIG << 8:
if (hcd->driver->flags & HCD_USB2) {
bufp = hs_rh_config_descriptor;
len = sizeof hs_rh_config_descriptor;
} else {
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
}
break;
case USB_DT_STRING << 8:
urb->actual_length = rh_string (
wValue & 0xff, hcd,
ubuf, wLength);
break;
default:
goto error;
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
ubuf [0] = 0;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
// wValue == urb->dev->devaddr
dev_dbg (hcd->controller, "root hub device address %d\n",
wValue);
break;
 
/* INTERFACE REQUESTS (no defined feature/status flags) */
 
/* ENDPOINT REQUESTS */
 
case EndpointRequest | USB_REQ_GET_STATUS:
// ENDPOINT_HALT flag
ubuf [0] = 0;
ubuf [1] = 0;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->controller, "no endpoint features yet\n");
break;
 
/* CLASS REQUESTS (and errors) */
 
default:
/* non-generic request */
urb->status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex,
ubuf, wLength);
break;
error:
/* "protocol stall" on error */
urb->status = -EPIPE;
dev_dbg (hcd->controller, "unsupported hub control message (maxchild %d)\n",
urb->dev->maxchild);
}
if (urb->status) {
urb->actual_length = 0;
dev_dbg (hcd->controller, "CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d\n",
typeReq, wValue, wIndex, wLength, urb->status);
}
if (bufp) {
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
// always USB_DIR_IN, toward host
memcpy (ubuf, bufp, len);
}
 
/* any errors get returned through the urb completion */
local_irq_save (flags);
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/*
* Root Hub interrupt transfers are synthesized with a timer.
* Completions are called in_interrupt() but not in_irq().
*/
 
static void rh_report_status (unsigned long ptr);
 
static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
{
int len = 1 + (urb->dev->maxchild / 8);
 
/* rh_timer protected by hcd_data_lock */
if (hcd->rh_timer.data
|| urb->status != -EINPROGRESS
|| urb->transfer_buffer_length < len
|| !HCD_IS_RUNNING (hcd->state)) {
dev_dbg (hcd->controller,
"not queuing rh status urb, stat %d\n",
urb->status);
return -EINVAL;
}
 
init_timer (&hcd->rh_timer);
hcd->rh_timer.function = rh_report_status;
hcd->rh_timer.data = (unsigned long) urb;
/* USB 2.0 spec says 256msec; this is close enough */
hcd->rh_timer.expires = jiffies26 + HZ/4;
add_timer (&hcd->rh_timer);
urb->hcpriv = hcd; /* nonzero to indicate it's queued */
return 0;
}
 
/* timer callback */
 
static void rh_report_status (unsigned long ptr)
{
struct urb *urb;
struct usb_hcd *hcd;
int length = 0;
unsigned long flags;
 
urb = (struct urb *) ptr;
local_irq_save (flags);
spin_lock (&urb->lock);
 
/* do nothing if the urb's been unlinked */
if (!urb->dev
|| urb->status != -EINPROGRESS
|| (hcd = urb->dev->bus->hcpriv) == 0) {
spin_unlock (&urb->lock);
local_irq_restore (flags);
return;
}
 
if (!HCD_IS_SUSPENDED (hcd->state))
length = hcd->driver->hub_status_data (
hcd, urb->transfer_buffer);
 
/* complete the status urb, or retrigger the timer */
spin_lock (&hcd_data_lock);
if (length > 0) {
hcd->rh_timer.data = 0;
urb->actual_length = length;
urb->status = 0;
urb->hcpriv = 0;
} else
mod_timer (&hcd->rh_timer, jiffies26 + HZ/4);
spin_unlock (&hcd_data_lock);
spin_unlock (&urb->lock);
 
/* local irqs are always blocked in completions */
if (length > 0)
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
}
 
/*-------------------------------------------------------------------------*/
 
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
if (usb_pipeint (urb->pipe)) {
int retval;
unsigned long flags;
 
spin_lock_irqsave (&hcd_data_lock, flags);
retval = rh_status_urb (hcd, urb);
 
spin_unlock_irqrestore (&hcd_data_lock, flags);
return retval;
}
if (usb_pipecontrol (urb->pipe))
return rh_call_control (hcd, urb);
else
return -EINVAL;
}
 
/*-------------------------------------------------------------------------*/
 
void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
unsigned long flags;
 
/* note: always a synchronous unlink */
del_timer_sync (&hcd->rh_timer);
hcd->rh_timer.data = 0;
 
local_irq_save (flags);
urb->hcpriv = 0;
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
}
 
/*-------------------------------------------------------------------------*/
 
/* exported only within usbcore */
struct usb_bus *usb_bus_get (struct usb_bus *bus)
{
struct class_device *tmp;
 
if (!bus)
return NULL;
 
tmp = class_device_get(&bus->class_dev);
if (tmp)
return to_usb_bus(tmp);
else
return NULL;
}
 
/* exported only within usbcore */
void usb_bus_put (struct usb_bus *bus)
{
if (bus)
class_device_put(&bus->class_dev);
}
 
/*-------------------------------------------------------------------------*/
 
static void usb_host_release(struct class_device *class_dev)
{
struct usb_bus *bus = to_usb_bus(class_dev);
 
if (bus->release)
bus->release(bus);
}
 
static struct class usb_host_class = {
.name = "usb_host",
.release = &usb_host_release,
};
 
void usb_host_init(void)
{
class_register(&usb_host_class);
}
 
void usb_host_cleanup(void)
{
class_unregister(&usb_host_class);
}
 
/**
* usb_bus_init - shared initialization code
* @bus: the bus structure being initialized
*
* This code is used to initialize a usb_bus structure, memory for which is
* separately managed.
*/
void usb_bus_init (struct usb_bus *bus)
{
memset (&bus->devmap, 0, sizeof(struct usb_devmap));
 
bus->devnum_next = 1;
 
bus->root_hub = NULL;
bus->hcpriv = NULL;
bus->busnum = -1;
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
 
INIT_LIST_HEAD (&bus->bus_list);
}
EXPORT_SYMBOL (usb_bus_init);
 
/**
* usb_alloc_bus - creates a new USB host controller structure
* @op: pointer to a struct usb_operations that this bus structure should use
* Context: !in_interrupt()
*
* Creates a USB host controller bus structure with the specified
* usb_operations and initializes all the necessary internal objects.
*
* If no memory is available, NULL is returned.
*
* The caller should call usb_put_bus() when it is finished with the structure.
*/
struct usb_bus *usb_alloc_bus (struct usb_operations *op)
{
struct usb_bus *bus;
 
bus = kmalloc (sizeof *bus, GFP_KERNEL);
if (!bus)
return NULL;
memset(bus, 0, sizeof(struct usb_bus));
usb_bus_init (bus);
bus->op = op;
return bus;
}
EXPORT_SYMBOL (usb_alloc_bus);
 
/*-------------------------------------------------------------------------*/
 
/**
* usb_register_bus - registers the USB host controller with the usb core
* @bus: pointer to the bus to register
* Context: !in_interrupt()
*
* Assigns a bus number, and links the controller into usbcore data
* structures so that it can be seen by scanning the bus list.
*/
int usb_register_bus(struct usb_bus *bus)
{
int busnum;
int retval;
 
down (&usb_bus_list_lock);
 
busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
if (busnum < USB_MAXBUS) {
set_bit (busnum, busmap.busmap);
bus->busnum = busnum;
} else
warn ("too many buses");
 
snprintf26(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum);
bus->class_dev.class = &usb_host_class;
bus->class_dev.dev = bus->controller;
retval = class_device_register(&bus->class_dev);
if (retval) {
clear_bit(busnum, busmap.busmap);
up(&usb_bus_list_lock);
return retval;
}
 
/* Add it to the local list of buses */
list_add (&bus->bus_list, &usb_bus_list);
up (&usb_bus_list_lock);
usbfs_add_bus (bus);
 
dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
return 0;
}
EXPORT_SYMBOL (usb_register_bus);
 
/**
* usb_deregister_bus - deregisters the USB host controller
* @bus: pointer to the bus to deregister
* Context: !in_interrupt()
*
* Recycles the bus number, and unlinks the controller from usbcore data
* structures so that it won't be seen by scanning the bus list.
*/
void usb_deregister_bus (struct usb_bus *bus)
{
dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);
 
/*
* NOTE: make sure that all the devices are removed by the
* controller code, as well as having it call this when cleaning
* itself up
*/
down (&usb_bus_list_lock);
list_del (&bus->bus_list);
up (&usb_bus_list_lock);
 
usbfs_remove_bus (bus);
 
clear_bit (bus->busnum, busmap.busmap);
 
class_device_unregister(&bus->class_dev);
}
EXPORT_SYMBOL (usb_deregister_bus);
 
/**
* usb_register_root_hub - called by HCD to register its root hub
* @usb_dev: the usb root hub device to be registered.
* @parent_dev: the parent device of this root hub.
*
* The USB host controller calls this function to register the root hub
* properly with the USB subsystem. It sets up the device properly in
* the driverfs tree, and then calls usb_new_device() to register the
* usb device. It also assigns the root hub's USB address (always 1).
*/
int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
{
const int devnum = 1;
int retval;
 
sprintf26 (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum);
usb_dev->state = USB_STATE_DEFAULT;
 
usb_dev->devnum = devnum;
usb_dev->bus->devnum_next = devnum + 1;
set_bit (devnum, usb_dev->bus->devmap.devicemap);
 
retval = usb_new_device (usb_dev, parent_dev);
 
if (retval)
dev_err (parent_dev, "can't register root hub for %s, %d\n",
usb_dev->dev.bus_id, retval);
return retval;
}
EXPORT_SYMBOL (usb_register_root_hub);
 
 
/*-------------------------------------------------------------------------*/
 
/**
* usb_calc_bus_time - approximate periodic transaction time in nanoseconds
* @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
* @is_input: true iff the transaction sends data to the host
* @isoc: true for isochronous transactions, false for interrupt ones
* @bytecount: how many bytes in the transaction.
*
* Returns approximate bus time in nanoseconds for a periodic transaction.
* See USB 2.0 spec section 5.11.3; only periodic transfers need to be
* scheduled in software, this function is only used for such scheduling.
*/
long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
{
unsigned long tmp;
 
switch (speed) {
case USB_SPEED_LOW: /* INTR only */
if (is_input) {
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
} else {
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
}
case USB_SPEED_FULL: /* ISOC or INTR */
if (isoc) {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
} else {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (9107L + BW_HOST_DELAY + tmp);
}
case USB_SPEED_HIGH: /* ISOC or INTR */
// FIXME adjust for input vs output
if (isoc)
tmp = HS_USECS (bytecount);
else
tmp = HS_USECS_ISO (bytecount);
return tmp;
default:
dbg ("bogus device speed!");
return -1;
}
}
EXPORT_SYMBOL (usb_calc_bus_time);
 
/*
* usb_check_bandwidth():
*
* old_alloc is from host_controller->bandwidth_allocated in microseconds;
* bustime is from calc_bus_time(), but converted to microseconds.
*
* returns <bustime in us> if successful,
* or -ENOSPC if bandwidth request fails.
*
* FIXME:
* This initial implementation does not use Endpoint.bInterval
* in managing bandwidth allocation.
* It probably needs to be expanded to use Endpoint.bInterval.
* This can be done as a later enhancement (correction).
*
* This will also probably require some kind of
* frame allocation tracking...meaning, for example,
* that if multiple drivers request interrupts every 10 USB frames,
* they don't all have to be allocated at
* frame numbers N, N+10, N+20, etc. Some of them could be at
* N+11, N+21, N+31, etc., and others at
* N+12, N+22, N+32, etc.
*
* Similarly for isochronous transfers...
*
* Individual HCDs can schedule more directly ... this logic
* is not correct for high speed transfers.
*/
int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
{
unsigned int pipe = urb->pipe;
long bustime;
int is_in = usb_pipein (pipe);
int is_iso = usb_pipeisoc (pipe);
int old_alloc = dev->bus->bandwidth_allocated;
int new_alloc;
 
 
bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
usb_maxpacket (dev, pipe, !is_in)));
if (is_iso)
bustime /= urb->number_of_packets;
 
new_alloc = old_alloc + (int) bustime;
if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
#ifdef DEBUG
char *mode =
#ifdef CONFIG_USB_BANDWIDTH
"";
#else
"would have ";
#endif
dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
mode, old_alloc, bustime, new_alloc);
#endif
#ifdef CONFIG_USB_BANDWIDTH
bustime = -ENOSPC; /* report error */
#endif
}
 
return bustime;
}
EXPORT_SYMBOL (usb_check_bandwidth);
 
 
/**
* usb_claim_bandwidth - records bandwidth for a periodic transfer
* @dev: source/target of request
* @urb: request (urb->dev == dev)
* @bustime: bandwidth consumed, in (average) microseconds per frame
* @isoc: true iff the request is isochronous
*
* Bus bandwidth reservations are recorded purely for diagnostic purposes.
* HCDs are expected not to overcommit periodic bandwidth, and to record such
* reservations whenever endpoints are added to the periodic schedule.
*
* FIXME averaging per-frame is suboptimal. Better to sum over the HCD's
* entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
* for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
* large its periodic schedule is.
*/
void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
{
dev->bus->bandwidth_allocated += bustime;
if (isoc)
dev->bus->bandwidth_isoc_reqs++;
else
dev->bus->bandwidth_int_reqs++;
urb->bandwidth = bustime;
 
#ifdef USB_BANDWIDTH_MESSAGES
dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
bustime,
isoc ? "ISOC" : "INTR",
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
#endif
}
EXPORT_SYMBOL (usb_claim_bandwidth);
 
 
/**
* usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
* @dev: source/target of request
* @urb: request (urb->dev == dev)
* @isoc: true iff the request is isochronous
*
* This records that previously allocated bandwidth has been released.
* Bandwidth is released when endpoints are removed from the host controller's
* periodic schedule.
*/
void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
{
dev->bus->bandwidth_allocated -= urb->bandwidth;
if (isoc)
dev->bus->bandwidth_isoc_reqs--;
else
dev->bus->bandwidth_int_reqs--;
 
#ifdef USB_BANDWIDTH_MESSAGES
dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
urb->bandwidth,
isoc ? "ISOC" : "INTR",
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
#endif
urb->bandwidth = 0;
}
EXPORT_SYMBOL (usb_release_bandwidth);
 
 
/*-------------------------------------------------------------------------*/
 
/*
* Generic HC operations.
*/
 
/*-------------------------------------------------------------------------*/
 
/* called from khubd, or root hub init threads for hcd-private init */
static int hcd_alloc_dev (struct usb_device *udev)
{
struct hcd_dev *dev;
struct usb_hcd *hcd;
unsigned long flags;
 
if (!udev || udev->hcpriv)
return -EINVAL;
if (!udev->bus || !udev->bus->hcpriv)
return -ENODEV;
hcd = udev->bus->hcpriv;
if (hcd->state == USB_STATE_QUIESCING)
return -ENOLINK;
 
dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
memset (dev, 0, sizeof *dev);
 
INIT_LIST_HEAD (&dev->dev_list);
INIT_LIST_HEAD (&dev->urb_list);
 
spin_lock_irqsave (&hcd_data_lock, flags);
list_add (&dev->dev_list, &hcd->dev_list);
// refcount is implicit
udev->hcpriv = dev;
spin_unlock_irqrestore (&hcd_data_lock, flags);
 
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
static void urb_unlink (struct urb *urb)
{
unsigned long flags;
struct usb_device *dev;
 
/* Release any periodic transfer bandwidth */
if (urb->bandwidth)
usb_release_bandwidth (urb->dev, urb,
usb_pipeisoc (urb->pipe));
 
/* clear all state linking urb to this dev (and hcd) */
 
spin_lock_irqsave (&hcd_data_lock, flags);
list_del_init (&urb->urb_list);
dev = urb->dev;
spin_unlock_irqrestore (&hcd_data_lock, flags);
usb_put_dev (dev);
}
 
 
/* may be called in any context with a valid urb->dev usecount
* caller surrenders "ownership" of urb
* expects usb_submit_urb() to have sanity checked and conditioned all
* inputs in the urb
*/
static int hcd_submit_urb (struct urb *urb, int mem_flags)
{
int status;
struct usb_hcd *hcd = urb->dev->bus->hcpriv;
struct hcd_dev *dev = urb->dev->hcpriv;
unsigned long flags;
 
if (!hcd || !dev)
return -ENODEV;
 
/*
* FIXME: make urb timeouts be generic, keeping the HCD cores
* as simple as possible.
*/
 
// NOTE: a generic device/urb monitoring hook would go here.
// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
// It would catch submission paths for all urbs.
 
/*
* Atomically queue the urb, first to our records, then to the HCD.
* Access to urb->status is controlled by urb->lock ... changes on
* i/o completion (normal or fault) or unlinking.
*/
 
// FIXME: verify that quiescing hc works right (RH cleans up)
spin_lock_irqsave (&hcd_data_lock, flags);
if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
usb_get_dev (urb->dev);
list_add_tail (&urb->urb_list, &dev->urb_list);
status = 0;
} else {
INIT_LIST_HEAD (&urb->urb_list);
status = -ESHUTDOWN;
}
spin_unlock_irqrestore (&hcd_data_lock, flags);
if (status)
return status;
 
/* increment urb's reference count as part of giving it to the HCD
* (which now controls it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
urb = usb_get_urb (urb);
if (urb->dev == hcd->self.root_hub) {
/* NOTE: requirement on hub callers (usbfs and the hub
* driver, for now) that URBs' urb->transfer_buffer be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data.
*/
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
status = rh_urb_enqueue (hcd, urb);
goto done;
}
 
/* lower level hcd code should use *_dma exclusively,
* unless it uses pio or talks to another transport.
*/
if (hcd->controller->dma_mask) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
urb->setup_dma = dma_map_single (
hcd->controller,
urb->setup_packet,
sizeof (struct usb_ctrlrequest),
DMA_TO_DEVICE);
if (urb->transfer_buffer_length != 0
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
urb->transfer_dma = dma_map_single (
hcd->controller,
urb->transfer_buffer,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? DMA_FROM_DEVICE
: DMA_TO_DEVICE);
}
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
 
done:
if (status) {
usb_put_urb (urb);
urb_unlink (urb);
}
return status;
}
 
/*-------------------------------------------------------------------------*/
 
/* called in any context */
static int hcd_get_frame_number (struct usb_device *udev)
{
struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
if (!HCD_IS_RUNNING (hcd->state))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd);
}
 
/*-------------------------------------------------------------------------*/
 
/* this makes the hcd giveback() the urb more quickly, by kicking it
* off hardware queues (which may take a while) and returning it as
* soon as practical. we've already set up the urb's return status,
* but we can't know if the callback completed already.
*/
static void
unlink1 (struct usb_hcd *hcd, struct urb *urb)
{
if (urb == (struct urb *) hcd->rh_timer.data)
usb_rh_status_dequeue (hcd, urb);
else {
int value;
 
/* failures "should" be harmless */
value = hcd->driver->urb_dequeue (hcd, urb);
if (value != 0)
dev_dbg (hcd->controller,
"dequeue %p --> %d\n",
urb, value);
}
}
 
struct completion_splice { // modified urb context:
/* did we complete? */
struct completion done;
 
/* original urb data */
usb_complete_t complete;
void *context;
};
 
static void unlink_complete (struct urb *urb, struct pt_regs *regs)
{
struct completion_splice *splice;
 
splice = (struct completion_splice *) urb->context;
 
/* issue original completion call */
urb->complete = splice->complete;
urb->context = splice->context;
urb->complete (urb, regs);
 
/* then let the synchronous unlink call complete */
complete (&splice->done);
}
 
/*
* called in any context; note ASYNC_UNLINK restrictions
*
* caller guarantees urb won't be recycled till both unlink()
* and the urb's completion function return
*/
static int hcd_unlink_urb (struct urb *urb)
{
struct hcd_dev *dev;
struct usb_hcd *hcd = 0;
struct device *sys = 0;
unsigned long flags;
struct completion_splice splice;
int retval;
 
if (!urb)
return -EINVAL;
 
/*
* we contend for urb->status with the hcd core,
* which changes it while returning the urb.
*
* Caller guaranteed that the urb pointer hasn't been freed, and
* that it was submitted. But as a rule it can't know whether or
* not it's already been unlinked ... so we respect the reversed
* lock sequence needed for the usb_hcd_giveback_urb() code paths
* (urb lock, then hcd_data_lock) in case some other CPU is now
* unlinking it.
*/
spin_lock_irqsave (&urb->lock, flags);
spin_lock (&hcd_data_lock);
 
if (!urb->dev || !urb->dev->bus) {
retval = -ENODEV;
goto done;
}
 
dev = urb->dev->hcpriv;
sys = &urb->dev->dev;
hcd = urb->dev->bus->hcpriv;
if (!dev || !hcd) {
retval = -ENODEV;
goto done;
}
 
/* running ~= hc unlink handshake works (irq, timer, etc)
* halted ~= no unlink handshake is needed
* suspended, resuming == should never happen
*/
WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
 
if (!urb->hcpriv) {
retval = -EINVAL;
goto done;
}
 
/* Any status except -EINPROGRESS means something already started to
* unlink this URB from the hardware. So there's no more work to do.
*
* FIXME use better explicit urb state
*/
if (urb->status != -EINPROGRESS) {
retval = -EBUSY;
goto done;
}
 
/* PCI IRQ setup can easily be broken so that USB controllers
* never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address().
*/
if (!hcd->saw_irq) {
dev_warn (hcd->controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help."
"\n");
hcd->saw_irq = 1;
}
 
/* maybe set up to block until the urb's completion fires. the
* lower level hcd code is always async, locking on urb->status
* updates; an intercepted completion unblocks us.
*/
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
if (in_interrupt ()) {
dev_dbg (hcd->controller, "non-async unlink in_interrupt");
retval = -EWOULDBLOCK;
goto done;
}
/* synchronous unlink: block till we see the completion */
init_completion (&splice.done);
splice.complete = urb->complete;
splice.context = urb->context;
urb->complete = unlink_complete;
urb->context = &splice;
urb->status = -ENOENT;
} else {
/* asynchronous unlink */
urb->status = -ECONNRESET;
}
 
spin_unlock (&hcd_data_lock);
spin_unlock_irqrestore (&urb->lock, flags);
// FIXME remove splicing, so this becomes unlink1 (hcd, urb);
if (urb == (struct urb *) hcd->rh_timer.data) {
usb_rh_status_dequeue (hcd, urb);
retval = 0;
} else {
retval = hcd->driver->urb_dequeue (hcd, urb);
/* hcds shouldn't really fail these calls, but... */
if (retval) {
dev_dbg (sys, "dequeue %p --> %d\n", urb, retval);
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
spin_lock_irqsave (&urb->lock, flags);
urb->complete = splice.complete;
urb->context = splice.context;
spin_unlock_irqrestore (&urb->lock, flags);
}
goto bye;
}
}
 
/* block till giveback, if needed */
if (urb->transfer_flags & URB_ASYNC_UNLINK)
return -EINPROGRESS;
wait_for_completion (&splice.done);
return 0;
 
done:
spin_unlock (&hcd_data_lock);
spin_unlock_irqrestore (&urb->lock, flags);
bye:
if (retval && sys && sys->driver)
dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
return retval;
}
 
/*-------------------------------------------------------------------------*/
 
/* disables the endpoint: cancels any pending urbs, then synchronizes with
* the hcd to make sure all endpoint state is gone from hardware. use for
* set_configuration, set_interface, driver removal, physical disconnect.
*
* example: a qh stored in hcd_dev.ep[], holding state related to endpoint
* type, maxpacket size, toggle, halt status, and scheduling.
*/
static void hcd_endpoint_disable (struct usb_device *udev, int endpoint)
{
struct hcd_dev *dev;
struct usb_hcd *hcd;
struct urb *urb;
unsigned epnum = endpoint & USB_ENDPOINT_NUMBER_MASK;
 
dev = udev->hcpriv;
hcd = udev->bus->hcpriv;
 
WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
 
local_irq_disable ();
 
rescan:
/* (re)block new requests, as best we can */
if (endpoint & USB_DIR_IN) {
usb_endpoint_halt (udev, epnum, 0);
udev->epmaxpacketin [epnum] = 0;
} else {
usb_endpoint_halt (udev, epnum, 1);
udev->epmaxpacketout [epnum] = 0;
}
 
/* then kill any current requests */
spin_lock (&hcd_data_lock);
list_for_each_entry (urb, &dev->urb_list, urb_list) {
int tmp = urb->pipe;
 
/* ignore urbs for other endpoints */
if (usb_pipeendpoint (tmp) != epnum)
continue;
/* NOTE assumption that only ep0 is a control endpoint */
if (epnum != 0 && ((tmp ^ endpoint) & USB_DIR_IN))
continue;
 
/* another cpu may be in hcd, spinning on hcd_data_lock
* to giveback() this urb. the races here should be
* small, but a full fix needs a new "can't submit"
* urb state.
*/
if (urb->status != -EINPROGRESS)
continue;
usb_get_urb (urb);
spin_unlock (&hcd_data_lock);
 
spin_lock (&urb->lock);
tmp = urb->status;
if (tmp == -EINPROGRESS)
urb->status = -ESHUTDOWN;
spin_unlock (&urb->lock);
 
/* kick hcd unless it's already returning this */
if (tmp == -EINPROGRESS) {
tmp = urb->pipe;
unlink1 (hcd, urb);
dev_dbg (hcd->controller,
"shutdown urb %p pipe %08x ep%d%s%s\n",
urb, tmp, usb_pipeendpoint (tmp),
(tmp & USB_DIR_IN) ? "in" : "out",
({ char *s; \
switch (usb_pipetype (tmp)) { \
case PIPE_CONTROL: s = ""; break; \
case PIPE_BULK: s = "-bulk"; break; \
case PIPE_INTERRUPT: s = "-intr"; break; \
default: s = "-iso"; break; \
}; s;}));
}
usb_put_urb (urb);
 
/* list contents may have changed */
goto rescan;
}
spin_unlock (&hcd_data_lock);
local_irq_enable ();
 
/* synchronize with the hardware, so old configuration state
* clears out immediately (and will be freed).
*/
might_sleep ();
if (hcd->driver->endpoint_disable)
hcd->driver->endpoint_disable (hcd, dev, endpoint);
}
 
/*-------------------------------------------------------------------------*/
 
/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup.
* we're guaranteed that the device is fully quiesced. also, that each
* endpoint has been hcd_endpoint_disabled.
*/
 
static int hcd_free_dev (struct usb_device *udev)
{
struct hcd_dev *dev;
struct usb_hcd *hcd;
unsigned long flags;
 
if (!udev || !udev->hcpriv)
return -EINVAL;
 
if (!udev->bus || !udev->bus->hcpriv)
return -ENODEV;
 
// should udev->devnum == -1 ??
 
dev = udev->hcpriv;
hcd = udev->bus->hcpriv;
 
/* device driver problem with refcounts? */
if (!list_empty (&dev->urb_list)) {
dev_dbg (hcd->controller, "free busy dev, %s devnum %d (bug!)\n",
hcd->self.bus_name, udev->devnum);
return -EINVAL;
}
 
spin_lock_irqsave (&hcd_data_lock, flags);
list_del (&dev->dev_list);
udev->hcpriv = NULL;
spin_unlock_irqrestore (&hcd_data_lock, flags);
 
kfree (dev);
return 0;
}
 
/*
* usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
*
* When registering a USB bus through the HCD framework code, use this
* usb_operations vector. The PCI glue layer does so automatically; only
* bus glue for non-PCI system busses will need to use this.
*/
struct usb_operations usb_hcd_operations = {
.allocate = hcd_alloc_dev,
.get_frame_number = hcd_get_frame_number,
.submit_urb = hcd_submit_urb,
.unlink_urb = hcd_unlink_urb,
.deallocate = hcd_free_dev,
.buffer_alloc = hcd_buffer_alloc,
.buffer_free = hcd_buffer_free,
.disable = hcd_endpoint_disable,
};
EXPORT_SYMBOL (usb_hcd_operations);
 
/*-------------------------------------------------------------------------*/
 
/**
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
* @urb: urb being returned to the USB device driver.
* @regs: pt_regs, passed down to the URB completion handler
* Context: in_interrupt()
*
* This hands the URB from HCD to its USB device driver, using its
* completion function. The HCD has freed all per-urb resources
* (and is done using urb->hcpriv). It also released all HCD locks;
* the device driver won't cause problems if it frees, modifies,
* or resubmits this URB.
*/
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
{
urb_unlink (urb);
 
// NOTE: a generic device/urb monitoring hook would go here.
// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
// It would catch exit/unlink paths for all urbs.
 
 
/* lower level hcd code should use *_dma exclusively */
if (hcd->controller->dma_mask) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
pci_unmap_single (hcd->pdev, urb->setup_dma,
sizeof (struct usb_ctrlrequest),
PCI_DMA_TODEVICE);
if (urb->transfer_buffer_length != 0
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
pci_unmap_single (hcd->pdev, urb->transfer_dma,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
/* pass ownership to the completion handler */
urb->complete (urb, regs);
usb_put_urb (urb);
}
EXPORT_SYMBOL (usb_hcd_giveback_urb);
 
/*-------------------------------------------------------------------------*/
 
/**
* usb_hcd_irq - hook IRQs to HCD framework (bus glue)
* @irq: the IRQ being raised
* @__hcd: pointer to the HCD whose IRQ is beinng signaled
* @r: saved hardware registers
*
* When registering a USB bus through the HCD framework code, use this
* to handle interrupts. The PCI glue layer does so automatically; only
* bus glue for non-PCI system busses will need to use this.
*/
irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
 
if (/*unlikely*/ (hcd->state == USB_STATE_HALT)) /* irq sharing? */
return IRQ_NONE;
 
hcd->saw_irq = 1;
hcd->driver->irq (hcd, r);
if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd);
return IRQ_HANDLED;
}
EXPORT_SYMBOL (usb_hcd_irq);
 
/*-------------------------------------------------------------------------*/
 
static void hcd_panic (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
struct usb_device *hub = hcd->self.root_hub;
unsigned i;
 
/* hc's root hub is removed later removed in hcd->stop() */
hub->state = USB_STATE_NOTATTACHED;
for (i = 0; i < hub->maxchild; i++) {
if (hub->children [i])
usb_disconnect (&hub->children [i]);
}
}
 
/**
* usb_hc_died - report abnormal shutdown of a host controller (bus glue)
* @hcd: pointer to the HCD representing the controller
*
* This is called by bus glue to report a USB host controller that died
* while operations may still have been pending. It's called automatically
* by the PCI glue, so only glue for non-PCI busses should need to call it.
*/
void usb_hc_died (struct usb_hcd *hcd)
{
dev_err (hcd->controller, "HC died; cleaning up\n");
 
/* clean up old urbs and devices; needs a task context */
INIT_WORK (&hcd->work, hcd_panic, hcd);
(void) schedule_work (&hcd->work);
}
EXPORT_SYMBOL (usb_hc_died);
 
/shark/trunk/drivers/usb/core/hcd.h
0,0 → 1,424
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
 
#ifdef __KERNEL__
 
/* This file contains declarations of usbcore internals that are mostly
* used or exposed by Host Controller Drivers.
*/
 
/*
* USB Packet IDs (PIDs)
*/
#define USB_PID_UNDEF_0 0xf0
#define USB_PID_OUT 0xe1
#define USB_PID_ACK 0xd2
#define USB_PID_DATA0 0xc3
#define USB_PID_PING 0xb4 /* USB 2.0 */
#define USB_PID_SOF 0xa5
#define USB_PID_NYET 0x96 /* USB 2.0 */
#define USB_PID_DATA2 0x87 /* USB 2.0 */
#define USB_PID_SPLIT 0x78 /* USB 2.0 */
#define USB_PID_IN 0x69
#define USB_PID_NAK 0x5a
#define USB_PID_DATA1 0x4b
#define USB_PID_PREAMBLE 0x3c /* Token mode */
#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
#define USB_PID_SETUP 0x2d
#define USB_PID_STALL 0x1e
#define USB_PID_MDATA 0x0f /* USB 2.0 */
 
/*-------------------------------------------------------------------------*/
 
/*
* USB Host Controller Driver (usb_hcd) framework
*
* Since "struct usb_bus" is so thin, you can't share much code in it.
* This framework is a layer over that, and should be more sharable.
*/
 
/*-------------------------------------------------------------------------*/
 
struct usb_hcd { /* usb_bus.hcpriv points to this */
 
/*
* housekeeping
*/
struct usb_bus self; /* hcd is-a bus */
 
const char *product_desc; /* product/vendor string */
const char *description; /* "ehci-hcd" etc */
 
struct timer_list rh_timer; /* drives root hub */
struct list_head dev_list; /* devices on this bus */
struct work_struct work;
 
/*
* hardware info/state
*/
struct hc_driver *driver; /* hw-specific hooks */
unsigned saw_irq : 1;
int irq; /* irq allocated */
void *regs; /* device memory/io */
struct device *controller; /* handle to hardware */
 
/* a few non-PCI controllers exist, mostly for OHCI */
struct pci_dev *pdev; /* pci is typical */
#ifdef CONFIG_PCI
int region; /* pci region for regs */
u32 pci_state [16]; /* for PM state save */
#endif
 
#define HCD_BUFFER_POOLS 4
struct pci_pool *pool [HCD_BUFFER_POOLS];
 
int state;
# define __ACTIVE 0x01
# define __SUSPEND 0x04
# define __TRANSIENT 0x80
 
# define USB_STATE_HALT 0
# define USB_STATE_RUNNING (__ACTIVE)
# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)
# define USB_STATE_SUSPENDED (__SUSPEND)
 
#define HCD_IS_RUNNING(state) ((state) & __ACTIVE)
#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND)
 
/* more shared queuing code would be good; it should support
* smarter scheduling, handle transaction translators, etc;
* input size of periodic table to an interrupt scheduler.
* (ohci 32, uhci 1024, ehci 256/512/1024).
*/
};
 
/* 2.4 does this a bit differently ... */
static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
{
return &hcd->self;
}
 
 
struct hcd_dev { /* usb_device.hcpriv points to this */
struct list_head dev_list; /* on this hcd */
struct list_head urb_list; /* pending on this dev */
 
/* per-configuration HC/HCD state, such as QH or ED */
void *ep[32];
};
 
// urb.hcpriv is really hardware-specific
 
struct hcd_timeout { /* timeouts we allocate */
struct list_head timeout_list;
struct timer_list timer;
};
 
/*-------------------------------------------------------------------------*/
 
/*
* FIXME usb_operations should vanish or become hc_driver,
* when usb_bus and usb_hcd become the same thing.
*/
 
struct usb_operations {
int (*allocate)(struct usb_device *);
int (*deallocate)(struct usb_device *);
int (*get_frame_number) (struct usb_device *usb_dev);
int (*submit_urb) (struct urb *urb, int mem_flags);
int (*unlink_urb) (struct urb *urb);
 
/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
int mem_flags,
dma_addr_t *dma);
void (*buffer_free)(struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
 
void (*disable)(struct usb_device *udev, int bEndpointAddress);
};
 
/* each driver provides one of these, and hardware init support */
 
struct pt_regs;
 
struct hc_driver {
const char *description; /* "ehci-hcd" etc */
 
/* irq handler */
void (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
 
int flags;
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
#define HCD_USB11 0x0010 /* USB 1.1 */
#define HCD_USB2 0x0020 /* USB 2.0 */
 
/* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
 
/* called after all devices were suspended */
int (*suspend) (struct usb_hcd *hcd, u32 state);
 
/* called before any devices get resumed */
int (*resume) (struct usb_hcd *hcd);
 
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
 
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
 
/* memory lifecycle */
struct usb_hcd *(*hcd_alloc) (void);
void (*hcd_free) (struct usb_hcd *hcd);
 
/* manage i/o requests, device state */
int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,
int mem_flags);
int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
 
/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd,
struct hcd_dev *dev, int bEndpointAddress);
 
/* root hub support */
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
int (*hub_control) (struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
};
 
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
extern void usb_bus_init (struct usb_bus *bus);
extern void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
 
#ifdef CONFIG_PCI
struct pci_dev;
struct pci_device_id;
extern int usb_hcd_pci_probe (struct pci_dev *dev,
const struct pci_device_id *id);
extern void usb_hcd_pci_remove (struct pci_dev *dev);
 
#ifdef CONFIG_PM
extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);
extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PM */
 
#endif /* CONFIG_PCI */
 
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
int hcd_buffer_create (struct usb_hcd *hcd);
void hcd_buffer_destroy (struct usb_hcd *hcd);
 
void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
int mem_flags, dma_addr_t *dma);
void hcd_buffer_free (struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
 
/* generic bus glue, needed for host controllers that don't use PCI */
extern struct usb_operations usb_hcd_operations;
extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
extern void usb_hc_died (struct usb_hcd *hcd);
 
/* -------------------------------------------------------------------------- */
 
/* Enumeration is only for the hub driver, or HCD virtual root hubs */
extern int usb_new_device(struct usb_device *dev, struct device *parent);
extern void usb_choose_address(struct usb_device *dev);
extern void usb_disconnect(struct usb_device **);
 
/* exported to hub driver ONLY to support usb_reset_device () */
extern int usb_get_configuration(struct usb_device *dev);
extern void usb_destroy_configuration(struct usb_device *dev);
extern int usb_set_address(struct usb_device *dev);
 
/* use these only before the device's address has been set */
#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30))
#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | USB_DIR_IN)
 
/*-------------------------------------------------------------------------*/
 
/*
* HCD Root Hub support
*/
 
#include "hub.h"
 
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
#define DeviceRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define DeviceOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
 
#define InterfaceRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
 
#define EndpointRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
 
/* table 9.6 standard features */
#define DEVICE_REMOTE_WAKEUP 1
#define ENDPOINT_HALT 0
 
/* class requests from the USB 2.0 hub spec, table 11-15 */
/* GetBusState and SetHubDescriptor are optional, omitted */
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
 
 
/*-------------------------------------------------------------------------*/
 
/*
* Generic bandwidth allocation constants/support
*/
#define FRAME_TIME_USECS 1000L
#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
/* Trying not to use worst-case bit-stuffing
of (7/6 * 8 * bytecount) = 9.33 * bytecount */
/* bytecount = data payload byte count */
 
#define NS_TO_US(ns) ((ns + 500L) / 1000L)
/* convert & round nanoseconds to microseconds */
 
extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
int bustime, int isoc);
extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
int isoc);
 
/*
* Full/low speed bandwidth allocation constants/support.
*/
#define BW_HOST_DELAY 1000L /* nanoseconds */
#define BW_HUB_LS_SETUP 333L /* nanoseconds */
/* 4 full-speed bit times (est.) */
 
#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
 
extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
 
/*
* Ceiling microseconds (typical) for that many bytes at high speed
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
* to preallocate bandwidth)
*/
#define USB2_HOST_DELAY 5 /* nsec, guess */
#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
 
extern long usb_calc_bus_time (int speed, int is_input,
int isoc, int bytecount);
 
/*-------------------------------------------------------------------------*/
 
extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
 
extern int usb_register_bus (struct usb_bus *);
extern void usb_deregister_bus (struct usb_bus *);
 
extern int usb_register_root_hub (struct usb_device *usb_dev,
struct device *parent_dev);
 
/* for portability to 2.4, hcds should call this */
static inline int hcd_register_root (struct usb_hcd *hcd)
{
return usb_register_root_hub (
hcd_to_bus (hcd)->root_hub, hcd->controller);
}
 
/*-------------------------------------------------------------------------*/
 
/* exported only within usbcore */
 
extern struct list_head usb_bus_list;
extern struct semaphore usb_bus_list_lock;
 
extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
extern void usb_bus_put (struct usb_bus *bus);
 
extern int usb_find_interface_driver (struct usb_device *dev,
struct usb_interface *interface);
 
#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
 
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
 
/*
* USB device fs stuff
*/
 
#ifdef CONFIG_USB_DEVICEFS
 
/*
* these are expected to be called from the USB core/hub thread
* with the kernel lock held
*/
extern void usbfs_add_bus(struct usb_bus *bus);
extern void usbfs_remove_bus(struct usb_bus *bus);
extern void usbfs_add_device(struct usb_device *dev);
extern void usbfs_remove_device(struct usb_device *dev);
extern void usbfs_update_special (void);
 
extern int usbfs_init(void);
extern void usbfs_cleanup(void);
 
#else /* CONFIG_USB_DEVICEFS */
 
static inline void usbfs_add_bus(struct usb_bus *bus) {}
static inline void usbfs_remove_bus(struct usb_bus *bus) {}
static inline void usbfs_add_device(struct usb_device *dev) {}
static inline void usbfs_remove_device(struct usb_device *dev) {}
static inline void usbfs_update_special (void) {}
 
static inline int usbfs_init(void) { return 0; }
static inline void usbfs_cleanup(void) { }
 
#endif /* CONFIG_USB_DEVICEFS */
 
/*-------------------------------------------------------------------------*/
 
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
// bleech -- resurfaced in 2.4.11 or 2.4.12
#define bitmap DeviceRemovable
 
 
/*-------------------------------------------------------------------------*/
 
/* random stuff */
 
#define RUN_CONTEXT (in_irq () ? "in_irq" \
: (in_interrupt () ? "in_interrupt" : "can sleep"))
 
 
#endif /* __KERNEL__ */
 
/shark/trunk/drivers/usb/core/config.c
0,0 → 1,487
#include <linuxcomp.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
 
 
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXENDPOINTS 30 /* Hard limit */
 
/* these maximums are arbitrary */
#define USB_MAXCONFIG 8
#define USB_MAXINTERFACES 32
 
static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
struct usb_descriptor_header *header;
unsigned char *begin;
int numskipped;
 
header = (struct usb_descriptor_header *)buffer;
if (header->bDescriptorType != USB_DT_ENDPOINT) {
warn("unexpected descriptor 0x%X, expecting endpoint, 0x%X",
header->bDescriptorType, USB_DT_ENDPOINT);
return -EINVAL;
}
 
if (header->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
else if (header->bLength >= USB_DT_ENDPOINT_SIZE)
memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE);
else {
warn("invalid endpoint descriptor");
return -EINVAL;
}
 
if ((endpoint->desc.bEndpointAddress & ~USB_ENDPOINT_DIR_MASK) >= 16) {
warn("invalid endpoint address 0x%X",
endpoint->desc.bEndpointAddress);
return -EINVAL;
}
 
le16_to_cpus(&endpoint->desc.wMaxPacketSize);
 
buffer += header->bLength;
size -= header->bLength;
 
/* Skip over any Class Specific or Vendor Specific descriptors */
begin = buffer;
numskipped = 0;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
 
/* If we find another "proper" descriptor then we're done */
if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
(header->bDescriptorType == USB_DT_INTERFACE))
break;
 
dbg("skipping descriptor 0x%X", header->bDescriptorType);
numskipped++;
 
buffer += header->bLength;
size -= header->bLength;
}
if (numskipped) {
dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
endpoint->extra = begin;
endpoint->extralen = buffer - begin;
}
 
return buffer - buffer0;
}
 
static void usb_release_intf(struct device *dev)
{
struct usb_interface *intf;
int j;
 
intf = to_usb_interface(dev);
 
if (intf->altsetting) {
for (j = 0; j < intf->num_altsetting; j++) {
struct usb_host_interface *as = &intf->altsetting[j];
 
kfree(as->endpoint);
}
kfree(intf->altsetting);
}
kfree(intf);
}
 
static int usb_parse_interface(struct usb_host_config *config, unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
struct usb_interface_descriptor *d;
int inum, asnum;
struct usb_interface *interface;
struct usb_host_interface *ifp;
int len, numskipped;
struct usb_descriptor_header *header;
unsigned char *begin;
int i, retval;
 
d = (struct usb_interface_descriptor *) buffer;
if (d->bDescriptorType != USB_DT_INTERFACE) {
warn("unexpected descriptor 0x%X, expecting interface, 0x%X",
d->bDescriptorType, USB_DT_INTERFACE);
return -EINVAL;
}
 
inum = d->bInterfaceNumber;
if (inum >= config->desc.bNumInterfaces) {
 
/* Skip to the next interface descriptor */
buffer += d->bLength;
size -= d->bLength;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *) buffer;
 
if (header->bDescriptorType == USB_DT_INTERFACE)
break;
buffer += header->bLength;
size -= header->bLength;
}
return buffer - buffer0;
}
 
interface = config->interface[inum];
asnum = d->bAlternateSetting;
if (asnum >= interface->num_altsetting) {
warn("invalid alternate setting %d for interface %d",
asnum, inum);
return -EINVAL;
}
 
ifp = &interface->altsetting[asnum];
if (ifp->desc.bLength) {
warn("duplicate descriptor for interface %d altsetting %d",
inum, asnum);
return -EINVAL;
}
memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE);
 
buffer += d->bLength;
size -= d->bLength;
 
/* Skip over any Class Specific or Vendor Specific descriptors */
begin = buffer;
numskipped = 0;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
 
/* If we find another "proper" descriptor then we're done */
if ((header->bDescriptorType == USB_DT_INTERFACE) ||
(header->bDescriptorType == USB_DT_ENDPOINT))
break;
 
dbg("skipping descriptor 0x%X", header->bDescriptorType);
numskipped++;
 
buffer += header->bLength;
size -= header->bLength;
}
if (numskipped) {
dbg("skipped %d class/vendor specific interface descriptors", numskipped);
ifp->extra = begin;
ifp->extralen = buffer - begin;
}
 
if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) {
warn("too many endpoints for interface %d altsetting %d",
inum, asnum);
return -EINVAL;
}
 
len = ifp->desc.bNumEndpoints * sizeof(struct usb_host_endpoint);
ifp->endpoint = kmalloc(len, GFP_KERNEL);
if (!ifp->endpoint) {
err("out of memory");
return -ENOMEM;
}
memset(ifp->endpoint, 0, len);
 
for (i = 0; i < ifp->desc.bNumEndpoints; i++) {
if (size < USB_DT_ENDPOINT_SIZE) {
warn("ran out of descriptors while parsing endpoints");
return -EINVAL;
}
 
retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
if (retval < 0)
return retval;
 
buffer += retval;
size -= retval;
}
 
return buffer - buffer0;
}
 
int usb_parse_configuration(struct usb_host_config *config, char *buffer, int size)
{
int nintf, nintf_orig;
int i, j;
struct usb_interface *interface;
char *buffer2;
int size2;
struct usb_descriptor_header *header;
int numskipped, len;
char *begin;
int retval;
 
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
config->desc.bLength < USB_DT_CONFIG_SIZE) {
warn("invalid configuration descriptor");
return -EINVAL;
}
config->desc.wTotalLength = size;
 
nintf = nintf_orig = config->desc.bNumInterfaces;
if (nintf > USB_MAXINTERFACES) {
warn("too many interfaces (%d max %d)",
nintf, USB_MAXINTERFACES);
config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES;
}
 
for (i = 0; i < nintf; ++i) {
interface = config->interface[i] =
kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
dbg("kmalloc IF %p, numif %i", interface, i);
if (!interface) {
err("out of memory");
return -ENOMEM;
}
memset(interface, 0, sizeof(struct usb_interface));
interface->dev.release = usb_release_intf;
device_initialize(&interface->dev);
}
 
/* Go through the descriptors, checking their length and counting the
* number of altsettings for each interface */
buffer2 = buffer;
size2 = size;
j = 0;
while (size2 >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *) buffer2;
if ((header->bLength > size2) || (header->bLength < 2)) {
warn("invalid descriptor of length %d", header->bLength);
return -EINVAL;
}
 
if (header->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *d;
 
if (header->bLength < USB_DT_INTERFACE_SIZE) {
warn("invalid interface descriptor");
return -EINVAL;
}
d = (struct usb_interface_descriptor *) header;
i = d->bInterfaceNumber;
if (i >= nintf_orig) {
warn("invalid interface number (%d/%d)",
i, nintf_orig);
return -EINVAL;
}
if (i < nintf)
++config->interface[i]->num_altsetting;
 
} else if ((header->bDescriptorType == USB_DT_DEVICE ||
header->bDescriptorType == USB_DT_CONFIG) && j) {
warn("unexpected descriptor type 0x%X", header->bDescriptorType);
return -EINVAL;
}
 
j = 1;
buffer2 += header->bLength;
size2 -= header->bLength;
}
 
/* Allocate the altsetting arrays */
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
interface = config->interface[i];
if (interface->num_altsetting > USB_MAXALTSETTING) {
warn("too many alternate settings for interface %d (%d max %d)\n",
i, interface->num_altsetting, USB_MAXALTSETTING);
return -EINVAL;
}
if (interface->num_altsetting == 0) {
warn("no alternate settings for interface %d", i);
return -EINVAL;
}
 
len = sizeof(*interface->altsetting) * interface->num_altsetting;
interface->altsetting = kmalloc(len, GFP_KERNEL);
if (!interface->altsetting) {
err("couldn't kmalloc interface->altsetting");
return -ENOMEM;
}
memset(interface->altsetting, 0, len);
}
 
buffer += config->desc.bLength;
size -= config->desc.bLength;
 
/* Skip over any Class Specific or Vendor Specific descriptors */
begin = buffer;
numskipped = 0;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
 
/* If we find another "proper" descriptor then we're done */
if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
(header->bDescriptorType == USB_DT_INTERFACE))
break;
 
dbg("skipping descriptor 0x%X", header->bDescriptorType);
numskipped++;
 
buffer += header->bLength;
size -= header->bLength;
}
if (numskipped) {
dbg("skipped %d class/vendor specific configuration descriptors", numskipped);
config->extra = begin;
config->extralen = buffer - begin;
}
 
/* Parse all the interface/altsetting descriptors */
while (size >= sizeof(struct usb_descriptor_header)) {
retval = usb_parse_interface(config, buffer, size);
if (retval < 0)
return retval;
 
buffer += retval;
size -= retval;
}
 
/* Check for missing altsettings */
for (i = 0; i < nintf; ++i) {
interface = config->interface[i];
for (j = 0; j < interface->num_altsetting; ++j) {
if (!interface->altsetting[j].desc.bLength) {
warn("missing altsetting %d for interface %d", j, i);
return -EINVAL;
}
}
}
 
return size;
}
 
// hub-only!! ... and only exported for reset/reinit path.
// otherwise used internally on disconnect/destroy path
void usb_destroy_configuration(struct usb_device *dev)
{
int c, i;
 
if (!dev->config)
return;
 
if (dev->rawdescriptors) {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
kfree(dev->rawdescriptors[i]);
 
kfree(dev->rawdescriptors);
}
 
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
struct usb_host_config *cf = &dev->config[c];
 
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
struct usb_interface *ifp = cf->interface[i];
 
if (ifp)
put_device(&ifp->dev);
}
}
kfree(dev->config);
}
 
 
// hub-only!! ... and only in reset path, or usb_new_device()
// (used by real hubs and virtual root hubs)
int usb_get_configuration(struct usb_device *dev)
{
int ncfg = dev->descriptor.bNumConfigurations;
int result;
unsigned int cfgno, length;
unsigned char *buffer;
unsigned char *bigbuffer;
struct usb_config_descriptor *desc;
 
if (ncfg > USB_MAXCONFIG) {
warn("too many configurations (%d max %d)",
ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
 
if (ncfg < 1) {
warn("no configurations");
return -EINVAL;
}
 
length = ncfg * sizeof(struct usb_host_config);
dev->config = kmalloc(length, GFP_KERNEL);
if (!dev->config) {
err("out of memory");
return -ENOMEM;
}
memset(dev->config, 0, length);
 
length = ncfg * sizeof(char *);
dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
if (!dev->rawdescriptors) {
err("out of memory");
return -ENOMEM;
}
memset(dev->rawdescriptors, 0, length);
 
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
err("unable to allocate memory for configuration descriptors");
return -ENOMEM;
}
desc = (struct usb_config_descriptor *)buffer;
 
for (cfgno = 0; cfgno < ncfg; cfgno++) {
/* We grab the first 8 bytes so we know how long the whole */
/* configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
if (result < 8) {
if (result < 0)
err("unable to get descriptor");
else {
warn("config descriptor too short (expected %i, got %i)", 8, result);
result = -EINVAL;
}
goto err;
}
 
/* Get the full buffer */
length = max((int) le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE);
 
bigbuffer = kmalloc(length, GFP_KERNEL);
if (!bigbuffer) {
err("unable to allocate memory for configuration descriptors");
result = -ENOMEM;
goto err;
}
 
/* Now that we know the length, get the whole thing */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
if (result < 0) {
err("couldn't get all of config descriptors");
kfree(bigbuffer);
goto err;
}
 
if (result < length) {
err("config descriptor too short (expected %i, got %i)", length, result);
result = -EINVAL;
kfree(bigbuffer);
goto err;
}
 
dev->rawdescriptors[cfgno] = bigbuffer;
 
result = usb_parse_configuration(&dev->config[cfgno], bigbuffer, length);
if (result > 0)
dbg("descriptor data left");
else if (result < 0) {
++cfgno;
goto err;
}
}
 
kfree(buffer);
return 0;
err:
kfree(buffer);
dev->descriptor.bNumConfigurations = cfgno;
return result;
}
 
/shark/trunk/drivers/usb/core/hcd-pci.c
0,0 → 1,349
/*
* (C) Copyright David Brownell 2000-2002
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/usb.h>
#include "hcd.h"
 
 
/* PCI-based HCs are normal, but custom bus glue should be ok */
 
 
/*-------------------------------------------------------------------------*/
 
static void hcd_pci_release(struct usb_bus *bus)
{
struct usb_hcd *hcd = bus->hcpriv;
 
if (hcd)
hcd->driver->hcd_free(hcd);
}
 
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
 
/**
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
unsigned long resource, len;
void *base;
struct usb_hcd *hcd;
int retval, region;
char buf [8], *bufp = buf;
 
if (usb_disabled())
return -ENODEV;
 
if (!id || !(driver = (struct hc_driver *) id->driver_data))
return -EINVAL;
 
if (pci_enable_device (dev) < 0)
return -ENODEV;
if (!dev->irq) {
err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
pci_name(dev));
return -ENODEV;
}
if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
 
region = 0;
resource = pci_resource_start (dev, 0);
len = pci_resource_len (dev, 0);
if (!request_mem_region (resource, len, driver->description)) {
dbg ("controller already in use");
return -EBUSY;
}
base = ioremap_nocache (resource, len);
if (base == NULL) {
dbg ("error mapping memory");
retval = -EFAULT;
clean_1:
release_mem_region (resource, len);
err ("init %s fail, %d", pci_name(dev), retval);
return retval;
}
 
} else { // UHCI
resource = len = 0;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
continue;
 
resource = pci_resource_start (dev, region);
len = pci_resource_len (dev, region);
if (request_region (resource, len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dbg ("no i/o regions available");
return -EBUSY;
}
base = (void *) resource;
}
 
// driver->reset(), later on, will transfer device from
// control by SMM/BIOS to control by Linux (if needed)
 
hcd = driver->hcd_alloc ();
if (hcd == NULL){
dbg ("hcd alloc fail");
retval = -ENOMEM;
clean_2:
if (driver->flags & HCD_MEMORY) {
iounmap (base);
goto clean_1;
} else {
release_region (resource, len);
err ("init %s fail, %d", pci_name(dev), retval);
return retval;
}
}
 
// hcd zeroed everything
hcd->regs = base;
hcd->region = region;
 
pci_set_drvdata (dev, hcd);
hcd->driver = driver;
hcd->description = driver->description;
hcd->pdev = dev;
hcd->self.bus_name = pci_name(dev);
if (hcd->product_desc == NULL)
hcd->product_desc = "USB Host Controller";
hcd->self.controller = &dev->dev;
hcd->controller = hcd->self.controller;
 
if ((retval = hcd_buffer_create (hcd)) != 0) {
clean_3:
driver->hcd_free (hcd);
goto clean_2;
}
 
dev_info (hcd->controller, "%s\n", hcd->product_desc);
 
/* till now HC has been in an indeterminate state ... */
if (driver->reset && (retval = driver->reset (hcd)) < 0) {
dev_err (hcd->controller, "can't reset\n");
goto clean_3;
}
hcd->state = USB_STATE_HALT;
 
pci_set_master (dev);
#ifndef __sparc__
sprintf26 (buf, "%d", dev->irq);
#else
bufp = __irq_itoa(dev->irq);
#endif
 
retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
hcd->description, hcd);
if (retval != 0) {
dev_err (hcd->controller,
"request interrupt %s failed\n", bufp);
goto clean_3;
}
hcd->irq = dev->irq;
 
dev_info (hcd->controller, "irq %s, %s %p\n", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
base);
usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd;
hcd->self.release = &hcd_pci_release;
 
INIT_LIST_HEAD (&hcd->dev_list);
usb_register_bus (&hcd->self);
 
if ((retval = driver->start (hcd)) < 0)
usb_hcd_pci_remove (dev);
 
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_probe);
 
 
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
 
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
* Store this function in the HCD's struct pci_driver as remove().
*/
void usb_hcd_pci_remove (struct pci_dev *dev)
{
struct usb_hcd *hcd;
struct usb_device *hub;
 
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
dev_info (hcd->controller, "remove, state %x\n", hcd->state);
 
if (in_interrupt ())
BUG ();
 
hub = hcd->self.root_hub;
if (HCD_IS_RUNNING (hcd->state))
hcd->state = USB_STATE_QUIESCING;
 
dev_dbg (hcd->controller, "roothub graceful disconnect\n");
usb_disconnect (&hub);
 
hcd->driver->stop (hcd);
hcd_buffer_destroy (hcd);
hcd->state = USB_STATE_HALT;
pci_set_drvdata (dev, 0);
 
free_irq (hcd->irq, hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap (hcd->regs);
release_mem_region (pci_resource_start (dev, 0),
pci_resource_len (dev, 0));
} else {
release_region (pci_resource_start (dev, hcd->region),
pci_resource_len (dev, hcd->region));
}
 
usb_deregister_bus (&hcd->self);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
 
 
#ifdef CONFIG_PM
 
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
* @state: state that the controller is going into
*
* Store this function in the HCD's struct pci_driver as suspend().
*/
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
struct usb_hcd *hcd;
int retval = 0;
 
hcd = pci_get_drvdata(dev);
dev_dbg (hcd->controller, "suspend D%d --> D%d\n",
dev->current_state, state);
 
switch (hcd->state) {
case USB_STATE_HALT:
dev_dbg (hcd->controller, "halted; hcd not suspended\n");
break;
case USB_STATE_SUSPENDED:
dev_dbg (hcd->controller, "hcd already suspended\n");
break;
default:
/* remote wakeup needs hub->suspend() cooperation */
// pci_enable_wake (dev, 3, 1);
 
pci_save_state (dev, hcd->pci_state);
 
/* driver may want to disable DMA etc */
hcd->state = USB_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state);
if (retval)
dev_dbg (hcd->controller, "suspend fail, retval %d\n",
retval);
else
hcd->state = USB_STATE_SUSPENDED;
}
 
pci_set_power_state (dev, state);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
 
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
*/
int usb_hcd_pci_resume (struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
 
hcd = pci_get_drvdata(dev);
dev_dbg (hcd->controller, "resume from state D%d\n",
dev->current_state);
 
if (hcd->state != USB_STATE_SUSPENDED) {
dev_dbg (hcd->controller, "can't resume, not suspended!\n");
return -EL3HLT;
}
hcd->state = USB_STATE_RESUMING;
 
pci_set_power_state (dev, 0);
pci_restore_state (dev, hcd->pci_state);
 
/* remote wakeup needs hub->suspend() cooperation */
// pci_enable_wake (dev, 3, 0);
 
retval = hcd->driver->resume (hcd);
if (!HCD_IS_RUNNING (hcd->state)) {
dev_dbg (hcd->controller, "resume fail, retval %d\n", retval);
usb_hc_died (hcd);
}
 
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_resume);
 
#endif /* CONFIG_PM */
 
 
/shark/trunk/drivers/usb/core/buffer.c
0,0 → 1,139
/*
* DMA memory management for framework level HCD code (hc_driver)
*
* This implementation plugs in through generic "usb_bus" level methods,
* and works with real PCI, or when "pci device == null" makes sense.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
 
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
 
#include <linux/usb.h>
#include "hcd.h"
 
 
/*
* DMA-Coherent Buffers
*/
 
/* FIXME tune these based on pool statistics ... */
static const size_t pool_max [HCD_BUFFER_POOLS] = {
/* platforms without dma-friendly caches might need to
* prevent cacheline sharing...
*/
32,
128,
512,
PAGE_SIZE / 2
/* bigger --> allocate pages */
};
 
 
/* SETUP primitives */
 
/**
* hcd_buffer_create - initialize buffer pools
* @hcd: the bus whose buffer pools are to be initialized
* Context: !in_interrupt()
*
* Call this as part of initializing a host controller that uses the pci dma
* memory allocators. It initializes some pools of dma-consistent memory that
* will be shared by all drivers using that controller, or returns a negative
* errno value on error.
*
* Call hcd_buffer_destroy() to clean up after using those pools.
*/
int hcd_buffer_create (struct usb_hcd *hcd)
{
char name [16];
int i, size;
 
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (!(size = pool_max [i]))
continue;
snprintf26(name, sizeof name, "buffer-%d", size);
hcd->pool [i] = pci_pool_create (name, hcd->pdev,
size, size, 0);
if (!hcd->pool [i]) {
hcd_buffer_destroy (hcd);
return -ENOMEM;
}
}
return 0;
}
EXPORT_SYMBOL (hcd_buffer_create);
 
 
/**
* hcd_buffer_destroy - deallocate buffer pools
* @hcd: the bus whose buffer pools are to be destroyed
* Context: !in_interrupt()
*
* This frees the buffer pools created by hcd_buffer_create().
*/
void hcd_buffer_destroy (struct usb_hcd *hcd)
{
int i;
 
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
struct pci_pool *pool = hcd->pool [i];
if (pool) {
pci_pool_destroy (pool);
hcd->pool [i] = 0;
}
}
}
EXPORT_SYMBOL (hcd_buffer_destroy);
 
 
/* sometimes alloc/free could use kmalloc with SLAB_DMA, for
* better sharing and to leverage mm/slab.c intelligence.
*/
 
void *hcd_buffer_alloc (
struct usb_bus *bus,
size_t size,
int mem_flags,
dma_addr_t *dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
 
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i])
return pci_pool_alloc (hcd->pool [i], mem_flags, dma);
}
return pci_alloc_consistent (hcd->pdev, size, dma);
}
 
void hcd_buffer_free (
struct usb_bus *bus,
size_t size,
void *addr,
dma_addr_t dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
 
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i]) {
pci_pool_free (hcd->pool [i], addr, dma);
return;
}
}
pci_free_consistent (hcd->pdev, size, addr, dma);
}
/shark/trunk/drivers/usb/core/usb-debug.c
0,0 → 1,203
/*
* debug.c - USB debug helper routines.
*
* I just want these out of the way where they aren't in your
* face, but so that you can still use them..
*/
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
 
static void usb_show_endpoint(struct usb_host_endpoint *endpoint)
{
usb_show_endpoint_descriptor(&endpoint->desc);
}
 
static void usb_show_interface(struct usb_host_interface *altsetting)
{
int i;
 
usb_show_interface_descriptor(&altsetting->desc);
 
for (i = 0; i < altsetting->desc.bNumEndpoints; i++)
usb_show_endpoint(altsetting->endpoint + i);
}
 
static void usb_show_config(struct usb_host_config *config)
{
int i, j;
struct usb_interface *ifp;
 
usb_show_config_descriptor(&config->desc);
for (i = 0; i < config->desc.bNumInterfaces; i++) {
ifp = config->interface[i];
 
if (!ifp)
break;
 
printk("\n Interface: %d\n", i);
for (j = 0; j < ifp->num_altsetting; j++)
usb_show_interface(ifp->altsetting + j);
}
}
 
void usb_show_device(struct usb_device *dev)
{
int i;
 
usb_show_device_descriptor(&dev->descriptor);
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
usb_show_config(dev->config + i);
}
 
/*
* Parse and show the different USB descriptors.
*/
void usb_show_device_descriptor(struct usb_device_descriptor *desc)
{
if (!desc)
{
printk("Invalid USB device descriptor (NULL POINTER)\n");
return;
}
printk(" Length = %2d%s\n", desc->bLength,
desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");
printk(" DescriptorType = %02x\n", desc->bDescriptorType);
 
printk(" USB version = %x.%02x\n",
desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
printk(" Vendor:Product = %04x:%04x\n",
desc->idVendor, desc->idProduct);
printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0);
printk(" NumConfigurations = %d\n", desc->bNumConfigurations);
printk(" Device version = %x.%02x\n",
desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
 
printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n",
desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
switch (desc->bDeviceClass) {
case 0:
printk(" Per-interface classes\n");
break;
case USB_CLASS_AUDIO:
printk(" Audio device class\n");
break;
case USB_CLASS_COMM:
printk(" Communications class\n");
break;
case USB_CLASS_HID:
printk(" Human Interface Devices class\n");
break;
case USB_CLASS_PRINTER:
printk(" Printer device class\n");
break;
case USB_CLASS_MASS_STORAGE:
printk(" Mass Storage device class\n");
break;
case USB_CLASS_HUB:
printk(" Hub device class\n");
break;
case USB_CLASS_VENDOR_SPEC:
printk(" Vendor class\n");
break;
default:
printk(" Unknown class\n");
}
}
 
void usb_show_config_descriptor(struct usb_config_descriptor *desc)
{
printk("Configuration:\n");
printk(" bLength = %4d%s\n", desc->bLength,
desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)");
printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
printk(" wTotalLength = %04x\n", desc->wTotalLength);
printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces);
printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue);
printk(" iConfiguration = %02x\n", desc->iConfiguration);
printk(" bmAttributes = %02x\n", desc->bmAttributes);
printk(" bMaxPower = %4dmA\n", desc->bMaxPower * 2);
}
 
void usb_show_interface_descriptor(struct usb_interface_descriptor *desc)
{
printk(" Alternate Setting: %2d\n", desc->bAlternateSetting);
printk(" bLength = %4d%s\n", desc->bLength,
desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)");
printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber);
printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting);
printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints);
printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n",
desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol);
printk(" iInterface = %02x\n", desc->iInterface);
}
 
void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc)
{
char *LengthCommentString = (desc->bLength ==
USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength ==
USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)";
char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };
 
printk(" Endpoint:\n");
printk(" bLength = %4d%s\n",
desc->bLength, LengthCommentString);
printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress,
(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_CONTROL ? "i/o" :
(desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out");
printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes,
EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]);
printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize);
printk(" bInterval = %02x\n", desc->bInterval);
 
/* Audio extensions to the endpoint descriptor */
if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) {
printk(" bRefresh = %02x\n", desc->bRefresh);
printk(" bSynchAddress = %02x\n", desc->bSynchAddress);
}
}
 
void usb_show_string(struct usb_device *dev, char *id, int index)
{
char *buf;
 
if (!index)
return;
if (!(buf = kmalloc(256, GFP_KERNEL)))
return;
if (usb_string(dev, index, buf, 256) > 0)
dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf);
kfree(buf);
}
 
void usb_dump_urb (struct urb *urb)
{
printk ("urb :%p\n", urb);
printk ("dev :%p\n", urb->dev);
printk ("pipe :%08X\n", urb->pipe);
printk ("status :%d\n", urb->status);
printk ("transfer_flags :%08X\n", urb->transfer_flags);
printk ("transfer_buffer :%p\n", urb->transfer_buffer);
printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
printk ("actual_length :%d\n", urb->actual_length);
printk ("setup_packet :%p\n", urb->setup_packet);
printk ("start_frame :%d\n", urb->start_frame);
printk ("number_of_packets :%d\n", urb->number_of_packets);
printk ("interval :%d\n", urb->interval);
printk ("error_count :%d\n", urb->error_count);
printk ("context :%p\n", urb->context);
printk ("complete :%p\n", urb->complete);
}
 
/shark/trunk/drivers/usb/core/hub.c
0,0 → 1,1383
/*
* USB hub driver.
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
* (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/suspend.h>
 
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
 
#include "hcd.h"
#include "hub.h"
 
/* Wakes up khubd */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
static DECLARE_MUTEX(usb_address0_sem);
 
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */
 
static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
static pid_t khubd_pid = 0; /* PID of khubd */
static DECLARE_COMPLETION(khubd_exited);
 
//#ifdef DEBUG
static inline char *portspeed (int portstatus)
{
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
return "480 Mb/s";
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
return "1.5 Mb/s";
else
return "12 Mb/s";
}
//#endif
 
/* for dev_info, dev_dbg, etc */
static inline struct device *hubdev (struct usb_device *dev)
{
return &dev->actconfig->interface[0]->dev;
}
 
/* USB 2.0 spec Section 11.24.4.5 */
static int get_hub_descriptor(struct usb_device *dev, void *data, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
USB_DT_HUB << 8, 0, data, size, HZ * USB_CTRL_GET_TIMEOUT);
}
 
/*
* USB 2.0 spec Section 11.24.2.1
*/
static int clear_hub_feature(struct usb_device *dev, int feature)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ);
}
 
/*
* USB 2.0 spec Section 11.24.2.2
* BUG: doesn't handle port indicator selector in high byte of wIndex
*/
static int clear_port_feature(struct usb_device *dev, int port, int feature)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
}
 
/*
* USB 2.0 spec Section 11.24.2.13
* BUG: doesn't handle port indicator selector in high byte of wIndex
*/
static int set_port_feature(struct usb_device *dev, int port, int feature)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
}
 
/*
* USB 2.0 spec Section 11.24.2.6
*/
static int get_hub_status(struct usb_device *dev,
struct usb_hub_status *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
}
 
/*
* USB 2.0 spec Section 11.24.2.7
*/
static int get_port_status(struct usb_device *dev, int port,
struct usb_port_status *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
}
 
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
struct usb_hub *hub = (struct usb_hub *)urb->context;
unsigned long flags;
int status;
 
switch (urb->status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
return;
 
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
dev_dbg (&hub->intf->dev, "transfer --> %d\n", urb->status);
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = urb->status;
/* FALL THROUGH */
/* let khubd handle things */
case 0: /* we got data: port status changed */
break;
}
 
hub->nerrors = 0;
 
/* Something happened, let khubd figure it out */
spin_lock_irqsave(&hub_event_lock, flags);
if (list_empty(&hub->event_list)) {
list_add(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
 
resubmit:
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
/* ENODEV means we raced disconnect() */
&& status != -ENODEV)
dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status);
}
 
/* USB 2.0 spec Section 11.24.2.3 */
static inline int
hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt)
{
return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0),
HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER,
devinfo, tt, 0, 0, HZ);
}
 
/*
* enumeration blocks khubd for a long time. we use keventd instead, since
* long blocking there is the exception, not the rule. accordingly, HCDs
* talking to TTs must queue control transfers (not just bulk and iso), so
* both can talk to the same hub concurrently.
*/
static void hub_tt_kevent (void *arg)
{
struct usb_hub *hub = arg;
unsigned long flags;
 
spin_lock_irqsave (&hub->tt.lock, flags);
while (!list_empty (&hub->tt.clear_list)) {
struct list_head *temp;
struct usb_tt_clear *clear;
struct usb_device *dev;
int status;
 
temp = hub->tt.clear_list.next;
clear = list_entry (temp, struct usb_tt_clear, clear_list);
list_del (&clear->clear_list);
 
/* drop lock so HCD can concurrently report other TT errors */
spin_unlock_irqrestore (&hub->tt.lock, flags);
dev = interface_to_usbdev (hub->intf);
status = hub_clear_tt_buffer (dev, clear->devinfo, clear->tt);
spin_lock_irqsave (&hub->tt.lock, flags);
 
if (status)
err ("usb-%s-%s clear tt %d (%04x) error %d",
dev->bus->bus_name, dev->devpath,
clear->tt, clear->devinfo, status);
kfree (clear);
}
spin_unlock_irqrestore (&hub->tt.lock, flags);
}
 
/**
* usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
* @dev: the device whose split transaction failed
* @pipe: identifies the endpoint of the failed transaction
*
* High speed HCDs use this to tell the hub driver that some split control or
* bulk transaction failed in a way that requires clearing internal state of
* a transaction translator. This is normally detected (and reported) from
* interrupt context.
*
* It may not be possible for that hub to handle additional full (or low)
* speed transactions until that state is fully cleared out.
*/
void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)
{
struct usb_tt *tt = dev->tt;
unsigned long flags;
struct usb_tt_clear *clear;
 
/* we've got to cope with an arbitrary number of pending TT clears,
* since each TT has "at least two" buffers that can need it (and
* there can be many TTs per hub). even if they're uncommon.
*/
if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {
err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s",
dev->bus->bus_name, tt->hub->devpath);
/* FIXME recover somehow ... RESET_TT? */
return;
}
 
/* info that CLEAR_TT_BUFFER needs */
clear->tt = tt->multi ? dev->ttport : 1;
clear->devinfo = usb_pipeendpoint (pipe);
clear->devinfo |= dev->devnum << 4;
clear->devinfo |= usb_pipecontrol (pipe)
? (USB_ENDPOINT_XFER_CONTROL << 11)
: (USB_ENDPOINT_XFER_BULK << 11);
if (usb_pipein (pipe))
clear->devinfo |= 1 << 15;
/* tell keventd to clear state for this TT */
spin_lock_irqsave (&tt->lock, flags);
list_add_tail (&clear->clear_list, &tt->clear_list);
schedule_work (&tt->kevent);
spin_unlock_irqrestore (&tt->lock, flags);
}
 
static void hub_power_on(struct usb_hub *hub)
{
struct usb_device *dev;
int i;
 
/* Enable power to the ports */
dev_dbg(hubdev(interface_to_usbdev(hub->intf)),
"enabling power on all ports\n");
dev = interface_to_usbdev(hub->intf);
for (i = 0; i < hub->descriptor->bNbrPorts; i++)
set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
 
/* Wait for power to be enabled */
wait_ms(hub->descriptor->bPwrOn2PwrGood * 2);
}
 
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
struct usb_device *dev = interface_to_usbdev (hub->intf);
int ret;
 
ret = get_hub_status(dev, &hub->status->hub);
if (ret < 0)
dev_err (hubdev (dev),
"%s failed (err = %d)\n", __FUNCTION__, ret);
else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
*change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
return ret;
}
 
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
struct usb_device *dev = interface_to_usbdev (hub->intf);
struct device *hub_dev;
u16 hubstatus, hubchange;
unsigned int pipe;
int maxp, ret;
char *message;
 
hub->buffer = usb_buffer_alloc(dev, sizeof(*hub->buffer), GFP_KERNEL,
&hub->buffer_dma);
if (!hub->buffer) {
message = "can't allocate hub irq buffer";
ret = -ENOMEM;
goto fail;
}
 
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
message = "can't kmalloc hub status buffer";
ret = -ENOMEM;
goto fail;
}
 
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
message = "can't kmalloc hub descriptor";
ret = -ENOMEM;
goto fail;
}
 
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
*/
ret = get_hub_descriptor(dev, hub->descriptor,
sizeof(*hub->descriptor));
if (ret < 0) {
message = "can't read hub descriptor";
goto fail;
} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
message = "hub has too many ports!";
ret = -ENODEV;
goto fail;
}
 
hub_dev = hubdev(dev);
dev->maxchild = hub->descriptor->bNbrPorts;
dev_info (hub_dev, "%d port%s detected\n", dev->maxchild,
(dev->maxchild == 1) ? "" : "s");
 
le16_to_cpus(&hub->descriptor->wHubCharacteristics);
 
if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {
int i;
char portstr [USB_MAXCHILDREN + 1];
 
for (i = 0; i < dev->maxchild; i++)
portstr[i] = hub->descriptor->DeviceRemovable
[((i + 1) / 8)] & (1 << ((i + 1) % 8))
? 'F' : 'R';
portstr[dev->maxchild] = 0;
dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
} else
dev_dbg(hub_dev, "standalone hub\n");
 
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
case 0x00:
dev_dbg(hub_dev, "ganged power switching\n");
break;
case 0x01:
dev_dbg(hub_dev, "individual port power switching\n");
break;
case 0x02:
case 0x03:
dev_dbg(hub_dev, "unknown reserved power switching mode\n");
break;
}
 
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
case 0x00:
dev_dbg(hub_dev, "global over-current protection\n");
break;
case 0x08:
dev_dbg(hub_dev, "individual port over-current protection\n");
break;
case 0x10:
case 0x18:
dev_dbg(hub_dev, "no over-current protection\n");
break;
}
 
spin_lock_init (&hub->tt.lock);
INIT_LIST_HEAD (&hub->tt.clear_list);
INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);
switch (dev->descriptor.bDeviceProtocol) {
case 0:
break;
case 1:
dev_dbg(hub_dev, "Single TT\n");
hub->tt.hub = dev;
break;
case 2:
dev_dbg(hub_dev, "TT per port\n");
hub->tt.hub = dev;
hub->tt.multi = 1;
break;
default:
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
dev->descriptor.bDeviceProtocol);
break;
}
 
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
case 0x00:
if (dev->descriptor.bDeviceProtocol != 0)
dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");
break;
case 0x20:
dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");
break;
case 0x40:
dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");
break;
case 0x60:
dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");
break;
}
 
dev_dbg(hub_dev, "Port indicators are %s supported\n",
(hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND)
? "" : "not");
 
dev_dbg(hub_dev, "power on to power good time: %dms\n",
hub->descriptor->bPwrOn2PwrGood * 2);
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent);
 
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
message = "can't get hub status";
goto fail;
}
 
dev_dbg(hub_dev, "local power source is %s\n",
(hubstatus & HUB_STATUS_LOCAL_POWER)
? "lost (inactive)" : "good");
 
dev_dbg(hub_dev, "%sover-current condition exists\n",
(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
 
/* Start the interrupt endpoint */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
 
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
message = "couldn't allocate interrupt urb";
ret = -ENOMEM;
goto fail;
}
 
usb_fill_int_urb(hub->urb, dev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
ret = usb_submit_urb(hub->urb, GFP_KERNEL);
if (ret) {
message = "couldn't submit status urb";
goto fail;
}
/* Wake up khubd */
wake_up(&khubd_wait);
 
hub_power_on(hub);
 
return 0;
 
fail:
dev_err (&hub->intf->dev, "config failed, %s (err %d)\n",
message, ret);
/* hub_disconnect() frees urb and descriptor */
return ret;
}
 
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
unsigned long flags;
 
if (!hub)
return;
 
usb_set_intfdata (intf, NULL);
spin_lock_irqsave(&hub_event_lock, flags);
 
/* Delete it and then reset it */
list_del(&hub->event_list);
INIT_LIST_HEAD(&hub->event_list);
list_del(&hub->hub_list);
INIT_LIST_HEAD(&hub->hub_list);
 
spin_unlock_irqrestore(&hub_event_lock, flags);
 
down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */
up(&hub->khubd_sem);
 
/* assuming we used keventd, it must quiesce too */
if (hub->tt.hub)
flush_scheduled_work ();
 
if (hub->urb) {
usb_unlink_urb(hub->urb);
usb_free_urb(hub->urb);
hub->urb = NULL;
}
 
if (hub->descriptor) {
kfree(hub->descriptor);
hub->descriptor = NULL;
}
 
if (hub->status) {
kfree(hub->status);
hub->status = NULL;
}
 
if (hub->buffer) {
usb_buffer_free(interface_to_usbdev(intf),
sizeof(*hub->buffer), hub->buffer,
hub->buffer_dma);
hub->buffer = NULL;
}
 
/* Free the memory */
kfree(hub);
}
 
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *dev;
struct usb_hub *hub;
unsigned long flags;
 
desc = intf->altsetting + intf->act_altsetting;
dev = interface_to_usbdev(intf);
 
/* Some hubs have a subclass of 1, which AFAICT according to the */
/* specs is not defined, but it works */
if ((desc->desc.bInterfaceSubClass != 0) &&
(desc->desc.bInterfaceSubClass != 1)) {
descriptor_error:
dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
return -EIO;
}
 
/* Multiple endpoints? What kind of mutant ninja-hub is this? */
if (desc->desc.bNumEndpoints != 1) {
goto descriptor_error;
}
 
endpoint = &desc->endpoint[0].desc;
 
/* Output endpoint? Curiouser and curiouser.. */
if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {
goto descriptor_error;
}
 
/* If it's not an interrupt endpoint, we'd better punt! */
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_INT) {
goto descriptor_error;
return -EIO;
}
 
/* We found a hub */
dev_info (hubdev (dev), "USB hub found\n");
 
hub = kmalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
err("couldn't kmalloc hub struct");
return -ENOMEM;
}
 
memset(hub, 0, sizeof(*hub));
 
INIT_LIST_HEAD(&hub->event_list);
hub->intf = intf;
init_MUTEX(&hub->khubd_sem);
 
/* Record the new hub's existence */
spin_lock_irqsave(&hub_event_lock, flags);
INIT_LIST_HEAD(&hub->hub_list);
list_add(&hub->hub_list, &hub_list);
spin_unlock_irqrestore(&hub_event_lock, flags);
 
usb_set_intfdata (intf, hub);
 
if (hub_configure(hub, endpoint) >= 0)
return 0;
 
hub_disconnect (intf);
return -ENODEV;
}
 
static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
struct usb_device *hub = interface_to_usbdev (intf);
 
/* assert ifno == 0 (part of hub spec) */
switch (code) {
case USBDEVFS_HUB_PORTINFO: {
struct usbdevfs_hub_portinfo *info = user_data;
unsigned long flags;
int i;
 
spin_lock_irqsave(&hub_event_lock, flags);
if (hub->devnum <= 0)
info->nports = 0;
else {
info->nports = hub->maxchild;
for (i = 0; i < info->nports; i++) {
if (hub->children[i] == NULL)
info->port[i] = 0;
else
info->port[i] =
hub->children[i]->devnum;
}
}
spin_unlock_irqrestore(&hub_event_lock, flags);
 
return info->nports + 1;
}
 
default:
return -ENOSYS;
}
}
 
static int hub_reset(struct usb_hub *hub)
{
struct usb_device *dev = interface_to_usbdev(hub->intf);
int i;
 
/* Disconnect any attached devices */
for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
if (dev->children[i])
usb_disconnect(&dev->children[i]);
}
 
/* Attempt to reset the hub */
if (hub->urb)
usb_unlink_urb(hub->urb);
else
return -1;
 
if (usb_reset_device(dev))
return -1;
 
hub->urb->dev = dev;
if (usb_submit_urb(hub->urb, GFP_KERNEL))
return -1;
 
hub_power_on(hub);
 
return 0;
}
 
static void hub_start_disconnect(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
int i;
 
/* Find the device pointer to disconnect */
if (parent) {
for (i = 0; i < parent->maxchild; i++) {
if (parent->children[i] == dev) {
usb_disconnect(&parent->children[i]);
return;
}
}
}
 
err("cannot disconnect hub %s", dev->devpath);
}
 
static int hub_port_status(struct usb_device *dev, int port,
u16 *status, u16 *change)
{
struct usb_hub *hub = usb_get_intfdata(dev->actconfig->interface[0]);
int ret;
 
if (!hub)
return -ENODEV;
 
ret = get_port_status(dev, port + 1, &hub->status->port);
if (ret < 0)
dev_err (hubdev (dev),
"%s failed (err = %d)\n", __FUNCTION__, ret);
else {
*status = le16_to_cpu(hub->status->port.wPortStatus);
*change = le16_to_cpu(hub->status->port.wPortChange);
ret = 0;
}
return ret;
}
 
#define HUB_RESET_TRIES 5
#define HUB_PROBE_TRIES 2
#define HUB_ROOT_RESET_TIME 50 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
#define HUB_LONG_RESET_TIME 200
#define HUB_RESET_TIMEOUT 500
 
/* return: -1 on error, 0 on success, 1 on disconnect. */
static int hub_port_wait_reset(struct usb_device *hub, int port,
struct usb_device *dev, unsigned int delay)
{
int delay_time, ret;
u16 portstatus;
u16 portchange;
 
for (delay_time = 0;
delay_time < HUB_RESET_TIMEOUT;
delay_time += delay) {
/* wait to give the device a chance to reset */
wait_ms(delay);
/* read and decode port status */
ret = hub_port_status(hub, port, &portstatus, &portchange);
if (ret < 0) {
return -1;
}
 
/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return 1;
 
/* bomb out completely if something weird happened */
if ((portchange & USB_PORT_STAT_C_CONNECTION))
return -1;
 
/* if we`ve finished resetting, then break out of the loop */
if (!(portstatus & USB_PORT_STAT_RESET) &&
(portstatus & USB_PORT_STAT_ENABLE)) {
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
dev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
dev->speed = USB_SPEED_LOW;
else
dev->speed = USB_SPEED_FULL;
return 0;
}
 
/* switch to the long delay after two short delay failures */
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
 
dev_dbg (hubdev (hub),
"port %d not reset yet, waiting %dms\n",
port + 1, delay);
}
 
return -1;
}
 
/* return: -1 on error, 0 on success, 1 on disconnect. */
static int hub_port_reset(struct usb_device *hub, int port,
struct usb_device *dev, unsigned int delay)
{
int i, status;
 
/* Reset the port */
for (i = 0; i < HUB_RESET_TRIES; i++) {
set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
 
/* return on disconnect or reset */
status = hub_port_wait_reset(hub, port, dev, delay);
if (status != -1) {
clear_port_feature(hub,
port + 1, USB_PORT_FEAT_C_RESET);
dev->state = status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT;
return status;
}
 
dev_dbg (hubdev (hub),
"port %d not enabled, trying reset again...\n",
port + 1);
delay = HUB_LONG_RESET_TIME;
}
 
dev_err (hubdev (hub),
"Cannot enable port %i. Maybe the USB cable is bad?\n",
port + 1);
 
return -1;
}
 
int hub_port_disable(struct usb_device *hub, int port)
{
int ret;
 
ret = clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
if (ret)
dev_err(hubdev(hub), "cannot disable port %d (err = %d)\n",
port + 1, ret);
 
return ret;
}
 
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
*
* Apparently there are some bluetooth and irda-dongles and a number
* of low-speed devices which require longer delays of about 200-400ms.
* Not covered by the spec - but easy to deal with.
*
* This implementation uses 400ms minimum debounce timeout and checks
* every 25ms for transient disconnects to restart the delay.
*/
 
#define HUB_DEBOUNCE_TIMEOUT 400
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 4
 
/* return: -1 on error, 0 on success, 1 on disconnect. */
static int hub_port_debounce(struct usb_device *hub, int port)
{
int ret;
int delay_time, stable_count;
u16 portchange, portstatus;
unsigned connection;
 
connection = 0;
stable_count = 0;
for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; delay_time += HUB_DEBOUNCE_STEP) {
wait_ms(HUB_DEBOUNCE_STEP);
 
ret = hub_port_status(hub, port, &portstatus, &portchange);
if (ret < 0)
return -1;
 
if ((portstatus & USB_PORT_STAT_CONNECTION) == connection) {
if (connection) {
if (++stable_count == HUB_DEBOUNCE_STABLE)
break;
}
} else {
stable_count = 0;
}
connection = portstatus & USB_PORT_STAT_CONNECTION;
 
if ((portchange & USB_PORT_STAT_C_CONNECTION)) {
clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION);
}
}
 
dev_dbg (hubdev (hub),
"debounce: port %d: delay %dms stable %d status 0x%x\n",
port + 1, delay_time, stable_count, portstatus);
 
return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;
}
 
static void hub_port_connect_change(struct usb_hub *hubstate, int port,
u16 portstatus, u16 portchange)
{
struct usb_device *hub = interface_to_usbdev(hubstate->intf);
struct usb_device *dev;
unsigned int delay = HUB_SHORT_RESET_TIME;
int i;
 
dev_dbg (&hubstate->intf->dev,
"port %d, status %x, change %x, %s\n",
port + 1, portstatus, portchange, portspeed (portstatus));
 
/* Clear the connection change status */
clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
 
/* Disconnect any existing devices under this port */
if (hub->children[port])
usb_disconnect(&hub->children[port]);
 
/* Return now if nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
if (portstatus & USB_PORT_STAT_ENABLE)
hub_port_disable(hub, port);
 
return;
}
 
if (hub_port_debounce(hub, port)) {
dev_err (&hubstate->intf->dev,
"connect-debounce failed, port %d disabled\n",
port+1);
hub_port_disable(hub, port);
return;
}
 
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
if (!hub->parent)
delay = HUB_ROOT_RESET_TIME;
 
/* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */
if (portstatus & USB_PORT_STAT_LOW_SPEED)
delay = HUB_LONG_RESET_TIME;
 
down(&usb_address0_sem);
 
for (i = 0; i < HUB_PROBE_TRIES; i++) {
struct usb_device *pdev;
int len;
 
/* Allocate a new device struct */
dev = usb_alloc_dev(hub, hub->bus);
if (!dev) {
dev_err (&hubstate->intf->dev,
"couldn't allocate usb_device\n");
break;
}
 
dev->state = USB_STATE_POWERED;
 
/* Reset the device, and detect its speed */
if (hub_port_reset(hub, port, dev, delay)) {
usb_put_dev(dev);
break;
}
 
/* Find a new address for it */
usb_choose_address(dev);
 
/* Set up TT records, if needed */
if (hub->tt) {
dev->tt = hub->tt;
dev->ttport = hub->ttport;
} else if (dev->speed != USB_SPEED_HIGH
&& hub->speed == USB_SPEED_HIGH) {
dev->tt = &hubstate->tt;
dev->ttport = port + 1;
}
 
/* Save readable and stable topology id, distinguishing
* devices by location for diagnostics, tools, etc. The
* string is a path along hub ports, from the root. Each
* device's id will be stable until USB is re-cabled, and
* hubs are often labeled with these port numbers.
*
* Initial size: ".NN" times five hubs + NUL = 16 bytes max
* (quite rare, since most hubs have 4-6 ports).
*/
pdev = dev->parent;
if (pdev->devpath [0] != '0') /* parent not root? */
len = snprintf26(dev->devpath, sizeof dev->devpath,
"%s.%d", pdev->devpath, port + 1);
/* root == "0", root port 2 == "2", port 3 that hub "2.3" */
else
len = snprintf26(dev->devpath, sizeof dev->devpath,
"%d", port + 1);
if (len == sizeof dev->devpath)
dev_err (&hubstate->intf->dev,
"devpath size! usb/%03d/%03d path %s\n",
dev->bus->busnum, dev->devnum, dev->devpath);
dev_info (&hubstate->intf->dev,
"new USB device on port %d, assigned address %d\n",
port + 1, dev->devnum);
 
/* put the device in the global device tree. the hub port
* is the "bus_id"; hubs show in hierarchy like bridges
*/
dev->dev.parent = dev->parent->dev.parent->parent;
 
/* Run it through the hoops (find a driver, etc) */
if (!usb_new_device(dev, &hub->dev)) {
hub->children[port] = dev;
goto done;
}
 
/* Free the configuration if there was an error */
usb_put_dev(dev);
 
/* Switch to a long reset time */
delay = HUB_LONG_RESET_TIME;
}
 
hub_port_disable(hub, port);
done:
up(&usb_address0_sem);
}
 
static void hub_events(void)
{
unsigned long flags;
struct list_head *tmp;
struct usb_device *dev;
struct usb_hub *hub;
u16 hubstatus;
u16 hubchange;
u16 portstatus;
u16 portchange;
int i, ret;
 
/*
* We restart the list every time to avoid a deadlock with
* deleting hubs downstream from this one. This should be
* safe since we delete the hub from the event list.
* Not the most efficient, but avoids deadlocks.
*/
 
while (1) {
spin_lock_irqsave(&hub_event_lock, flags);
 
if (list_empty(&hub_event_list))
break;
 
/* Grab the next entry from the beginning of the list */
tmp = hub_event_list.next;
 
hub = list_entry(tmp, struct usb_hub, event_list);
dev = interface_to_usbdev(hub->intf);
 
list_del_init(tmp);
 
//** if (unlikely(down_trylock(&hub->khubd_sem)))
//** BUG(); /* never blocks, we were on list */
 
spin_unlock_irqrestore(&hub_event_lock, flags);
 
if (hub->error) {
dev_dbg (&hub->intf->dev, "resetting for error %d\n",
hub->error);
 
if (hub_reset(hub)) {
dev_dbg (&hub->intf->dev,
"can't reset; disconnecting\n");
up(&hub->khubd_sem);
hub_start_disconnect(dev);
continue;
}
 
hub->nerrors = 0;
hub->error = 0;
}
 
for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
ret = hub_port_status(dev, i, &portstatus, &portchange);
if (ret < 0) {
continue;
}
 
if (portchange & USB_PORT_STAT_C_CONNECTION) {
hub_port_connect_change(hub, i, portstatus, portchange);
} else if (portchange & USB_PORT_STAT_C_ENABLE) {
dev_dbg (hubdev (dev),
"port %d enable change, status %x\n",
i + 1, portstatus);
clear_port_feature(dev,
i + 1, USB_PORT_FEAT_C_ENABLE);
 
/*
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
* Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& (portstatus & USB_PORT_STAT_CONNECTION)
&& (dev->children[i])) {
dev_err (&hub->intf->dev,
"port %i "
"disabled by hub (EMI?), "
"re-enabling...",
i + 1);
hub_port_connect_change(hub,
i, portstatus, portchange);
}
}
 
if (portchange & USB_PORT_STAT_C_SUSPEND) {
dev_dbg (&hub->intf->dev,
"suspend change on port %d\n",
i + 1);
clear_port_feature(dev,
i + 1, USB_PORT_FEAT_C_SUSPEND);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
dev_err (&hub->intf->dev,
"over-current change on port %d\n",
i + 1);
clear_port_feature(dev,
i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
hub_power_on(hub);
}
 
if (portchange & USB_PORT_STAT_C_RESET) {
dev_dbg (&hub->intf->dev,
"reset change on port %d\n",
i + 1);
clear_port_feature(dev,
i + 1, USB_PORT_FEAT_C_RESET);
}
} /* end for i */
 
/* deal with hub status changes */
if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
dev_err (&hub->intf->dev, "get_hub_status failed\n");
else {
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
dev_dbg (&hub->intf->dev, "power change\n");
clear_hub_feature(dev, C_HUB_LOCAL_POWER);
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (&hub->intf->dev, "overcurrent change\n");
wait_ms(500); /* Cool down */
clear_hub_feature(dev, C_HUB_OVER_CURRENT);
hub_power_on(hub);
}
}
up(&hub->khubd_sem);
} /* end while (1) */
 
spin_unlock_irqrestore(&hub_event_lock, flags);
}
 
static int hub_thread(void *__hub)
{
/*
* This thread doesn't need any user-level access,
* so get rid of all our resources
*/
 
daemonize("khubd");
allow_signal(SIGKILL);
 
/* Send me a signal to get me die (for debugging) */
do {
hub_events();
wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list));
// if (current->flags & PF_FREEZE)
// refrigerator(PF_IOTHREAD);
} while (!signal_pending(current));
printk(KERN_DEBUG "File: %s @hub_thread_exit\n", __FILE__);
 
dbg("hub_thread exiting");
complete_and_exit(&khubd_exited, 0);
return 0;
}
 
static struct usb_device_id hub_id_table [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
};
 
MODULE_DEVICE_TABLE (usb, hub_id_table);
 
static struct usb_driver hub_driver = {
.owner = THIS_MODULE,
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
};
 
/*
* This should be a separate module.
*/
int usb_hub_init(void)
{
pid_t pid;
 
if (usb_register(&hub_driver) < 0) {
err("Unable to register USB hub driver");
return -1;
}
 
pid = kernel_thread(hub_thread, NULL, CLONE_KERNEL);
if (pid >= 0) {
khubd_pid = pid;
 
return 0;
}
 
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
err("failed to start hub_thread");
 
return -1;
}
 
void usb_hub_cleanup(void)
{
int ret;
 
/* Kill the thread */
ret = kill_proc(khubd_pid, SIGKILL, 1);
 
wait_for_completion(&khubd_exited);
 
/*
* Hub resources are freed for us by usb_deregister. It calls
* usb_driver_purge on every device which in turn calls that
* devices disconnect function if it is using this driver.
* The hub_disconnect function takes care of releasing the
* individual hub resources. -greg
*/
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
 
/*
* WARNING - If a driver calls usb_reset_device, you should simulate a
* disconnect() and probe() for other interfaces you doesn't claim. This
* is left up to the driver writer right now. This insures other drivers
* have a chance to re-setup their interface.
*
* Take a look at proc_resetdevice in devio.c for some sample code to
* do this.
* Use this only from within your probe function, otherwise use
* usb_reset_device() below, which ensure proper locking
*/
int usb_physical_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
struct usb_device_descriptor *descriptor;
int i, ret, port = -1;
 
if (!parent) {
err("attempting to reset root hub!");
return -EINVAL;
}
 
for (i = 0; i < parent->maxchild; i++)
if (parent->children[i] == dev) {
port = i;
break;
}
 
if (port < 0)
return -ENOENT;
 
descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
if (!descriptor) {
return -ENOMEM;
}
 
down(&usb_address0_sem);
 
/* Send a reset to the device */
if (hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) {
hub_port_disable(parent, port);
up(&usb_address0_sem);
kfree(descriptor);
return(-ENODEV);
}
 
/* Reprogram the Address */
ret = usb_set_address(dev);
if (ret < 0) {
err("USB device not accepting new address (error=%d)", ret);
hub_port_disable(parent, port);
up(&usb_address0_sem);
kfree(descriptor);
return ret;
}
 
/* Let the SET_ADDRESS settle */
wait_ms(10);
 
up(&usb_address0_sem);
 
/*
* Now we fetch the configuration descriptors for the device and
* see if anything has changed. If it has, we dump the current
* parsed descriptors and reparse from scratch. Then we leave
* the device alone for the caller to finish setting up.
*
* If nothing changed, we reprogram the configuration and then
* the alternate settings.
*/
 
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
sizeof(*descriptor));
if (ret < 0) {
kfree(descriptor);
return ret;
}
 
le16_to_cpus(&descriptor->bcdUSB);
le16_to_cpus(&descriptor->idVendor);
le16_to_cpus(&descriptor->idProduct);
le16_to_cpus(&descriptor->bcdDevice);
 
if (memcmp(&dev->descriptor, descriptor, sizeof(*descriptor))) {
kfree(descriptor);
usb_destroy_configuration(dev);
 
ret = usb_get_device_descriptor(dev);
if (ret < sizeof(dev->descriptor)) {
if (ret < 0)
err("unable to get device %s descriptor "
"(error=%d)", dev->devpath, ret);
else
err("USB device %s descriptor short read "
"(expected %Zi, got %i)",
dev->devpath,
sizeof(dev->descriptor), ret);
 
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
dev->devnum = -1;
return -EIO;
}
 
ret = usb_get_configuration(dev);
if (ret < 0) {
err("unable to get configuration (error=%d)", ret);
usb_destroy_configuration(dev);
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
dev->devnum = -1;
return 1;
}
 
usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue);
return 1;
}
 
kfree(descriptor);
 
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0,
dev->actconfig->desc.bConfigurationValue, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
err("failed to set dev %s active configuration (error=%d)",
dev->devpath, ret);
return ret;
}
dev->state = USB_STATE_CONFIGURED;
 
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf = dev->actconfig->interface[i];
struct usb_interface_descriptor *as;
 
as = &intf->altsetting[intf->act_altsetting].desc;
ret = usb_set_interface(dev, as->bInterfaceNumber,
as->bAlternateSetting);
if (ret < 0) {
err("failed to set active alternate setting "
"for dev %s interface %d (error=%d)",
dev->devpath, i, ret);
return ret;
}
}
 
return 0;
}
 
int usb_reset_device(struct usb_device *udev)
{
struct device *gdev = &udev->dev;
int r;
// down_read(&gdev->bus->subsys.rwsem);
r = usb_physical_reset_device(udev);
// up_read(&gdev->bus->subsys.rwsem);
 
return r;
}
/shark/trunk/drivers/usb/core/file.c
0,0 → 1,242
/*
* drivers/usb/file.c
*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
 
#define MAX_USB_MINORS 256
static struct file_operations *usb_minors[MAX_USB_MINORS];
static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
 
static int usb_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
struct file_operations *c;
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
 
spin_lock (&minor_lock);
c = usb_minors[minor];
 
if (!c || !(new_fops = fops_get(c))) {
spin_unlock(&minor_lock);
return err;
}
spin_unlock(&minor_lock);
 
old_fops = file->f_op;
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
 
static struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
 
static void release_usb_class_dev(struct class_device *class_dev)
{
dbg("%s - %s", __FUNCTION__, class_dev->class_id);
kfree(class_dev);
}
 
static struct class usb_class = {
.name = "usb",
.release = &release_usb_class_dev,
};
 
int usb_major_init(void)
{
if (register_chrdev(USB_MAJOR, "usb", &usb_fops)) {
err("unable to get major %d for usb devices", USB_MAJOR);
return -EBUSY;
}
 
devfs_mk_dir("usb");
class_register(&usb_class);
return 0;
}
 
void usb_major_cleanup(void)
{
class_unregister(&usb_class);
devfs_remove("usb");
unregister_chrdev(USB_MAJOR, "usb");
}
 
static ssize_t show_dev(struct class_device *class_dev, char *buf)
{
int minor = (int)(long)class_get_devdata(class_dev);
return print_dev_t(buf, MKDEV(USB_MAJOR, minor));
}
static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
 
/**
* usb_register_dev - register a USB device, and ask for a minor number
* @intf: pointer to the usb_interface that is being registered
* @class_driver: pointer to the usb_class_driver for this device
*
* This should be called by all USB drivers that use the USB major number.
* If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
* dynamically allocated out of the list of available ones. If it is not
* enabled, the minor number will be based on the next available free minor,
* starting at the class_driver->minor_base.
*
* This function also creates the devfs file for the usb device, if devfs
* is enabled, and creates a usb class device in the sysfs tree.
*
* usb_deregister_dev() must be called when the driver is done with
* the minor numbers given out by this function.
*
* Returns -EINVAL if something bad happens with trying to register a
* device, and 0 on success.
*/
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
char name[BUS_ID_SIZE];
struct class_device *class_dev;
char *temp;
 
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
*/
minor_base = 0;
#endif
intf->minor = -1;
 
dbg ("looking for a minor, starting at %d", minor_base);
 
if (class_driver->fops == NULL)
goto exit;
 
spin_lock (&minor_lock);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor])
continue;
 
usb_minors[minor] = class_driver->fops;
 
retval = 0;
break;
}
spin_unlock (&minor_lock);
 
if (retval)
goto exit;
 
intf->minor = minor;
 
/* handle the devfs registration */
snprintf26(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
devfs_mk_cdev(MKDEV(USB_MAJOR, minor), class_driver->mode, name);
 
/* create a usb class device for this usb interface */
class_dev = kmalloc(sizeof(*class_dev), GFP_KERNEL);
if (class_dev) {
memset(class_dev, 0x00, sizeof(struct class_device));
class_dev->class = &usb_class;
class_dev->dev = &intf->dev;
 
temp = strrchr(name, '/');
if (temp && (temp[1] != 0x00))
++temp;
else
temp = name;
snprintf26(class_dev->class_id, BUS_ID_SIZE, "%s", temp);
class_set_devdata(class_dev, (void *)(long)intf->minor);
class_device_register(class_dev);
class_device_create_file(class_dev, &class_device_attr_dev);
intf->class_dev = class_dev;
}
exit:
return retval;
}
EXPORT_SYMBOL(usb_register_dev);
 
/**
* usb_deregister_dev - deregister a USB device's dynamic minor.
* @intf: pointer to the usb_interface that is being deregistered
* @class_driver: pointer to the usb_class_driver for this device
*
* 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 function also cleans up the devfs file for the usb device, if devfs
* is enabled, and removes the usb class device from the sysfs tree.
*
* This should be called by all drivers that use the USB major number.
*/
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
char name[BUS_ID_SIZE];
 
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
#endif
 
if (intf->minor == -1)
return;
 
dbg ("removing %d minor", intf->minor);
 
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
 
snprintf26(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
devfs_remove (name);
 
if (intf->class_dev) {
class_device_unregister(intf->class_dev);
intf->class_dev = NULL;
}
intf->minor = -1;
}
EXPORT_SYMBOL(usb_deregister_dev);
 
 
/shark/trunk/drivers/usb/core/devices.c
0,0 → 1,673
/*
* devices.c
* (C) Copyright 1999 Randy Dunlap.
* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
* (C) Copyright 1999 Deti Fliegl (new USB architecture)
*
* 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
*
*************************************************************
*
* <mountpoint>/devices contains USB topology, device, config, class,
* interface, & endpoint data.
*
* I considered using /proc/bus/usb/devices/device# for each device
* as it is attached or detached, but I didn't like this for some
* reason -- maybe it's just too deep of a directory structure.
* I also don't like looking in multiple places to gather and view
* the data. Having only one file for ./devices also prevents race
* conditions that could arise if a program was reading device info
* for devices that are being removed (unplugged). (That is, the
* program may find a directory for devnum_12 then try to open it,
* but it was just unplugged, so the directory is now deleted.
* But programs would just have to be prepared for situations like
* this in any plug-and-play environment.)
*
* 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Converted the whole proc stuff to real
* read methods. Now not the whole device list needs to fit
* into one page, only the device list for one bus.
* Added a poll method to /proc/bus/usb/devices, to wake
* up an eventual usbd
* 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Turned into its own filesystem
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
* Converted file reading routine to dump to buffer once
* per device, not per bus
*
* $Id: devices.c,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*/
 
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
#include <linux/usbdevice_fs.h>
#include <asm/uaccess.h>
 
#include "hcd.h"
 
#define MAX_TOPO_LEVEL 6
 
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
 
static char *format_topo =
/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
 
static char *format_string_manufacturer =
/* S: Manufacturer=xxxx */
"S: Manufacturer=%.100s\n";
 
static char *format_string_product =
/* S: Product=xxxx */
"S: Product=%.100s\n";
 
#ifdef ALLOW_SERIAL_NUMBER
static char *format_string_serialnumber =
/* S: SerialNumber=xxxx */
"S: SerialNumber=%.100s\n";
#endif
 
static char *format_bandwidth =
/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
"B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
static char *format_device1 =
/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
"D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
 
static char *format_device2 =
/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */
"P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
 
static char *format_config =
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
static char *format_iface =
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
"I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
 
static char *format_endpt =
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
 
 
/*
* Need access to the driver and USB bus lists.
* extern struct list_head usb_bus_list;
* However, these will come from functions that return ptrs to each of them.
*/
 
static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
static unsigned int conndiscevcnt = 0;
 
/* this struct stores the poll state for <mountpoint>/devices pollers */
struct usb_device_status {
unsigned int lastev;
};
 
struct class_info {
int class;
char *class_name;
};
 
static const struct class_info clas_info[] =
{ /* max. 5 chars. per name string */
{USB_CLASS_PER_INTERFACE, ">ifc"},
{USB_CLASS_AUDIO, "audio"},
{USB_CLASS_COMM, "comm."},
{USB_CLASS_HID, "HID"},
{USB_CLASS_HUB, "hub"},
{USB_CLASS_PHYSICAL, "PID"},
{USB_CLASS_PRINTER, "print"},
{USB_CLASS_MASS_STORAGE, "stor."},
{USB_CLASS_CDC_DATA, "data"},
{USB_CLASS_APP_SPEC, "app."},
{USB_CLASS_VENDOR_SPEC, "vend."},
{USB_CLASS_STILL_IMAGE, "still"},
{USB_CLASS_CSCID, "scard"},
{USB_CLASS_CONTENT_SEC, "c-sec"},
{-1, "unk."} /* leave as last */
};
 
/*****************************************************************/
 
void usbdevfs_conn_disc_event(void)
{
conndiscevcnt++;
wake_up(&deviceconndiscwq);
}
 
static const char *class_decode(const int class)
{
int ix;
 
for (ix = 0; clas_info[ix].class != -1; ix++)
if (clas_info[ix].class == class)
break;
return (clas_info[ix].class_name);
}
 
static char *usb_dump_endpoint_descriptor (
int speed,
char *start,
char *end,
const struct usb_endpoint_descriptor *desc
)
{
char dir, unit, *type;
unsigned interval, in, bandwidth = 1;
 
if (start > end)
return start;
in = (desc->bEndpointAddress & USB_DIR_IN);
dir = in ? 'I' : 'O';
if (speed == USB_SPEED_HIGH) {
switch (desc->wMaxPacketSize & (0x03 << 11)) {
case 1 << 11: bandwidth = 2; break;
case 2 << 11: bandwidth = 3; break;
}
}
 
/* this isn't checking for illegal values */
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
case USB_ENDPOINT_XFER_CONTROL:
type = "Ctrl";
if (speed == USB_SPEED_HIGH) /* uframes per NAK */
interval = desc->bInterval;
else
interval = 0;
dir = 'B'; /* ctrl is bidirectional */
break;
case USB_ENDPOINT_XFER_ISOC:
type = "Isoc";
interval = 1 << (desc->bInterval - 1);
break;
case USB_ENDPOINT_XFER_BULK:
type = "Bulk";
if (speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
interval = desc->bInterval;
else
interval = 0;
break;
case USB_ENDPOINT_XFER_INT:
type = "Int.";
if (speed == USB_SPEED_HIGH) {
interval = 1 << (desc->bInterval - 1);
} else
interval = desc->bInterval;
break;
default: /* "can't happen" */
return start;
}
interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
if (interval % 1000)
unit = 'u';
else {
unit = 'm';
interval /= 1000;
}
 
start += sprintf26(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
(desc->wMaxPacketSize & 0x07ff) * bandwidth,
interval, unit);
return start;
}
 
static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno)
{
struct usb_interface_descriptor *desc = &iface->altsetting[setno].desc;
 
if (start > end)
return start;
lock_kernel(); /* driver might be unloaded */
start += sprintf26(start, format_iface,
desc->bInterfaceNumber,
desc->bAlternateSetting,
desc->bNumEndpoints,
desc->bInterfaceClass,
class_decode(desc->bInterfaceClass),
desc->bInterfaceSubClass,
desc->bInterfaceProtocol,
iface->driver ? iface->driver->name : "(none)");
unlock_kernel();
return start;
}
 
static char *usb_dump_interface(
int speed,
char *start,
char *end,
const struct usb_interface *iface,
int setno
) {
struct usb_host_interface *desc = &iface->altsetting[setno];
int i;
 
start = usb_dump_interface_descriptor(start, end, iface, setno);
for (i = 0; i < desc->desc.bNumEndpoints; i++) {
if (start > end)
return start;
start = usb_dump_endpoint_descriptor(speed,
start, end, &desc->endpoint[i].desc);
}
return start;
}
 
/* TBD:
* 0. TBDs
* 1. marking active config and ifaces (code lists all, but should mark
* which ones are active, if any)
* 2. add <halted> status to each endpoint line
*/
 
static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active)
{
if (start > end)
return start;
start += sprintf26(start, format_config,
active ? '*' : ' ', /* mark active/actual/current cfg. */
desc->bNumInterfaces,
desc->bConfigurationValue,
desc->bmAttributes,
desc->bMaxPower * 2);
return start;
}
 
static char *usb_dump_config (
int speed,
char *start,
char *end,
const struct usb_host_config *config,
int active
)
{
int i, j;
struct usb_interface *interface;
 
if (start > end)
return start;
if (!config) /* getting these some in 2.3.7; none in 2.3.6 */
return start + sprintf26(start, "(null Cfg. desc.)\n");
start = usb_dump_config_descriptor(start, end, &config->desc, active);
for (i = 0; i < config->desc.bNumInterfaces; i++) {
interface = config->interface[i];
if (!interface)
break;
for (j = 0; j < interface->num_altsetting; j++) {
if (start > end)
return start;
start = usb_dump_interface(speed,
start, end, interface, j);
}
}
return start;
}
 
/*
* Dump the different USB descriptors.
*/
static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc)
{
if (start > end)
return start;
start += sprintf26 (start, format_device1,
desc->bcdUSB >> 8, desc->bcdUSB & 0xff,
desc->bDeviceClass,
class_decode (desc->bDeviceClass),
desc->bDeviceSubClass,
desc->bDeviceProtocol,
desc->bMaxPacketSize0,
desc->bNumConfigurations);
if (start > end)
return start;
start += sprintf26(start, format_device2,
desc->idVendor, desc->idProduct,
desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
return start;
}
 
/*
* Dump the different strings that this device holds.
*/
static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev)
{
char *buf;
 
if (start > end)
return start;
buf = kmalloc(128, GFP_KERNEL);
if (!buf)
return start;
if (dev->descriptor.iManufacturer) {
if (usb_string(dev, dev->descriptor.iManufacturer, buf, 128) > 0)
start += sprintf26(start, format_string_manufacturer, buf);
}
if (start > end)
goto out;
if (dev->descriptor.iProduct) {
if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0)
start += sprintf26(start, format_string_product, buf);
}
if (start > end)
goto out;
#ifdef ALLOW_SERIAL_NUMBER
if (dev->descriptor.iSerialNumber) {
if (usb_string(dev, dev->descriptor.iSerialNumber, buf, 128) > 0)
start += sprintf26(start, format_string_serialnumber, buf);
}
#endif
out:
kfree(buf);
return start;
}
 
static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
{
int i;
 
if (start > end)
return start;
start = usb_dump_device_descriptor(start, end, &dev->descriptor);
 
if (start > end)
return start;
start = usb_dump_device_strings (start, end, dev);
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (start > end)
return start;
start = usb_dump_config(dev->speed,
start, end, dev->config + i,
/* active ? */
(dev->config + i) == dev->actconfig);
}
return start;
}
 
 
#ifdef PROC_EXTRA /* TBD: may want to add this code later */
 
static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc)
{
int leng = USB_DT_HUB_NONVAR_SIZE;
unsigned char *ptr = (unsigned char *)desc;
 
if (start > end)
return start;
start += sprintf26(start, "Interface:");
while (leng && start <= end) {
start += sprintf26(start, " %02x", *ptr);
ptr++; leng--;
}
*start++ = '\n';
return start;
}
 
static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index)
{
if (start > end)
return start;
start += sprintf26(start, "Interface:");
if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
start += sprintf26(start, "%s: %.100s ", id, dev->stringindex[index]);
return start;
}
 
#endif /* PROC_EXTRA */
 
/*****************************************************************/
 
/* This is a recursive function. Parameters:
* buffer - the user-space buffer to write data into
* nbytes - the maximum number of bytes to write
* skip_bytes - the number of bytes to skip before writing anything
* file_offset - the offset into the devices file on completion
*/
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
{
int chix;
int ret, cnt = 0;
int parent_devnum = 0;
char *pages_start, *data_end, *speed;
unsigned int length;
ssize_t total_written = 0;
/* don't bother with anything else if we're not writing any data */
if (*nbytes <= 0)
return 0;
if (level > MAX_TOPO_LEVEL)
return total_written;
/* allocate 2^1 pages = 8K (on i386); should be more than enough for one device */
if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1)))
return -ENOMEM;
if (usbdev->parent && usbdev->parent->devnum != -1)
parent_devnum = usbdev->parent->devnum;
/*
* So the root hub's parent is 0 and any device that is
* plugged into the root hub has a parent of 0.
*/
switch (usbdev->speed) {
case USB_SPEED_LOW:
speed = "1.5"; break;
case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */
case USB_SPEED_FULL:
speed = "12 "; break;
case USB_SPEED_HIGH:
speed = "480"; break;
default:
speed = "?? ";
}
data_end = pages_start + sprintf26(pages_start, format_topo,
bus->busnum, level, parent_devnum,
index, count, usbdev->devnum,
speed, usbdev->maxchild);
/*
* level = topology-tier level;
* parent_devnum = parent device number;
* index = parent's connector number;
* count = device count at this level
*/
/* If this is the root hub, display the bandwidth information */
if (level == 0) {
int max;
 
/* high speed reserves 80%, full/low reserves 90% */
if (usbdev->speed == USB_SPEED_HIGH)
max = 800;
else
max = FRAME_TIME_MAX_USECS_ALLOC;
 
/* report "average" periodic allocation over a microsecond.
* the schedules are actually bursty, HCDs need to deal with
* that and just compute/report this average.
*/
data_end += sprintf26(data_end, format_bandwidth,
bus->bandwidth_allocated, max,
(100 * bus->bandwidth_allocated + max / 2)
/ max,
bus->bandwidth_int_reqs,
bus->bandwidth_isoc_reqs);
}
data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev);
if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))
data_end += sprintf26(data_end, "(truncated)\n");
length = data_end - pages_start;
/* if we can start copying some data to the user */
if (length > *skip_bytes) {
length -= *skip_bytes;
if (length > *nbytes)
length = *nbytes;
if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
free_pages((unsigned long)pages_start, 1);
if (total_written == 0)
return -EFAULT;
return total_written;
}
*nbytes -= length;
*file_offset += length;
total_written += length;
*buffer += length;
*skip_bytes = 0;
} else
*skip_bytes -= length;
free_pages((unsigned long)pages_start, 1);
/* Now look at all of this device's children. */
for (chix = 0; chix < usbdev->maxchild; chix++) {
if (usbdev->children[chix]) {
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, usbdev->children[chix],
bus, level + 1, chix, ++cnt);
if (ret == -EFAULT)
return total_written;
total_written += ret;
}
}
return total_written;
}
 
static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
struct list_head *buslist;
struct usb_bus *bus;
ssize_t ret, total_written = 0;
loff_t skip_bytes = *ppos;
 
if (*ppos < 0)
return -EINVAL;
if (nbytes <= 0)
return 0;
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
 
/* enumerate busses */
down (&usb_bus_list_lock);
for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) {
/* print devices for this bus */
bus = list_entry(buslist, struct usb_bus, bus_list);
/* recurse through all children of the root hub */
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
if (ret < 0) {
up(&usb_bus_list_lock);
return ret;
}
total_written += ret;
}
up (&usb_bus_list_lock);
return total_written;
}
 
/* Kernel lock for "lastev" protection */
static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
{
struct usb_device_status *st = (struct usb_device_status *)file->private_data;
unsigned int mask = 0;
 
lock_kernel();
if (!st) {
st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
if (!st) {
unlock_kernel();
return POLLIN;
}
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
kfree(st);
goto lost_race;
}
 
/*
* need to prevent the module from being unloaded, since
* proc_unregister does not call the release method and
* we would have a memory leak
*/
st->lastev = conndiscevcnt;
file->private_data = st;
mask = POLLIN;
}
lost_race:
if (file->f_mode & FMODE_READ)
poll_wait(file, &deviceconndiscwq, wait);
if (st->lastev != conndiscevcnt)
mask |= POLLIN;
st->lastev = conndiscevcnt;
unlock_kernel();
return mask;
}
 
static int usb_device_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
 
static int usb_device_release(struct inode *inode, struct file *file)
{
if (file->private_data) {
kfree(file->private_data);
file->private_data = NULL;
}
 
return 0;
}
 
static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
{
loff_t ret;
 
lock_kernel();
 
switch (orig) {
case 0:
file->f_pos = offset;
ret = file->f_pos;
break;
case 1:
file->f_pos += offset;
ret = file->f_pos;
break;
case 2:
default:
ret = -EINVAL;
}
 
unlock_kernel();
return ret;
}
 
struct file_operations usbdevfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
.open = usb_device_open,
.release = usb_device_release,
};
/shark/trunk/drivers/usb/core/unused/inode.c
0,0 → 1,779
/*****************************************************************************/
 
/*
* inode.c -- Inode/Dentry functions for the USB device file system.
*
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
* Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
* 0.1 04.01.2000 Created
* 0.2 10.12.2001 converted to use the vfs layer better
*/
 
/*****************************************************************************/
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/usb.h>
#include <linux/namei.h>
#include <linux/usbdevice_fs.h>
#include <linux/smp_lock.h>
#include <linux/parser.h>
#include <asm/byteorder.h>
 
static struct super_operations usbfs_ops;
static struct file_operations default_file_operations;
static struct inode_operations usbfs_dir_inode_operations;
static struct vfsmount *usbdevfs_mount;
static struct vfsmount *usbfs_mount;
static int usbdevfs_mount_count; /* = 0 */
static int usbfs_mount_count; /* = 0 */
 
static struct dentry *devices_usbdevfs_dentry;
static struct dentry *devices_usbfs_dentry;
static int num_buses; /* = 0 */
 
static uid_t devuid; /* = 0 */
static uid_t busuid; /* = 0 */
static uid_t listuid; /* = 0 */
static gid_t devgid; /* = 0 */
static gid_t busgid; /* = 0 */
static gid_t listgid; /* = 0 */
static umode_t devmode = S_IWUSR | S_IRUGO;
static umode_t busmode = S_IXUGO | S_IRUGO;
static umode_t listmode = S_IRUGO;
 
enum {
Opt_devuid, Opt_devgid, Opt_devmode,
Opt_busuid, Opt_busgid, Opt_busmode,
Opt_listuid, Opt_listgid, Opt_listmode,
Opt_err,
};
 
static match_table_t tokens = {
{Opt_devuid, "devuid=%u"},
{Opt_devgid, "devgid=%u"},
{Opt_devmode, "devmode=%o"},
{Opt_busuid, "busuid=%u"},
{Opt_busgid, "busgid=%u"},
{Opt_busmode, "busmode=%o"},
{Opt_listuid, "listuid=%u"},
{Opt_listgid, "listgid=%u"},
{Opt_listmode, "listmode=%o"},
{Opt_err, NULL}
};
 
static int parse_options(struct super_block *s, char *data)
{
char *p;
int option;
 
while ((p = strsep(&data, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
continue;
 
token = match_token(p, tokens, args);
switch (token) {
case Opt_devuid:
if (match_int(&args[0], &option))
return -EINVAL;
devuid = option;
break;
case Opt_devgid:
if (match_int(&args[0], &option))
return -EINVAL;
devgid = option;
break;
case Opt_devmode:
if (match_octal(&args[0], &option))
return -EINVAL;
devmode = option & S_IRWXUGO;
break;
case Opt_busuid:
if (match_int(&args[0], &option))
return -EINVAL;
busuid = option;
break;
case Opt_busgid:
if (match_int(&args[0], &option))
return -EINVAL;
busgid = option;
break;
case Opt_busmode:
if (match_octal(&args[0], &option))
return -EINVAL;
busmode = option & S_IRWXUGO;
break;
case Opt_listuid:
if (match_int(&args[0], &option))
return -EINVAL;
listuid = option;
break;
case Opt_listgid:
if (match_int(&args[0], &option))
return -EINVAL;
listgid = option;
break;
case Opt_listmode:
if (match_octal(&args[0], &option))
return -EINVAL;
listmode = option & S_IRWXUGO;
break;
default:
err("usbfs: unrecognised mount option \"%s\" "
"or missing value\n", p);
return -EINVAL;
}
}
 
return 0;
}
 
static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
 
if (inode) {
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &default_file_operations;
break;
case S_IFDIR:
inode->i_op = &usbfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
 
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode->i_nlink++;
break;
}
}
return inode;
}
 
/* SMP-safe */
static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
dev_t dev)
{
struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
int error = -EPERM;
 
if (dentry->d_inode)
return -EEXIST;
 
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
error = 0;
}
return error;
}
 
static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
{
int res;
 
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = usbfs_mknod (dir, dentry, mode, 0);
if (!res)
dir->i_nlink++;
return res;
}
 
static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode)
{
mode = (mode & S_IALLUGO) | S_IFREG;
return usbfs_mknod (dir, dentry, mode, 0);
}
 
static inline int usbfs_positive (struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
 
static int usbfs_empty (struct dentry *dentry)
{
struct list_head *list;
 
spin_lock(&dcache_lock);
 
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (usbfs_positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
}
 
spin_unlock(&dcache_lock);
return 1;
}
 
static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
down(&inode->i_sem);
dentry->d_inode->i_nlink--;
dput(dentry);
up(&inode->i_sem);
d_delete(dentry);
return 0;
}
 
static void d_unhash(struct dentry *dentry)
{
dget(dentry);
spin_lock(&dcache_lock);
switch (atomic_read(&dentry->d_count)) {
default:
spin_unlock(&dcache_lock);
shrink_dcache_parent(dentry);
spin_lock(&dcache_lock);
if (atomic_read(&dentry->d_count) != 2)
break;
case 2:
__d_drop(dentry);
}
spin_unlock(&dcache_lock);
}
 
static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error = -ENOTEMPTY;
struct inode * inode = dentry->d_inode;
 
down(&inode->i_sem);
d_unhash(dentry);
if (usbfs_empty(dentry)) {
dentry->d_inode->i_nlink -= 2;
dput(dentry);
inode->i_flags |= S_DEAD;
dir->i_nlink--;
error = 0;
}
up(&inode->i_sem);
if (!error)
d_delete(dentry);
dput(dentry);
return error;
}
 
 
/* default file operations */
static ssize_t default_read_file (struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
 
static ssize_t default_write_file (struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
 
static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
{
loff_t retval = -EINVAL;
 
down(&file->f_dentry->d_inode->i_sem);
switch(orig) {
case 0:
if (offset > 0) {
file->f_pos = offset;
retval = file->f_pos;
}
break;
case 1:
if ((offset + file->f_pos) > 0) {
file->f_pos += offset;
retval = file->f_pos;
}
break;
default:
break;
}
up(&file->f_dentry->d_inode->i_sem);
return retval;
}
 
static int default_open (struct inode *inode, struct file *file)
{
if (inode->u.generic_ip)
file->private_data = inode->u.generic_ip;
 
return 0;
}
 
static struct file_operations default_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
.llseek = default_file_lseek,
};
 
static struct inode_operations usbfs_dir_inode_operations = {
.lookup = simple_lookup,
};
 
static struct super_operations usbfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
};
 
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
 
if (parse_options(sb, data)) {
warn("usbfs: mount parameter error:");
return -EINVAL;
}
 
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = USBDEVICE_SUPER_MAGIC;
sb->s_op = &usbfs_ops;
inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
 
if (!inode) {
dbg("%s: could not get inode!",__FUNCTION__);
return -ENOMEM;
}
 
root = d_alloc_root(inode);
if (!root) {
dbg("%s: could not get root dentry!",__FUNCTION__);
iput(inode);
return -ENOMEM;
}
sb->s_root = root;
return 0;
}
 
static struct dentry * get_dentry(struct dentry *parent, const char *name)
{
struct qstr qstr;
 
qstr.name = name;
qstr.len = strlen(name);
qstr.hash = full_name_hash(name,qstr.len);
return lookup_hash(&qstr,parent);
}
 
 
/*
* fs_create_by_name - create a file, given a name
* @name: name of file
* @mode: type of file
* @parent: dentry of directory to create it in
* @dentry: resulting dentry of file
*
* This function handles both regular files and directories.
*/
static int fs_create_by_name (const char *name, mode_t mode,
struct dentry *parent, struct dentry **dentry)
{
int error = 0;
 
/* If the parent is not specified, we create it in the root.
* We need the root dentry to do this, which is in the super
* block. A pointer to that is in the struct vfsmount that we
* have around.
*/
if (!parent ) {
if (usbfs_mount && usbfs_mount->mnt_sb) {
parent = usbfs_mount->mnt_sb->s_root;
}
}
 
if (!parent) {
dbg("Ah! can not find a parent!");
return -EFAULT;
}
 
*dentry = NULL;
down(&parent->d_inode->i_sem);
*dentry = get_dentry (parent, name);
if (!IS_ERR(dentry)) {
if ((mode & S_IFMT) == S_IFDIR)
error = usbfs_mkdir (parent->d_inode, *dentry, mode);
else
error = usbfs_create (parent->d_inode, *dentry, mode);
} else
error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem);
 
return error;
}
 
static struct dentry *fs_create_file (const char *name, mode_t mode,
struct dentry *parent, void *data,
struct file_operations *fops,
uid_t uid, gid_t gid)
{
struct dentry *dentry;
int error;
 
dbg("creating file '%s'",name);
 
error = fs_create_by_name (name, mode, parent, &dentry);
if (error) {
dentry = NULL;
} else {
if (dentry->d_inode) {
if (data)
dentry->d_inode->u.generic_ip = data;
if (fops)
dentry->d_inode->i_fop = fops;
dentry->d_inode->i_uid = uid;
dentry->d_inode->i_gid = gid;
}
}
 
return dentry;
}
 
static void fs_remove_file (struct dentry *dentry)
{
struct dentry *parent = dentry->d_parent;
if (!parent || !parent->d_inode)
return;
 
down(&parent->d_inode->i_sem);
if (usbfs_positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode))
usbfs_rmdir(parent->d_inode, dentry);
else
usbfs_unlink(parent->d_inode, dentry);
dput(dentry);
}
}
up(&parent->d_inode->i_sem);
}
 
/* --------------------------------------------------------------------- */
 
 
 
/*
* The usbdevfs name is now deprecated (as of 2.5.1).
* It will be removed when the 2.7.x development cycle is started.
* You have been warned :)
*/
static struct file_system_type usbdevice_fs_type;
 
static struct super_block *usb_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, usbfs_fill_super);
}
 
static struct file_system_type usbdevice_fs_type = {
.owner = THIS_MODULE,
.name = "usbdevfs",
.get_sb = usb_get_sb,
.kill_sb = kill_litter_super,
};
 
static struct file_system_type usb_fs_type = {
.owner = THIS_MODULE,
.name = "usbfs",
.get_sb = usb_get_sb,
.kill_sb = kill_litter_super,
};
 
/* --------------------------------------------------------------------- */
 
static int create_special_files (void)
{
struct dentry *parent;
int retval;
 
/* create the devices special file */
retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count);
if (retval) {
err ("Unable to get usbdevfs mount");
goto exit;
}
 
retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count);
if (retval) {
err ("Unable to get usbfs mount");
goto error_clean_usbdevfs_mount;
}
 
parent = usbfs_mount->mnt_sb->s_root;
devices_usbfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
NULL, &usbdevfs_devices_fops,
listuid, listgid);
if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
retval = -ENODEV;
goto error_clean_mounts;
}
 
parent = usbdevfs_mount->mnt_sb->s_root;
devices_usbdevfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
NULL, &usbdevfs_devices_fops,
listuid, listgid);
if (devices_usbdevfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
retval = -ENODEV;
goto error_remove_file;
}
 
goto exit;
error_remove_file:
fs_remove_file (devices_usbfs_dentry);
devices_usbfs_dentry = NULL;
 
error_clean_mounts:
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
 
error_clean_usbdevfs_mount:
simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
 
exit:
return retval;
}
 
static void remove_special_files (void)
{
if (devices_usbdevfs_dentry)
fs_remove_file (devices_usbdevfs_dentry);
if (devices_usbfs_dentry)
fs_remove_file (devices_usbfs_dentry);
devices_usbdevfs_dentry = NULL;
devices_usbfs_dentry = NULL;
simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
}
 
void usbfs_update_special (void)
{
struct inode *inode;
 
if (devices_usbdevfs_dentry) {
inode = devices_usbdevfs_dentry->d_inode;
if (inode)
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
if (devices_usbfs_dentry) {
inode = devices_usbfs_dentry->d_inode;
if (inode)
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
}
 
void usbfs_add_bus(struct usb_bus *bus)
{
struct dentry *parent;
char name[8];
int retval;
 
/* create the special files if this is the first bus added */
if (num_buses == 0) {
retval = create_special_files();
if (retval)
return;
}
++num_buses;
 
sprintf (name, "%03d", bus->busnum);
 
parent = usbfs_mount->mnt_sb->s_root;
bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
bus, NULL, busuid, busgid);
if (bus->usbfs_dentry == NULL) {
err ("error creating usbfs bus entry");
return;
}
 
parent = usbdevfs_mount->mnt_sb->s_root;
bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
bus, NULL, busuid, busgid);
if (bus->usbdevfs_dentry == NULL) {
err ("error creating usbdevfs bus entry");
return;
}
 
usbfs_update_special();
usbdevfs_conn_disc_event();
}
 
 
void usbfs_remove_bus(struct usb_bus *bus)
{
if (bus->usbfs_dentry) {
fs_remove_file (bus->usbfs_dentry);
bus->usbfs_dentry = NULL;
}
if (bus->usbdevfs_dentry) {
fs_remove_file (bus->usbdevfs_dentry);
bus->usbdevfs_dentry = NULL;
}
 
--num_buses;
if (num_buses <= 0) {
remove_special_files();
num_buses = 0;
}
 
usbfs_update_special();
usbdevfs_conn_disc_event();
}
 
void usbfs_add_device(struct usb_device *dev)
{
char name[8];
int i;
int i_size;
 
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
&usbdevfs_device_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
return;
}
dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbdevfs_dentry, dev,
&usbdevfs_device_file_operations,
devuid, devgid);
if (dev->usbdevfs_dentry == NULL) {
err ("error creating usbdevfs device entry");
return;
}
 
/* Set the size of the device's file to be
* equal to the size of the device descriptors. */
i_size = sizeof (struct usb_device_descriptor);
for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
struct usb_config_descriptor *config =
(struct usb_config_descriptor *)dev->rawdescriptors[i];
i_size += le16_to_cpu (config->wTotalLength);
}
if (dev->usbfs_dentry->d_inode)
dev->usbfs_dentry->d_inode->i_size = i_size;
if (dev->usbdevfs_dentry->d_inode)
dev->usbdevfs_dentry->d_inode->i_size = i_size;
 
usbfs_update_special();
usbdevfs_conn_disc_event();
}
 
void usbfs_remove_device(struct usb_device *dev)
{
struct dev_state *ds;
struct siginfo sinfo;
 
if (dev->usbfs_dentry) {
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
if (dev->usbdevfs_dentry) {
fs_remove_file (dev->usbdevfs_dentry);
dev->usbdevfs_dentry = NULL;
}
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
list_del_init(&ds->list);
down_write(&ds->devsem);
ds->dev = NULL;
up_write(&ds->devsem);
if (ds->discsignr) {
sinfo.si_signo = SIGPIPE;
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ds->disccontext;
send_sig_info(ds->discsignr, &sinfo, ds->disctask);
}
}
usbfs_update_special();
usbdevfs_conn_disc_event();
}
 
/* --------------------------------------------------------------------- */
 
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *usbdir = NULL;
#endif
 
int __init usbfs_init(void)
{
int retval;
 
retval = usb_register(&usbdevfs_driver);
if (retval)
return retval;
 
retval = register_filesystem(&usb_fs_type);
if (retval) {
usb_deregister(&usbdevfs_driver);
return retval;
}
retval = register_filesystem(&usbdevice_fs_type);
if (retval) {
unregister_filesystem(&usb_fs_type);
usb_deregister(&usbdevfs_driver);
return retval;
}
 
#ifdef CONFIG_PROC_FS
/* create mount point for usbdevfs */
usbdir = proc_mkdir("usb", proc_bus);
#endif
 
return 0;
}
 
void __exit usbfs_cleanup(void)
{
usb_deregister(&usbdevfs_driver);
unregister_filesystem(&usb_fs_type);
unregister_filesystem(&usbdevice_fs_type);
#ifdef CONFIG_PROC_FS
if (usbdir)
remove_proc_entry("usb", proc_bus);
#endif
}
 
/shark/trunk/drivers/usb/core/unused/devio.c
0,0 → 1,1279
/*****************************************************************************/
 
/*
* devio.c -- User space communication with USB devices.
*
* Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: devio.c,v 1.1 2004-09-13 15:04:47 giacomo Exp $
*
* This file implements the usbdevfs/x/y files, where
* x is the bus number and y the device number.
*
* It allows user space programs/"drivers" to communicate directly
* with USB devices without intervening kernel driver.
*
* Revision history
* 22.12.1999 0.1 Initial release (split from proc_usb.c)
* 04.01.2000 0.2 Turned into its own filesystem
*/
 
/*****************************************************************************/
 
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/signal.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
 
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
 
struct async {
struct list_head asynclist;
struct dev_state *ps;
struct task_struct *task;
unsigned int signr;
unsigned int intf;
void __user *userbuffer;
void __user *userurb;
struct urb *urb;
};
 
static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
 
lock_kernel();
 
switch (orig) {
case 0:
file->f_pos = offset;
ret = file->f_pos;
break;
case 1:
file->f_pos += offset;
ret = file->f_pos;
break;
case 2:
default:
ret = -EINVAL;
}
 
unlock_kernel();
return ret;
}
 
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
ssize_t ret = 0;
unsigned len;
loff_t pos;
int i;
 
pos = *ppos;
down_read(&ps->devsem);
if (!ps->dev) {
ret = -ENODEV;
goto err;
} else if (pos < 0) {
ret = -EINVAL;
goto err;
}
 
if (pos < sizeof(struct usb_device_descriptor)) {
len = sizeof(struct usb_device_descriptor) - pos;
if (len > nbytes)
len = nbytes;
if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) {
ret = -EFAULT;
goto err;
}
 
*ppos += len;
buf += len;
nbytes -= len;
ret += len;
}
 
pos = sizeof(struct usb_device_descriptor);
for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) {
struct usb_config_descriptor *config =
(struct usb_config_descriptor *)ps->dev->rawdescriptors[i];
unsigned int length = le16_to_cpu(config->wTotalLength);
 
if (*ppos < pos + length) {
len = length - (*ppos - pos);
if (len > nbytes)
len = nbytes;
 
if (copy_to_user(buf,
ps->dev->rawdescriptors[i] + (*ppos - pos), len)) {
ret = -EFAULT;
goto err;
}
 
*ppos += len;
buf += len;
nbytes -= len;
ret += len;
}
 
pos += length;
}
 
err:
up_read(&ps->devsem);
return ret;
}
 
extern inline unsigned int ld2(unsigned int x)
{
unsigned int r = 0;
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 4) {
x >>= 2;
r += 2;
}
if (x >= 2)
r++;
return r;
}
 
/*
* async list handling
*/
 
static struct async *alloc_async(unsigned int numisoframes)
{
unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
struct async *as = kmalloc(assize, GFP_KERNEL);
if (!as)
return NULL;
memset(as, 0, assize);
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
if (!as->urb) {
kfree(as);
return NULL;
}
return as;
}
 
static void free_async(struct async *as)
{
if (as->urb->transfer_buffer)
kfree(as->urb->transfer_buffer);
if (as->urb->setup_packet)
kfree(as->urb->setup_packet);
usb_free_urb(as->urb);
kfree(as);
}
 
extern __inline__ void async_newpending(struct async *as)
{
struct dev_state *ps = as->ps;
unsigned long flags;
spin_lock_irqsave(&ps->lock, flags);
list_add_tail(&as->asynclist, &ps->async_pending);
spin_unlock_irqrestore(&ps->lock, flags);
}
 
extern __inline__ void async_removepending(struct async *as)
{
struct dev_state *ps = as->ps;
unsigned long flags;
spin_lock_irqsave(&ps->lock, flags);
list_del_init(&as->asynclist);
spin_unlock_irqrestore(&ps->lock, flags);
}
 
extern __inline__ struct async *async_getcompleted(struct dev_state *ps)
{
unsigned long flags;
struct async *as = NULL;
 
spin_lock_irqsave(&ps->lock, flags);
if (!list_empty(&ps->async_completed)) {
as = list_entry(ps->async_completed.next, struct async, asynclist);
list_del_init(&as->asynclist);
}
spin_unlock_irqrestore(&ps->lock, flags);
return as;
}
 
extern __inline__ struct async *async_getpending(struct dev_state *ps, void __user *userurb)
{
unsigned long flags;
struct async *as;
 
spin_lock_irqsave(&ps->lock, flags);
list_for_each_entry(as, &ps->async_pending, asynclist)
if (as->userurb == userurb) {
list_del_init(&as->asynclist);
spin_unlock_irqrestore(&ps->lock, flags);
return as;
}
spin_unlock_irqrestore(&ps->lock, flags);
return NULL;
}
 
static void async_completed(struct urb *urb, struct pt_regs *regs)
{
struct async *as = (struct async *)urb->context;
struct dev_state *ps = as->ps;
struct siginfo sinfo;
 
spin_lock(&ps->lock);
list_move_tail(&as->asynclist, &ps->async_completed);
spin_unlock(&ps->lock);
if (as->signr) {
sinfo.si_signo = as->signr;
sinfo.si_errno = as->urb->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = (void *)as->userurb;
send_sig_info(as->signr, &sinfo, as->task);
}
wake_up(&ps->wait);
}
 
static void destroy_async (struct dev_state *ps, struct list_head *list)
{
struct async *as;
unsigned long flags;
 
spin_lock_irqsave(&ps->lock, flags);
while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist);
list_del_init(&as->asynclist);
spin_unlock_irqrestore(&ps->lock, flags);
/* usb_unlink_urb calls the completion handler with status == -ENOENT */
usb_unlink_urb(as->urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
while ((as = async_getcompleted(ps)))
free_async(as);
}
 
static void destroy_async_on_interface (struct dev_state *ps, unsigned int intf)
{
struct list_head *p, *q, hitlist;
unsigned long flags;
 
INIT_LIST_HEAD(&hitlist);
spin_lock_irqsave(&ps->lock, flags);
list_for_each_safe(p, q, &ps->async_pending)
if (intf == list_entry(p, struct async, asynclist)->intf)
list_move_tail(p, &hitlist);
spin_unlock_irqrestore(&ps->lock, flags);
destroy_async(ps, &hitlist);
}
 
extern __inline__ void destroy_all_async(struct dev_state *ps)
{
destroy_async(ps, &ps->async_pending);
}
 
/*
* interface claims are made only at the request of user level code,
* which can also release them (explicitly or by closing files).
* they're also undone when devices disconnect.
*/
 
static int driver_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
return -ENODEV;
}
 
static void driver_disconnect(struct usb_interface *intf)
{
struct dev_state *ps = usb_get_intfdata (intf);
 
if (!ps)
return;
 
/* this waits till synchronous requests complete */
down_write (&ps->devsem);
 
/* prevent new I/O requests */
ps->dev = 0;
ps->ifclaimed = 0;
usb_set_intfdata (intf, NULL);
 
/* force async requests to complete */
destroy_all_async (ps);
 
up_write (&ps->devsem);
}
 
struct usb_driver usbdevfs_driver = {
.owner = THIS_MODULE,
.name = "usbfs",
.probe = driver_probe,
.disconnect = driver_disconnect,
};
 
static int claimintf(struct dev_state *ps, unsigned int intf)
{
struct usb_device *dev = ps->dev;
struct usb_interface *iface;
int err;
 
if (intf >= 8*sizeof(ps->ifclaimed) || !dev
|| intf >= dev->actconfig->desc.bNumInterfaces)
return -EINVAL;
/* already claimed */
if (test_bit(intf, &ps->ifclaimed))
return 0;
iface = dev->actconfig->interface[intf];
err = -EBUSY;
lock_kernel();
if (!usb_interface_claimed(iface)) {
usb_driver_claim_interface(&usbdevfs_driver, iface, ps);
set_bit(intf, &ps->ifclaimed);
err = 0;
}
unlock_kernel();
return err;
}
 
static int releaseintf(struct dev_state *ps, unsigned int intf)
{
struct usb_device *dev;
struct usb_interface *iface;
int err;
 
if (intf >= 8*sizeof(ps->ifclaimed))
return -EINVAL;
err = -EINVAL;
dev = ps->dev;
down(&dev->serialize);
if (test_and_clear_bit(intf, &ps->ifclaimed)) {
iface = dev->actconfig->interface[intf];
usb_driver_release_interface(&usbdevfs_driver, iface);
err = 0;
}
up(&dev->serialize);
return err;
}
 
static int checkintf(struct dev_state *ps, unsigned int intf)
{
if (intf >= 8*sizeof(ps->ifclaimed))
return -EINVAL;
if (test_bit(intf, &ps->ifclaimed))
return 0;
/* if not yet claimed, claim it for the driver */
printk(KERN_WARNING "usbfs: process %d (%s) did not claim interface %u before use\n",
current->pid, current->comm, intf);
return claimintf(ps, intf);
}
 
static int findintfep(struct usb_device *dev, unsigned int ep)
{
unsigned int i, j, e;
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_endpoint_descriptor *endpt;
 
if (ep & ~(USB_DIR_IN|0xf))
return -EINVAL;
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
iface = dev->actconfig->interface[i];
for (j = 0; j < iface->num_altsetting; j++) {
alts = &iface->altsetting[j];
for (e = 0; e < alts->desc.bNumEndpoints; e++) {
endpt = &alts->endpoint[e].desc;
if (endpt->bEndpointAddress == ep)
return i;
}
}
}
return -ENOENT;
}
 
static int findintfif(struct usb_device *dev, unsigned int ifn)
{
unsigned int i, j;
struct usb_interface *iface;
struct usb_host_interface *alts;
 
if (ifn & ~0xff)
return -EINVAL;
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
iface = dev->actconfig->interface[i];
for (j = 0; j < iface->num_altsetting; j++) {
alts = &iface->altsetting[j];
if (alts->desc.bInterfaceNumber == ifn)
return i;
}
}
return -ENOENT;
}
 
static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsigned int index)
{
int ret;
 
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
return 0;
 
switch (requesttype & USB_RECIP_MASK) {
case USB_RECIP_ENDPOINT:
if ((ret = findintfep(ps->dev, index & 0xff)) < 0)
return ret;
if ((ret = checkintf(ps, ret)))
return ret;
break;
 
case USB_RECIP_INTERFACE:
if ((ret = findintfif(ps->dev, index & 0xff)) < 0)
return ret;
if ((ret = checkintf(ps, ret)))
return ret;
break;
}
return 0;
}
 
/*
* file operations
*/
static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_device *dev;
struct dev_state *ps;
int ret;
 
/*
* no locking necessary here, as both sys_open (actually filp_open)
* and the hub thread have the kernel lock
* (still acquire the kernel lock for safety)
*/
ret = -ENOMEM;
if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
goto out_nolock;
 
lock_kernel();
ret = -ENOENT;
dev = inode->u.generic_ip;
if (!dev) {
kfree(ps);
goto out;
}
ret = 0;
ps->dev = dev;
ps->file = file;
spin_lock_init(&ps->lock);
INIT_LIST_HEAD(&ps->async_pending);
INIT_LIST_HEAD(&ps->async_completed);
init_waitqueue_head(&ps->wait);
init_rwsem(&ps->devsem);
ps->discsignr = 0;
ps->disctask = current;
ps->disccontext = NULL;
ps->ifclaimed = 0;
wmb();
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
unlock_kernel();
out_nolock:
return ret;
}
 
static int usbdev_release(struct inode *inode, struct file *file)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
unsigned int i;
 
lock_kernel();
list_del_init(&ps->list);
 
if (ps->dev) {
for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++)
if (test_bit(i, &ps->ifclaimed))
releaseintf(ps, i);
}
unlock_kernel();
destroy_all_async(ps);
kfree(ps);
return 0;
}
 
static int proc_control(struct dev_state *ps, void __user *arg)
{
struct usb_device *dev = ps->dev;
struct usbdevfs_ctrltransfer ctrl;
unsigned int tmo;
unsigned char *tbuf;
int i, ret;
 
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
if ((ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex)))
return ret;
if (ctrl.wLength > PAGE_SIZE)
return -EINVAL;
if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
return -ENOMEM;
tmo = (ctrl.timeout * HZ + 999) / 1000;
if (ctrl.bRequestType & 0x80) {
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) {
free_page((unsigned long)tbuf);
return -EINVAL;
}
i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
if ((i > 0) && ctrl.wLength) {
if (copy_to_user(ctrl.data, tbuf, ctrl.wLength)) {
free_page((unsigned long)tbuf);
return -EFAULT;
}
}
} else {
if (ctrl.wLength) {
if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) {
free_page((unsigned long)tbuf);
return -EFAULT;
}
}
i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
}
free_page((unsigned long)tbuf);
if (i<0) {
printk(KERN_DEBUG "usbfs: USBDEVFS_CONTROL failed "
"cmd %s dev %d rqt %u rq %u len %u ret %d\n",
current->comm,
dev->devnum, ctrl.bRequestType, ctrl.bRequest, ctrl.wLength, i);
}
return i;
}
 
static int proc_bulk(struct dev_state *ps, void __user *arg)
{
struct usb_device *dev = ps->dev;
struct usbdevfs_bulktransfer bulk;
unsigned int tmo, len1, pipe;
int len2;
unsigned char *tbuf;
int i, ret;
 
if (copy_from_user(&bulk, arg, sizeof(bulk)))
return -EFAULT;
if ((ret = findintfep(ps->dev, bulk.ep)) < 0)
return ret;
if ((ret = checkintf(ps, ret)))
return ret;
if (bulk.ep & USB_DIR_IN)
pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
else
pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
return -EINVAL;
len1 = bulk.len;
if (!(tbuf = kmalloc(len1, GFP_KERNEL)))
return -ENOMEM;
tmo = (bulk.timeout * HZ + 999) / 1000;
if (bulk.ep & 0x80) {
if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
kfree(tbuf);
return -EINVAL;
}
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
if (!i && len2) {
if (copy_to_user(bulk.data, tbuf, len2)) {
kfree(tbuf);
return -EFAULT;
}
}
} else {
if (len1) {
if (copy_from_user(tbuf, bulk.data, len1)) {
kfree(tbuf);
return -EFAULT;
}
}
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
}
kfree(tbuf);
if (i < 0) {
printk(KERN_WARNING "usbfs: USBDEVFS_BULK failed dev %d ep 0x%x len %u ret %d\n",
dev->devnum, bulk.ep, bulk.len, i);
return i;
}
return len2;
}
 
static int proc_resetep(struct dev_state *ps, void __user *arg)
{
unsigned int ep;
int ret;
 
if (get_user(ep, (unsigned int __user *)arg))
return -EFAULT;
if ((ret = findintfep(ps->dev, ep)) < 0)
return ret;
if ((ret = checkintf(ps, ret)))
return ret;
usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
return 0;
}
 
static int proc_clearhalt(struct dev_state *ps, void __user *arg)
{
unsigned int ep;
int pipe;
int ret;
 
if (get_user(ep, (unsigned int __user *)arg))
return -EFAULT;
if ((ret = findintfep(ps->dev, ep)) < 0)
return ret;
if ((ret = checkintf(ps, ret)))
return ret;
if (ep & USB_DIR_IN)
pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
else
pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f);
 
return usb_clear_halt(ps->dev, pipe);
}
 
static int proc_getdriver(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_getdriver gd;
struct usb_interface *interface;
int ret;
 
if (copy_from_user(&gd, arg, sizeof(gd)))
return -EFAULT;
if ((ret = findintfif(ps->dev, gd.interface)) < 0)
return ret;
interface = usb_ifnum_to_if(ps->dev, gd.interface);
if (!interface)
return -EINVAL;
if (!interface->driver)
return -ENODATA;
strcpy(gd.driver, interface->driver->name);
if (copy_to_user(arg, &gd, sizeof(gd)))
return -EFAULT;
return 0;
}
 
static int proc_connectinfo(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_connectinfo ci;
 
ci.devnum = ps->dev->devnum;
ci.slow = ps->dev->speed == USB_SPEED_LOW;
if (copy_to_user(arg, &ci, sizeof(ci)))
return -EFAULT;
return 0;
}
 
static int proc_resetdevice(struct dev_state *ps)
{
int i, ret;
 
ret = usb_reset_device(ps->dev);
if (ret < 0)
return ret;
 
for (i = 0; i < ps->dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf = ps->dev->actconfig->interface[i];
 
/* Don't simulate interfaces we've claimed */
if (test_bit(i, &ps->ifclaimed))
continue;
 
err ("%s - this function is broken", __FUNCTION__);
if (intf->driver && ps->dev) {
usb_probe_interface (&intf->dev);
}
}
 
return 0;
}
 
static int proc_setintf(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_setinterface setintf;
struct usb_interface *interface;
int ret;
 
if (copy_from_user(&setintf, arg, sizeof(setintf)))
return -EFAULT;
if ((ret = findintfif(ps->dev, setintf.interface)) < 0)
return ret;
interface = usb_ifnum_to_if(ps->dev, setintf.interface);
if (!interface)
return -EINVAL;
if (interface->driver) {
if ((ret = checkintf(ps, ret)))
return ret;
}
if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting))
return -EINVAL;
return 0;
}
 
static int proc_setconfig(struct dev_state *ps, void __user *arg)
{
unsigned int u;
 
if (get_user(u, (unsigned int __user *)arg))
return -EFAULT;
return usb_set_configuration(ps->dev, u);
}
 
static int proc_submiturb(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
struct usbdevfs_iso_packet_desc *isopkt = NULL;
struct usb_endpoint_descriptor *ep_desc;
struct async *as;
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
int ret, interval = 0, intf = -1;
 
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
URB_NO_FSBR|URB_ZERO_PACKET))
return -EINVAL;
if (!uurb.buffer)
return -EINVAL;
if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX))
return -EINVAL;
if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
if ((intf = findintfep(ps->dev, uurb.endpoint)) < 0)
return intf;
if ((ret = checkintf(ps, intf)))
return ret;
}
switch(uurb.type) {
case USBDEVFS_URB_TYPE_CONTROL:
if ((uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) != 0) {
if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint)))
return -ENOENT;
if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_CONTROL)
return -EINVAL;
}
/* min 8 byte setup packet, max arbitrary */
if (uurb.buffer_length < 8 || uurb.buffer_length > PAGE_SIZE)
return -EINVAL;
if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
return -ENOMEM;
if (copy_from_user(dr, uurb.buffer, 8)) {
kfree(dr);
return -EFAULT;
}
if (uurb.buffer_length < (le16_to_cpup(&dr->wLength) + 8)) {
kfree(dr);
return -EINVAL;
}
if ((ret = check_ctrlrecip(ps, dr->bRequestType, le16_to_cpup(&dr->wIndex)))) {
kfree(dr);
return ret;
}
uurb.endpoint = (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK);
uurb.number_of_packets = 0;
uurb.buffer_length = le16_to_cpup(&dr->wLength);
uurb.buffer += 8;
if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) {
kfree(dr);
return -EFAULT;
}
break;
 
case USBDEVFS_URB_TYPE_BULK:
uurb.number_of_packets = 0;
if (uurb.buffer_length > 16384)
return -EINVAL;
if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length))
return -EFAULT;
break;
 
case USBDEVFS_URB_TYPE_ISO:
/* arbitrary limit */
if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128)
return -EINVAL;
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets;
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
return -ENOMEM;
if (copy_from_user(isopkt, &((struct usbdevfs_urb *)arg)->iso_frame_desc, isofrmlen)) {
kfree(isopkt);
return -EFAULT;
}
for (totlen = u = 0; u < uurb.number_of_packets; u++) {
if (isopkt[u].length > 1023) {
kfree(isopkt);
return -EINVAL;
}
totlen += isopkt[u].length;
}
if (totlen > 32768) {
kfree(isopkt);
return -EINVAL;
}
uurb.buffer_length = totlen;
break;
 
case USBDEVFS_URB_TYPE_INTERRUPT:
uurb.number_of_packets = 0;
if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint)))
return -ENOENT;
interval = ep_desc->bInterval;
if (uurb.buffer_length > 16384)
return -EINVAL;
if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length))
return -EFAULT;
break;
 
default:
return -EINVAL;
}
if (!(as = alloc_async(uurb.number_of_packets))) {
if (isopkt)
kfree(isopkt);
if (dr)
kfree(dr);
return -ENOMEM;
}
if (!(as->urb->transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) {
if (isopkt)
kfree(isopkt);
if (dr)
kfree(dr);
free_async(as);
return -ENOMEM;
}
as->urb->dev = ps->dev;
as->urb->pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN);
as->urb->transfer_flags = uurb.flags;
as->urb->transfer_buffer_length = uurb.buffer_length;
as->urb->setup_packet = (unsigned char*)dr;
as->urb->start_frame = uurb.start_frame;
as->urb->number_of_packets = uurb.number_of_packets;
as->urb->interval = interval;
as->urb->context = as;
as->urb->complete = async_completed;
for (totlen = u = 0; u < uurb.number_of_packets; u++) {
as->urb->iso_frame_desc[u].offset = totlen;
as->urb->iso_frame_desc[u].length = isopkt[u].length;
totlen += isopkt[u].length;
}
if (isopkt)
kfree(isopkt);
as->ps = ps;
as->userurb = arg;
if (uurb.endpoint & USB_DIR_IN)
as->userbuffer = uurb.buffer;
else
as->userbuffer = NULL;
as->signr = uurb.signr;
as->intf = intf;
as->task = current;
if (!(uurb.endpoint & USB_DIR_IN)) {
if (copy_from_user(as->urb->transfer_buffer, uurb.buffer, as->urb->transfer_buffer_length)) {
free_async(as);
return -EFAULT;
}
}
async_newpending(as);
if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
printk(KERN_DEBUG "usbfs: usb_submit_urb returned %d\n", ret);
async_removepending(as);
free_async(as);
return ret;
}
return 0;
}
 
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
{
struct async *as;
 
as = async_getpending(ps, arg);
if (!as)
return -EINVAL;
usb_unlink_urb(as->urb);
return 0;
}
 
static int processcompl(struct async *as)
{
struct urb *urb = as->urb;
unsigned int i;
 
if (as->userbuffer)
if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
return -EFAULT;
if (put_user(urb->status,
&((struct usbdevfs_urb *)as->userurb)->status))
return -EFAULT;
if (put_user(urb->actual_length,
&((struct usbdevfs_urb *)as->userurb)->actual_length))
return -EFAULT;
if (put_user(urb->error_count,
&((struct usbdevfs_urb *)as->userurb)->error_count))
return -EFAULT;
 
if (!(usb_pipeisoc(urb->pipe)))
return 0;
for (i = 0; i < urb->number_of_packets; i++) {
if (put_user(urb->iso_frame_desc[i].actual_length,
&((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length))
return -EFAULT;
if (put_user(urb->iso_frame_desc[i].status,
&((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status))
return -EFAULT;
}
return 0;
}
 
static int proc_reapurb(struct dev_state *ps, void __user *arg)
{
DECLARE_WAITQUEUE(wait, current);
struct async *as = NULL;
void __user *addr;
int ret;
 
add_wait_queue(&ps->wait, &wait);
while (ps->dev) {
__set_current_state(TASK_INTERRUPTIBLE);
if ((as = async_getcompleted(ps)))
break;
if (signal_pending(current))
break;
up_read(&ps->devsem);
schedule();
down_read(&ps->devsem);
}
remove_wait_queue(&ps->wait, &wait);
set_current_state(TASK_RUNNING);
if (as) {
ret = processcompl(as);
addr = as->userurb;
free_async(as);
if (ret)
return ret;
if (put_user(addr, (void **)arg))
return -EFAULT;
return 0;
}
if (signal_pending(current))
return -EINTR;
return -EIO;
}
 
static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
{
struct async *as;
void __user *addr;
int ret;
 
if (!(as = async_getcompleted(ps)))
return -EAGAIN;
ret = processcompl(as);
addr = as->userurb;
free_async(as);
if (ret)
return ret;
if (put_user(addr, (void **)arg))
return -EFAULT;
return 0;
}
 
static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_disconnectsignal ds;
 
if (copy_from_user(&ds, arg, sizeof(ds)))
return -EFAULT;
if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
return -EINVAL;
ps->discsignr = ds.signr;
ps->disccontext = ds.context;
return 0;
}
 
static int proc_claiminterface(struct dev_state *ps, void __user *arg)
{
unsigned int intf;
int ret;
 
if (get_user(intf, (unsigned int __user *)arg))
return -EFAULT;
if ((ret = findintfif(ps->dev, intf)) < 0)
return ret;
return claimintf(ps, ret);
}
 
static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
{
unsigned int intf;
int ret;
 
if (get_user(intf, (unsigned int __user *)arg))
return -EFAULT;
if ((ret = findintfif(ps->dev, intf)) < 0)
return ret;
if ((ret = releaseintf(ps, intf)) < 0)
return ret;
destroy_async_on_interface (ps, intf);
return 0;
}
 
static int proc_ioctl (struct dev_state *ps, void __user *arg)
{
struct usbdevfs_ioctl ctrl;
int size;
void *buf = 0;
int retval = 0;
struct usb_interface *ifp = 0;
struct usb_driver *driver = 0;
 
/* get input parameters and alloc buffer */
if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
return -EFAULT;
if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
if ((buf = kmalloc (size, GFP_KERNEL)) == 0)
return -ENOMEM;
if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
if (copy_from_user (buf, ctrl.data, size)) {
kfree (buf);
return -EFAULT;
}
} else {
memset (buf, 0, size);
}
}
 
if (!ps->dev)
retval = -ENODEV;
else if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
retval = -EINVAL;
else switch (ctrl.ioctl_code) {
 
/* disconnect kernel driver from interface, leaving it unbound. */
/* maybe unbound - you get no guarantee it stays unbound */
case USBDEVFS_DISCONNECT:
/* this function is misdesigned - retained for compatibility */
lock_kernel();
driver = ifp->driver;
if (driver) {
dbg ("disconnect '%s' from dev %d interface %d",
driver->name, ps->dev->devnum, ctrl.ifno);
usb_unbind_interface(&ifp->dev);
} else
retval = -ENODATA;
unlock_kernel();
break;
 
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
lock_kernel();
retval = usb_probe_interface (&ifp->dev);
unlock_kernel();
break;
 
/* talk directly to the interface's driver */
default:
/* BKL used here to protect against changing the binding
* of this driver to this device, as well as unloading its
* driver module.
*/
lock_kernel ();
driver = ifp->driver;
if (driver == 0 || driver->ioctl == 0) {
unlock_kernel();
retval = -ENOSYS;
} else {
if (!try_module_get (driver->owner)) {
unlock_kernel();
retval = -ENOSYS;
break;
}
unlock_kernel ();
retval = driver->ioctl (ifp, ctrl.ioctl_code, buf);
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
module_put (driver->owner);
}
}
 
/* cleanup and return */
if (retval >= 0
&& (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
&& size > 0
&& copy_to_user (ctrl.data, buf, size) != 0)
retval = -EFAULT;
if (buf != 0)
kfree (buf);
return retval;
}
 
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
* changing. But there's no mechanism to ensure that...
*/
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
int ret = -ENOTTY;
 
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
down_read(&ps->devsem);
if (!ps->dev) {
up_read(&ps->devsem);
return -ENODEV;
}
switch (cmd) {
case USBDEVFS_CONTROL:
ret = proc_control(ps, (void __user *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
 
case USBDEVFS_BULK:
ret = proc_bulk(ps, (void __user *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
 
case USBDEVFS_RESETEP:
ret = proc_resetep(ps, (void __user *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
 
case USBDEVFS_RESET:
ret = proc_resetdevice(ps);
break;
 
case USBDEVFS_CLEAR_HALT:
ret = proc_clearhalt(ps, (void __user *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
 
case USBDEVFS_GETDRIVER:
ret = proc_getdriver(ps, (void __user *)arg);
break;
 
case USBDEVFS_CONNECTINFO:
ret = proc_connectinfo(ps, (void __user *)arg);
break;
 
case USBDEVFS_SETINTERFACE:
ret = proc_setintf(ps, (void __user *)arg);
break;
 
case USBDEVFS_SETCONFIGURATION:
ret = proc_setconfig(ps, (void __user *)arg);
break;
 
case USBDEVFS_SUBMITURB:
ret = proc_submiturb(ps, (void __user *)arg);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
 
case USBDEVFS_DISCARDURB:
ret = proc_unlinkurb(ps, (void __user *)arg);
break;
 
case USBDEVFS_REAPURB:
ret = proc_reapurb(ps, (void __user *)arg);
break;
 
case USBDEVFS_REAPURBNDELAY:
ret = proc_reapurbnonblock(ps, (void __user *)arg);
break;
 
case USBDEVFS_DISCSIGNAL:
ret = proc_disconnectsignal(ps, (void __user *)arg);
break;
 
case USBDEVFS_CLAIMINTERFACE:
ret = proc_claiminterface(ps, (void __user *)arg);
break;
 
case USBDEVFS_RELEASEINTERFACE:
ret = proc_releaseinterface(ps, (void __user *)arg);
break;
 
case USBDEVFS_IOCTL:
ret = proc_ioctl(ps, (void __user *) arg);
break;
}
up_read(&ps->devsem);
if (ret >= 0)
inode->i_atime = CURRENT_TIME;
return ret;
}
 
/* No kernel lock - fine */
static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
unsigned int mask = 0;
 
poll_wait(file, &ps->wait, wait);
if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
mask |= POLLOUT | POLLWRNORM;
if (!ps->dev)
mask |= POLLERR | POLLHUP;
return mask;
}
 
struct file_operations usbdevfs_device_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
.ioctl = usbdev_ioctl,
.open = usbdev_open,
.release = usbdev_release,
};
/shark/trunk/drivers/usb/core/hub.h
0,0 → 1,195
#ifndef __LINUX_HUB_H
#define __LINUX_HUB_H
 
/*
* Hub protocol and driver data structures.
*
* Some of these are known to the "virtual root hub" code
* in host controller drivers.
*/
 
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/compiler.h> /* likely()/unlikely() */
 
/*
* Hub request types
*/
 
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
 
/*
* Hub class requests
* See USB 2.0 spec Table 11-16
*/
#define HUB_CLEAR_TT_BUFFER 8
#define HUB_RESET_TT 9
#define HUB_GET_TT_STATE 10
#define HUB_STOP_TT 11
 
/*
* Hub Class feature numbers
* See USB 2.0 spec Table 11-17
*/
#define C_HUB_LOCAL_POWER 0
#define C_HUB_OVER_CURRENT 1
 
/*
* Port feature numbers
* See USB 2.0 spec Table 11-17
*/
#define USB_PORT_FEAT_CONNECTION 0
#define USB_PORT_FEAT_ENABLE 1
#define USB_PORT_FEAT_SUSPEND 2
#define USB_PORT_FEAT_OVER_CURRENT 3
#define USB_PORT_FEAT_RESET 4
#define USB_PORT_FEAT_POWER 8
#define USB_PORT_FEAT_LOWSPEED 9
#define USB_PORT_FEAT_HIGHSPEED 10
#define USB_PORT_FEAT_C_CONNECTION 16
#define USB_PORT_FEAT_C_ENABLE 17
#define USB_PORT_FEAT_C_SUSPEND 18
#define USB_PORT_FEAT_C_OVER_CURRENT 19
#define USB_PORT_FEAT_C_RESET 20
#define USB_PORT_FEAT_TEST 21
#define USB_PORT_FEAT_INDICATOR 22
 
/*
* Hub Status and Hub Change results
* See USB 2.0 spec Table 11-19 and Table 11-20
*/
struct usb_port_status {
__u16 wPortStatus;
__u16 wPortChange;
} __attribute__ ((packed));
 
/*
* wPortStatus bit field
* See USB 2.0 spec Table 11-21
*/
#define USB_PORT_STAT_CONNECTION 0x0001
#define USB_PORT_STAT_ENABLE 0x0002
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
/* bits 5 to 7 are reserved */
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400
#define USB_PORT_STAT_TEST 0x0800
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
 
/*
* wPortChange bit field
* See USB 2.0 spec Table 11-22
* Bits 0 to 4 shown, bits 5 to 15 are reserved
*/
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
 
/*
* wHubCharacteristics (masks)
* See USB 2.0 spec Table 11-13, offset 3
*/
#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
#define HUB_CHAR_PORTIND 0x0080 /* D7 */
 
struct usb_hub_status {
__u16 wHubStatus;
__u16 wHubChange;
} __attribute__ ((packed));
 
/*
* Hub Status & Hub Change bit masks
* See USB 2.0 spec Table 11-19 and Table 11-20
* Bits 0 and 1 for wHubStatus and wHubChange
* Bits 2 to 15 are reserved for both
*/
#define HUB_STATUS_LOCAL_POWER 0x0001
#define HUB_STATUS_OVERCURRENT 0x0002
#define HUB_CHANGE_LOCAL_POWER 0x0001
#define HUB_CHANGE_OVERCURRENT 0x0002
 
 
/*
* Hub descriptor
* See USB 2.0 spec Table 11-13
*/
 
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
#define USB_DT_HUB_NONVAR_SIZE 7
 
struct usb_hub_descriptor {
__u8 bDescLength;
__u8 bDescriptorType;
__u8 bNbrPorts;
__u16 wHubCharacteristics;
__u8 bPwrOn2PwrGood;
__u8 bHubContrCurrent;
/* add 1 bit for hub status change; round to bytes */
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed));
 
struct usb_device;
 
/*
* 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 */
 
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
spinlock_t lock;
struct list_head clear_list; /* of usb_tt_clear */
struct work_struct kevent;
};
 
struct usb_tt_clear {
struct list_head clear_list;
unsigned tt;
u16 devinfo;
};
 
extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
 
struct usb_hub {
struct usb_interface *intf; /* the "real" device */
struct urb *urb; /* for interrupt polling pipe */
 
/* buffer for urb ... 1 bit each for hub and children, rounded up */
char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];
dma_addr_t buffer_dma; /* DMA address for buffer */
union {
struct usb_hub_status hub;
struct usb_port_status port;
} *status; /* buffer for status reports */
 
int error; /* last reported error */
int nerrors; /* track consecutive errors */
 
struct list_head hub_list; /* all hubs */
struct list_head event_list; /* hubs w/data or errs ready */
 
struct usb_hub_descriptor *descriptor; /* class descriptor */
struct semaphore khubd_sem;
struct usb_tt tt; /* Transaction Translator */
};
 
#endif /* __LINUX_HUB_H */
/shark/trunk/drivers/usb/core/message.c
0,0 → 1,1263
/*
* message.c - synchronous message handling
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
 
#include <linux/pci.h> /* for scatterlist macros */
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <asm/byteorder.h>
 
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
 
static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
{
//printk(KERN_INFO "api\n");
complete((struct completion *)urb->context);
}
 
 
static void timeout_kill(unsigned long data)
{
struct urb *urb = (struct urb *) data;
 
dev_warn(&urb->dev->dev, "%s timeout on ep%d%s\n",
usb_pipecontrol(urb->pipe) ? "control" : "bulk",
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out");
usb_unlink_urb(urb);
}
 
// Starts urb and waits for completion or timeout
// note that this call is NOT interruptible, while
// many device driver i/o requests should be interruptible
static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
{
struct completion done;
struct timer_list timer;
int status;
struct pt_regs *regs;
 
init_completion(&done);
urb->context = &done;
urb->transfer_flags |= URB_ASYNC_UNLINK;
urb->actual_length = 0;
 
status = usb_submit_urb(urb, GFP_NOIO);
 
if (status == 0) {
if (timeout > 0) {
init_timer(&timer);
timer.expires = jiffies26 + timeout;
timer.data = (unsigned long)urb;
timer.function = timeout_kill;
/* grr. timeout _should_ include submit delays. */
add_timer(&timer);
}
wait_for_completion(&done);
status = urb->status;
/* note: HCDs return ETIMEDOUT for other reasons too */
if (status == -ECONNRESET)
status = -ETIMEDOUT;
if (timeout > 0)
del_timer_sync(&timer);
}
 
if (actual_length)
*actual_length = urb->actual_length;
usb_free_urb(urb);
return status;
}
 
/*-------------------------------------------------------------------*/
// returns status (negative) or length (positive)
int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
struct usb_ctrlrequest *cmd, void *data, int len, int timeout)
{
struct urb *urb;
int retv;
int length;
 
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
return -ENOMEM;
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, len,
usb_api_blocking_completion, 0);
retv = usb_start_wait_urb(urb, timeout, &length);
if (retv < 0)
return retv;
else
return length;
}
 
/**
* usb_control_msg - Builds a control urb, sends it off and waits for completion
* @dev: pointer to the usb device to send the message to
* @pipe: endpoint "pipe" to send the message to
* @request: USB message request value
* @requesttype: USB message request type value
* @value: USB message value
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
* @timeout: time in jiffies26 to wait for the message to complete before
* timing out (if 0 the wait is forever)
* Context: !in_interrupt ()
*
* This function sends a simple control message to a specified endpoint
* and waits for the message to complete, or timeout.
*
* If successful, it returns the number of bytes transferred, otherwise a negative error number.
*
* Don't use this function from within an interrupt context, like a
* bottom half handler. If you need an asynchronous message, or need to send
* a message from within interrupt context, use usb_submit_urb()
* If a thread in your driver uses this call, make sure your disconnect()
* method can wait for it to complete. Since you don't have a handle on
* the URB used, you can't cancel the request.
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
__u16 value, __u16 index, void *data, __u16 size, int timeout)
{
struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
int ret;
if (!dr)
return -ENOMEM;
 
dr->bRequestType= requesttype;
dr->bRequest = request;
dr->wValue = cpu_to_le16p(&value);
dr->wIndex = cpu_to_le16p(&index);
dr->wLength = cpu_to_le16p(&size);
 
//dbg("usb_control_msg");
 
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
 
kfree(dr);
 
return ret;
}
 
 
/**
* usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
* @usb_dev: pointer to the usb device to send the message to
* @pipe: endpoint "pipe" to send the message to
* @data: pointer to the data to send
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred in bytes
* @timeout: time in jiffies26 to wait for the message to complete before
* timing out (if 0 the wait is forever)
* Context: !in_interrupt ()
*
* This function sends a simple bulk message to a specified endpoint
* and waits for the message to complete, or timeout.
*
* If successful, it returns 0, otherwise a negative error number.
* The number of actual bytes transferred will be stored in the
* actual_length paramater.
*
* Don't use this function from within an interrupt context, like a
* bottom half handler. If you need an asynchronous message, or need to
* send a message from within interrupt context, use usb_submit_urb()
* If a thread in your driver uses this call, make sure your disconnect()
* method can wait for it to complete. Since you don't have a handle on
* the URB used, you can't cancel the request.
*/
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
{
struct urb *urb;
 
if (len < 0)
return -EINVAL;
 
urb=usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
 
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, 0);
 
return usb_start_wait_urb(urb,timeout,actual_length);
}
 
/*-------------------------------------------------------------------*/
 
static void sg_clean (struct usb_sg_request *io)
{
if (io->urbs) {
while (io->entries--)
usb_free_urb (io->urbs [io->entries]);
kfree (io->urbs);
io->urbs = 0;
}
if (io->dev->dev.dma_mask != 0)
usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
io->dev = 0;
}
 
static void sg_complete (struct urb *urb, struct pt_regs *regs)
{
struct usb_sg_request *io = (struct usb_sg_request *) urb->context;
unsigned long flags;
 
spin_lock_irqsave (&io->lock, flags);
 
/* In 2.5 we require hcds' endpoint queues not to progress after fault
* reports, until the completion callback (this!) returns. That lets
* device driver code (like this routine) unlink queued urbs first,
* if it needs to, since the HC won't work on them at all. So it's
* not possible for page N+1 to overwrite page N, and so on.
*
* That's only for "hard" faults; "soft" faults (unlinks) sometimes
* complete before the HCD can get requests away from hardware,
* though never during cleanup after a hard fault.
*/
if (io->status
&& (io->status != -ECONNRESET
|| urb->status != -ECONNRESET)
&& urb->actual_length) {
dev_err (io->dev->bus->controller,
"dev %s ep%d%s scatterlist error %d/%d\n",
io->dev->devpath,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
urb->status, io->status);
// BUG ();
}
 
if (urb->status && urb->status != -ECONNRESET) {
int i, found, status;
 
io->status = urb->status;
 
/* the previous urbs, and this one, completed already.
* unlink pending urbs so they won't rx/tx bad data.
*/
for (i = 0, found = 0; i < io->entries; i++) {
if (!io->urbs [i])
continue;
if (found) {
status = usb_unlink_urb (io->urbs [i]);
if (status != -EINPROGRESS && status != -EBUSY)
dev_err (&io->dev->dev,
"%s, unlink --> %d\n",
__FUNCTION__, status);
} else if (urb == io->urbs [i])
found = 1;
}
}
urb->dev = 0;
 
/* on the last completion, signal usb_sg_wait() */
io->bytes += urb->actual_length;
io->count--;
if (!io->count)
complete (&io->complete);
 
spin_unlock_irqrestore (&io->lock, flags);
}
 
 
/**
* usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request
* @io: request block being initialized. until usb_sg_wait() returns,
* treat this as a pointer to an opaque block of memory,
* @dev: the usb device that will send or receive the data
* @pipe: endpoint "pipe" used to transfer the data
* @period: polling rate for interrupt endpoints, in frames or
* (for high speed endpoints) microframes; ignored for bulk
* @sg: scatterlist entries
* @nents: how many entries in the scatterlist
* @length: how many bytes to send from the scatterlist, or zero to
* send every byte identified in the list.
* @mem_flags: SLAB_* flags affecting memory allocations in this call
*
* Returns zero for success, else a negative errno value. This initializes a
* scatter/gather request, allocating resources such as I/O mappings and urb
* memory (except maybe memory used by USB controller drivers).
*
* The request must be issued using usb_sg_wait(), which waits for the I/O to
* complete (or to be canceled) and then cleans up all resources allocated by
* usb_sg_init().
*
* The request may be canceled with usb_sg_cancel(), either before or after
* usb_sg_wait() is called.
*/
int usb_sg_init (
struct usb_sg_request *io,
struct usb_device *dev,
unsigned pipe,
unsigned period,
struct scatterlist *sg,
int nents,
size_t length,
int mem_flags
)
{
int i;
int urb_flags;
int dma;
 
{
int i;
for (i=0; i<20; i++)
{
wait_ms26(300);
printk(KERN_INFO "usb_sg_init!!!!!!!\n");
}
}
 
if (!io || !dev || !sg
|| usb_pipecontrol (pipe)
|| usb_pipeisoc (pipe)
|| nents <= 0)
return -EINVAL;
 
spin_lock_init (&io->lock);
io->dev = dev;
io->pipe = pipe;
io->sg = sg;
io->nents = nents;
 
/* not all host controllers use DMA (like the mainstream pci ones);
* they can use PIO (sl811) or be software over another transport.
*/
dma = (dev->dev.dma_mask != 0);
if (dma)
io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
else
io->entries = nents;
 
/* initialize all the urbs we'll use */
if (io->entries <= 0)
return io->entries;
 
io->count = 0;
io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags);
if (!io->urbs)
goto nomem;
 
urb_flags = URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP
| URB_NO_INTERRUPT;
if (usb_pipein (pipe))
urb_flags |= URB_SHORT_NOT_OK;
 
for (i = 0; i < io->entries; i++, io->count = i) {
unsigned len;
 
io->urbs [i] = usb_alloc_urb (0, mem_flags);
if (!io->urbs [i]) {
io->entries = i;
goto nomem;
}
 
io->urbs [i]->dev = 0;
io->urbs [i]->pipe = pipe;
io->urbs [i]->interval = period;
io->urbs [i]->transfer_flags = urb_flags;
 
io->urbs [i]->complete = sg_complete;
io->urbs [i]->context = io;
io->urbs [i]->status = -EINPROGRESS;
io->urbs [i]->actual_length = 0;
 
if (dma) {
/* hc may use _only_ transfer_dma */
io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
len = sg_dma_len (sg + i);
} else {
/* hc may use _only_ transfer_buffer */
io->urbs [i]->transfer_buffer =
page_address (sg [i].page) + sg [i].offset;
len = sg [i].length;
}
 
if (length) {
len = min_t (unsigned, len, length);
length -= len;
if (length == 0)
io->entries = i + 1;
}
io->urbs [i]->transfer_buffer_length = len;
}
io->urbs [--i]->transfer_flags &= ~URB_NO_INTERRUPT;
 
/* transaction state */
io->status = 0;
io->bytes = 0;
init_completion (&io->complete);
return 0;
 
nomem:
sg_clean (io);
return -ENOMEM;
}
 
 
/**
* usb_sg_wait - synchronously execute scatter/gather request
* @io: request block handle, as initialized with usb_sg_init().
* some fields become accessible when this call returns.
* Context: !in_interrupt ()
*
* This function blocks until the specified I/O operation completes. It
* leverages the grouping of the related I/O requests to get good transfer
* rates, by queueing the requests. At higher speeds, such queuing can
* significantly improve USB throughput.
*
* There are three kinds of completion for this function.
* (1) success, where io->status is zero. The number of io->bytes
* transferred is as requested.
* (2) error, where io->status is a negative errno value. The number
* of io->bytes transferred before the error is usually less
* than requested, and can be nonzero.
* (3) cancelation, a type of error with status -ECONNRESET that
* is initiated by usb_sg_cancel().
*
* When this function returns, all memory allocated through usb_sg_init() or
* this call will have been freed. The request block parameter may still be
* passed to usb_sg_cancel(), or it may be freed. It could also be
* reinitialized and then reused.
*
* Data Transfer Rates:
*
* Bulk transfers are valid for full or high speed endpoints.
* The best full speed data rate is 19 packets of 64 bytes each
* per frame, or 1216 bytes per millisecond.
* The best high speed data rate is 13 packets of 512 bytes each
* per microframe, or 52 KBytes per millisecond.
*
* The reason to use interrupt transfers through this API would most likely
* be to reserve high speed bandwidth, where up to 24 KBytes per millisecond
* could be transferred. That capability is less useful for low or full
* speed interrupt endpoints, which allow at most one packet per millisecond,
* of at most 8 or 64 bytes (respectively).
*/
void usb_sg_wait (struct usb_sg_request *io)
{
int i;
unsigned long flags;
 
/* queue the urbs. */
spin_lock_irqsave (&io->lock, flags);
for (i = 0; i < io->entries && !io->status; i++) {
int retval;
 
io->urbs [i]->dev = io->dev;
retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC);
 
/* after we submit, let completions or cancelations fire;
* we handshake using io->status.
*/
spin_unlock_irqrestore (&io->lock, flags);
switch (retval) {
/* maybe we retrying will recover */
case -ENXIO: // hc didn't queue this one
case -EAGAIN:
case -ENOMEM:
io->urbs [i]->dev = 0;
retval = 0;
i--;
yield ();
break;
 
/* no error? continue immediately.
*
* NOTE: to work better with UHCI (4K I/O buffer may
* need 3K of TDs) it may be good to limit how many
* URBs are queued at once; N milliseconds?
*/
case 0:
cpu_relax ();
break;
 
/* fail any uncompleted urbs */
default:
io->urbs [i]->dev = 0;
io->urbs [i]->status = retval;
dev_dbg (&io->dev->dev, "%s, submit --> %d\n",
__FUNCTION__, retval);
usb_sg_cancel (io);
}
spin_lock_irqsave (&io->lock, flags);
if (retval && io->status == -ECONNRESET)
io->status = retval;
}
spin_unlock_irqrestore (&io->lock, flags);
 
/* OK, yes, this could be packaged as non-blocking.
* So could the submit loop above ... but it's easier to
* solve neither problem than to solve both!
*/
wait_for_completion (&io->complete);
 
sg_clean (io);
}
 
/**
* usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait()
* @io: request block, initialized with usb_sg_init()
*
* This stops a request after it has been started by usb_sg_wait().
* It can also prevents one initialized by usb_sg_init() from starting,
* so that call just frees resources allocated to the request.
*/
void usb_sg_cancel (struct usb_sg_request *io)
{
unsigned long flags;
 
spin_lock_irqsave (&io->lock, flags);
 
/* shut everything down, if it didn't already */
if (!io->status) {
int i;
 
io->status = -ECONNRESET;
for (i = 0; i < io->entries; i++) {
int retval;
 
if (!io->urbs [i]->dev)
continue;
retval = usb_unlink_urb (io->urbs [i]);
if (retval != -EINPROGRESS && retval != -EBUSY)
dev_warn (&io->dev->dev, "%s, unlink --> %d\n",
__FUNCTION__, retval);
}
}
spin_unlock_irqrestore (&io->lock, flags);
}
 
/*-------------------------------------------------------------------*/
 
/**
* usb_get_descriptor - issues a generic GET_DESCRIPTOR request
* @dev: the device whose descriptor is being retrieved
* @type: the descriptor type (USB_DT_*)
* @index: the number of the descriptor
* @buf: where to put the descriptor
* @size: how big is "buf"?
* Context: !in_interrupt ()
*
* Gets a USB descriptor. Convenience functions exist to simplify
* getting some types of descriptors. Use
* usb_get_device_descriptor() for USB_DT_DEVICE,
* and usb_get_string() or usb_string() for USB_DT_STRING.
* Configuration descriptors (USB_DT_CONFIG) are part of the device
* structure, at least for the current configuration.
* In addition to a number of USB-standard descriptors, some
* devices also use class-specific or vendor-specific descriptors.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
{
int i = 5;
int result = 0;
memset(buf,0,size); // Make sure we parse really received data
 
while (i--) {
/* retries if the returned length was 0; flakey device */
if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
(type << 8) + index, 0, buf, size,
HZ * USB_CTRL_GET_TIMEOUT)) > 0
|| result == -EPIPE)
break;
}
return result;
}
 
/**
* usb_get_string - gets a string descriptor
* @dev: the device whose string descriptor is being retrieved
* @langid: code for language chosen (from string descriptor zero)
* @index: the number of the descriptor
* @buf: where to put the string
* @size: how big is "buf"?
* Context: !in_interrupt ()
*
* Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character,
* in little-endian byte order).
* The usb_string() function will often be a convenient way to turn
* these strings into kernel-printable form.
*
* Strings may be referenced in device, configuration, interface, or other
* descriptors, and could also be used in vendor-specific ways.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
(USB_DT_STRING << 8) + index, langid, buf, size,
HZ * USB_CTRL_GET_TIMEOUT);
}
 
/**
* usb_get_device_descriptor - (re)reads the device descriptor
* @dev: the device whose device descriptor is being updated
* Context: !in_interrupt ()
*
* Updates the copy of the device descriptor stored in the device structure,
* which dedicates space for this purpose. Note that several fields are
* converted to the host CPU's byte order: the USB version (bcdUSB), and
* vendors product and version fields (idVendor, idProduct, and bcdDevice).
* That lets device drivers compare against non-byteswapped constants.
*
* There's normally no need to use this call, although some devices
* will change their descriptors after events like updating firmware.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_device_descriptor(struct usb_device *dev)
{
int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,
sizeof(dev->descriptor));
if (ret >= 0) {
le16_to_cpus(&dev->descriptor.bcdUSB);
le16_to_cpus(&dev->descriptor.idVendor);
le16_to_cpus(&dev->descriptor.idProduct);
le16_to_cpus(&dev->descriptor.bcdDevice);
}
return ret;
}
 
/**
* usb_get_status - issues a GET_STATUS call
* @dev: the device whose status is being checked
* @type: USB_RECIP_*; for device, interface, or endpoint
* @target: zero (for device), else interface or endpoint number
* @data: pointer to two bytes of bitmap data
* Context: !in_interrupt ()
*
* Returns device, interface, or endpoint status. Normally only of
* interest to see if the device is self powered, or has enabled the
* remote wakeup facility; or whether a bulk or interrupt endpoint
* is halted ("stalled").
*
* Bits in these status bitmaps are set using the SET_FEATURE request,
* and cleared using the CLEAR_FEATURE request. The usb_clear_halt()
* function should be used to clear halt ("stall") status.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_status(struct usb_device *dev, int type, int target, void *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2,
HZ * USB_CTRL_GET_TIMEOUT);
}
 
/**
* usb_clear_halt - tells device to clear endpoint halt/stall condition
* @dev: device whose endpoint is halted
* @pipe: endpoint "pipe" being cleared
* Context: !in_interrupt ()
*
* This is used to clear halt conditions for bulk and interrupt endpoints,
* as reported by URB completion status. Endpoints that are halted are
* sometimes referred to as being "stalled". Such endpoints are unable
* to transmit or receive data until the halt status is cleared. Any URBs
* queued for such an endpoint should normally be unlinked by the driver
* before clearing the halt condition, as described in sections 5.7.5
* and 5.8.5 of the USB 2.0 spec.
*
* Note that control and isochronous endpoints don't halt, although control
* endpoints report "protocol stall" (for unsupported requests) using the
* same status code used to report a true stall.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns zero on success, or else the status code returned by the
* underlying usb_control_msg() call.
*/
int usb_clear_halt(struct usb_device *dev, int pipe)
{
int result;
int endp = usb_pipeendpoint(pipe);
if (usb_pipein (pipe))
endp |= USB_DIR_IN;
 
/* we don't care if it wasn't halted first. in fact some devices
* (like some ibmcam model 1 units) seem to expect hosts to make
* this request for iso endpoints, which can't halt!
*/
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0,
HZ * USB_CTRL_SET_TIMEOUT);
 
/* don't un-halt or force to DATA0 except on success */
if (result < 0)
return result;
 
/* NOTE: seems like Microsoft and Apple don't bother verifying
* the clear "took", so some devices could lock up if you check...
* such as the Hagiwara FlashGate DUAL. So we won't bother.
*
* NOTE: make sure the logic here doesn't diverge much from
* the copy in usb-storage, for as long as we need two copies.
*/
 
/* toggle was reset by the clear, then ep was reactivated */
usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
 
return 0;
}
 
/**
* usb_disable_endpoint -- Disable an endpoint by address
* @dev: the device whose endpoint is being disabled
* @epaddr: the endpoint's address. Endpoint number for output,
* endpoint number + USB_DIR_IN for input
*
* Deallocates hcd/hardware state for this endpoint ... and nukes all
* pending urbs.
*
* If the HCD hasn't registered a disable() function, this marks the
* endpoint as halted and sets its maxpacket size to 0 to prevent
* further submissions.
*/
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
{
if (dev && dev->bus && dev->bus->op && dev->bus->op->disable)
dev->bus->op->disable(dev, epaddr);
else {
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
 
if (usb_endpoint_out(epaddr)) {
usb_endpoint_halt(dev, epnum, 1);
dev->epmaxpacketout[epnum] = 0;
} else {
usb_endpoint_halt(dev, epnum, 0);
dev->epmaxpacketin[epnum] = 0;
}
}
}
 
/**
* usb_disable_interface -- Disable all endpoints for an interface
* @dev: the device whose interface is being disabled
* @intf: pointer to the interface descriptor
*
* Disables all the endpoints for the interface's current altsetting.
*/
void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
{
struct usb_host_interface *hintf =
&intf->altsetting[intf->act_altsetting];
int i;
 
for (i = 0; i < hintf->desc.bNumEndpoints; ++i) {
usb_disable_endpoint(dev,
hintf->endpoint[i].desc.bEndpointAddress);
}
}
 
/*
* usb_disable_device - Disable all the endpoints for a USB device
* @dev: the device whose endpoints are being disabled
* @skip_ep0: 0 to disable endpoint 0, 1 to skip it.
*
* Disables all the device's endpoints, potentially including endpoint 0.
* Deallocates hcd/hardware state for the endpoints (nuking all or most
* pending urbs) and usbcore state for the interfaces, so that usbcore
* must usb_set_configuration() before any interfaces could be used.
*/
void usb_disable_device(struct usb_device *dev, int skip_ep0)
{
int i;
 
dev_dbg(&dev->dev, "%s nuking %s URBs\n", __FUNCTION__,
skip_ep0 ? "non-ep0" : "all");
for (i = skip_ep0; i < 16; ++i) {
usb_disable_endpoint(dev, i);
usb_disable_endpoint(dev, i + USB_DIR_IN);
}
dev->toggle[0] = dev->toggle[1] = 0;
dev->halted[0] = dev->halted[1] = 0;
 
/* getting rid of interfaces will disconnect
* any drivers bound to them (a key side effect)
*/
if (dev->actconfig) {
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *interface;
 
/* remove this interface */
interface = dev->actconfig->interface[i];
dev_dbg (&dev->dev, "unregistering interface %s\n",
interface->dev.bus_id);
device_del(&interface->dev);
}
dev->actconfig = 0;
if (dev->state == USB_STATE_CONFIGURED)
dev->state = USB_STATE_ADDRESS;
}
}
 
 
/*
* usb_enable_endpoint - Enable an endpoint for USB communications
* @dev: the device whose interface is being enabled
* @epd: pointer to the endpoint descriptor
*
* Marks the endpoint as running, resets its toggle, and stores
* its maxpacket value. For control endpoints, both the input
* and output sides are handled.
*/
void usb_enable_endpoint(struct usb_device *dev,
struct usb_endpoint_descriptor *epd)
{
int maxsize = epd->wMaxPacketSize;
unsigned int epaddr = epd->bEndpointAddress;
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
int is_control = ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_CONTROL);
 
if (usb_endpoint_out(epaddr) || is_control) {
usb_endpoint_running(dev, epnum, 1);
usb_settoggle(dev, epnum, 1, 0);
dev->epmaxpacketout[epnum] = maxsize;
}
if (!usb_endpoint_out(epaddr) || is_control) {
usb_endpoint_running(dev, epnum, 0);
usb_settoggle(dev, epnum, 0, 0);
dev->epmaxpacketin[epnum] = maxsize;
}
}
 
/*
* usb_enable_interface - Enable all the endpoints for an interface
* @dev: the device whose interface is being enabled
* @intf: pointer to the interface descriptor
*
* Enables all the endpoints for the interface's current altsetting.
*/
void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf)
{
struct usb_host_interface *hintf =
&intf->altsetting[intf->act_altsetting];
int i;
 
for (i = 0; i < hintf->desc.bNumEndpoints; ++i)
usb_enable_endpoint(dev, &hintf->endpoint[i].desc);
}
 
/**
* usb_set_interface - Makes a particular alternate setting be current
* @dev: the device whose interface is being updated
* @interface: the interface being updated
* @alternate: the setting being chosen.
* Context: !in_interrupt ()
*
* This is used to enable data transfers on interfaces that may not
* be enabled by default. Not all devices support such configurability.
* Only the driver bound to an interface may change its setting.
*
* Within any given configuration, each interface may have several
* alternative settings. These are often used to control levels of
* bandwidth consumption. For example, the default setting for a high
* speed interrupt endpoint may not send more than 64 bytes per microframe,
* while interrupt transfers of up to 3KBytes per microframe are legal.
* Also, isochronous endpoints may never be part of an
* interface's default setting. To access such bandwidth, alternate
* interface settings must be made current.
*
* Note that in the Linux USB subsystem, bandwidth associated with
* an endpoint in a given alternate setting is not reserved until an URB
* is submitted that needs that bandwidth. Some other operating systems
* allocate bandwidth early, when a configuration is chosen.
*
* This call is synchronous, and may not be used in an interrupt context.
* Also, drivers must not change altsettings while urbs are scheduled for
* endpoints in that interface; all such urbs must first be completed
* (perhaps forced by unlinking).
*
* Returns zero on success, or else the status code returned by the
* underlying usb_control_msg() call.
*/
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
struct usb_interface *iface;
int ret;
int manual = 0;
 
iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
warn("selecting invalid interface %d", interface);
return -EINVAL;
}
 
if (alternate < 0 || alternate >= iface->num_altsetting)
return -EINVAL;
 
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
iface->altsetting[alternate]
.desc.bAlternateSetting,
interface, NULL, 0, HZ * 5);
 
/* 9.4.10 says devices don't need this and are free to STALL the
* request if the interface only has one alternate setting.
*/
if (ret == -EPIPE && iface->num_altsetting == 1) {
dbg("manual set_interface for dev %d, iface %d, alt %d",
dev->devnum, interface, alternate);
manual = 1;
} else if (ret < 0)
return ret;
 
/* FIXME drivers shouldn't need to replicate/bugfix the logic here
* when they implement async or easily-killable versions of this or
* other "should-be-internal" functions (like clear_halt).
* should hcd+usbcore postprocess control requests?
*/
 
/* prevent submissions using previous endpoint settings */
usb_disable_interface(dev, iface);
 
iface->act_altsetting = alternate;
 
/* If the interface only has one altsetting and the device didn't
* accept the request, we attempt to carry out the equivalent action
* by manually clearing the HALT feature for each endpoint in the
* new altsetting.
*/
if (manual) {
struct usb_host_interface *iface_as =
&iface->altsetting[alternate];
int i;
 
for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
unsigned int epaddr =
iface_as->endpoint[i].desc.bEndpointAddress;
unsigned int pipe =
__create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr)
| (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN);
 
usb_clear_halt(dev, pipe);
}
}
 
/* 9.1.1.5: reset toggles for all endpoints in the new altsetting
*
* Note:
* Despite EP0 is always present in all interfaces/AS, the list of
* endpoints from the descriptor does not contain EP0. Due to its
* omnipresence one might expect EP0 being considered "affected" by
* any SetInterface request and hence assume toggles need to be reset.
* However, EP0 toggles are re-synced for every individual transfer
* during the SETUP stage - hence EP0 toggles are "don't care" here.
* (Likewise, EP0 never "halts" on well designed devices.)
*/
usb_enable_interface(dev, iface);
 
return 0;
}
 
/**
* usb_reset_configuration - lightweight device reset
* @dev: the device whose configuration is being reset
*
* This issues a standard SET_CONFIGURATION request to the device using
* the current configuration. The effect is to reset most USB-related
* state in the device, including interface altsettings (reset to zero),
* endpoint halts (cleared), and data toggle (only for bulk and interrupt
* endpoints). Other usbcore state is unchanged, including bindings of
* usb device drivers to interfaces.
*
* Because this affects multiple interfaces, avoid using this with composite
* (multi-interface) devices. Instead, the driver for each interface may
* use usb_set_interface() on the interfaces it claims. Resetting the whole
* configuration would affect other drivers' interfaces.
*
* Returns zero on success, else a negative error code.
*/
int usb_reset_configuration(struct usb_device *dev)
{
int i, retval;
struct usb_host_config *config;
 
/* caller must own dev->serialize (config won't change)
* and the usb bus readlock (so driver bindings are stable);
* so calls during probe() are fine
*/
 
for (i = 1; i < 16; ++i) {
usb_disable_endpoint(dev, i);
usb_disable_endpoint(dev, i + USB_DIR_IN);
}
 
config = dev->actconfig;
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0,
config->desc.bConfigurationValue, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
if (retval < 0) {
dev->state = USB_STATE_ADDRESS;
return retval;
}
 
dev->toggle[0] = dev->toggle[1] = 0;
dev->halted[0] = dev->halted[1] = 0;
 
/* re-init hc/hcd interface/endpoint state */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
struct usb_interface *intf = config->interface[i];
 
intf->act_altsetting = 0;
usb_enable_interface(dev, intf);
}
return 0;
}
 
/**
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
* @configuration: the configuration being chosen.
* Context: !in_interrupt ()
*
* This is used to enable non-default device modes. Not all devices
* use this kind of configurability; many devices only have one
* configuration.
*
* USB device configurations may affect Linux interoperability,
* power consumption and the functionality available. For example,
* the default configuration is limited to using 100mA of bus power,
* so that when certain device functionality requires more power,
* and the device is bus powered, that functionality should be in some
* non-default device configuration. Other device modes may also be
* reflected as configuration options, such as whether two ISDN
* channels are available independently; and choosing between open
* standard device protocols (like CDC) or proprietary ones.
*
* Note that USB has an additional level of device configurability,
* associated with interfaces. That configurability is accessed using
* usb_set_interface().
*
* This call is synchronous. The calling context must be able to sleep,
* and must not hold the driver model lock for USB; usb device driver
* probe() methods may not use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On succesful completion, each interface
* in the original device configuration has been destroyed, and each one
* in the new configuration has been probed by all relevant usb device
* drivers currently known to the kernel.
*/
int usb_set_configuration(struct usb_device *dev, int configuration)
{
int i, ret;
struct usb_host_config *cp = NULL;
/* dev->serialize guards all config changes */
down(&dev->serialize);
 
for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue == configuration) {
cp = &dev->config[i];
break;
}
}
if ((!cp && configuration != 0)) {
ret = -EINVAL;
goto out;
}
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
 
/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device (dev, 1); // Skip ep0
 
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
goto out;
 
dev->actconfig = cp;
if (!configuration)
dev->state = USB_STATE_ADDRESS;
else {
dev->state = USB_STATE_CONFIGURED;
 
/* re-initialize hc/hcd/usbcore interface/endpoint state.
* this triggers binding of drivers to interfaces; and
* maybe probe() calls will choose different altsettings.
*/
for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
struct usb_interface *intf = cp->interface[i];
struct usb_interface_descriptor *desc;
 
intf->act_altsetting = 0;
desc = &intf->altsetting [0].desc;
usb_enable_interface(dev, intf);
 
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.dma_mask = dev->dev.dma_mask;
sprintf26 (&intf->dev.bus_id[0], "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration,
desc->bInterfaceNumber);
dev_dbg (&dev->dev,
"registering %s (config #%d, interface %d)\n",
intf->dev.bus_id, configuration,
desc->bInterfaceNumber);
device_add (&intf->dev);
usb_create_driverfs_intf_files (intf);
}
}
 
out:
up(&dev->serialize);
return ret;
}
 
/**
* usb_string - returns ISO 8859-1 version of a string descriptor
* @dev: the device whose string descriptor is being retrieved
* @index: the number of the descriptor
* @buf: where to put the string
* @size: how big is "buf"?
* Context: !in_interrupt ()
*
* This converts the UTF-16LE encoded strings returned by devices, from
* usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
* that are more usable in most kernel contexts. Note that all characters
* in the chosen descriptor that can't be encoded using ISO-8859-1
* are converted to the question mark ("?") character, and this function
* chooses strings in the first language supported by the device.
*
* The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
* subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
* and is appropriate for use many uses of English and several other
* Western European languages. (But it doesn't include the "Euro" symbol.)
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Returns length of the string (>= 0) or usb_control_msg status (< 0).
*/
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
unsigned char *tbuf;
int err, len;
unsigned int u, idx;
 
if (size <= 0 || !buf || !index)
return -EINVAL;
buf[0] = 0;
tbuf = kmalloc(256, GFP_KERNEL);
if (!tbuf)
return -ENOMEM;
 
/* get langid for strings if it's not yet known */
if (!dev->have_langid) {
err = usb_get_string(dev, 0, 0, tbuf, 4);
if (err < 0) {
err("error getting string descriptor 0 (error=%d)", err);
goto errout;
} else if (err < 4 || tbuf[0] < 4) {
err("string descriptor 0 too short");
err = -EINVAL;
goto errout;
} else {
dev->have_langid = -1;
dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
/* always use the first langid listed */
dbg("USB device number %d default language ID 0x%x",
dev->devnum, dev->string_langid);
}
}
 
/*
* ask for the length of the string
*/
 
err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);
if(err<2)
goto errout;
len=tbuf[0];
err = usb_get_string(dev, dev->string_langid, index, tbuf, len);
if (err < 0)
goto errout;
 
size--; /* leave room for trailing NULL char in output buffer */
for (idx = 0, u = 2; u < err; u += 2) {
if (idx >= size)
break;
if (tbuf[u+1]) /* high byte */
buf[idx++] = '?'; /* non ISO-8859-1 character */
else
buf[idx++] = tbuf[u];
}
buf[idx] = 0;
err = idx;
 
errout:
kfree(tbuf);
return err;
}
 
// synchronous request completion model
EXPORT_SYMBOL(usb_control_msg);
EXPORT_SYMBOL(usb_bulk_msg);
 
EXPORT_SYMBOL(usb_sg_init);
EXPORT_SYMBOL(usb_sg_cancel);
EXPORT_SYMBOL(usb_sg_wait);
 
// synchronous control message convenience routines
EXPORT_SYMBOL(usb_get_descriptor);
EXPORT_SYMBOL(usb_get_device_descriptor);
EXPORT_SYMBOL(usb_get_status);
EXPORT_SYMBOL(usb_get_string);
EXPORT_SYMBOL(usb_string);
 
// synchronous calls that also maintain usbcore state
EXPORT_SYMBOL(usb_clear_halt);
EXPORT_SYMBOL(usb_reset_configuration);
EXPORT_SYMBOL(usb_set_configuration);
EXPORT_SYMBOL(usb_set_interface);
 
/shark/trunk/drivers/usb/core/driverfs.c
0,0 → 1,227
/*
* drivers/usb/core/driverfs.c
*
* (C) Copyright 2002 David Brownell
* (C) Copyright 2002 Greg Kroah-Hartman
* (C) Copyright 2002 IBM Corp.
*
* All of the driverfs file attributes for usb devices and interfaces.
*
*/
 
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
 
#include "usb.h"
 
/* Active configuration fields */
#define usb_actconfig_show(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
\
udev = to_usb_device (dev); \
if (udev->actconfig) \
return sprintf26 (buf, format_string, udev->actconfig->desc.field); \
else return 0; \
} \
 
#define usb_actconfig_attr(field, format_string) \
usb_actconfig_show(field,format_string) \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
 
usb_actconfig_attr (bNumInterfaces, "%2d\n")
usb_actconfig_attr (bmAttributes, "%2x\n")
usb_actconfig_attr (bMaxPower, "%3dmA\n")
 
/* configuration value is always present, and r/w */
usb_actconfig_show(bConfigurationValue,"%u\n");
 
static ssize_t
set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
{
struct usb_device *udev = udev = to_usb_device (dev);
int config, value;
 
if (sscanf26(buf, "%u", &config) != 1 || config > 255)
return -EINVAL;
value = usb_set_configuration (udev, config);
return (value < 0) ? value : count;
}
 
static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
show_bConfigurationValue, set_bConfigurationValue);
 
/* String fields */
static ssize_t show_product (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
 
udev = to_usb_device (dev);
 
len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(product,S_IRUGO,show_product,NULL);
 
static ssize_t
show_manufacturer (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
 
udev = to_usb_device (dev);
 
len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(manufacturer,S_IRUGO,show_manufacturer,NULL);
 
static ssize_t
show_serial (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
 
udev = to_usb_device (dev);
 
len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(serial,S_IRUGO,show_serial,NULL);
 
static ssize_t
show_speed (struct device *dev, char *buf)
{
struct usb_device *udev;
char *speed;
 
udev = to_usb_device (dev);
 
switch (udev->speed) {
case USB_SPEED_LOW:
speed = "1.5";
break;
case USB_SPEED_UNKNOWN:
case USB_SPEED_FULL:
speed = "12";
break;
case USB_SPEED_HIGH:
speed = "480";
break;
default:
speed = "unknown";
}
return sprintf26 (buf, "%s\n", speed);
}
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
 
/* Descriptor fields */
#define usb_descriptor_attr(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
\
udev = to_usb_device (dev); \
return sprintf26 (buf, format_string, udev->descriptor.field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
 
usb_descriptor_attr (idVendor, "%04x\n")
usb_descriptor_attr (idProduct, "%04x\n")
usb_descriptor_attr (bcdDevice, "%04x\n")
usb_descriptor_attr (bDeviceClass, "%02x\n")
usb_descriptor_attr (bDeviceSubClass, "%02x\n")
usb_descriptor_attr (bDeviceProtocol, "%02x\n")
usb_descriptor_attr (bNumConfigurations, "%d\n")
 
 
void usb_create_driverfs_dev_files (struct usb_device *udev)
{
struct device *dev = &udev->dev;
 
/* current configuration's attributes */
device_create_file (dev, &dev_attr_bNumInterfaces);
device_create_file (dev, &dev_attr_bConfigurationValue);
device_create_file (dev, &dev_attr_bmAttributes);
device_create_file (dev, &dev_attr_bMaxPower);
 
/* device attributes */
device_create_file (dev, &dev_attr_idVendor);
device_create_file (dev, &dev_attr_idProduct);
device_create_file (dev, &dev_attr_bcdDevice);
device_create_file (dev, &dev_attr_bDeviceClass);
device_create_file (dev, &dev_attr_bDeviceSubClass);
device_create_file (dev, &dev_attr_bDeviceProtocol);
device_create_file (dev, &dev_attr_bNumConfigurations);
 
/* speed varies depending on how you connect the device */
device_create_file (dev, &dev_attr_speed);
// FIXME iff there are other speed configs, show how many
 
if (udev->descriptor.iManufacturer)
device_create_file (dev, &dev_attr_manufacturer);
if (udev->descriptor.iProduct)
device_create_file (dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_create_file (dev, &dev_attr_serial);
}
 
/* Interface fields */
#define usb_intf_attr(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_interface *intf; \
int alt; \
\
intf = to_usb_interface (dev); \
alt = intf->act_altsetting; \
\
return sprintf26 (buf, format_string, intf->altsetting[alt].desc.field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
 
usb_intf_attr (bInterfaceNumber, "%02x\n")
usb_intf_attr (bAlternateSetting, "%2d\n")
usb_intf_attr (bNumEndpoints, "%02x\n")
usb_intf_attr (bInterfaceClass, "%02x\n")
usb_intf_attr (bInterfaceSubClass, "%02x\n")
usb_intf_attr (bInterfaceProtocol, "%02x\n")
usb_intf_attr (iInterface, "%02x\n")
 
void usb_create_driverfs_intf_files (struct usb_interface *intf)
{
device_create_file (&intf->dev, &dev_attr_bInterfaceNumber);
device_create_file (&intf->dev, &dev_attr_bAlternateSetting);
device_create_file (&intf->dev, &dev_attr_bNumEndpoints);
device_create_file (&intf->dev, &dev_attr_bInterfaceClass);
device_create_file (&intf->dev, &dev_attr_bInterfaceSubClass);
device_create_file (&intf->dev, &dev_attr_bInterfaceProtocol);
device_create_file (&intf->dev, &dev_attr_iInterface);
}
/shark/trunk/drivers/usb/core/urb.c
0,0 → 1,422
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/init.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "hcd.h"
 
/**
* usb_init_urb - initializes a urb so that it can be used by a USB driver
* @urb: pointer to the urb to initialize
*
* Initializes a urb so that the USB subsystem can use it properly.
*
* If a urb is created with a call to usb_alloc_urb() it is not
* necessary to call this function. Only use this if you allocate the
* space for a struct urb on your own. If you call this function, be
* careful when freeing the memory for your urb that it is no longer in
* use by the USB core.
*
* Only use this function if you _really_ understand what you are doing.
*/
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
urb->count = (atomic_t)ATOMIC_INIT(1);
spin_lock_init(&urb->lock);
}
}
 
/**
* usb_alloc_urb - creates a new urb for a USB driver to use
* @iso_packets: number of iso packets for this urb
* @mem_flags: the type of memory to allocate, see kmalloc() for a list of
* valid options for this.
*
* Creates an urb for the USB driver to use, initializes a few internal
* structures, incrementes the usage counter, and returns a pointer to it.
*
* If no memory is available, NULL is returned.
*
* If the driver want to use this urb for interrupt, control, or bulk
* endpoints, pass '0' as the number of iso packets.
*
* The driver must call usb_free_urb() when it is finished with the urb.
*/
struct urb *usb_alloc_urb(int iso_packets, int mem_flags)
{
struct urb *urb;
 
urb = (struct urb *)kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
err("alloc_urb: kmalloc failed");
return NULL;
}
usb_init_urb(urb);
return urb;
}
 
/**
* usb_free_urb - frees the memory used by a urb when all users of it are finished
* @urb: pointer to the urb to free
*
* Must be called when a user of a urb is finished with it. When the last user
* of the urb calls this function, the memory of the urb is freed.
*
* Note: The transfer buffer associated with the urb is not freed, that must be
* done elsewhere.
*/
void usb_free_urb(struct urb *urb)
{
if (urb)
if (atomic_dec_and_test(&urb->count))
kfree(urb);
}
 
/**
* usb_get_urb - increments the reference count of the urb
* @urb: pointer to the urb to modify
*
* This must be called whenever a urb is transferred from a device driver to a
* host controller driver. This allows proper reference counting to happen
* for urbs.
*
* A pointer to the urb with the incremented reference counter is returned.
*/
struct urb * usb_get_urb(struct urb *urb)
{
if (urb) {
atomic_inc(&urb->count);
return urb;
} else
return NULL;
}
/*-------------------------------------------------------------------*/
 
/**
* usb_submit_urb - issue an asynchronous transfer request for an endpoint
* @urb: pointer to the urb describing the request
* @mem_flags: the type of memory to allocate, see kmalloc() for a list
* of valid options for this.
*
* This submits a transfer request, and transfers control of the URB
* describing that request to the USB subsystem. Request completion will
* be indicated later, asynchronously, by calling the completion handler.
* The three types of completion are success, error, and unlink
* (also called "request cancellation").
* URBs may be submitted in interrupt context.
*
* The caller must have correctly initialized the URB before submitting
* it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
* available to ensure that most fields are correctly initialized, for
* the particular kind of transfer, although they will not initialize
* any transfer flags.
*
* Successful submissions return 0; otherwise this routine returns a
* negative error number. If the submission is successful, the complete()
* callback from the urb will be called exactly once, when the USB core and
* host controller driver are finished with the urb. When the completion
* function is called, control of the URB is returned to the device
* driver which issued the request. The completion handler may then
* immediately free or reuse that URB.
*
* For control endpoints, the synchronous usb_control_msg() call is
* often used (in non-interrupt context) instead of this call.
* That is often used through convenience wrappers, for the requests
* that are standardized in the USB 2.0 specification. For bulk
* endpoints, a synchronous usb_bulk_msg() call is available.
*
* Request Queuing:
*
* URBs may be submitted to endpoints before previous ones complete, to
* minimize the impact of interrupt latencies and system overhead on data
* throughput. This is required for continuous isochronous data streams,
* and may also be required for some kinds of interrupt transfers. Such
* queueing also maximizes bandwidth utilization by letting USB controllers
* start work on later requests before driver software has finished the
* completion processing for earlier requests.
*
* Bulk and Isochronous URBs may always be queued. At this writing, all
* mainstream host controller drivers support queueing for control and
* interrupt transfer requests.
*
* Reserved Bandwidth Transfers:
*
* Periodic transfers (interrupt or isochronous) are performed repeatedly,
* using the interval specified in the urb. Submitting the first urb to
* the endpoint reserves the bandwidth necessary to make those transfers.
* If the USB subsystem can't allocate sufficient bandwidth to perform
* the periodic request, submitting such a periodic request should fail.
*
* Device drivers must explicitly request that repetition, by ensuring that
* some URB is always on the endpoint's queue (except possibly for short
* periods during completion callacks). When there is no longer an urb
* queued, the endpoint's bandwidth reservation is canceled. This means
* drivers can use their completion handlers to ensure they keep bandwidth
* they need, by reinitializing and resubmitting the just-completed urb
* until the driver longer needs that periodic bandwidth.
*
* Memory Flags:
*
* The general rules for how to decide which mem_flags to use
* are the same as for kmalloc. There are four
* different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
* GFP_ATOMIC.
*
* GFP_NOFS is not ever used, as it has not been implemented yet.
*
* GFP_ATOMIC is used when
* (a) you are inside a completion handler, an interrupt, bottom half,
* tasklet or timer, or
* (b) you are holding a spinlock or rwlock (does not apply to
* semaphores), or
* (c) current->state != TASK_RUNNING, this is the case only after
* you've changed it.
*
* GFP_NOIO is used in the block io path and error handling of storage
* devices.
*
* All other situations use GFP_KERNEL.
*
* Some more specific rules for mem_flags can be inferred, such as
* (1) start_xmit, timeout, and receive methods of network drivers must
* use GFP_ATOMIC (they are called with a spinlock held);
* (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
* called with a spinlock held);
* (3) If you use a kernel thread with a network driver you must use
* GFP_NOIO, unless (b) or (c) apply;
* (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
* apply or your are in a storage driver's block io path;
* (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
* (6) changing firmware on a running storage or net device uses
* GFP_NOIO, unless b) or c) apply
*
*/
int usb_submit_urb(struct urb *urb, int mem_flags)
{
int pipe, temp, max;
struct usb_device *dev;
struct usb_operations *op;
int is_out;
 
if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL;
if (!(dev = urb->dev) ||
(dev->state < USB_STATE_DEFAULT) ||
(!dev->bus) || (dev->devnum <= 0))
return -ENODEV;
if (!(op = dev->bus->op) || !op->submit_urb)
return -ENODEV;
 
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->bandwidth = 0;
 
/* Lots of sanity checks, so HCDs can rely on clean data
* and don't need to duplicate tests
*/
pipe = urb->pipe;
temp = usb_pipetype (pipe);
is_out = usb_pipeout (pipe);
 
if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
 
/* (actually HCDs may need to duplicate this, endpoint might yet
* stall due to queued bulk/intr transactions that complete after
* we check)
*/
if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out))
return -EPIPE;
 
/* FIXME there should be a sharable lock protecting us against
* config/altsetting changes and disconnects, kicking in here.
* (here == before maxpacket, and eventually endpoint type,
* checks get made.)
*/
 
max = usb_maxpacket (dev, pipe, is_out);
if (max <= 0) {
dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)",
__FUNCTION__,
usb_pipeendpoint (pipe), is_out ? "OUT" : "IN",
dev->bus->bus_name, dev->devpath,
max);
return -EMSGSIZE;
}
 
/* periodic transfers limit size per frame/uframe,
* but drivers only control those sizes for ISO.
* while we're checking, initialize return status.
*/
if (temp == PIPE_ISOCHRONOUS) {
int n, len;
 
/* "high bandwidth" mode, 1-3 packets/uframe? */
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x03ff;
max *= mult;
}
 
if (urb->number_of_packets <= 0)
return -EINVAL;
for (n = 0; n < urb->number_of_packets; n++) {
len = urb->iso_frame_desc [n].length;
if (len < 0 || len > max)
return -EMSGSIZE;
urb->iso_frame_desc [n].status = -EXDEV;
urb->iso_frame_desc [n].actual_length = 0;
}
}
 
/* the I/O buffer must be mapped/unmapped, except when length=0 */
if (urb->transfer_buffer_length < 0)
return -EMSGSIZE;
 
#ifdef DEBUG
/* stuff that drivers shouldn't do, but which shouldn't
* cause problems in HCDs if they get it wrong.
*/
{
unsigned int orig_flags = urb->transfer_flags;
unsigned int allowed;
 
/* enforce simple/standard policy */
allowed = URB_ASYNC_UNLINK; // affects later unlinks
allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
allowed |= URB_NO_INTERRUPT;
switch (temp) {
case PIPE_BULK:
if (is_out)
allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */
case PIPE_CONTROL:
allowed |= URB_NO_FSBR; /* only affects UHCI */
/* FALLTHROUGH */
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
break;
case PIPE_ISOCHRONOUS:
allowed |= URB_ISO_ASAP;
break;
}
urb->transfer_flags &= allowed;
 
/* fail if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) {
err ("BOGUS urb flags, %x --> %x",
orig_flags, urb->transfer_flags);
return -EINVAL;
}
}
#endif
/*
* Force periodic transfer intervals to be legal values that are
* a power of two (so HCDs don't need to).
*
* FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
* supports different values... this uses EHCI/UHCI defaults (and
* EHCI can use smaller non-default values).
*/
switch (temp) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
/* too small? */
if (urb->interval <= 0)
return -EINVAL;
/* too big? */
switch (dev->speed) {
case USB_SPEED_HIGH: /* units are microframes */
// NOTE usb handles 2^15
if (urb->interval > (1024 * 8))
urb->interval = 1024 * 8;
temp = 1024 * 8;
break;
case USB_SPEED_FULL: /* units are frames/msec */
case USB_SPEED_LOW:
if (temp == PIPE_INTERRUPT) {
if (urb->interval > 255)
return -EINVAL;
// NOTE ohci only handles up to 32
temp = 128;
} else {
if (urb->interval > 1024)
urb->interval = 1024;
// NOTE usb and ohci handle up to 2^15
temp = 1024;
}
break;
default:
return -EINVAL;
}
/* power of two? */
while (temp > urb->interval)
temp >>= 1;
urb->interval = temp;
}
 
return op->submit_urb (urb, mem_flags);
}
 
/*-------------------------------------------------------------------*/
 
/**
* usb_unlink_urb - abort/cancel a transfer request for an endpoint
* @urb: pointer to urb describing a previously submitted request
*
* This routine cancels an in-progress request. URBs complete only
* once per submission, and may be canceled only once per submission.
* Successful cancelation means the requests's completion handler will
* be called with a status code indicating that the request has been
* canceled (rather than any other code) and will quickly be removed
* from host controller data structures.
*
* When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this
* request is synchronous. Success is indicated by returning zero,
* at which time the urb will have been unlinked and its completion
* handler will have been called with urb->status -ENOENT. Failure is
* indicated by any other return value.
*
* The synchronous cancelation mode may not be used
* when unlinking an urb from an interrupt context, such as a bottom
* half or a completion handler; or when holding a spinlock; or in
* other cases when the caller can't schedule().
*
* When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
* request is asynchronous. Success is indicated by returning -EINPROGRESS,
* at which time the urb will normally not have been unlinked.
* The completion function will see urb->status -ECONNRESET. Failure
* is indicated by any other return value.
*/
int usb_unlink_urb(struct urb *urb)
{
if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
return urb->dev->bus->op->unlink_urb(urb);
else
return -ENODEV;
}
 
EXPORT_SYMBOL(usb_init_urb);
EXPORT_SYMBOL(usb_alloc_urb);
EXPORT_SYMBOL(usb_free_urb);
EXPORT_SYMBOL(usb_get_urb);
EXPORT_SYMBOL(usb_submit_urb);
EXPORT_SYMBOL(usb_unlink_urb);
 
/shark/trunk/drivers/usb/core/usb.c
0,0 → 1,1547
/*
* drivers/usb/usb.c
*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
* generic USB things that the real drivers can use..
*
* Think of this as a "USB library" rather than anything else.
* It should be considered a slave, with no callbacks. Callbacks
* are evil.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
 
#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
 
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
 
#include "hcd.h"
#include "usb.h"
 
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
extern int usb_major_init(void);
extern void usb_major_cleanup(void);
extern int usb_host_init(void);
extern void usb_host_cleanup(void);
 
 
int nousb; /* Disable USB when built into kernel image */
/* Not honored on modular build */
 
 
static int generic_probe (struct device *dev)
{
return 0;
}
static int generic_remove (struct device *dev)
{
return 0;
}
 
static struct device_driver usb_generic_driver = {
.name = "usb",
.bus = &usb_bus_type,
.probe = generic_probe,
.remove = generic_remove,
};
 
static int usb_generic_driver_data;
 
/* needs to be called with BKL held */
int usb_probe_interface(struct device *dev)
{
struct usb_interface * intf = to_usb_interface(dev);
struct usb_driver * driver = to_usb_driver(dev->driver);
const struct usb_device_id *id;
int error = -ENODEV;
 
dev_dbg(dev, "%s\n", __FUNCTION__);
 
if (!driver->probe)
return error;
 
id = usb_match_id (intf, driver->id_table);
if (id) {
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
down (&driver->serialize);
error = driver->probe (intf, id);
up (&driver->serialize);
}
if (!error)
intf->driver = driver;
 
return error;
}
 
int usb_unbind_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_driver *driver = to_usb_driver(dev->driver);
 
down(&driver->serialize);
 
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
 
if (intf->driver && intf->driver->disconnect)
intf->driver->disconnect(intf);
 
/* force a release and re-initialize the interface */
usb_driver_release_interface(driver, intf);
 
up(&driver->serialize);
 
return 0;
}
 
/**
* usb_register - register a USB driver
* @new_driver: USB operations for the driver
*
* Registers a USB driver with the USB core. The list of unattached
* interfaces will be rescanned whenever a new driver is added, allowing
* the new driver to attach to any recognized devices.
* Returns a negative error code on failure and 0 on success.
*
* NOTE: if you want your driver to use the USB major number, you must call
* usb_register_dev() to enable that functionality. This function no longer
* takes care of that.
*/
int usb_register(struct usb_driver *new_driver)
{
int retval = 0;
 
if (nousb)
return -ENODEV;
 
new_driver->driver.name = (char *)new_driver->name;
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
 
init_MUTEX(&new_driver->serialize);
 
retval = driver_register(&new_driver->driver);
 
if (!retval) {
info("registered new driver %s", new_driver->name);
usbfs_update_special();
} else {
err("problem %d when registering driver %s",
retval, new_driver->name);
}
 
return retval;
}
 
/**
* usb_deregister - unregister a USB driver
* @driver: USB operations of the driver to unregister
* Context: !in_interrupt (), must be called with BKL held
*
* Unlinks the specified driver from the internal USB driver list.
*
* NOTE: If you called usb_register_dev(), you still need to call
* usb_deregister_dev() to clean up your driver's allocated minor numbers,
* this * call will no longer do it for you.
*/
void usb_deregister(struct usb_driver *driver)
{
info("deregistering driver %s", driver->name);
 
driver_unregister (&driver->driver);
 
usbfs_update_special();
}
 
/**
* usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal)
* @dev: the device whose current configuration is considered
* @ifnum: the desired interface
*
* This walks the device descriptor for the currently active configuration
* and returns a pointer to the interface with that particular interface
* number, or null.
*
* Note that configuration descriptors are not required to assign interface
* numbers sequentially, so that it would be incorrect to assume that
* the first interface in that descriptor corresponds to interface zero.
* This routine helps device drivers avoid such mistakes.
* However, you should make sure that you do the right thing with any
* alternate settings available for this interfaces.
*/
struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
{
int i;
 
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)
if (dev->actconfig->interface[i]->altsetting[0]
.desc.bInterfaceNumber == ifnum)
return dev->actconfig->interface[i];
 
return NULL;
}
 
/**
* usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number
* @dev: the device whose current configuration+altsettings is considered
* @epnum: the desired endpoint, masked with USB_DIR_IN as appropriate.
*
* This walks the device descriptor for the currently active configuration,
* and returns a pointer to the endpoint with that particular endpoint
* number, or null.
*
* Note that interface descriptors are not required to list endpoint
* numbers in any standardized order, so that it would be wrong to
* assume that ep2in precedes either ep5in, ep2out, or even ep1out.
* This routine helps device drivers avoid such mistakes.
*/
struct usb_endpoint_descriptor *
usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
{
int i, k;
 
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf;
struct usb_host_interface *alt;
 
/* only endpoints in current altseting are active */
intf = dev->actconfig->interface[i];
alt = intf->altsetting + intf->act_altsetting;
 
for (k = 0; k < alt->desc.bNumEndpoints; k++)
if (epnum == alt->endpoint[k].desc.bEndpointAddress)
return &alt->endpoint[k].desc;
}
 
return NULL;
}
 
/**
* usb_driver_claim_interface - bind a driver to an interface
* @driver: the driver to be bound
* @iface: the interface to which it will be bound
* @priv: driver data associated with that interface
*
* This is used by usb device drivers that need to claim more than one
* interface on a device when probing (audio and acm are current examples).
* No device driver should directly modify internal usb_interface or
* usb_device structure members.
*
* Few drivers should need to use this routine, since the most natural
* way to bind to an interface is to return the private data from
* the driver's probe() method.
*/
int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
{
if (!iface || !driver)
return -EINVAL;
 
/* this is mainly to lock against usbfs */
lock_kernel();
if (iface->driver) {
unlock_kernel();
err ("%s driver booted %s off interface %p",
driver->name, iface->driver->name, iface);
return -EBUSY;
} else {
dbg("%s driver claimed interface %p", driver->name, iface);
}
 
iface->driver = driver;
usb_set_intfdata(iface, priv);
unlock_kernel();
return 0;
}
 
/**
* usb_interface_claimed - returns true iff an interface is claimed
* @iface: the interface being checked
*
* This should be used by drivers to check other interfaces to see if
* they are available or not. If another driver has claimed the interface,
* they may not claim it. Otherwise it's OK to claim it using
* usb_driver_claim_interface().
*
* Returns true (nonzero) iff the interface is claimed, else false (zero).
*/
int usb_interface_claimed(struct usb_interface *iface)
{
if (!iface)
return 0;
 
return (iface->driver != NULL);
} /* usb_interface_claimed() */
 
/**
* usb_driver_release_interface - unbind a driver from an interface
* @driver: the driver to be unbound
* @iface: the interface from which it will be unbound
*
* In addition to unbinding the driver, this re-initializes the interface
* by selecting altsetting 0, the default alternate setting.
*
* This can be used by drivers to release an interface without waiting
* for their disconnect() methods to be called.
*
* When the USB subsystem disconnect()s a driver from some interface,
* it automatically invokes this method for that interface. That
* means that even drivers that used usb_driver_claim_interface()
* usually won't need to call this.
*
* This call is synchronous, and may not be used in an interrupt context.
*/
void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
{
/* this should never happen, don't release something that's not ours */
if (iface->driver && iface->driver != driver)
return;
 
usb_set_interface(interface_to_usbdev(iface),
iface->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_intfdata(iface, NULL);
iface->driver = NULL;
}
 
/**
* usb_match_id - find first usb_device_id matching device or interface
* @interface: the interface of interest
* @id: array of usb_device_id structures, terminated by zero entry
*
* usb_match_id searches an array of usb_device_id's and returns
* the first one matching the device or interface, or null.
* This is used when binding (or rebinding) a driver to an interface.
* Most USB device drivers will use this indirectly, through the usb core,
* but some layered driver frameworks use it directly.
* These device tables are exported with MODULE_DEVICE_TABLE, through
* modutils and "modules.usbmap", to support the driver loading
* functionality of USB hotplugging.
*
* What Matches:
*
* The "match_flags" element in a usb_device_id controls which
* members are used. If the corresponding bit is set, the
* value in the device_id must match its corresponding member
* in the device or interface descriptor, or else the device_id
* does not match.
*
* "driver_info" is normally used only by device drivers,
* but you can create a wildcard "matches anything" usb_device_id
* as a driver's "modules.usbmap" entry if you provide an id with
* only a nonzero "driver_info" field. If you do this, the USB device
* driver's probe() routine should use additional intelligence to
* decide whether to bind to the specified interface.
*
* What Makes Good usb_device_id Tables:
*
* The match algorithm is very simple, so that intelligence in
* driver selection must come from smart driver id records.
* Unless you have good reasons to use another selection policy,
* provide match elements only in related groups, and order match
* specifiers from specific to general. Use the macros provided
* for that purpose if you can.
*
* The most specific match specifiers use device descriptor
* data. These are commonly used with product-specific matches;
* the USB_DEVICE macro lets you provide vendor and product IDs,
* and you can also match against ranges of product revisions.
* These are widely used for devices with application or vendor
* specific bDeviceClass values.
*
* Matches based on device class/subclass/protocol specifications
* are slightly more general; use the USB_DEVICE_INFO macro, or
* its siblings. These are used with single-function devices
* where bDeviceClass doesn't specify that each interface has
* its own class.
*
* Matches based on interface class/subclass/protocol are the
* most general; they let drivers bind to any interface on a
* multiple-function device. Use the USB_INTERFACE_INFO
* macro, or its siblings, to match class-per-interface style
* devices (as recorded in bDeviceClass).
*
* Within those groups, remember that not all combinations are
* meaningful. For example, don't give a product version range
* without vendor and product IDs; or specify a protocol without
* its associated class and subclass.
*/
const struct usb_device_id *
usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_host_interface *intf;
struct usb_device *dev;
 
/* proc_connectinfo in devio.c may call us with id == NULL. */
if (id == NULL)
return NULL;
 
intf = &interface->altsetting [interface->act_altsetting];
dev = interface_to_usbdev(interface);
 
/* It is important to check that id->driver_info is nonzero,
since an entry that is all zeroes except for a nonzero
id->driver_info is the way to create an entry that
indicates that the driver want to examine every
device and interface. */
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
id->driver_info; id++) {
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != dev->descriptor.idVendor)
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->idProduct != dev->descriptor.idProduct)
continue;
 
/* No need to test id->bcdDevice_lo != 0, since 0 is never
greater than any unsigned number. */
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
(id->bcdDevice_lo > dev->descriptor.bcdDevice))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
(id->bcdDevice_hi < dev->descriptor.bcdDevice))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
(id->bDeviceClass != dev->descriptor.bDeviceClass))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
(id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
(id->bInterfaceClass != intf->desc.bInterfaceClass))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
continue;
 
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
continue;
 
return id;
}
 
return NULL;
}
 
/**
* usb_find_interface - find usb_interface pointer for driver and device
* @drv: the driver whose current configuration is considered
* @minor: the minor number of the desired device
*
* This walks the driver device list and returns a pointer to the interface
* with the matching minor. Note, this only works for devices that share the
* USB major number.
*/
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
struct list_head *entry;
struct device *dev;
struct usb_interface *intf;
 
list_for_each(entry, &drv->driver.devices) {
dev = container_of(entry, struct device, driver_list);
 
/* can't look at usb devices, only interfaces */
if (dev->driver == &usb_generic_driver)
continue;
 
intf = to_usb_interface(dev);
if (intf->minor == -1)
continue;
if (intf->minor == minor)
return intf;
}
 
/* no device found that matches */
return NULL;
}
 
static int usb_device_match (struct device *dev, struct device_driver *drv)
{
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
 
/* check for generic driver, which we don't match any device with */
if (drv == &usb_generic_driver)
return 0;
 
intf = to_usb_interface(dev);
 
usb_drv = to_usb_driver(drv);
id = usb_drv->id_table;
id = usb_match_id (intf, usb_drv->id_table);
if (id)
return 1;
 
return 0;
}
 
 
#ifdef CONFIG_HOTPLUG
 
/*
* USB hotplugging invokes what /proc/sys/kernel/hotplug says
* (normally /sbin/hotplug) when USB devices get added or removed.
*
* This invokes a user mode policy agent, typically helping to load driver
* or other modules, configure the device, and more. Drivers can provide
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
* We're called either from khubd (the typical case) or from root hub
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the
* device (and this configuration!) are still present.
*/
static int usb_hotplug (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct usb_interface *intf;
struct usb_device *usb_dev;
char *scratch;
int i = 0;
int length = 0;
 
dbg ("%s", __FUNCTION__);
 
if (!dev)
return -ENODEV;
 
/* Must check driver_data here, as on remove driver is always NULL */
if ((dev->driver == &usb_generic_driver) ||
(dev->driver_data == &usb_generic_driver_data))
return 0;
 
intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev (intf);
if (usb_dev->devnum < 0) {
dbg ("device already deleted ??");
return -ENODEV;
}
if (!usb_dev->bus) {
dbg ("bus already removed?");
return -ENODEV;
}
 
scratch = buffer;
 
#ifdef CONFIG_USB_DEVICEFS
/* If this is available, userspace programs can directly read
* all the device descriptors we don't tell them about. Or
* even act as usermode drivers.
*
* FIXME reduce hardwired intelligence here
*/
envp [i++] = scratch;
length += snprintf (scratch, buffer_size - length,
"DEVICE=/proc/bus/usb/%03d/%03d",
usb_dev->bus->busnum, usb_dev->devnum);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
#endif
 
/* per-device configurations are common */
envp [i++] = scratch;
length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x",
usb_dev->descriptor.idVendor,
usb_dev->descriptor.idProduct,
usb_dev->descriptor.bcdDevice);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
 
/* class-based driver binding models */
envp [i++] = scratch;
length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d",
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
 
if (usb_dev->descriptor.bDeviceClass == 0) {
int alt = intf->act_altsetting;
 
/* 2.4 only exposed interface zero. in 2.5, hotplug
* agents are called for all interfaces, and can use
* $DEVPATH/bInterfaceNumber if necessary.
*/
envp [i++] = scratch;
length += snprintf (scratch, buffer_size - length,
"INTERFACE=%d/%d/%d",
intf->altsetting[alt].desc.bInterfaceClass,
intf->altsetting[alt].desc.bInterfaceSubClass,
intf->altsetting[alt].desc.bInterfaceProtocol);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
 
}
envp [i++] = 0;
 
return 0;
}
 
#else
 
static int usb_hotplug (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
 
#endif /* CONFIG_HOTPLUG */
 
/**
* usb_release_dev - free a usb device structure when all users of it are finished.
* @dev: device that's been disconnected
*
* Will be called only by the device core when all users of this usb device are
* done.
*/
static void usb_release_dev(struct device *dev)
{
struct usb_device *udev;
 
udev = to_usb_device(dev);
 
if (udev->bus && udev->bus->op && udev->bus->op->deallocate)
udev->bus->op->deallocate(udev);
usb_destroy_configuration(udev);
usb_bus_put(udev->bus);
kfree (udev);
}
 
/**
* usb_alloc_dev - allocate a usb device structure (usbcore-internal)
* @parent: hub to which device is connected
* @bus: bus used to access the device
* Context: !in_interrupt ()
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
*
* This call is synchronous, and may not be used in an interrupt context.
*/
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
{
struct usb_device *dev;
 
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
 
memset(dev, 0, sizeof(*dev));
 
bus = usb_bus_get(bus);
if (!bus) {
kfree(dev);
return NULL;
}
 
device_initialize(&dev->dev);
dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
 
if (!parent)
dev->devpath [0] = '0';
dev->bus = bus;
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
 
init_MUTEX(&dev->serialize);
 
if (dev->bus->op->allocate)
dev->bus->op->allocate(dev);
 
return dev;
}
 
/**
* usb_get_dev - increments the reference count of the usb device structure
* @dev: the device being referenced
*
* Each live reference to a device should be refcounted.
*
* Drivers for USB interfaces should normally record such references in
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_dev(), in their disconnect() methods.
*
* A pointer to the device with the incremented reference counter is returned.
*/
struct usb_device *usb_get_dev (struct usb_device *dev)
{
struct device *tmp;
 
if (!dev)
return NULL;
 
tmp = get_device(&dev->dev);
if (tmp)
return to_usb_device(tmp);
else
return NULL;
}
 
/**
* usb_put_dev - release a use of the usb device structure
* @dev: device that's been disconnected
*
* Must be called when a user of a device is finished with it. When the last
* user of the device calls this function, the memory of the device is freed.
*/
void usb_put_dev(struct usb_device *dev)
{
if (dev)
put_device(&dev->dev);
}
 
static struct usb_device *match_device(struct usb_device *dev,
u16 vendor_id, u16 product_id)
{
struct usb_device *ret_dev = NULL;
int child;
 
dbg("looking at vendor %d, product %d",
dev->descriptor.idVendor,
dev->descriptor.idProduct);
 
/* see if this device matches */
if ((dev->descriptor.idVendor == vendor_id) &&
(dev->descriptor.idProduct == product_id)) {
dbg ("found the device!");
ret_dev = usb_get_dev(dev);
goto exit;
}
 
/* look through all of the children of this device */
for (child = 0; child < dev->maxchild; ++child) {
if (dev->children[child]) {
ret_dev = match_device(dev->children[child],
vendor_id, product_id);
if (ret_dev)
goto exit;
}
}
exit:
return ret_dev;
}
 
/**
* usb_find_device - find a specific usb device in the system
* @vendor_id: the vendor id of the device to find
* @product_id: the product id of the device to find
*
* Returns a pointer to a struct usb_device if such a specified usb
* device is present in the system currently. The usage count of the
* device will be incremented if a device is found. Make sure to call
* usb_put_dev() when the caller is finished with the device.
*
* If a device with the specified vendor and product id is not found,
* NULL is returned.
*/
struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
{
struct list_head *buslist;
struct usb_bus *bus;
struct usb_device *dev = NULL;
down(&usb_bus_list_lock);
for (buslist = usb_bus_list.next;
buslist != &usb_bus_list;
buslist = buslist->next) {
bus = container_of(buslist, struct usb_bus, bus_list);
dev = match_device(bus->root_hub, vendor_id, product_id);
if (dev)
goto exit;
}
exit:
up(&usb_bus_list_lock);
return dev;
}
 
/**
* usb_get_current_frame_number - return current bus frame number
* @dev: the device whose bus is being queried
*
* Returns the current frame number for the USB host controller
* used with the given USB device. This can be used when scheduling
* isochronous requests.
*
* Note that different kinds of host controller have different
* "scheduling horizons". While one type might support scheduling only
* 32 frames into the future, others could support scheduling up to
* 1024 frames into the future.
*/
int usb_get_current_frame_number(struct usb_device *dev)
{
return dev->bus->op->get_frame_number (dev);
}
 
/*-------------------------------------------------------------------*/
/*
* __usb_get_extra_descriptor() finds a descriptor of specific type in the
* extra field of the interface and endpoint descriptor structs.
*/
 
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
{
struct usb_descriptor_header *header;
 
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
 
if (header->bLength < 2) {
err("invalid descriptor length of %d", header->bLength);
return -1;
}
 
if (header->bDescriptorType == type) {
*ptr = header;
return 0;
}
 
buffer += header->bLength;
size -= header->bLength;
}
return -1;
}
 
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
* Context: !in_interrupt ()
*
* Something got disconnected. Get rid of it, and all of its children.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
*
* This call is synchronous, and may not be used in an interrupt context.
*/
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *dev = *pdev;
struct usb_bus *bus;
struct usb_operations *ops;
int i;
 
might_sleep ();
 
if (!dev) {
pr_debug ("%s nodev\n", __FUNCTION__);
return;
}
bus = dev->bus;
if (!bus) {
pr_debug ("%s nobus\n", __FUNCTION__);
return;
}
ops = bus->op;
 
*pdev = NULL;
 
/* mark the device as inactive, so any further urb submissions for
* this device will fail.
*/
dev->state = USB_STATE_NOTATTACHED;
down(&dev->serialize);
 
dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum);
 
/* Free up all the children before we remove this device */
for (i = 0; i < USB_MAXCHILDREN; i++) {
struct usb_device **child = dev->children + i;
if (*child)
usb_disconnect(child);
}
 
/* deallocate hcd/hardware state ... nuking all pending urbs and
* cleaning up all state associated with the current configuration
*/
usb_disable_device(dev, 0);
 
dev_dbg (&dev->dev, "unregistering device\n");
/* Free the device number and remove the /proc/bus/usb entry */
if (dev->devnum > 0) {
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
usbfs_remove_device(dev);
}
up(&dev->serialize);
device_unregister(&dev->dev);
}
 
/**
* usb_choose_address - pick device address (usbcore-internal)
* @dev: newly detected device (in DEFAULT state)
*
* Picks a device address. It's up to the hub (or root hub) driver
* to handle and manage enumeration, starting from the DEFAULT state.
* Only hub drivers (but not virtual root hub drivers for host
* controllers) should ever call this.
*/
void usb_choose_address(struct usb_device *dev)
{
int devnum;
// FIXME needs locking for SMP!!
/* why? this is called only from the hub thread,
* which hopefully doesn't run on multiple CPU's simultaneously 8-)
*/
 
/* Try to allocate the next devnum beginning at bus->devnum_next. */
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next);
if (devnum >= 128)
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
 
dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
 
if (devnum < 128) {
set_bit(devnum, dev->bus->devmap.devicemap);
dev->devnum = devnum;
}
}
 
 
// hub-only!! ... and only exported for reset/reinit path.
// otherwise used internally, for usb_new_device()
int usb_set_address(struct usb_device *dev)
{
int retval;
 
if (dev->devnum == 0)
return -EINVAL;
if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)
return -EINVAL;
retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
if (retval == 0)
dev->state = USB_STATE_ADDRESS;
return retval;
}
 
/*
* By the time we get here, we chose a new device address
* and is in the default state. We need to identify the thing and
* get the ball rolling..
*
* Returns 0 for success, != 0 for error.
*
* This call is synchronous, and may not be used in an interrupt context.
*
* Only the hub driver should ever call this; root hub registration
* uses it only indirectly.
*/
#define NEW_DEVICE_RETRYS 2
#define SET_ADDRESS_RETRYS 2
int usb_new_device(struct usb_device *dev, struct device *parent)
{
int err = -EINVAL;
int i;
int j;
 
/*
* Set the driver for the usb device to point to the "generic" driver.
* This prevents the main usb device from being sent to the usb bus
* probe function. Yes, it's a hack, but a nice one :)
*
* Do it asap, so more driver model stuff (like the device.h message
* utilities) can be used in hcd submit/unlink code paths.
*/
usb_generic_driver.bus = &usb_bus_type;
dev->dev.parent = parent;
dev->dev.driver = &usb_generic_driver;
dev->dev.bus = &usb_bus_type;
dev->dev.driver_data = &usb_generic_driver_data;
if (dev->dev.bus_id[0] == 0)
sprintf26 (&dev->dev.bus_id[0], "%d-%s",
dev->bus->busnum, dev->devpath);
 
/* dma masks come from the controller; readonly, except to hcd */
dev->dev.dma_mask = parent->dma_mask;
 
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
*/
switch (dev->speed) {
case USB_SPEED_HIGH: /* fixed at 64 */
i = 64;
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, read the first 8
* bytes from the device descriptor to get bMaxPacketSize0;
* then correct our initial (small) guess.
*/
// FALLTHROUGH
case USB_SPEED_LOW: /* fixed at 8 */
i = 8;
break;
default:
goto fail;
}
dev->epmaxpacketin [0] = i;
dev->epmaxpacketout[0] = i;
 
for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {
for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {
err = usb_set_address(dev);
if (err >= 0)
break;
wait_ms(200);
}
if (err < 0) {
dev_err(&dev->dev,
"device not accepting address %d, error %d\n",
dev->devnum, err);
goto fail;
}
 
wait_ms(10); /* Let the SET_ADDRESS settle */
/* high and low speed devices don't need this... */
 
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
if (err >= 8)
break;
wait_ms(100);
}
 
if (err < 8) {
dev_err(&dev->dev, "device descriptor read/8, error %d\n", err);
goto fail;
}
if (dev->speed == USB_SPEED_FULL) {
usb_disable_endpoint(dev, 0);
usb_endpoint_running(dev, 0, 1);
usb_endpoint_running(dev, 0, 0);
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
}
 
/* USB device state == addressed ... still not usable */
 
err = usb_get_device_descriptor(dev);
if (err < (signed)sizeof(dev->descriptor)) {
dev_err(&dev->dev, "device descriptor read/all, error %d\n", err);
goto fail;
}
 
err = usb_get_configuration(dev);
if (err < 0) {
dev_err(&dev->dev, "can't read configurations, error %d\n",
err);
goto fail;
}
 
/* Tell the world! */
dev_dbg(&dev->dev, "new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
 
#ifdef DEBUG
if (dev->descriptor.iProduct)
usb_show_string(dev, "Product", dev->descriptor.iProduct);
if (dev->descriptor.iManufacturer)
usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
if (dev->descriptor.iSerialNumber)
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
#endif
 
/* put device-specific files into sysfs */
err = device_add (&dev->dev);
if (err) {
dev_err(&dev->dev, "can't device_add, error %d\n", err);
goto fail;
}
usb_create_driverfs_dev_files (dev);
 
/* choose and set the configuration. that registers the interfaces
* with the driver core, and lets usb device drivers bind to them.
*/
if (dev->descriptor.bNumConfigurations != 1) {
dev_info(&dev->dev,
"configuration #%d chosen from %d choices\n",
dev->config[0].desc.bConfigurationValue,
dev->descriptor.bNumConfigurations);
}
err = usb_set_configuration(dev,
dev->config[0].desc.bConfigurationValue);
if (err) {
dev_err(&dev->dev, "can't set config #%d, error %d\n",
dev->config[0].desc.bConfigurationValue, err);
device_del(&dev->dev);
goto fail;
}
/* USB device state == configured ... usable */
 
/* add a /proc/bus/usb entry */
usbfs_add_device(dev);
 
return 0;
fail:
dev->state = USB_STATE_DEFAULT;
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
dev->devnum = -1;
return err;
}
 
/**
* usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP
* @dev: device the buffer will be used with
* @size: requested buffer size
* @mem_flags: affect whether allocation may block
* @dma: used to return DMA address of buffer
*
* Return value is either null (indicating no buffer could be allocated), or
* the cpu-space pointer to a buffer that may be used to perform DMA to the
* specified device. Such cpu-space buffers are returned along with the DMA
* address (through the pointer provided).
*
* These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags
* to avoid behaviors like using "DMA bounce buffers", or tying down I/O
* mapping hardware for long idle periods. The implementation varies between
* platforms, depending on details of how DMA will work to this device.
* Using these buffers also helps prevent cacheline sharing problems on
* architectures where CPU caches are not DMA-coherent.
*
* When the buffer is no longer used, free it with usb_buffer_free().
*/
void *usb_buffer_alloc (
struct usb_device *dev,
size_t size,
int mem_flags,
dma_addr_t *dma
)
{
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
return 0;
return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
}
 
/**
* usb_buffer_free - free memory allocated with usb_buffer_alloc()
* @dev: device the buffer was used with
* @size: requested buffer size
* @addr: CPU address of buffer
* @dma: DMA address of buffer
*
* This reclaims an I/O buffer, letting it be reused. The memory must have
* been allocated using usb_buffer_alloc(), and the parameters must match
* those provided in that allocation request.
*/
void usb_buffer_free (
struct usb_device *dev,
size_t size,
void *addr,
dma_addr_t dma
)
{
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
return;
dev->bus->op->buffer_free (dev->bus, size, addr, dma);
}
 
/**
* usb_buffer_map - create DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer/setup_packet will be mapped
*
* Return value is either null (indicating no buffer could be mapped), or
* the parameter. URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are
* added to urb->transfer_flags if the operation succeeds. If the device
* is connected to this system through a non-DMA controller, this operation
* always succeeds.
*
* This call would normally be used for an urb which is reused, perhaps
* as the target of a large periodic transfer, with usb_buffer_dmasync()
* calls to synchronize memory and dma state.
*
* Reverse the effect of this call with usb_buffer_unmap().
*/
struct urb *usb_buffer_map (struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
 
if (!urb
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
return 0;
 
if (controller->dma_mask) {
urb->transfer_dma = dma_map_single (controller,
urb->transfer_buffer, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (usb_pipecontrol (urb->pipe))
urb->setup_dma = dma_map_single (controller,
urb->setup_packet,
sizeof (struct usb_ctrlrequest),
DMA_TO_DEVICE);
// FIXME generic api broken like pci, can't report errors
// if (urb->transfer_dma == DMA_ADDR_INVALID) return 0;
} else
urb->transfer_dma = ~0;
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
return urb;
}
 
/**
* usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
* @urb: urb whose transfer_buffer/setup_packet will be synchronized
*/
void usb_buffer_dmasync (struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
 
if (!urb
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
return;
 
if (controller->dma_mask) {
dma_sync_single (controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (usb_pipecontrol (urb->pipe))
dma_sync_single (controller,
urb->setup_dma,
sizeof (struct usb_ctrlrequest),
DMA_TO_DEVICE);
}
}
 
/**
* usb_buffer_unmap - free DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer will be unmapped
*
* Reverses the effect of usb_buffer_map().
*/
void usb_buffer_unmap (struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
 
if (!urb
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
return;
 
if (controller->dma_mask) {
dma_unmap_single (controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (usb_pipecontrol (urb->pipe))
dma_unmap_single (controller,
urb->setup_dma,
sizeof (struct usb_ctrlrequest),
DMA_TO_DEVICE);
}
urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
}
 
/**
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to map
* @nents: the number of entries in the scatterlist
*
* Return value is either < 0 (indicating no buffers could be mapped), or
* the number of DMA mapping array entries in the scatterlist.
*
* The caller is responsible for placing the resulting DMA addresses from
* the scatterlist into URB transfer buffer pointers, and for setting the
* URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
*
* Top I/O rates come from queuing URBs, instead of waiting for each one
* to complete before starting the next I/O. This is particularly easy
* to do with scatterlists. Just allocate and submit one URB for each DMA
* mapping entry returned, stopping on the first error or when all succeed.
* Better yet, use the usb_sg_*() calls, which do that (and more) for you.
*
* This call would normally be used when translating scatterlist requests,
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
* may be able to coalesce mappings for improved I/O efficiency.
*
* Reverse the effect of this call with usb_buffer_unmap_sg().
*/
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct device *controller;
 
if (!dev
|| usb_pipecontrol (pipe)
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !controller->dma_mask)
return -1;
 
// FIXME generic api broken like pci, can't report errors
return dma_map_sg (controller, sg, nents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
 
/**
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to synchronize
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Use this when you are re-using a scatterlist's data buffers for
* another USB request.
*/
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
 
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !controller->dma_mask)
return;
 
dma_sync_sg (controller, sg, n_hw_ents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
 
/**
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to unmap
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Reverses the effect of usb_buffer_map_sg().
*/
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
 
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !controller->dma_mask)
return;
 
dma_unmap_sg (controller, sg, n_hw_ents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
 
static int usb_device_suspend(struct device *dev, u32 state)
{
struct usb_interface *intf;
struct usb_driver *driver;
 
if ((dev->driver == NULL) ||
(dev->driver == &usb_generic_driver) ||
(dev->driver_data == &usb_generic_driver_data))
return 0;
 
intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
 
if (driver->suspend)
return driver->suspend(intf, state);
return 0;
}
 
static int usb_device_resume(struct device *dev)
{
struct usb_interface *intf;
struct usb_driver *driver;
 
if ((dev->driver == NULL) ||
(dev->driver == &usb_generic_driver) ||
(dev->driver_data == &usb_generic_driver_data))
return 0;
 
intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
 
if (driver->resume)
return driver->resume(intf);
return 0;
}
 
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.hotplug = usb_hotplug,
.suspend = usb_device_suspend,
.resume = usb_device_resume,
};
 
#ifndef MODULE
 
static int __init usb_setup_disable(char *str)
{
nousb = 1;
return 1;
}
 
/* format to disable USB on kernel command line is: nousb */
__setup("nousb", usb_setup_disable);
 
#endif
 
/*
* for external read access to <nousb>
*/
int usb_disabled(void)
{
return nousb;
}
 
/*
* Init
*/
/*static*/ int __init usb_init(void)
{
if (nousb) {
info("USB support disabled\n");
return 0;
}
 
bus_register(&usb_bus_type);
usb_host_init();
usb_major_init();
usbfs_init();
usb_hub_init();
 
driver_register(&usb_generic_driver);
 
return 0;
}
 
/*
* Cleanup
*/
/*static*/ void __exit usb_exit(void)
{
/* This will matter if shutdown/reboot does exitcalls. */
if (nousb)
return;
 
driver_unregister(&usb_generic_driver);
// usb_major_cleanup();
// usbfs_cleanup();
// usb_hub_cleanup();
// usb_host_cleanup();
// bus_unregister(&usb_bus_type);
}
 
subsys_initcall(usb_init);
module_exit(usb_exit);
 
/*
* USB may be built into the kernel or be built as modules.
* These symbols are exported for device (or host controller)
* driver modules to use.
*/
EXPORT_SYMBOL(usb_epnum_to_ep_desc);
 
EXPORT_SYMBOL(usb_register);
EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_disabled);
 
EXPORT_SYMBOL(usb_alloc_dev);
EXPORT_SYMBOL(usb_put_dev);
EXPORT_SYMBOL(usb_get_dev);
EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
EXPORT_SYMBOL(usb_driver_claim_interface);
EXPORT_SYMBOL(usb_interface_claimed);
EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_match_id);
EXPORT_SYMBOL(usb_find_interface);
EXPORT_SYMBOL(usb_ifnum_to_if);
 
EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_disconnect);
 
EXPORT_SYMBOL(__usb_get_extra_descriptor);
 
EXPORT_SYMBOL(usb_find_device);
EXPORT_SYMBOL(usb_get_current_frame_number);
 
EXPORT_SYMBOL (usb_buffer_alloc);
EXPORT_SYMBOL (usb_buffer_free);
 
EXPORT_SYMBOL (usb_buffer_map);
EXPORT_SYMBOL (usb_buffer_dmasync);
EXPORT_SYMBOL (usb_buffer_unmap);
 
EXPORT_SYMBOL (usb_buffer_map_sg);
EXPORT_SYMBOL (usb_buffer_dmasync_sg);
EXPORT_SYMBOL (usb_buffer_unmap_sg);
 
MODULE_LICENSE("GPL");
/shark/trunk/drivers/usb/host/ohci-hub.c
0,0 → 1,271
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
 
/*-------------------------------------------------------------------------*/
 
/*
* OHCI Root Hub ... the nonsharable stuff
*
* Registers don't need cpu_to_le32, that happens transparently
*/
 
/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
* The erratum (#4) description is incorrect. AMD's workaround waits
* till some bits (mostly reserved) are clear; ok for all revs.
*/
#define read_roothub(hc, register, mask) ({ \
u32 temp = readl (&hc->regs->roothub.register); \
if (temp == -1) \
disable (hc); \
else if (hc->flags & OHCI_QUIRK_AMD756) \
while (temp & mask) \
temp = readl (&hc->regs->roothub.register); \
temp; })
 
static u32 roothub_a (struct ohci_hcd *hc)
{ return read_roothub (hc, a, 0xfc0fe000); }
static inline u32 roothub_b (struct ohci_hcd *hc)
{ return readl (&hc->regs->roothub.b); }
static inline u32 roothub_status (struct ohci_hcd *hc)
{ return readl (&hc->regs->roothub.status); }
static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
 
/*-------------------------------------------------------------------------*/
 
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
label, num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
 
 
/*-------------------------------------------------------------------------*/
 
/* build "status change" packet (one or two bytes) from HC registers */
 
static int
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports, i, changed = 0, length = 1;
 
ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) {
if (!HCD_IS_RUNNING(ohci->hcd.state))
return -ESHUTDOWN;
ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",
ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
return 0;
}
 
/* init status */
if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
buf [0] = changed = 1;
else
buf [0] = 0;
if (ports > 7) {
buf [1] = 0;
length++;
}
 
/* look at each port */
for (i = 0; i < ports; i++) {
u32 status = roothub_portstatus (ohci, i);
 
status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC;
if (status) {
changed = 1;
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
}
}
return changed ? length : 0;
}
 
/*-------------------------------------------------------------------------*/
 
static void
ohci_hub_descriptor (
struct ohci_hcd *ohci,
struct usb_hub_descriptor *desc
) {
u32 rh = roothub_a (ohci);
int ports = rh & RH_A_NDP;
u16 temp;
 
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
desc->bHubContrCurrent = 0;
 
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
 
temp = 0;
if (rh & RH_A_PSM) /* per-port power switching? */
temp |= 0x0001;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
temp |= 0x0010;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
temp |= 0x0008;
desc->wHubCharacteristics = cpu_to_le16 (temp);
 
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
desc->bitmap [0] = rh & RH_B_DR;
if (ports > 7) {
desc->bitmap [1] = (rh & RH_B_DR) >> 8;
desc->bitmap [2] = desc->bitmap [3] = 0xff;
} else
desc->bitmap [1] = 0xff;
}
 
/*-------------------------------------------------------------------------*/
 
static int ohci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports = hcd_to_bus (hcd)->root_hub->maxchild;
u32 temp;
int retval = 0;
 
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
writel (RH_HS_OCIC, &ohci->regs->roothub.status);
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
 
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
temp = RH_PS_CCS;
break;
case USB_PORT_FEAT_C_ENABLE:
temp = RH_PS_PESC;
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
break;
case USB_PORT_FEAT_POWER:
temp = RH_PS_LSDA;
break;
case USB_PORT_FEAT_C_CONNECTION:
temp = RH_PS_CSC;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
temp = RH_PS_OCIC;
break;
case USB_PORT_FEAT_C_RESET:
temp = RH_PS_PRSC;
break;
default:
goto error;
}
writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
// readl (&ohci->regs->roothub.portstatus [wIndex]);
break;
case GetHubDescriptor:
ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
*(u32 *) buf = cpu_to_le32 (temp);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = roothub_portstatus (ohci, wIndex);
*(u32 *) buf = cpu_to_le32 (temp);
 
#ifndef OHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
#endif
dbg_port (ohci, "GetStatus", wIndex + 1, temp);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
// FIXME: this can be cleared, yes?
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_POWER:
writel (RH_PS_PPS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_RESET:
temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
if (temp & RH_PS_CCS)
writel (RH_PS_PRS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
default:
goto error;
}
break;
 
default:
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
return retval;
}
 
/shark/trunk/drivers/usb/host/ohci-mem.c
0,0 → 1,147
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
 
/*-------------------------------------------------------------------------*/
 
/*
* There's basically three types of memory:
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use pci_pool or pci_alloc_consistent
* - driver buffers, read/written by HC ... the hcd glue or the
* device driver provides us with dma addresses
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
*/
 
/*-------------------------------------------------------------------------*/
 
static struct usb_hcd *ohci_hcd_alloc (void)
{
struct ohci_hcd *ohci;
 
ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL);
if (ohci != 0) {
memset (ohci, 0, sizeof (struct ohci_hcd));
ohci->hcd.product_desc = "OHCI Host Controller";
return &ohci->hcd;
}
return 0;
}
 
static void ohci_hcd_free (struct usb_hcd *hcd)
{
kfree (hcd_to_ohci (hcd));
}
 
/*-------------------------------------------------------------------------*/
 
static int ohci_mem_init (struct ohci_hcd *ohci)
{
ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
sizeof (struct td),
32 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->td_cache)
return -ENOMEM;
ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,
sizeof (struct ed),
16 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->ed_cache) {
pci_pool_destroy (ohci->td_cache);
return -ENOMEM;
}
return 0;
}
 
static void ohci_mem_cleanup (struct ohci_hcd *ohci)
{
if (ohci->td_cache) {
pci_pool_destroy (ohci->td_cache);
ohci->td_cache = 0;
}
if (ohci->ed_cache) {
pci_pool_destroy (ohci->ed_cache);
ohci->ed_cache = 0;
}
}
 
/*-------------------------------------------------------------------------*/
 
/* ohci "done list" processing needs this mapping */
static inline struct td *
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
{
struct td *td;
 
td_dma &= TD_MASK;
td = hc->td_hash [TD_HASH_FUNC(td_dma)];
while (td && td->td_dma != td_dma)
td = td->td_hash;
return td;
}
 
/* TDs ... */
static struct td *
td_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct td *td;
 
td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
/* in case hc fetches it, make it look dead */
memset (td, 0, sizeof *td);
td->hwNextTD = cpu_to_le32 (dma);
td->td_dma = dma;
/* hashed in td_fill */
}
return td;
}
 
static void
td_free (struct ohci_hcd *hc, struct td *td)
{
struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
 
while (*prev && *prev != td)
prev = &(*prev)->td_hash;
if (*prev)
*prev = td->td_hash;
else if ((td->hwINFO & TD_DONE) != 0)
ohci_dbg (hc, "no hash for td %p\n", td);
pci_pool_free (hc->td_cache, td, td->td_dma);
}
 
/*-------------------------------------------------------------------------*/
 
/* EDs ... */
static struct ed *
ed_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct ed *ed;
 
ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
}
return ed;
}
 
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
pci_pool_free (hc->ed_cache, ed, ed->dma);
}
 
/shark/trunk/drivers/usb/host/ehci-dbg.c
0,0 → 1,688
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* this file is part of ehci-hcd.c */
 
#define ehci_dbg(ehci, fmt, args...) \
dev_dbg ((ehci)->hcd.controller , fmt , ## args )
#define ehci_err(ehci, fmt, args...) \
dev_err ((ehci)->hcd.controller , fmt , ## args )
#define ehci_info(ehci, fmt, args...) \
dev_info ((ehci)->hcd.controller , fmt , ## args )
#define ehci_warn(ehci, fmt, args...) \
dev_warn ((ehci)->hcd.controller , fmt , ## args )
 
#ifdef EHCI_VERBOSE_DEBUG
# define vdbg dbg
# define ehci_vdbg ehci_dbg
#else
# define vdbg(fmt,args...) do { } while (0)
# define ehci_vdbg(ehci, fmt, args...) do { } while (0)
#endif
 
#ifdef DEBUG
 
/* check the values in the HCSPARAMS register
* (host controller _Structural_ parameters)
* see EHCI spec, Table 2-4 for each value
*/
static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
{
u32 params = readl (&ehci->caps->hcs_params);
 
ehci_dbg (ehci,
"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
label, params,
HCS_DEBUG_PORT (params),
HCS_INDICATOR (params) ? " ind" : "",
HCS_N_CC (params),
HCS_N_PCC (params),
HCS_PORTROUTED (params) ? "" : " ordered",
HCS_PPC (params) ? "" : " !ppc",
HCS_N_PORTS (params)
);
/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
if (HCS_PORTROUTED (params)) {
int i;
char buf [46], tmp [7], byte;
 
buf[0] = 0;
for (i = 0; i < HCS_N_PORTS (params); i++) {
byte = readb (&ehci->caps->portroute[(i>>1)]);
sprintf26(tmp, "%d ",
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
strcat(buf, tmp);
}
ehci_dbg (ehci, "%s portroute %s\n",
label, buf);
}
}
#else
 
static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
 
#endif
 
#ifdef DEBUG
 
/* check the values in the HCCPARAMS register
* (host controller _Capability_ parameters)
* see EHCI Spec, Table 2-5 for each value
* */
static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
{
u32 params = readl (&ehci->caps->hcc_params);
 
if (HCC_ISOC_CACHE (params)) {
ehci_dbg (ehci,
"%s hcc_params %04x caching frame %s%s%s\n",
label, params,
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
HCC_CANPARK (params) ? " park" : "",
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
} else {
ehci_dbg (ehci,
"%s hcc_params %04x thresh %d uframes %s%s%s\n",
label,
params,
HCC_ISOC_THRES (params),
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
HCC_CANPARK (params) ? " park" : "",
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
}
}
#else
 
static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
 
#endif
 
#ifdef DEBUG
 
static void __attribute__((__unused__))
dbg_qtd (char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
cpu_to_le32p (&qtd->hw_next),
cpu_to_le32p (&qtd->hw_alt_next),
cpu_to_le32p (&qtd->hw_token),
cpu_to_le32p (&qtd->hw_buf [0]));
if (qtd->hw_buf [1])
ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
cpu_to_le32p (&qtd->hw_buf [1]),
cpu_to_le32p (&qtd->hw_buf [2]),
cpu_to_le32p (&qtd->hw_buf [3]),
cpu_to_le32p (&qtd->hw_buf [4]));
}
 
static void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current);
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
 
static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{
return snprintf26(buf, len,
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", status,
(status & STS_ASS) ? " Async" : "",
(status & STS_PSS) ? " Periodic" : "",
(status & STS_RECL) ? " Recl" : "",
(status & STS_HALT) ? " Halt" : "",
(status & STS_IAA) ? " IAA" : "",
(status & STS_FATAL) ? " FATAL" : "",
(status & STS_FLR) ? " FLR" : "",
(status & STS_PCD) ? " PCD" : "",
(status & STS_ERR) ? " ERR" : "",
(status & STS_INT) ? " INT" : ""
);
}
 
static int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
{
return snprintf26(buf, len,
"%s%sintrenable %02x%s%s%s%s%s%s",
label, label [0] ? " " : "", enable,
(enable & STS_IAA) ? " IAA" : "",
(enable & STS_FATAL) ? " FATAL" : "",
(enable & STS_FLR) ? " FLR" : "",
(enable & STS_PCD) ? " PCD" : "",
(enable & STS_ERR) ? " ERR" : "",
(enable & STS_INT) ? " INT" : ""
);
}
 
static const char *const fls_strings [] =
{ "1024", "512", "256", "??" };
 
static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
{
return snprintf26(buf, len,
"%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
label, label [0] ? " " : "", command,
(command & CMD_PARK) ? "park" : "(park)",
CMD_PARK_CNT (command),
(command >> 16) & 0x3f,
(command & CMD_LRESET) ? " LReset" : "",
(command & CMD_IAAD) ? " IAAD" : "",
(command & CMD_ASE) ? " Async" : "",
(command & CMD_PSE) ? " Periodic" : "",
fls_strings [(command >> 2) & 0x3],
(command & CMD_RESET) ? " Reset" : "",
(command & CMD_RUN) ? "RUN" : "HALT"
);
}
 
static int
dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
{
char *sig;
 
/* signaling state */
switch (status & (3 << 10)) {
case 0 << 10: sig = "se0"; break;
case 1 << 10: sig = "k"; break; /* low speed */
case 2 << 10: sig = "j"; break;
default: sig = "?"; break;
}
 
return snprintf26(buf, len,
"%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", port, status,
(status & PORT_POWER) ? " POWER" : "",
(status & PORT_OWNER) ? " OWNER" : "",
sig,
(status & PORT_RESET) ? " RESET" : "",
(status & PORT_SUSPEND) ? " SUSPEND" : "",
(status & PORT_RESUME) ? " RESUME" : "",
(status & PORT_OCC) ? " OCC" : "",
(status & PORT_OC) ? " OC" : "",
(status & PORT_PEC) ? " PEC" : "",
(status & PORT_PE) ? " PE" : "",
(status & PORT_CSC) ? " CSC" : "",
(status & PORT_CONNECT) ? " CONNECT" : ""
);
}
 
#else
static inline void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{}
 
static inline int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{ return 0; }
 
static inline int __attribute__((__unused__))
dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
{ return 0; }
 
static inline int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
{ return 0; }
 
static inline int __attribute__((__unused__))
dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
{ return 0; }
 
#endif /* DEBUG */
 
/* functions have the "wrong" filename when they're output... */
#define dbg_status(ehci, label, status) { \
char _buf [80]; \
dbg_status_buf (_buf, sizeof _buf, label, status); \
ehci_dbg (ehci, "%s\n", _buf); \
}
 
#define dbg_cmd(ehci, label, command) { \
char _buf [80]; \
dbg_command_buf (_buf, sizeof _buf, label, command); \
ehci_dbg (ehci, "%s\n", _buf); \
}
 
#define dbg_port(ehci, label, port, status) { \
char _buf [80]; \
dbg_port_buf (_buf, sizeof _buf, label, port, status); \
ehci_dbg (ehci, "%s\n", _buf); \
}
 
/*-------------------------------------------------------------------------*/
 
#ifdef STUB_DEBUG_FILES
 
static inline void create_debug_files (struct ehci_hcd *bus) { }
static inline void remove_debug_files (struct ehci_hcd *bus) { }
 
#else
 
/* troubleshooting help: expose state in driverfs */
 
#define speed_char(info1) ({ char tmp; \
switch (info1 & (3 << 12)) { \
case 0 << 12: tmp = 'f'; break; \
case 1 << 12: tmp = 'l'; break; \
case 2 << 12: tmp = 'h'; break; \
default: tmp = '?'; break; \
}; tmp; })
 
static inline char token_mark (u32 token)
{
token = le32_to_cpu (token);
if (token & QTD_STS_ACTIVE)
return '*';
if (token & QTD_STS_HALT)
return '-';
if (!IS_SHORT_READ (token))
return ' ';
/* tries to advance through hw_alt_next */
return '/';
}
 
static void qh_lines (
struct ehci_hcd *ehci,
struct ehci_qh *qh,
char **nextp,
unsigned *sizep
)
{
u32 scratch;
u32 hw_curr;
struct list_head *entry;
struct ehci_qtd *td;
unsigned temp;
unsigned size = *sizep;
char *next = *nextp;
char mark;
 
if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
mark = '@';
else
mark = token_mark (qh->hw_token);
if (mark == '/') { /* qh_alt_next controls qh advance? */
if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
mark = '#'; /* blocked */
else if (qh->hw_alt_next == EHCI_LIST_END)
mark = '.'; /* use hw_qtd_next */
/* else alt_next points to some other qtd */
}
scratch = cpu_to_le32p (&qh->hw_info1);
hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0;
temp = snprintf26(next, size,
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f,
scratch, cpu_to_le32p (&qh->hw_info2),
cpu_to_le32p (&qh->hw_token), mark,
(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
? "data0" : "data1",
(cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
size -= temp;
next += temp;
 
/* hc may be modifying the list as we read it ... */
list_for_each (entry, &qh->qtd_list) {
td = list_entry (entry, struct ehci_qtd, qtd_list);
scratch = cpu_to_le32p (&td->hw_token);
mark = ' ';
if (hw_curr == td->qtd_dma)
mark = '*';
else if (qh->hw_qtd_next == td->qtd_dma)
mark = '+';
else if (QTD_LENGTH (scratch)) {
if (td->hw_alt_next == ehci->async->hw_alt_next)
mark = '#';
else if (td->hw_alt_next != EHCI_LIST_END)
mark = '/';
}
temp = snprintf26(next, size,
"\n\t%p%c%s len=%d %08x urb %p",
td, mark, ({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0: tmp = "out"; break;
case 1: tmp = "in"; break;
case 2: tmp = "setup"; break;
default: tmp = "?"; break;
} tmp;}),
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
if (temp < 0)
temp = 0;
else if (size < temp)
temp = size;
size -= temp;
next += temp;
if (temp == size)
goto done;
}
 
temp = snprintf26(next, size, "\n");
if (temp < 0)
temp = 0;
else if (size < temp)
temp = size;
size -= temp;
next += temp;
 
done:
*sizep = size;
*nextp = next;
}
 
static ssize_t
show_async (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size;
char *next;
struct ehci_qh *qh;
 
*buf = 0;
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
 
/* dumps a snapshot of the async schedule.
* usually empty except for long-term bulk reads, or head.
* one QH per line, and TDs we know about
*/
spin_lock_irqsave (&ehci->lock, flags);
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
qh_lines (ehci, qh, &next, &size);
if (ehci->reclaim && size > 0) {
temp = snprintf26(next, size, "\nreclaim =\n");
size -= temp;
next += temp;
 
for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
qh_lines (ehci, qh, &next, &size);
}
spin_unlock_irqrestore (&ehci->lock, flags);
 
return strlen (buf);
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
 
#define DBG_SCHED_LIMIT 64
 
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
union ehci_shadow p, *seen;
unsigned temp, size, seen_count;
char *next;
unsigned i, tag;
 
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
 
temp = snprintf26(next, size, "size = %d\n", ehci->periodic_size);
size -= temp;
next += temp;
 
/* dump a snapshot of the periodic schedule.
* iso changes, interrupt usually doesn't.
*/
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ehci->periodic_size; i++) {
p = ehci->pshadow [i];
if (likely (!p.ptr))
continue;
tag = Q_NEXT_TYPE (ehci->periodic [i]);
 
temp = snprintf26(next, size, "%4d: ", i);
size -= temp;
next += temp;
 
do {
switch (tag) {
case Q_TYPE_QH:
temp = snprintf26(next, size, " qh%d-%04x/%p",
p.qh->period,
le32_to_cpup (&p.qh->hw_info2)
/* uframe masks */
& 0xffff,
p.qh);
size -= temp;
next += temp;
/* don't repeat what follows this qh */
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp].ptr != p.ptr)
continue;
if (p.qh->qh_next.ptr)
temp = snprintf26(next, size,
" ...");
p.ptr = 0;
break;
}
/* show more info the first time around */
if (temp == seen_count && p.ptr) {
u32 scratch = cpu_to_le32p (
&p.qh->hw_info1);
struct ehci_qtd *qtd;
char *type = "";
 
/* count tds, get ep direction */
temp = 0;
list_for_each_entry (qtd,
&p.qh->qtd_list,
qtd_list) {
temp++;
switch (0x03 & (le32_to_cpu (
qtd->hw_token) >> 8)) {
case 0: type = "out"; continue;
case 1: type = "in"; continue;
}
}
 
temp = snprintf26(next, size,
" (%c%d ep%d%s "
"[%d/%d] q%d p%d)",
speed_char (scratch),
scratch & 0x007f,
(scratch >> 8) & 0x000f, type,
p.qh->usecs, p.qh->c_usecs,
temp,
0x7ff & (scratch >> 16));
 
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++].qh = p.qh;
} else
temp = 0;
if (p.qh) {
tag = Q_NEXT_TYPE (p.qh->hw_next);
p = p.qh->qh_next;
}
break;
case Q_TYPE_FSTN:
temp = snprintf26(next, size,
" fstn-%8x/%p", p.fstn->hw_prev,
p.fstn);
tag = Q_NEXT_TYPE (p.fstn->hw_next);
p = p.fstn->fstn_next;
break;
case Q_TYPE_ITD:
temp = snprintf26(next, size,
" itd/%p", p.itd);
tag = Q_NEXT_TYPE (p.itd->hw_next);
p = p.itd->itd_next;
break;
case Q_TYPE_SITD:
temp = snprintf26(next, size,
" sitd/%p", p.sitd);
tag = Q_NEXT_TYPE (p.sitd->hw_next);
p = p.sitd->sitd_next;
break;
}
size -= temp;
next += temp;
} while (p.ptr);
 
temp = snprintf26(next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ehci->lock, flags);
kfree (seen);
 
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
 
#undef DBG_SCHED_LIMIT
 
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size, i;
char *next, scratch [80];
static char fmt [] = "%*s\n";
static char label [] = "";
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
 
spin_lock_irqsave (&ehci->lock, flags);
 
/* Capability Registers */
i = readw (&ehci->caps->hci_version);
temp = snprintf26(next, size,
"PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n",
pci_name(hcd->pdev),
i >> 8, i & 0x0ff, ehci->hcd.state);
size -= temp;
next += temp;
 
// FIXME interpret both types of params
i = readl (&ehci->caps->hcs_params);
temp = snprintf26(next, size, "structural params 0x%08x\n", i);
size -= temp;
next += temp;
 
i = readl (&ehci->caps->hcc_params);
temp = snprintf26(next, size, "capability params 0x%08x\n", i);
size -= temp;
next += temp;
 
/* Operational Registers */
temp = dbg_status_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->status));
temp = snprintf26(next, size, fmt, temp, scratch);
size -= temp;
next += temp;
 
temp = dbg_command_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->command));
temp = snprintf26(next, size, fmt, temp, scratch);
size -= temp;
next += temp;
 
temp = dbg_intr_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->intr_enable));
temp = snprintf26(next, size, fmt, temp, scratch);
size -= temp;
next += temp;
 
temp = snprintf26(next, size, "uframe %04x\n",
readl (&ehci->regs->frame_index));
size -= temp;
next += temp;
 
for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
temp = dbg_port_buf (scratch, sizeof scratch, label, i,
readl (&ehci->regs->port_status [i]));
temp = snprintf26(next, size, fmt, temp, scratch);
size -= temp;
next += temp;
}
 
if (ehci->reclaim) {
temp = snprintf26(next, size, "reclaim qh %p%s\n",
ehci->reclaim,
ehci->reclaim_ready ? " ready" : "");
size -= temp;
next += temp;
}
 
#ifdef EHCI_STATS
temp = snprintf26(next, size,
"irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
size -= temp;
next += temp;
 
temp = snprintf26(next, size, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink);
size -= temp;
next += temp;
#endif
 
spin_unlock_irqrestore (&ehci->lock, flags);
 
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 
static inline void create_debug_files (struct ehci_hcd *bus)
{
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_async);
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_periodic);
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_registers);
}
 
static inline void remove_debug_files (struct ehci_hcd *bus)
{
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_async);
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_periodic);
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_registers);
}
 
#endif /* STUB_DEBUG_FILES */
 
/shark/trunk/drivers/usb/host/uhci-hub.c
0,0 → 1,184
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
*/
 
static __u8 root_hub_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
0x00, /* __u16 wHubCharacteristics; */
0x00,
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
 
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned int io_addr = uhci->io_addr;
int i, len = 1;
 
*buf = 0;
for (i = 0; i < uhci->rh_numports; i++) {
*buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
len = (i + 1) / 8 + 1;
}
 
return !!*buf;
}
 
#define OK(x) len = (x); break
 
#define CLR_RH_PORTSTAT(x) \
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
status = (status & 0xfff5) & ~(x); \
outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
 
#define SET_RH_PORTSTAT(x) \
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
status = (status & 0xfff5) | (x); \
outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
 
 
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int i, status, retval = 0, len = 0;
unsigned int io_addr = uhci->io_addr;
__u16 cstatus;
char c_p_r[8];
 
for (i = 0; i < 8; i++)
c_p_r[i] = 0;
 
switch (typeReq) {
/* Request Destination:
without flags: Device,
RH_INTERFACE: interface,
RH_ENDPOINT: endpoint,
RH_CLASS means HUB here,
RH_OTHER | RH_CLASS almost ever means HUB_PORT here
*/
 
case GetHubStatus:
*(__u32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
((status & USBPORTSC_PEC) >> (3 - 1)) |
(c_p_r[wIndex - 1] << (0 + 4));
status = (status & USBPORTSC_CCS) |
((status & USBPORTSC_PE) >> (2 - 1)) |
((status & USBPORTSC_SUSP) >> (12 - 2)) |
((status & USBPORTSC_PR) >> (9 - 4)) |
(1 << 8) | /* power on */
((status & USBPORTSC_LSDA) << (-8 + 9));
 
*(__u16 *)buf = cpu_to_le16(status);
*(__u16 *)(buf + 2) = cpu_to_le16(cstatus);
OK(4);
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
break;
default:
goto err;
}
break;
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
OK(0); /* hub power over current */
default:
goto err;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > uhci->rh_numports)
goto err;
 
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR);
mdelay(50); /* USB v1.1 7.1.7.3 */
c_p_r[wIndex - 1] = 1;
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
SET_RH_PORTSTAT(USBPORTSC_PE);
mdelay(10);
SET_RH_PORTSTAT(0xa);
OK(0);
case USB_PORT_FEAT_POWER:
OK(0); /* port power ** */
case USB_PORT_FEAT_ENABLE:
SET_RH_PORTSTAT(USBPORTSC_PE);
OK(0);
default:
goto err;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > uhci->rh_numports)
goto err;
 
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
SET_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
CLR_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
OK(0);
case USB_PORT_FEAT_POWER:
OK(0); /* port power */
case USB_PORT_FEAT_C_CONNECTION:
SET_RH_PORTSTAT(USBPORTSC_CSC);
OK(0);
case USB_PORT_FEAT_C_OVER_CURRENT:
OK(0); /* port power over current */
case USB_PORT_FEAT_C_RESET:
c_p_r[wIndex - 1] = 0;
OK(0);
default:
goto err;
}
break;
case GetHubDescriptor:
len = min_t(unsigned int, wLength,
min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
memcpy(buf, root_hub_hub_des, len);
if (len > 2)
buf[2] = uhci->rh_numports;
OK(len);
default:
err:
retval = -EPIPE;
}
 
return retval;
}
 
/shark/trunk/drivers/usb/host/ehci-hcd.c
0,0 → 1,1032
/*
* Copyright (c) 2000-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
 
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/usb.h>
#include <linux/moduleparam.h>
 
#include "../core/hcd.h"
 
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
 
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI hc_driver implementation ... experimental, incomplete.
* Based on the final 1.0 register interface specification.
*
* USB 2.0 shows up in upcoming www.pcmcia.org technology.
* First was PCMCIA, like ISA; then CardBus, which is PCI.
* Next comes "CardBay", using USB 2.0 signals.
*
* Contains additional contributions by Brad Hards, Rory Bolt, and others.
* Special thanks to Intel and VIA for providing host controllers to
* test this driver on, and Cypress (including In-System Design) for
* providing early devices for those host controllers to talk to!
*
* HISTORY:
*
* 2002-11-29 Correct handling for hw async_next register.
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations.
* 2002-07-25 Sanity check PCI reads, mostly for better cardbus support,
* clean up HC run state handshaking.
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
* 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other
* missing pieces: enabling 64bit dma, handoff from BIOS/SMM.
* 2002-05-07 Some error path cleanups to report better errors; wmb();
* use non-CVS version id; better iso bandwidth claim.
* 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on
* errors in submit path. Bugfixes to interrupt scheduling/processing.
* 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift
* more checking to generic hcd framework (db). Make it work with
* Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
* 2002-01-14 Minor cleanup; version synch.
* 2002-01-08 Fix roothub handoff of FS/LS to companion controllers.
* 2002-01-04 Control/Bulk queuing behaves.
*
* 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
* 2001-June Works with usb-storage and NEC EHCI on 2.4
*/
 
#define DRIVER_VERSION "2003-Jun-13"
#define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
static const char hcd_name [] = "ehci_hcd";
 
 
// #define EHCI_VERBOSE_DEBUG
// #define have_split_iso
 
#ifdef DEBUG
#define EHCI_STATS
#endif
 
/* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
 
#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
 
/* Initial IRQ latency: lower than default */
static int log2_irq_thresh = 0; // 0 to 6
module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
 
#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
 
/*-------------------------------------------------------------------------*/
 
#include "ehci.h"
#include "ehci-dbg.c"
 
/*-------------------------------------------------------------------------*/
 
/*
* handshake - spin reading hc until handshake completes or fails
* @ptr: address of hc register to be read
* @mask: bits to look at in result of read
* @done: value of those bits when handshake succeeds
* @usec: timeout in microseconds
*
* Returns negative errno, or zero on success
*
* Success happens when the "mask" bits have the specified value (hardware
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*
* That last failure should_only happen in cases like physical cardbus eject
* before driver shutdown. But it also seems to be caused by bugs in cardbus
* bridge shutdown: shutting down the bridge before the devices using it.
*/
static int handshake (u32 *ptr, u32 mask, u32 done, int usec)
{
u32 result;
 
do {
result = readl (ptr);
if (result == ~(u32)0) /* card removed */
return -ENODEV;
result &= mask;
if (result == done)
return 0;
udelay (1);
usec--;
} while (usec > 0);
return -ETIMEDOUT;
}
 
/*
* hc states include: unknown, halted, ready, running
* transitional states are messy just now
* trying to avoid "running" unless urbs are active
* a "ready" hc can be finishing prefetched work
*/
 
/* force HC to halt state from unknown (EHCI spec section 2.3) */
static int ehci_halt (struct ehci_hcd *ehci)
{
u32 temp = readl (&ehci->regs->status);
 
if ((temp & STS_HALT) != 0)
return 0;
 
temp = readl (&ehci->regs->command);
temp &= ~CMD_RUN;
writel (temp, &ehci->regs->command);
return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125);
}
 
/* reset a non-running (STS_HALT == 1) controller */
static int ehci_reset (struct ehci_hcd *ehci)
{
u32 command = readl (&ehci->regs->command);
 
command |= CMD_RESET;
dbg_cmd (ehci, "reset", command);
writel (command, &ehci->regs->command);
ehci->hcd.state = USB_STATE_HALT;
return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
}
 
/* idle the controller (from running) */
static void ehci_ready (struct ehci_hcd *ehci)
{
u32 temp;
 
#ifdef DEBUG
if (!HCD_IS_RUNNING (ehci->hcd.state))
BUG ();
#endif
 
/* wait for any schedule enables/disables to take effect */
temp = 0;
if (ehci->async->qh_next.qh)
temp = STS_ASS;
if (ehci->next_uframe != -1)
temp |= STS_PSS;
if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
temp, 16 * 125) != 0) {
ehci->hcd.state = USB_STATE_HALT;
return;
}
 
/* then disable anything that's still active */
temp = readl (&ehci->regs->command);
temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
writel (temp, &ehci->regs->command);
 
/* hardware can take 16 microframes to turn off ... */
if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
0, 16 * 125) != 0) {
ehci->hcd.state = USB_STATE_HALT;
return;
}
}
 
/*-------------------------------------------------------------------------*/
 
#include "ehci-hub.c"
#include "ehci-mem.c"
#include "ehci-q.c"
#include "ehci-sched.c"
 
/*-------------------------------------------------------------------------*/
 
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
static void ehci_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
 
spin_lock_irqsave (&ehci->lock, flags);
 
/* lost IAA irqs wedge things badly; seen with a vt8235 */
if (ehci->reclaim) {
u32 status = readl (&ehci->regs->status);
 
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
ehci->reclaim_ready = 1;
}
}
 
/* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
start_unlink_async (ehci, ehci->async);
 
/* ehci could run by timer, without IRQs ... */
ehci_work (ehci, NULL);
 
spin_unlock_irqrestore (&ehci->lock, flags);
}
 
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
* off the controller (maybe it can boot from highspeed USB disks).
*/
static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
{
if (cap & (1 << 16)) {
int msec = 500;
 
/* request handoff to OS */
cap &= 1 << 24;
pci_write_config_dword (ehci->hcd.pdev, where, cap);
 
/* and wait a while for it to happen */
do {
wait_ms (10);
msec -= 10;
pci_read_config_dword (ehci->hcd.pdev, where, &cap);
} while ((cap & (1 << 16)) && msec);
if (cap & (1 << 16)) {
ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
where, cap);
return 1;
}
ehci_dbg (ehci, "BIOS handoff succeeded\n");
}
return 0;
}
 
static int
ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
{
struct ehci_hcd *ehci;
 
ehci = container_of (self, struct ehci_hcd, reboot_notifier);
 
/* make BIOS/etc use companion controller during reboot */
writel (0, &ehci->regs->configured_flag);
return 0;
}
 
 
/* called by khubd or root hub init threads */
 
static int ehci_hc_reset (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
 
spin_lock_init (&ehci->lock);
 
ehci->caps = (struct ehci_caps *) hcd->regs;
ehci->regs = (struct ehci_regs *) (hcd->regs +
readb (&ehci->caps->length));
dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset");
 
/* EHCI 0.96 and later may have "extended capabilities" */
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
while (temp) {
u32 cap;
 
pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
switch (cap & 0xff) {
case 1: /* BIOS/SMM/... handoff */
if (bios_handoff (ehci, temp, cap) != 0)
return -EOPNOTSUPP;
break;
case 0: /* illegal reserved capability */
ehci_warn (ehci, "illegal capability!\n");
cap = 0;
/* FALLTHROUGH */
default: /* unknown */
break;
}
temp = (cap >> 8) & 0xff;
}
 
/* cache this readonly data; minimize PCI reads */
ehci->hcs_params = readl (&ehci->caps->hcs_params);
 
/* force HC to halt state */
return ehci_halt (ehci);
}
 
static int ehci_start (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
struct usb_device *udev;
struct usb_bus *bus;
int retval;
u32 hcc_params;
u8 tempbyte;
 
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
*/
ehci->periodic_size = DEFAULT_I_TDPS;
if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0)
return retval;
 
/* controllers may cache some of the periodic schedule ... */
hcc_params = readl (&ehci->caps->hcc_params);
if (HCC_ISOC_CACHE (hcc_params)) // full frame cache
ehci->i_thresh = 8;
else // N microframes cached
ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
 
ehci->reclaim = 0;
ehci->next_uframe = -1;
 
/* controller state: unknown --> reset */
 
/* EHCI spec section 4.1 */
if ((retval = ehci_reset (ehci)) != 0) {
ehci_mem_cleanup (ehci);
return retval;
}
writel (INTR_MASK, &ehci->regs->intr_enable);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
 
/*
* dedicate a qh for the async ring head, since we couldn't unlink
* a 'real' qh without stopping the async schedule [4.8]. use it
* as the 'reclamation list head' too.
* its dummy is used in hw_alt_next of many tds, to prevent the qh
* from automatically advancing to the next td after short reads.
*/
ehci->async->qh_next.qh = 0;
ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
ehci->async->hw_qtd_next = EHCI_LIST_END;
ehci->async->qh_state = QH_STATE_LINKED;
ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
 
/*
* hcc_params controls whether ehci->regs->segment must (!!!)
* be used; it constrains QH/ITD/SITD and QTD locations.
* pci_pool consistent memory always uses segment zero.
* streaming mappings for I/O buffers, like pci_map_single(),
* can return segments above 4GB, if the device allows.
*
* NOTE: the dma mask is visible through dma_supported(), so
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
* Scsi_Host.highmem_io, and so forth. It's readonly to all
* host side drivers though.
*/
if (HCC_64BIT_ADDR (hcc_params)) {
writel (0, &ehci->regs->segment);
#if 0
// this is deeply broken on almost all architectures
if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
ehci_info (ehci, "enabled 64bit PCI DMA\n");
#endif
}
 
/* help hc dma work well with cachelines */
pci_set_mwi (ehci->hcd.pdev);
 
/* clear interrupt enables, set irq latency */
temp = readl (&ehci->regs->command) & 0x0fff;
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp |= 1 << (16 + log2_irq_thresh);
// if hc can park (ehci >= 0.96), default is 3 packets per async QH
if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
/* periodic schedule size can be smaller than default */
temp &= ~(3 << 2);
temp |= (EHCI_TUNE_FLS << 2);
switch (EHCI_TUNE_FLS) {
case 0: ehci->periodic_size = 1024; break;
case 1: ehci->periodic_size = 512; break;
case 2: ehci->periodic_size = 256; break;
default: BUG ();
}
}
temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
// Philips, Intel, and maybe others need CMD_RUN before the
// root hub will detect new devices (why?); NEC doesn't
temp |= CMD_RUN;
writel (temp, &ehci->regs->command);
dbg_cmd (ehci, "init", temp);
 
/* set async sleep time = 10 us ... ? */
 
init_timer (&ehci->watchdog);
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
 
/* wire up the root hub */
bus = hcd_to_bus (hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus);
if (!udev) {
done2:
ehci_mem_cleanup (ehci);
return -ENOMEM;
}
 
/*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is
* involved with the root hub.
*/
ehci->reboot_notifier.notifier_call = ehci_reboot;
register_reboot_notifier (&ehci->reboot_notifier);
 
ehci->hcd.state = USB_STATE_RUNNING;
writel (FLAG_CF, &ehci->regs->configured_flag);
readl (&ehci->regs->command); /* unblock posted write */
 
/* PCI Serial Bus Release Number is at 0x60 offset */
pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
temp = readw (&ehci->caps->hci_version);
ehci_info (ehci,
"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
temp >> 8, temp & 0xff, DRIVER_VERSION);
 
/*
* From here on, khubd concurrently accesses the root
* hub; drivers will be talking to enumerated devices.
*
* Before this point the HC was idle/ready. After, khubd
* and device drivers may start it running.
*/
udev->speed = USB_SPEED_HIGH;
if (hcd_register_root (hcd) != 0) {
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
ehci_reset (ehci);
bus->root_hub = 0;
usb_put_dev (udev);
retval = -ENODEV;
goto done2;
}
 
create_debug_files (ehci);
 
return 0;
}
 
/* always called by thread; normally rmmod */
 
static void ehci_stop (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
 
ehci_dbg (ehci, "stop\n");
 
/* no more interrupts ... */
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
if (in_interrupt ()) { /* must not happen!! */
ehci_err (ehci, "stopped in_interrupt!\n");
return;
}
del_timer_sync (&ehci->watchdog);
ehci_reset (ehci);
 
/* let companion controllers work when we aren't */
writel (0, &ehci->regs->configured_flag);
unregister_reboot_notifier (&ehci->reboot_notifier);
 
remove_debug_files (ehci);
 
/* root hub is shut down separately (first, when possible) */
spin_lock_irq (&ehci->lock);
ehci_work (ehci, NULL);
spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci);
 
#ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
ehci_dbg (ehci, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink);
#endif
 
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
}
 
static int ehci_get_frame (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
}
 
/*-------------------------------------------------------------------------*/
 
#ifdef CONFIG_PM
 
/* suspend/resume, section 4.3 */
 
static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports;
int i;
 
ehci_dbg (ehci, "suspend to %d\n", state);
 
ports = HCS_N_PORTS (ehci->hcs_params);
 
// FIXME: This assumes what's probably a D3 level suspend...
 
// FIXME: usb wakeup events on this bus should resume the machine.
// pci config register PORTWAKECAP controls which ports can do it;
// bios may have initted the register...
 
/* suspend each port, then stop the hc */
for (i = 0; i < ports; i++) {
int temp = readl (&ehci->regs->port_status [i]);
 
if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0)
continue;
ehci_dbg (ehci, "suspend port %d", i);
temp |= PORT_SUSPEND;
writel (temp, &ehci->regs->port_status [i]);
}
 
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
 
// save pci FLADJ value
 
/* who tells PCI to reduce power consumption? */
 
return 0;
}
 
static int ehci_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports;
int i;
 
ehci_dbg (ehci, "resume\n");
 
ports = HCS_N_PORTS (ehci->hcs_params);
 
// FIXME: if controller didn't retain state,
// return and let generic code clean it up
// test configured_flag ?
 
/* resume HC and each port */
// restore pci FLADJ value
// khubd and drivers will set HC running, if needed;
hcd->state = USB_STATE_RUNNING;
// FIXME Philips/Intel/... etc don't really have a "READY"
// state ... turn on CMD_RUN too
for (i = 0; i < ports; i++) {
int temp = readl (&ehci->regs->port_status [i]);
 
if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0)
continue;
ehci_dbg (ehci, "resume port %d", i);
temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */
 
wait_ms (20);
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
}
readl (&ehci->regs->command); /* unblock posted writes */
return 0;
}
 
#endif
 
/*-------------------------------------------------------------------------*/
 
/*
* ehci_work is called from some interrupts, timers, and so on.
* it calls driver completion functions, after dropping ehci->lock.
*/
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
if (ehci->reclaim_ready)
end_unlink_async (ehci, regs);
scan_async (ehci, regs);
if (ehci->next_uframe != -1)
scan_periodic (ehci, regs);
 
/* the IO watchdog guards against hardware or driver bugs that
* misplace IRQs, and should let us run completely without IRQs.
*/
if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0))
timer_action (ehci, TIMER_IO_WATCHDOG);
}
 
/*-------------------------------------------------------------------------*/
 
static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 status;
int bh;
 
spin_lock (&ehci->lock);
 
status = readl (&ehci->regs->status);
 
/* e.g. cardbus physical eject */
if (status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n");
goto dead;
}
 
status &= INTR_MASK;
if (!status) /* irq sharing? */
goto done;
 
/* clear (just) interrupts */
writel (status, &ehci->regs->status);
readl (&ehci->regs->command); /* unblock posted write */
bh = 0;
 
#ifdef EHCI_VERBOSE_DEBUG
/* unrequested/ignored: Port Change Detect, Frame List Rollover */
dbg_status (ehci, "irq", status);
#endif
 
/* INT, ERR, and IAA interrupt rates can be throttled */
 
/* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
if (likely ((status & STS_ERR) == 0))
COUNT (ehci->stats.normal);
else
COUNT (ehci->stats.error);
bh = 1;
}
 
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
ehci->reclaim_ready = 1;
bh = 1;
}
 
/* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) {
ehci_err (ehci, "fatal error\n");
dead:
ehci_reset (ehci);
/* generic layer kills/unlinks all urbs, then
* uses ehci_stop to clean up the rest
*/
bh = 1;
}
 
if (bh)
ehci_work (ehci, regs);
done:
spin_unlock (&ehci->lock);
}
 
/*-------------------------------------------------------------------------*/
 
/*
* non-error returns are a promise to giveback() the urb later
* we drop ownership so next owner (or urb unlink) can get it
*
* urb + dev is in hcd_dev.urb_list
* we're queueing TDs onto software and hardware lists
*
* hcd-specific init for hcpriv hasn't been done yet
*
* NOTE: control, bulk, and interrupt share the same code to append TDs
* to a (possibly active) QH, and the same QH scanning code.
*/
static int ehci_urb_enqueue (
struct usb_hcd *hcd,
struct urb *urb,
int mem_flags
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct list_head qtd_list;
 
urb->transfer_flags &= ~EHCI_STATE_UNLINK;
INIT_LIST_HEAD (&qtd_list);
 
switch (usb_pipetype (urb->pipe)) {
// case PIPE_CONTROL:
// case PIPE_BULK:
default:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
return submit_async (ehci, urb, &qtd_list, mem_flags);
 
case PIPE_INTERRUPT:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
return intr_submit (ehci, urb, &qtd_list, mem_flags);
 
case PIPE_ISOCHRONOUS:
if (urb->dev->speed == USB_SPEED_HIGH)
return itd_submit (ehci, urb, mem_flags);
#ifdef have_split_iso
else
return sitd_submit (ehci, urb, mem_flags);
#else
dbg ("no split iso support yet");
return -ENOSYS;
#endif /* have_split_iso */
}
}
 
/* remove from hardware lists
* completions normally happen asynchronously
*/
 
static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct ehci_qh *qh;
unsigned long flags;
 
spin_lock_irqsave (&ehci->lock, flags);
switch (usb_pipetype (urb->pipe)) {
// case PIPE_CONTROL:
// case PIPE_BULK:
default:
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
 
/* if we need to use IAA and it's busy, defer */
if (qh->qh_state == QH_STATE_LINKED
&& ehci->reclaim
&& HCD_IS_RUNNING (ehci->hcd.state)
) {
struct ehci_qh *last;
 
for (last = ehci->reclaim;
last->reclaim;
last = last->reclaim)
continue;
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;
 
/* bypass IAA if the hc can't care */
} else if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
end_unlink_async (ehci, NULL);
 
/* something else might have unlinked the qh by now */
if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
break;
 
case PIPE_INTERRUPT:
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
if (qh->qh_state == QH_STATE_LINKED) {
/* messy, can spin or block a microframe ... */
intr_deschedule (ehci, qh, 1);
/* qh_state == IDLE */
}
qh_completions (ehci, qh, NULL);
 
/* reschedule QH iff another request is queued */
if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) {
int status;
 
status = qh_schedule (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags);
 
if (status != 0) {
// shouldn't happen often, but ...
// FIXME kill those tds' urbs
err ("can't reschedule qh %p, err %d",
qh, status);
}
return status;
}
break;
 
case PIPE_ISOCHRONOUS:
// itd or sitd ...
 
// wait till next completion, do it then.
// completion irqs can wait up to 1024 msec,
urb->transfer_flags |= EHCI_STATE_UNLINK;
break;
}
spin_unlock_irqrestore (&ehci->lock, flags);
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
// bulk qh holds the data toggle
 
static void
ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int epnum;
unsigned long flags;
struct ehci_qh *qh;
 
/* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */
 
epnum = ep & USB_ENDPOINT_NUMBER_MASK;
if (epnum != 0 && (ep & USB_DIR_IN))
epnum |= 0x10;
 
rescan:
spin_lock_irqsave (&ehci->lock, flags);
qh = (struct ehci_qh *) dev->ep [epnum];
if (!qh)
goto done;
 
if (!HCD_IS_RUNNING (ehci->hcd.state))
qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) {
case QH_STATE_UNLINK: /* wait for hw to finish? */
spin_unlock_irqrestore (&ehci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1);
goto rescan;
case QH_STATE_IDLE: /* fully unlinked */
if (list_empty (&qh->qtd_list)) {
qh_put (ehci, qh);
break;
}
/* else FALL THROUGH */
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. just leak this memory.
*/
ehci_err (ehci, "qh %p (#%d) state %d%s\n",
qh, epnum, qh->qh_state,
list_empty (&qh->qtd_list) ? "" : "(has tds)");
break;
}
dev->ep [epnum] = 0;
done:
spin_unlock_irqrestore (&ehci->lock, flags);
return;
}
 
/*-------------------------------------------------------------------------*/
 
static const struct hc_driver ehci_driver = {
.description = hcd_name,
 
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
 
/*
* basic lifecycle operations
*/
.reset = ehci_hc_reset,
.start = ehci_start,
#ifdef CONFIG_PM
.suspend = ehci_suspend,
.resume = ehci_resume,
#endif
.stop = ehci_stop,
 
/*
* memory lifecycle (except per-request)
*/
.hcd_alloc = ehci_hcd_alloc,
.hcd_free = ehci_hcd_free,
 
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
 
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
 
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
};
 
/*-------------------------------------------------------------------------*/
 
/* EHCI spec says PCI is required. */
 
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
.driver_data = (unsigned long) &ehci_driver,
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
 
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ehci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
 
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
 
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
};
 
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_LICENSE ("GPL");
 
static int __init init (void)
{
if (usb_disabled())
return -ENODEV;
 
pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
hcd_name,
sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
 
return pci_module_init (&ehci_pci_driver);
}
module_init (init);
 
static void __exit cleanup (void)
{
pci_unregister_driver (&ehci_pci_driver);
}
module_exit (cleanup);
/shark/trunk/drivers/usb/host/ehci-q.c
0,0 → 1,1070
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* this file is part of ehci-hcd.c */
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI hardware queue manipulation ... the core. QH/QTD manipulation.
*
* Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
* entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
* buffers needed for the larger number). We use one QH per endpoint, queue
* multiple urbs (all three types) per endpoint. URBs may need several qtds.
*
* ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
* interrupts) needs careful scheduling. Performance improvements can be
* an ongoing challenge. That's in "ehci-sched.c".
*
* USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
* or otherwise through transaction translators (TTs) in USB 2.0 hubs using
* (b) special fields in qh entries or (c) split iso entries. TTs will
* buffer low/full speed data so the host collects it at high speed.
*/
 
/*-------------------------------------------------------------------------*/
 
/* fill a qtd, returning how much of the buffer we were able to queue up */
 
static int
qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
int token, int maxpacket)
{
int i, count;
u64 addr = buf;
 
/* one buffer entry per 4K ... first might be short or unaligned */
qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
count = 0x1000 - (buf & 0x0fff); /* rest of that page */
if (likely (len < count)) /* ... iff needed */
count = len;
else {
buf += 0x1000;
buf &= ~0x0fff;
 
/* per-qtd limit: from 16K to 20K (best alignment) */
for (i = 1; count < len && i < 5; i++) {
addr = buf;
qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
buf += 0x1000;
if ((count + 0x1000) < len)
count += 0x1000;
else
count = len;
}
 
/* short packets may only terminate transfers */
if (count != len)
count -= (count % maxpacket);
}
qtd->hw_token = cpu_to_le32 ((count << 16) | token);
qtd->length = count;
 
return count;
}
 
/*-------------------------------------------------------------------------*/
 
/* update halted (but potentially linked) qh */
 
static inline void
qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
{
qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
qh->hw_alt_next = EHCI_LIST_END;
 
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
}
 
/*-------------------------------------------------------------------------*/
 
static void qtd_copy_status (
struct ehci_hcd *ehci,
struct urb *urb,
size_t length,
u32 token
)
{
/* count IN/OUT bytes, not SETUP (even short packets) */
if (likely (QTD_PID (token) != 2))
urb->actual_length += length - QTD_LENGTH (token);
 
/* don't modify error codes */
if (unlikely (urb->status != -EINPROGRESS))
return;
 
/* force cleanup after short read; not always an error */
if (unlikely (IS_SHORT_READ (token)))
urb->status = -EREMOTEIO;
 
/* serious "can't proceed" faults reported by the hardware */
if (token & QTD_STS_HALT) {
if (token & QTD_STS_BABBLE) {
/* FIXME "must" disable babbling device's port too */
urb->status = -EOVERFLOW;
} else if (token & QTD_STS_MMF) {
/* fs/ls interrupt xfer missed the complete-split */
urb->status = -EPROTO;
} else if (token & QTD_STS_DBE) {
urb->status = (QTD_PID (token) == 1) /* IN ? */
? -ENOSR /* hc couldn't read data */
: -ECOMM; /* hc couldn't write data */
} else if (token & QTD_STS_XACT) {
/* timeout, bad crc, wrong PID, etc; retried */
if (QTD_CERR (token))
urb->status = -EPIPE;
else {
ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n",
urb->dev->devpath,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out");
urb->status = -EPROTO;
}
/* CERR nonzero + no errors + halt --> stall */
} else if (QTD_CERR (token))
urb->status = -EPIPE;
else /* unknown */
urb->status = -EPROTO;
 
ehci_vdbg (ehci,
"dev%d ep%d%s qtd token %08x --> status %d\n",
usb_pipedevice (urb->pipe),
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
token, urb->status);
 
/* stall indicates some recovery action is needed */
if (urb->status == -EPIPE) {
int pipe = urb->pipe;
 
if (!usb_pipecontrol (pipe))
usb_endpoint_halt (urb->dev,
usb_pipeendpoint (pipe),
usb_pipeout (pipe));
 
/* if async CSPLIT failed, try cleaning out the TT buffer */
} else if (urb->dev->tt && !usb_pipeint (urb->pipe)
&& QTD_CERR(token) == 0) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg (&tt->dev,
"clear tt buffer port %d, a%d ep%d t%08x\n",
urb->dev->ttport, urb->dev->devnum,
usb_pipeendpoint (urb->pipe), token);
#endif /* DEBUG */
usb_hub_tt_clear_buffer (urb->dev, urb->pipe);
}
}
}
 
static void
ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
{
if (likely (urb->hcpriv != 0)) {
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
 
/* S-mask in a QH means it's an interrupt urb */
if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) {
 
/* ... update hc-wide periodic stats (for usbfs) */
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
}
qh_put (ehci, qh);
}
 
spin_lock (&urb->lock);
urb->hcpriv = 0;
switch (urb->status) {
case -EINPROGRESS: /* success */
urb->status = 0;
default: /* fault */
COUNT (ehci->stats.complete);
break;
case -EREMOTEIO: /* fault or normal */
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
urb->status = 0;
COUNT (ehci->stats.complete);
break;
case -ECONNRESET: /* canceled */
case -ENOENT:
COUNT (ehci->stats.unlink);
break;
}
spin_unlock (&urb->lock);
 
/* complete() can reenter this HCD */
spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
spin_lock (&ehci->lock);
}
 
 
/*
* Process and free completed qtds for a qh, returning URBs to drivers.
* Chases up to qh->hw_current. Returns number of completions called,
* indicating how much "real" work we did.
*/
#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT)
static unsigned
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
{
struct ehci_qtd *last = 0, *end = qh->dummy;
struct list_head *entry, *tmp;
int stopped;
unsigned count = 0;
int do_status = 0;
u8 state;
 
if (unlikely (list_empty (&qh->qtd_list)))
return count;
 
/* completions (or tasks on other cpus) must never clobber HALT
* till we've gone through and cleaned everything up, even when
* they add urbs to this qh's queue or mark them for unlinking.
*
* NOTE: unlinking expects to be done in queue order.
*/
state = qh->qh_state;
qh->qh_state = QH_STATE_COMPLETING;
stopped = (state == QH_STATE_IDLE);
 
/* remove de-activated QTDs from front of queue.
* after faults (including short reads), cleanup this urb
* then let the queue advance.
* if queue is stopped, handles unlinks.
*/
list_for_each_safe (entry, tmp, &qh->qtd_list) {
struct ehci_qtd *qtd;
struct urb *urb;
u32 token = 0;
 
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
urb = qtd->urb;
 
/* clean up any state from previous QTD ...*/
if (last) {
if (likely (last->urb != urb)) {
ehci_urb_done (ehci, last->urb, regs);
count++;
}
ehci_qtd_free (ehci, last);
last = 0;
}
 
/* ignore urbs submitted during completions we reported */
if (qtd == end)
break;
 
/* hardware copies qtd out of qh overlay */
rmb ();
token = le32_to_cpu (qtd->hw_token);
 
/* always clean up qtds the hc de-activated */
if ((token & QTD_STS_ACTIVE) == 0) {
 
if ((token & QTD_STS_HALT) != 0) {
stopped = 1;
 
/* magic dummy for some short reads; qh won't advance */
} else if (IS_SHORT_READ (token)
&& (qh->hw_alt_next & QTD_MASK)
== ehci->async->hw_alt_next) {
stopped = 1;
goto halt;
}
 
/* stop scanning when we reach qtds the hc is using */
} else if (likely (!stopped
&& HCD_IS_RUNNING (ehci->hcd.state))) {
break;
 
} else {
stopped = 1;
 
/* ignore active urbs unless some previous qtd
* for the urb faulted (including short read) or
* its urb was canceled. we may patch qh or qtds.
*/
if (likely (urb->status == -EINPROGRESS))
continue;
/* issue status after short control reads */
if (unlikely (do_status != 0)
&& QTD_PID (token) == 0 /* OUT */) {
do_status = 0;
continue;
}
 
/* token in overlay may be most current */
if (state == QH_STATE_IDLE
&& cpu_to_le32 (qtd->qtd_dma)
== qh->hw_current)
token = le32_to_cpu (qh->hw_token);
 
/* force halt for unlinked or blocked qh, so we'll
* patch the qh later and so that completions can't
* activate it while we "know" it's stopped.
*/
if ((HALT_BIT & qh->hw_token) == 0) {
halt:
qh->hw_token |= HALT_BIT;
wmb ();
}
}
/* remove it from the queue */
spin_lock (&urb->lock);
qtd_copy_status (ehci, urb, qtd->length, token);
do_status = (urb->status == -EREMOTEIO)
&& usb_pipecontrol (urb->pipe);
spin_unlock (&urb->lock);
 
if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
last = list_entry (qtd->qtd_list.prev,
struct ehci_qtd, qtd_list);
last->hw_next = qtd->hw_next;
}
list_del (&qtd->qtd_list);
last = qtd;
}
 
/* last urb's completion might still need calling */
if (likely (last != 0)) {
ehci_urb_done (ehci, last->urb, regs);
count++;
ehci_qtd_free (ehci, last);
}
 
/* restore original state; caller must unlink or relink */
qh->qh_state = state;
 
/* update qh after fault cleanup */
if (unlikely (stopped != 0)
/* some EHCI 0.95 impls will overlay dummy qtds */
|| qh->hw_qtd_next == EHCI_LIST_END) {
if (list_empty (&qh->qtd_list))
end = qh->dummy;
else {
end = list_entry (qh->qtd_list.next,
struct ehci_qtd, qtd_list);
/* first qtd may already be partially processed */
if (cpu_to_le32 (end->qtd_dma) == qh->hw_current)
end = 0;
}
if (end)
qh_update (ehci, qh, end);
}
 
return count;
}
 
/*-------------------------------------------------------------------------*/
 
// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
// ... and packet size, for any kind of endpoint descriptor
#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
 
/*
* reverse of qh_urb_transaction: free a list of TDs.
* used for cleanup after errors, before HC sees an URB's TDs.
*/
static void qtd_list_free (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list
) {
struct list_head *entry, *temp;
 
list_for_each_safe (entry, temp, qtd_list) {
struct ehci_qtd *qtd;
 
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
list_del (&qtd->qtd_list);
ehci_qtd_free (ehci, qtd);
}
}
 
/*
* create a list of filled qtds for this URB; won't link into qh.
*/
static struct list_head *
qh_urb_transaction (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *head,
int flags
) {
struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf;
int len, maxpacket;
int is_input;
u32 token;
 
/*
* URBs map to sequences of QTDs: one logical transaction
*/
qtd = ehci_qtd_alloc (ehci, flags);
if (unlikely (!qtd))
return 0;
list_add_tail (&qtd->qtd_list, head);
qtd->urb = urb;
 
token = QTD_STS_ACTIVE;
token |= (EHCI_TUNE_CERR << 10);
/* for split transactions, SplitXState initialized to zero */
 
len = urb->transfer_buffer_length;
is_input = usb_pipein (urb->pipe);
if (usb_pipecontrol (urb->pipe)) {
/* SETUP pid */
qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
token | (2 /* "setup" */ << 8), 8);
 
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
qtd_prev = qtd;
qtd = ehci_qtd_alloc (ehci, flags);
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
}
 
/*
* data transfer stage: buffer setup
*/
if (likely (len > 0))
buf = urb->transfer_dma;
else
buf = 0;
 
// FIXME this 'buf' check break some zlps...
if (!buf || is_input)
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
 
maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
 
/*
* buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len)
* and may serve as a control status ack
*/
for (;;) {
int this_qtd_len;
 
this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
len -= this_qtd_len;
buf += this_qtd_len;
if (is_input)
qtd->hw_alt_next = ehci->async->hw_alt_next;
 
/* qh makes control packets use qtd toggle; maybe switch it */
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
token ^= QTD_TOGGLE;
 
if (likely (len <= 0))
break;
 
qtd_prev = qtd;
qtd = ehci_qtd_alloc (ehci, flags);
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
}
 
/* unless the bulk/interrupt caller wants a chance to clean
* up after short reads, hc should advance qh past this urb
*/
if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
|| usb_pipecontrol (urb->pipe)))
qtd->hw_alt_next = EHCI_LIST_END;
 
/*
* control requests may need a terminating data "status" ack;
* bulk ones may need a terminating short packet (zero length).
*/
if (likely (buf != 0)) {
int one_more = 0;
 
if (usb_pipecontrol (urb->pipe)) {
one_more = 1;
token ^= 0x0100; /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
} else if (usb_pipebulk (urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
one_more = 1;
}
if (one_more) {
qtd_prev = qtd;
qtd = ehci_qtd_alloc (ehci, flags);
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
 
/* never any data in such packets */
qtd_fill (qtd, 0, 0, token, 0);
}
}
 
/* by default, enable interrupt on urb completion */
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
return head;
 
cleanup:
qtd_list_free (ehci, urb, head);
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/*
* Hardware maintains data toggle (like OHCI) ... here we (re)initialize
* the hardware data toggle in the QH, and set the pseudo-toggle in udev
* so we can see if usb_clear_halt() was called. NOP for control, since
* we set up qh->hw_info1 to always use the QTD toggle bits.
*/
static inline void
clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
{
vdbg ("clear toggle, dev %d ep 0x%x-%s",
udev->devnum, ep, is_out ? "out" : "in");
qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
usb_settoggle (udev, ep, is_out, 1);
}
 
// Would be best to create all qh's from config descriptors,
// when each interface/altsetting is established. Unlink
// any previous qh and cancel its urbs first; endpoints are
// implicitly reset then (data toggle too).
// That'd mean updating how usbcore talks to HCDs. (2.5?)
 
 
/*
* Each QH holds a qtd list; a QH is used for everything except iso.
*
* For interrupt urbs, the scheduler must set the microframe scheduling
* mask(s) each time the QH gets scheduled. For highspeed, that's
* just one microframe in the s-mask. For split interrupt transactions
* there are additional complications: c-mask, maybe FSTNs.
*/
static struct ehci_qh *
qh_make (
struct ehci_hcd *ehci,
struct urb *urb,
int flags
) {
struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
u32 info1 = 0, info2 = 0;
int is_input, type;
int maxp = 0;
 
if (!qh)
return qh;
 
/*
* init endpoint/device data for this QH
*/
info1 |= usb_pipeendpoint (urb->pipe) << 8;
info1 |= usb_pipedevice (urb->pipe) << 0;
 
is_input = usb_pipein (urb->pipe);
type = usb_pipetype (urb->pipe);
maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
 
/* Compute interrupt scheduling parameters just once, and save.
* - allowing for high bandwidth, how many nsec/uframe are used?
* - split transactions need a second CSPLIT uframe; same question
* - splits also need a schedule gap (for full/low speed I/O)
* - qh has a polling interval
*
* For control/bulk requests, the HC or TT handles these.
*/
if (type == PIPE_INTERRUPT) {
qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
hb_mult (maxp) * max_packet (maxp));
qh->start = NO_FRAME;
 
if (urb->dev->speed == USB_SPEED_HIGH) {
qh->c_usecs = 0;
qh->gap_uf = 0;
 
/* FIXME handle HS periods of less than 1 frame. */
qh->period = urb->interval >> 3;
if (qh->period < 1) {
dbg ("intr period %d uframes, NYET!",
urb->interval);
goto done;
}
} else {
/* gap is f(FS/LS transfer times) */
qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
is_input, 0, maxp) / (125 * 1000);
 
/* FIXME this just approximates SPLIT/CSPLIT times */
if (is_input) { // SPLIT, gap, CSPLIT+DATA
qh->c_usecs = qh->usecs + HS_USECS (0);
qh->usecs = HS_USECS (1);
} else { // SPLIT+DATA, gap, CSPLIT
qh->usecs += HS_USECS (1);
qh->c_usecs = HS_USECS (0);
}
 
qh->period = urb->interval;
}
}
 
/* using TT? */
switch (urb->dev->speed) {
case USB_SPEED_LOW:
info1 |= (1 << 12); /* EPS "low" */
/* FALL THROUGH */
 
case USB_SPEED_FULL:
/* EPS 0 means "full" */
if (type != PIPE_INTERRUPT)
info1 |= (EHCI_TUNE_RL_TT << 28);
if (type == PIPE_CONTROL) {
info1 |= (1 << 27); /* for TT */
info1 |= 1 << 14; /* toggle from qtd */
}
info1 |= maxp << 16;
 
info2 |= (EHCI_TUNE_MULT_TT << 30);
info2 |= urb->dev->ttport << 23;
info2 |= urb->dev->tt->hub->devnum << 16;
 
/* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
 
break;
 
case USB_SPEED_HIGH: /* no TT involved */
info1 |= (2 << 12); /* EPS "high" */
if (type == PIPE_CONTROL) {
info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else if (type == PIPE_BULK) {
info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else { /* PIPE_INTERRUPT */
info1 |= max_packet (maxp) << 16;
info2 |= hb_mult (maxp) << 30;
}
break;
default:
dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
done:
qh_put (ehci, qh);
return 0;
}
 
/* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
 
/* init as live, toggle clear, advance to dummy */
qh->qh_state = QH_STATE_IDLE;
qh->hw_info1 = cpu_to_le32 (info1);
qh->hw_info2 = cpu_to_le32 (info2);
qh_update (ehci, qh, qh->dummy);
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
return qh;
}
#undef hb_mult
#undef hb_packet
 
/*-------------------------------------------------------------------------*/
 
/* move qh (and its qtds) onto async queue; maybe enable queue. */
 
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
u32 dma = QH_NEXT (qh->qh_dma);
struct ehci_qh *head;
 
/* (re)start the async schedule? */
head = ehci->async;
timer_action_done (ehci, TIMER_ASYNC_OFF);
if (!head->qh_next.qh) {
u32 cmd = readl (&ehci->regs->command);
 
if (!(cmd & CMD_ASE)) {
/* in case a clear of CMD_ASE didn't take yet */
(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
cmd |= CMD_ASE | CMD_RUN;
writel (cmd, &ehci->regs->command);
ehci->hcd.state = USB_STATE_RUNNING;
/* posted write need not be known to HC yet ... */
}
}
 
qh->hw_token &= ~HALT_BIT;
 
/* splice right after start */
qh->qh_next = head->qh_next;
qh->hw_next = head->hw_next;
wmb ();
 
head->qh_next.qh = qh;
head->hw_next = dma;
 
qh->qh_state = QH_STATE_LINKED;
/* qtd completions reported later by interrupt */
}
 
/*-------------------------------------------------------------------------*/
 
#define QH_ADDR_MASK __constant_le32_to_cpu(0x7f)
 
/*
* For control/bulk/interrupt, return QH with these TDs appended.
* Allocates and initializes the QH if necessary.
* Returns null if it can't allocate a QH it needs to.
* If the QH has TDs (urbs) already, that's great.
*/
static struct ehci_qh *qh_append_tds (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
int epnum,
void **ptr
)
{
struct ehci_qh *qh = 0;
 
qh = (struct ehci_qh *) *ptr;
if (unlikely (qh == 0)) {
/* can't sleep here, we have ehci->lock... */
qh = qh_make (ehci, urb, SLAB_ATOMIC);
*ptr = qh;
}
if (likely (qh != 0)) {
struct ehci_qtd *qtd;
 
if (unlikely (list_empty (qtd_list)))
qtd = 0;
else
qtd = list_entry (qtd_list->next, struct ehci_qtd,
qtd_list);
 
/* control qh may need patching after enumeration */
if (unlikely (epnum == 0)) {
/* set_address changes the address */
if ((qh->hw_info1 & QH_ADDR_MASK) == 0)
qh->hw_info1 |= cpu_to_le32 (
usb_pipedevice (urb->pipe));
 
/* for full speed, ep0 maxpacket can grow */
else if (!(qh->hw_info1
& __constant_cpu_to_le32 (0x3 << 12))) {
u32 info, max;
 
info = le32_to_cpu (qh->hw_info1);
max = urb->dev->descriptor.bMaxPacketSize0;
if (max > (0x07ff & (info >> 16))) {
info &= ~(0x07ff << 16);
info |= max << 16;
qh->hw_info1 = cpu_to_le32 (info);
}
}
 
/* usb_reset_device() briefly reverts to address 0 */
if (usb_pipedevice (urb->pipe) == 0)
qh->hw_info1 &= ~QH_ADDR_MASK;
}
 
/* usb_clear_halt() means qh data toggle gets reset */
if (unlikely (!usb_gettoggle (urb->dev,
(epnum & 0x0f), !(epnum & 0x10)))
&& !usb_pipecontrol (urb->pipe)) {
/* "never happens": drivers do stall cleanup right */
if (qh->qh_state != QH_STATE_IDLE
&& !list_empty (&qh->qtd_list)
&& qh->qh_state != QH_STATE_COMPLETING)
ehci_warn (ehci, "clear toggle dev%d "
"ep%d%s: not idle\n",
usb_pipedevice (urb->pipe),
epnum & 0x0f,
usb_pipein (urb->pipe)
? "in" : "out");
/* else we know this overlay write is safe */
clear_toggle (urb->dev,
epnum & 0x0f, !(epnum & 0x10), qh);
}
 
/* just one way to queue requests: swap with the dummy qtd.
* only hc or qh_completions() usually modify the overlay.
*/
if (likely (qtd != 0)) {
struct ehci_qtd *dummy;
dma_addr_t dma;
u32 token;
 
/* to avoid racing the HC, use the dummy td instead of
* the first td of our list (becomes new dummy). both
* tds stay deactivated until we're done, when the
* HC is allowed to fetch the old dummy (4.10.2).
*/
token = qtd->hw_token;
qtd->hw_token = HALT_BIT;
wmb ();
dummy = qh->dummy;
 
dma = dummy->qtd_dma;
*dummy = *qtd;
dummy->qtd_dma = dma;
 
list_del (&qtd->qtd_list);
list_add (&dummy->qtd_list, qtd_list);
__list_splice (qtd_list, qh->qtd_list.prev);
 
ehci_qtd_init (qtd, qtd->qtd_dma);
qh->dummy = qtd;
 
/* hc must see the new dummy at list end */
dma = qtd->qtd_dma;
qtd = list_entry (qh->qtd_list.prev,
struct ehci_qtd, qtd_list);
qtd->hw_next = QTD_NEXT (dma);
 
/* let the hc process these next qtds */
wmb ();
dummy->hw_token = token;
 
urb->hcpriv = qh_get (qh);
}
}
return qh;
}
 
/*-------------------------------------------------------------------------*/
 
static int
submit_async (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
int mem_flags
) {
struct ehci_qtd *qtd;
struct hcd_dev *dev;
int epnum;
unsigned long flags;
struct ehci_qh *qh = 0;
 
qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
dev = (struct hcd_dev *)urb->dev->hcpriv;
epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
epnum |= 0x10;
 
ehci_vdbg (ehci, "submit_async urb %p len %d ep%d%s qtd %p [qh %p]\n",
urb, urb->transfer_buffer_length,
epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
qtd, dev ? dev->ep [epnum] : (void *)~0);
 
spin_lock_irqsave (&ehci->lock, flags);
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
 
/* Control/bulk operations through TTs don't need scheduling,
* the HC and TT handle it when the TT has a buffer ready.
*/
if (likely (qh != 0)) {
if (likely (qh->qh_state == QH_STATE_IDLE))
qh_link_async (ehci, qh_get (qh));
}
spin_unlock_irqrestore (&ehci->lock, flags);
if (unlikely (qh == 0)) {
qtd_list_free (ehci, urb, qtd_list);
return -ENOMEM;
}
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/* the async qh for the qtds being reclaimed are now unlinked from the HC */
 
static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
 
static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;
 
timer_action_done (ehci, TIMER_IAA_WATCHDOG);
 
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = 0;
qh_put (ehci, qh); // refcount from reclaim
 
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
ehci->reclaim_ready = 0;
qh->reclaim = 0;
 
qh_completions (ehci, qh, regs);
 
if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh);
else {
qh_put (ehci, qh); // refcount from async list
 
/* it's not free to turn the async schedule on/off; leave it
* active but idle for a while once it empties.
*/
if (HCD_IS_RUNNING (ehci->hcd.state)
&& ehci->async->qh_next.qh == 0)
timer_action (ehci, TIMER_ASYNC_OFF);
}
 
if (next) {
ehci->reclaim = 0;
start_unlink_async (ehci, next);
}
}
 
/* makes sure the async qh will become idle */
/* caller must own ehci->lock */
 
static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
int cmd = readl (&ehci->regs->command);
struct ehci_qh *prev;
 
#ifdef DEBUG
if (ehci->reclaim
|| (qh->qh_state != QH_STATE_LINKED
&& qh->qh_state != QH_STATE_UNLINK_WAIT)
#ifdef CONFIG_SMP
// this macro lies except on SMP compiles
|| !spin_is_locked (&ehci->lock)
#endif
)
BUG ();
#endif
 
/* stop async schedule right now? */
if (unlikely (qh == ehci->async)) {
/* can't get here without STS_ASS set */
if (ehci->hcd.state != USB_STATE_HALT) {
writel (cmd & ~CMD_ASE, &ehci->regs->command);
wmb ();
// handshake later, if we need to
}
timer_action_done (ehci, TIMER_ASYNC_OFF);
return;
}
 
qh->qh_state = QH_STATE_UNLINK;
ehci->reclaim = qh = qh_get (qh);
 
prev = ehci->async;
while (prev->qh_next.qh != qh)
prev = prev->qh_next.qh;
 
prev->hw_next = qh->hw_next;
prev->qh_next = qh->qh_next;
wmb ();
 
if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
/* if (unlikely (qh->reclaim != 0))
* this will recurse, probably not much
*/
end_unlink_async (ehci, NULL);
return;
}
 
ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
timer_action (ehci, TIMER_IAA_WATCHDOG);
}
 
/*-------------------------------------------------------------------------*/
 
static void
scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
 
if (!++(ehci->stamp))
ehci->stamp++;
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
rescan:
qh = ehci->async->qh_next.qh;
if (likely (qh != 0)) {
do {
/* clean any finished work for this qh */
if (!list_empty (&qh->qtd_list)
&& qh->stamp != ehci->stamp) {
int temp;
 
/* unlinks could happen here; completion
* reporting drops the lock. rescan using
* the latest schedule, but don't rescan
* qhs we already finished (no looping).
*/
qh = qh_get (qh);
qh->stamp = ehci->stamp;
temp = qh_completions (ehci, qh, regs);
qh_put (ehci, qh);
if (temp != 0) {
goto rescan;
}
}
 
/* unlink idle entries, reducing HC PCI usage as well
* as HCD schedule-scanning costs. delay for any qh
* we just scanned, there's a not-unusual case that it
* doesn't stay idle for long.
* (plus, avoids some kind of re-activation race.)
*/
if (list_empty (&qh->qtd_list)) {
if (qh->stamp == ehci->stamp)
action = TIMER_ASYNC_SHRINK;
else if (!ehci->reclaim
&& qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
}
 
qh = qh->qh_next.qh;
} while (qh);
}
if (action == TIMER_ASYNC_SHRINK)
timer_action (ehci, TIMER_ASYNC_SHRINK);
}
/shark/trunk/drivers/usb/host/ohci-dbg.c
0,0 → 1,678
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
 
/*-------------------------------------------------------------------------*/
 
#ifdef DEBUG
 
#define edstring(ed_type) ({ char *temp; \
switch (ed_type) { \
case PIPE_CONTROL: temp = "ctrl"; break; \
case PIPE_BULK: temp = "bulk"; break; \
case PIPE_INTERRUPT: temp = "intr"; break; \
default: temp = "isoc"; break; \
}; temp;})
#define pipestring(pipe) edstring(usb_pipetype(pipe))
 
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header
*/
static void __attribute__((unused))
urb_print (struct urb * urb, char * str, int small)
{
unsigned int pipe= urb->pipe;
 
if (!urb->dev || !urb->dev->bus) {
dbg("%s URB: no dev", str);
return;
}
 
#ifndef OHCI_VERBOSE_DEBUG
if (urb->status != 0)
#endif
dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
str,
urb,
usb_pipedevice (pipe),
usb_pipeendpoint (pipe),
usb_pipeout (pipe)? "out" : "in",
pipestring (pipe),
urb->transfer_flags,
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
 
#ifdef OHCI_VERBOSE_DEBUG
if (!small) {
int i, len;
 
if (usb_pipecontrol (pipe)) {
printk (KERN_DEBUG __FILE__ ": setup(8):");
for (i = 0; i < 8 ; i++)
printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
printk ("\n");
}
if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
urb->actual_length,
urb->transfer_buffer_length);
len = usb_pipeout (pipe)?
urb->transfer_buffer_length: urb->actual_length;
for (i = 0; i < 16 && i < len; i++)
printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
printk ("%s stat:%d\n", i < len? "...": "", urb->status);
}
}
#endif
}
 
#define ohci_dbg_sw(ohci, next, size, format, arg...) \
do { \
if (next) { \
unsigned s_len; \
s_len = snprintf26(*next, *size, format, ## arg ); \
*size -= s_len; *next += s_len; \
} else \
ohci_dbg(ohci,format, ## arg ); \
} while (0);
 
 
static void ohci_dump_intr_mask (
struct ohci_hcd *ohci,
char *label,
u32 mask,
char **next,
unsigned *size)
{
ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
label,
mask,
(mask & OHCI_INTR_MIE) ? " MIE" : "",
(mask & OHCI_INTR_OC) ? " OC" : "",
(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
(mask & OHCI_INTR_FNO) ? " FNO" : "",
(mask & OHCI_INTR_UE) ? " UE" : "",
(mask & OHCI_INTR_RD) ? " RD" : "",
(mask & OHCI_INTR_SF) ? " SF" : "",
(mask & OHCI_INTR_WDH) ? " WDH" : "",
(mask & OHCI_INTR_SO) ? " SO" : ""
);
}
 
static void maybe_print_eds (
struct ohci_hcd *ohci,
char *label,
u32 value,
char **next,
unsigned *size)
{
if (value)
ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
}
 
static char *hcfs2string (int state)
{
switch (state) {
case OHCI_USB_RESET: return "reset";
case OHCI_USB_RESUME: return "resume";
case OHCI_USB_OPER: return "operational";
case OHCI_USB_SUSPEND: return "suspend";
}
return "?";
}
 
// dump control and status registers
static void
ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
{
struct ohci_regs *regs = controller->regs;
u32 temp;
 
temp = readl (&regs->revision) & 0xff;
ohci_dbg_sw (controller, next, size,
"OHCI %d.%d, %s legacy support registers\n",
0x03 & (temp >> 4), (temp & 0x0f),
(temp & 0x10) ? "with" : "NO");
 
temp = readl (&regs->control);
ohci_dbg_sw (controller, next, size,
"control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
temp,
(temp & OHCI_CTRL_RWE) ? " RWE" : "",
(temp & OHCI_CTRL_RWC) ? " RWC" : "",
(temp & OHCI_CTRL_IR) ? " IR" : "",
hcfs2string (temp & OHCI_CTRL_HCFS),
(temp & OHCI_CTRL_BLE) ? " BLE" : "",
(temp & OHCI_CTRL_CLE) ? " CLE" : "",
(temp & OHCI_CTRL_IE) ? " IE" : "",
(temp & OHCI_CTRL_PLE) ? " PLE" : "",
temp & OHCI_CTRL_CBSR
);
 
temp = readl (&regs->cmdstatus);
ohci_dbg_sw (controller, next, size,
"cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
(temp & OHCI_SOC) >> 16,
(temp & OHCI_OCR) ? " OCR" : "",
(temp & OHCI_BLF) ? " BLF" : "",
(temp & OHCI_CLF) ? " CLF" : "",
(temp & OHCI_HCR) ? " HCR" : ""
);
 
ohci_dump_intr_mask (controller, "intrstatus",
readl (&regs->intrstatus), next, size);
ohci_dump_intr_mask (controller, "intrenable",
readl (&regs->intrenable), next, size);
// intrdisable always same as intrenable
 
maybe_print_eds (controller, "ed_periodcurrent",
readl (&regs->ed_periodcurrent), next, size);
 
maybe_print_eds (controller, "ed_controlhead",
readl (&regs->ed_controlhead), next, size);
maybe_print_eds (controller, "ed_controlcurrent",
readl (&regs->ed_controlcurrent), next, size);
 
maybe_print_eds (controller, "ed_bulkhead",
readl (&regs->ed_bulkhead), next, size);
maybe_print_eds (controller, "ed_bulkcurrent",
readl (&regs->ed_bulkcurrent), next, size);
 
maybe_print_eds (controller, "donehead",
readl (&regs->donehead), next, size);
}
 
#define dbg_port_sw(hc,num,value,next,size) \
ohci_dbg_sw (hc, next, size, \
"roothub.portstatus [%d] " \
"0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
 
 
static void
ohci_dump_roothub (
struct ohci_hcd *controller,
int verbose,
char **next,
unsigned *size)
{
u32 temp, ndp, i;
 
temp = roothub_a (controller);
if (temp == ~(u32)0)
return;
ndp = (temp & RH_A_NDP);
 
if (verbose) {
ohci_dbg_sw (controller, next, size,
"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
((temp & RH_A_POTPGT) >> 24) & 0xff,
(temp & RH_A_NOCP) ? " NOCP" : "",
(temp & RH_A_OCPM) ? " OCPM" : "",
(temp & RH_A_DT) ? " DT" : "",
(temp & RH_A_NPS) ? " NPS" : "",
(temp & RH_A_PSM) ? " PSM" : "",
ndp
);
temp = roothub_b (controller);
ohci_dbg_sw (controller, next, size,
"roothub.b %08x PPCM=%04x DR=%04x\n",
temp,
(temp & RH_B_PPCM) >> 16,
(temp & RH_B_DR)
);
temp = roothub_status (controller);
ohci_dbg_sw (controller, next, size,
"roothub.status %08x%s%s%s%s%s%s\n",
temp,
(temp & RH_HS_CRWE) ? " CRWE" : "",
(temp & RH_HS_OCIC) ? " OCIC" : "",
(temp & RH_HS_LPSC) ? " LPSC" : "",
(temp & RH_HS_DRWE) ? " DRWE" : "",
(temp & RH_HS_OCI) ? " OCI" : "",
(temp & RH_HS_LPS) ? " LPS" : ""
);
}
 
for (i = 0; i < ndp; i++) {
temp = roothub_portstatus (controller, i);
dbg_port_sw (controller, i, temp, next, size);
}
}
 
static void ohci_dump (struct ohci_hcd *controller, int verbose)
{
ohci_dbg (controller, "OHCI controller state\n");
 
// dumps some of the state we know about
ohci_dump_status (controller, NULL, 0);
if (controller->hcca)
ohci_dbg (controller,
"hcca frame #%04x\n", controller->hcca->frame_no);
ohci_dump_roothub (controller, 1, NULL, 0);
}
 
static const char data0 [] = "DATA0";
static const char data1 [] = "DATA1";
 
static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
{
u32 tmp = le32_to_cpup (&td->hwINFO);
 
ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x",
label, td,
(tmp & TD_DONE) ? " (DONE)" : "",
td->urb, td->index,
le32_to_cpup (&td->hwNextTD));
if ((tmp & TD_ISO) == 0) {
const char *toggle, *pid;
u32 cbp, be;
 
switch (tmp & TD_T) {
case TD_T_DATA0: toggle = data0; break;
case TD_T_DATA1: toggle = data1; break;
case TD_T_TOGGLE: toggle = "(CARRY)"; break;
default: toggle = "(?)"; break;
}
switch (tmp & TD_DP) {
case TD_DP_SETUP: pid = "SETUP"; break;
case TD_DP_IN: pid = "IN"; break;
case TD_DP_OUT: pid = "OUT"; break;
default: pid = "(bad pid)"; break;
}
ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s", tmp,
TD_CC_GET(tmp), /* EC, */ toggle,
(tmp & TD_DI) >> 21, pid,
(tmp & TD_R) ? "R" : "");
cbp = le32_to_cpup (&td->hwCBP);
be = le32_to_cpup (&td->hwBE);
ohci_dbg (ohci, " cbp %08x be %08x (len %d)", cbp, be,
cbp ? (be + 1 - cbp) : 0);
} else {
unsigned i;
ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,
TD_CC_GET(tmp),
(tmp >> 24) & 0x07,
(tmp & TD_DI) >> 21,
tmp & 0x0000ffff);
ohci_dbg (ohci, " bp0 %08x be %08x",
le32_to_cpup (&td->hwCBP) & ~0x0fff,
le32_to_cpup (&td->hwBE));
for (i = 0; i < MAXPSW; i++) {
u16 psw = le16_to_cpup (&td->hwPSW [i]);
int cc = (psw >> 12) & 0x0f;
ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d", i,
psw, cc,
(cc >= 0x0e) ? "OFFSET" : "SIZE",
psw & 0x0fff);
}
}
}
 
/* caller MUST own hcd spinlock if verbose is set! */
static void __attribute__((unused))
ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
{
u32 tmp = ed->hwINFO;
char *type = "";
 
ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x",
label,
ed, ed->state, edstring (ed->type),
le32_to_cpup (&ed->hwNextED));
switch (tmp & (ED_IN|ED_OUT)) {
case ED_OUT: type = "-OUT"; break;
case ED_IN: type = "-IN"; break;
/* else from TDs ... control */
}
ohci_dbg (ohci,
" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
0x03ff & (le32_to_cpu (tmp) >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "",
(tmp & ED_SKIP) ? " SKIP" : "",
(tmp & ED_LOWSPEED) ? " LOW" : "",
0x000f & (le32_to_cpu (tmp) >> 7),
type,
0x007f & le32_to_cpu (tmp));
ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s",
tmp = le32_to_cpup (&ed->hwHeadP),
(ed->hwHeadP & ED_C) ? data1 : data0,
(ed->hwHeadP & ED_H) ? " HALT" : "",
le32_to_cpup (&ed->hwTailP),
verbose ? "" : " (not listing)");
if (verbose) {
struct list_head *tmp;
 
/* use ed->td_list because HC concurrently modifies
* hwNextTD as it accumulates ed_donelist.
*/
list_for_each (tmp, &ed->td_list) {
struct td *td;
td = list_entry (tmp, struct td, td_list);
ohci_dump_td (ohci, " ->", td);
}
}
}
 
#else
static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}
 
#undef OHCI_VERBOSE_DEBUG
 
#endif /* DEBUG */
 
/*-------------------------------------------------------------------------*/
 
#ifdef STUB_DEBUG_FILES
 
static inline void create_debug_files (struct ohci_hcd *bus) { }
static inline void remove_debug_files (struct ohci_hcd *bus) { }
 
#else
 
static inline struct ohci_hcd *dev_to_ohci (struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata (dev);
 
return hcd_to_ohci (hcd);
}
 
static ssize_t
show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
{
unsigned temp, size = count;
 
if (!ed)
return 0;
 
/* print first --> last */
while (ed->ed_prev)
ed = ed->ed_prev;
 
/* dump a snapshot of the bulk or control schedule */
while (ed) {
u32 info = ed->hwINFO;
u32 scratch = cpu_to_le32p (&ed->hwINFO);
struct list_head *entry;
struct td *td;
 
temp = snprintf26(buf, size,
"ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
ed,
(info & ED_LOWSPEED) ? 'l' : 'f',
scratch & 0x7f,
(scratch >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (scratch >> 16),
scratch,
(info & ED_SKIP) ? " s" : "",
(ed->hwHeadP & ED_H) ? " H" : "",
(ed->hwHeadP & ED_C) ? data1 : data0);
size -= temp;
buf += temp;
 
list_for_each (entry, &ed->td_list) {
u32 cbp, be;
 
td = list_entry (entry, struct td, td_list);
scratch = cpu_to_le32p (&td->hwINFO);
cbp = le32_to_cpup (&td->hwCBP);
be = le32_to_cpup (&td->hwBE);
temp = snprintf26(buf, size,
"\n\ttd %p %s %d cc=%x urb %p (%08x)",
td,
({ char *pid;
switch (scratch & TD_DP) {
case TD_DP_SETUP: pid = "setup"; break;
case TD_DP_IN: pid = "in"; break;
case TD_DP_OUT: pid = "out"; break;
default: pid = "(?)"; break;
} pid;}),
cbp ? (be + 1 - cbp) : 0,
TD_CC_GET (scratch), td->urb, scratch);
size -= temp;
buf += temp;
}
 
temp = snprintf26(buf, size, "\n");
size -= temp;
buf += temp;
 
ed = ed->ed_next;
}
return count - size;
}
 
static ssize_t
show_async (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
size_t temp;
unsigned long flags;
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
 
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);
temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);
spin_unlock_irqrestore (&ohci->lock, flags);
 
return temp;
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
 
 
#define DBG_SCHED_LIMIT 64
 
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ed **seen, *ed;
unsigned long flags;
unsigned temp, size, seen_count;
char *next;
unsigned i;
 
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
next = buf;
size = PAGE_SIZE;
 
temp = snprintf26(next, size, "size = %d\n", NUM_INTS);
size -= temp;
next += temp;
 
/* dump a snapshot of the periodic schedule (and load) */
spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < NUM_INTS; i++) {
if (!(ed = ohci->periodic [i]))
continue;
 
temp = snprintf26(next, size, "%2d [%3d]:", i, ohci->load [i]);
size -= temp;
next += temp;
 
do {
temp = snprintf26(next, size, " ed%d/%p",
ed->interval, ed);
size -= temp;
next += temp;
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp] == ed)
break;
}
 
/* show more info the first time around */
if (temp == seen_count) {
u32 info = ed->hwINFO;
u32 scratch = cpu_to_le32p (&ed->hwINFO);
 
temp = snprintf26(next, size,
" (%cs dev%d%s ep%d%s"
" max %d %08x%s%s)",
(info & ED_LOWSPEED) ? 'l' : 'f',
scratch & 0x7f,
(info & ED_ISO) ? " iso" : "",
(scratch >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (scratch >> 16),
scratch,
(info & ED_SKIP) ? " s" : "",
(ed->hwHeadP & ED_H) ? " H" : "");
size -= temp;
next += temp;
 
// FIXME some TD info too
 
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++] = ed;
 
ed = ed->ed_next;
 
} else {
/* we've seen it and what's after */
temp = 0;
ed = 0;
}
 
} while (ed);
 
temp = snprintf26(next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ohci->lock, flags);
kfree (seen);
 
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
 
 
#undef DBG_SCHED_LIMIT
 
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ohci_regs *regs;
unsigned long flags;
unsigned temp, size;
char *next;
u32 rdata;
 
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
regs = ohci->regs;
next = buf;
size = PAGE_SIZE;
 
spin_lock_irqsave (&ohci->lock, flags);
 
/* dump driver info, then registers in spec order */
 
ohci_dbg_sw (ohci, &next, &size,
"%s version " DRIVER_VERSION "\n", hcd_name);
 
ohci_dump_status(ohci, &next, &size);
 
/* hcca */
if (ohci->hcca)
ohci_dbg_sw (ohci, &next, &size,
"hcca frame 0x%04x\n", ohci->hcca->frame_no);
 
/* other registers mostly affect frame timings */
rdata = readl (&regs->fminterval);
temp = snprintf26(next, size,
"fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
rdata, (rdata >> 31) ? " FIT" : "",
(rdata >> 16) & 0xefff, rdata & 0xffff);
size -= temp;
next += temp;
 
rdata = readl (&regs->fmremaining);
temp = snprintf26(next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
rdata, (rdata >> 31) ? " FRT" : "",
rdata & 0x3fff);
size -= temp;
next += temp;
 
rdata = readl (&regs->periodicstart);
temp = snprintf26(next, size, "periodicstart 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
 
rdata = readl (&regs->lsthresh);
temp = snprintf26(next, size, "lsthresh 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
 
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
 
spin_unlock_irqrestore (&ohci->lock, flags);
 
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 
 
static inline void create_debug_files (struct ohci_hcd *bus)
{
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_async);
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_periodic);
class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_registers);
ohci_dbg (bus, "created debug files\n");
}
 
static inline void remove_debug_files (struct ohci_hcd *bus)
{
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_async);
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_periodic);
class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_registers);
}
 
#endif
 
/*-------------------------------------------------------------------------*/
 
/shark/trunk/drivers/usb/host/uhci-debug.c
0,0 → 1,625
/*
* UHCI-specific debugging code. Invaluable when something
* goes wrong, but don't get in my face.
*
* Kernel visible pointers are surrounded in []'s and bus
* visible pointers are surrounded in ()'s
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2001 Johannes Erdfelt
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
 
#include "uhci-hcd.h"
 
/* Handle REALLY large printk's so we don't overflow buffers */
static inline void lprintk(char *buf)
{
char *p;
 
/* Just write one line at a time */
while (buf) {
p = strchr(buf, '\n');
if (p)
*p = 0;
printk("%s\n", buf);
buf = p;
if (buf)
buf++;
}
}
 
static inline int uhci_is_skeleton_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
int i;
 
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (qh == uhci->skelqh[i])
return 1;
 
return 0;
}
 
static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
{
char *out = buf;
char *spid;
u32 status, token;
 
/* Try to make sure there's enough memory */
if (len < 160)
return 0;
 
status = td_status(td);
out += sprintf26(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
out += sprintf26(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
((status >> 27) & 3),
(status & TD_CTRL_SPD) ? "SPD " : "",
(status & TD_CTRL_LS) ? "LS " : "",
(status & TD_CTRL_IOC) ? "IOC " : "",
(status & TD_CTRL_ACTIVE) ? "Active " : "",
(status & TD_CTRL_STALLED) ? "Stalled " : "",
(status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
(status & TD_CTRL_BABBLE) ? "Babble " : "",
(status & TD_CTRL_NAK) ? "NAK " : "",
(status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
status & 0x7ff);
 
token = td_token(td);
switch (uhci_packetid(token)) {
case USB_PID_SETUP:
spid = "SETUP";
break;
case USB_PID_OUT:
spid = "OUT";
break;
case USB_PID_IN:
spid = "IN";
break;
default:
spid = "?";
break;
}
 
out += sprintf26(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
token >> 21,
((token >> 19) & 1),
(token >> 15) & 15,
(token >> 8) & 127,
(token & 0xff),
spid);
out += sprintf26(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
 
return out - buf;
}
 
static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
{
char *out = buf;
struct urb_priv *urbp;
struct list_head *head, *tmp;
struct uhci_td *td;
int i = 0, checked = 0, prevactive = 0;
 
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
 
out += sprintf26(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
qh, le32_to_cpu(qh->link), le32_to_cpu(qh->element));
 
if (qh->element & UHCI_PTR_QH)
out += sprintf26(out, "%*s Element points to QH (bug?)\n", space, "");
 
if (qh->element & UHCI_PTR_DEPTH)
out += sprintf26(out, "%*s Depth traverse\n", space, "");
 
if (qh->element & cpu_to_le32(8))
out += sprintf26(out, "%*s Bit 3 set (bug?)\n", space, "");
 
if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
out += sprintf26(out, "%*s Element is NULL (bug?)\n", space, "");
 
if (!qh->urbp) {
out += sprintf26(out, "%*s urbp == NULL\n", space, "");
goto out;
}
 
urbp = qh->urbp;
 
head = &urbp->td_list;
tmp = head->next;
 
td = list_entry(tmp, struct uhci_td, list);
 
if (cpu_to_le32(td->dma_handle) != (qh->element & ~UHCI_PTR_BITS))
out += sprintf26(out, "%*s Element != First TD\n", space, "");
 
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
out += sprintf26(out, "%*s%d: ", space + 2, "", i++);
out += uhci_show_td(td, out, len - (out - buf), 0);
 
if (i > 10 && !checked && prevactive && tmp != head &&
debug <= 2) {
struct list_head *ntmp = tmp;
struct uhci_td *ntd = td;
int active = 1, ni = i;
 
checked = 1;
 
while (ntmp != head && ntmp->next != head && active) {
ntd = list_entry(ntmp, struct uhci_td, list);
 
ntmp = ntmp->next;
 
active = td_status(ntd) & TD_CTRL_ACTIVE;
 
ni++;
}
 
if (active && ni > i) {
out += sprintf26(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
tmp = ntmp;
td = ntd;
i = ni;
}
}
 
prevactive = td_status(td) & TD_CTRL_ACTIVE;
}
 
if (list_empty(&urbp->queue_list) || urbp->queued)
goto out;
 
out += sprintf26(out, "%*sQueued QH's:\n", -space, "--");
 
head = &urbp->queue_list;
tmp = head->next;
 
while (tmp != head) {
struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
queue_list);
tmp = tmp->next;
 
out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
}
 
out:
return out - buf;
}
 
#define show_frame_num() \
if (!shown) { \
shown = 1; \
out += sprintf26(out, "- Frame %d\n", i); \
}
 
#ifdef CONFIG_PROC_FS
static const char *qh_names[] = {
"skel_int128_qh", "skel_int64_qh",
"skel_int32_qh", "skel_int16_qh",
"skel_int8_qh", "skel_int4_qh",
"skel_int2_qh", "skel_int1_qh",
"skel_ls_control_qh", "skel_hs_control_qh",
"skel_bulk_qh", "skel_term_qh"
};
 
#define show_qh_name() \
if (!shown) { \
shown = 1; \
out += sprintf26(out, "- %s\n", qh_names[i]); \
}
 
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
char *out = buf;
 
/* Try to make sure there's enough memory */
if (len < 80)
return 0;
 
out += sprintf26(out, " stat%d = %04x %s%s%s%s%s%s%s%s\n",
port,
status,
(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
(status & USBPORTSC_PR) ? "PortReset " : "",
(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
(status & USBPORTSC_RD) ? "ResumeDetect " : "",
(status & USBPORTSC_PEC) ? "EnableChange " : "",
(status & USBPORTSC_PE) ? "PortEnabled " : "",
(status & USBPORTSC_CSC) ? "ConnectChange " : "",
(status & USBPORTSC_CCS) ? "PortConnected " : "");
 
return out - buf;
}
 
static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
unsigned int io_addr = uhci->io_addr;
unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd;
unsigned char sof;
unsigned short portsc1, portsc2;
 
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
 
usbcmd = inw(io_addr + 0);
usbstat = inw(io_addr + 2);
usbint = inw(io_addr + 4);
usbfrnum = inw(io_addr + 6);
flbaseadd = inl(io_addr + 8);
sof = inb(io_addr + 12);
portsc1 = inw(io_addr + 16);
portsc2 = inw(io_addr + 18);
 
out += sprintf26(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n",
usbcmd,
(usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
(usbcmd & USBCMD_CF) ? "CF " : "",
(usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
(usbcmd & USBCMD_FGR) ? "FGR " : "",
(usbcmd & USBCMD_EGSM) ? "EGSM " : "",
(usbcmd & USBCMD_GRESET) ? "GRESET " : "",
(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
(usbcmd & USBCMD_RS) ? "RS " : "");
 
out += sprintf26(out, " usbstat = %04x %s%s%s%s%s%s\n",
usbstat,
(usbstat & USBSTS_HCH) ? "HCHalted " : "",
(usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
(usbstat & USBSTS_HSE) ? "HostSystemError " : "",
(usbstat & USBSTS_RD) ? "ResumeDetect " : "",
(usbstat & USBSTS_ERROR) ? "USBError " : "",
(usbstat & USBSTS_USBINT) ? "USBINT " : "");
 
out += sprintf26(out, " usbint = %04x\n", usbint);
out += sprintf26(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
0xfff & (4*(unsigned int)usbfrnum));
out += sprintf26(out, " flbaseadd = %08x\n", flbaseadd);
out += sprintf26(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf));
 
return out - buf;
}
 
static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
{
struct list_head *tmp;
char *out = buf;
int count = 0;
 
if (len < 200)
return 0;
 
out += sprintf26(out, "urb_priv [%p] ", urbp);
out += sprintf26(out, "urb [%p] ", urbp->urb);
out += sprintf26(out, "qh [%p] ", urbp->qh);
out += sprintf26(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
out += sprintf26(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
 
switch (usb_pipetype(urbp->urb->pipe)) {
case PIPE_ISOCHRONOUS: out += sprintf26(out, "ISO "); break;
case PIPE_INTERRUPT: out += sprintf26(out, "INT "); break;
case PIPE_BULK: out += sprintf26(out, "BLK "); break;
case PIPE_CONTROL: out += sprintf26(out, "CTL "); break;
}
 
out += sprintf26(out, "%s", (urbp->fsbr ? "FSBR " : ""));
out += sprintf26(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
 
if (urbp->status != -EINPROGRESS)
out += sprintf26(out, "Status=%d ", urbp->status);
//out += sprintf26(out, "Inserttime=%lx ",urbp->inserttime);
//out += sprintf26(out, "FSBRtime=%lx ",urbp->fsbrtime);
 
spin_lock(&urbp->urb->lock);
count = 0;
list_for_each(tmp, &urbp->td_list)
count++;
spin_unlock(&urbp->urb->lock);
out += sprintf26(out, "TDs=%d ",count);
 
if (urbp->queued)
out += sprintf26(out, "queued\n");
else {
spin_lock(&uhci->frame_list_lock);
count = 0;
list_for_each(tmp, &urbp->queue_list)
count++;
spin_unlock(&uhci->frame_list_lock);
out += sprintf26(out, "queued URBs=%d\n", count);
}
 
return out - buf;
}
 
static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
unsigned long flags;
struct list_head *head, *tmp;
int count;
 
out += sprintf26(out, "Main list URBs:");
spin_lock_irqsave(&uhci->urb_list_lock, flags);
if (list_empty(&uhci->urb_list))
out += sprintf26(out, " Empty\n");
else {
out += sprintf26(out, "\n");
count = 0;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
 
out += sprintf26(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
out += sprintf26(out, "Remove list URBs:");
spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
if (list_empty(&uhci->urb_remove_list))
out += sprintf26(out, " Empty\n");
else {
out += sprintf26(out, "\n");
count = 0;
head = &uhci->urb_remove_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
 
out += sprintf26(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
 
out += sprintf26(out, "Complete list URBs:");
spin_lock_irqsave(&uhci->complete_list_lock, flags);
if (list_empty(&uhci->complete_list))
out += sprintf26(out, " Empty\n");
else {
out += sprintf26(out, "\n");
count = 0;
head = &uhci->complete_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
 
out += sprintf26(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
 
return out - buf;
}
 
static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
{
unsigned long flags;
char *out = buf;
int i;
struct uhci_qh *qh;
struct uhci_td *td;
struct list_head *tmp, *head;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
out += sprintf26(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf));
 
out += sprintf26(out, "Frame List\n");
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
int shown = 0;
td = uhci->fl->frame_cpu[i];
if (!td)
continue;
 
if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
show_frame_num();
out += sprintf26(out, " frame list does not match td->dma_handle!\n");
}
show_frame_num();
 
head = &td->fl_list;
tmp = head;
do {
td = list_entry(tmp, struct uhci_td, fl_list);
tmp = tmp->next;
out += uhci_show_td(td, out, len - (out - buf), 4);
} while (tmp != head);
}
 
out += sprintf26(out, "Skeleton QH's\n");
 
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int shown = 0;
 
qh = uhci->skelqh[i];
 
if (debug > 1) {
show_qh_name();
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
 
/* Last QH is the Terminating QH, it's different */
if (i == UHCI_NUM_SKELQH - 1) {
if (qh->link != UHCI_PTR_TERM)
out += sprintf26(out, " bandwidth reclamation on!\n");
 
if (qh->element != cpu_to_le32(uhci->term_td->dma_handle))
out += sprintf26(out, " skel_term_qh element is not set to term_td!\n");
 
continue;
}
 
if (list_empty(&qh->list)) {
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH)) {
show_qh_name();
out += sprintf26(out, " skeleton QH not linked to next skeleton QH!\n");
}
}
 
continue;
}
 
show_qh_name();
 
head = &qh->list;
tmp = head->next;
 
while (tmp != head) {
qh = list_entry(tmp, struct uhci_qh, list);
 
tmp = tmp->next;
 
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
 
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH))
out += sprintf26(out, " last QH not linked to next skeleton!\n");
}
}
 
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 
if (debug > 2)
out += uhci_show_lists(uhci, out, len - (out - buf));
 
return out - buf;
}
 
#define MAX_OUTPUT (64 * 1024)
 
static struct proc_dir_entry *uhci_proc_root = NULL;
 
struct uhci_proc {
int size;
char *data;
struct uhci_hcd *uhci;
};
 
static int uhci_proc_open(struct inode *inode, struct file *file)
{
const struct proc_dir_entry *dp = PDE(inode);
struct uhci_hcd *uhci = dp->data;
struct uhci_proc *up;
int ret = -ENOMEM;
 
lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
if (!up)
goto out;
 
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!up->data) {
kfree(up);
goto out;
}
 
up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
 
file->private_data = up;
 
ret = 0;
out:
unlock_kernel();
return ret;
}
 
static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
{
struct uhci_proc *up;
loff_t new = -1;
 
lock_kernel();
up = file->private_data;
 
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
}
if (new < 0 || new > up->size) {
unlock_kernel();
return -EINVAL;
}
unlock_kernel();
return (file->f_pos = new);
}
 
static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
loff_t *ppos)
{
struct uhci_proc *up = file->private_data;
unsigned int pos;
unsigned int size;
 
pos = *ppos;
size = up->size;
if (pos >= size)
return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
nbytes = size - pos;
 
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EINVAL;
 
if (copy_to_user(buf, up->data + pos, nbytes))
return -EFAULT;
 
*ppos += nbytes;
 
return nbytes;
}
 
static int uhci_proc_release(struct inode *inode, struct file *file)
{
struct uhci_proc *up = file->private_data;
 
kfree(up->data);
kfree(up);
 
return 0;
}
 
static struct file_operations uhci_proc_operations = {
.open = uhci_proc_open,
.llseek = uhci_proc_lseek,
.read = uhci_proc_read,
// write: uhci_proc_write,
.release = uhci_proc_release,
};
#endif
 
/shark/trunk/drivers/usb/host/ehci.h
0,0 → 1,481
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#ifndef __LINUX_EHCI_HCD_H
#define __LINUX_EHCI_HCD_H
 
/* definitions used for the EHCI driver */
 
/* statistics can be kept for for tuning/monitoring */
struct ehci_stats {
/* irq usage */
unsigned long normal;
unsigned long error;
unsigned long reclaim;
unsigned long lost_iaa;
 
/* termination of urbs from core */
unsigned long complete;
unsigned long unlink;
};
 
/* ehci_hcd->lock guards shared data against other CPUs:
* ehci_hcd: async, reclaim, periodic (and shadow), ...
* hcd_dev: ep[]
* ehci_qh: qh_next, qtd_list
* ehci_qtd: qtd_list
*
* Also, hold this lock when talking to HC registers or
* when updating hw_* fields in shared qh/qtd/... structures.
*/
 
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
 
struct ehci_hcd { /* one per controller */
spinlock_t lock;
 
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
int reclaim_ready : 1;
 
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
u32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
unsigned i_thresh; /* uframes HC might cache */
 
union ehci_shadow *pshadow; /* mirror hw periodic table */
int next_uframe; /* scan periodic, start here */
unsigned periodic_sched; /* periodic activity count */
 
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
 
/* glue to PCI and HCD framework */
struct usb_hcd hcd;
struct ehci_caps *caps;
struct ehci_regs *regs;
u32 hcs_params; /* cached register copy */
 
/* per-HC memory pools (could be per-PCI-bus, but ...) */
struct pci_pool *qh_pool; /* qh per active urb */
struct pci_pool *qtd_pool; /* one or more per qh */
struct pci_pool *itd_pool; /* itd per iso urb */
struct pci_pool *sitd_pool; /* sitd per split iso urb */
 
struct timer_list watchdog;
struct notifier_block reboot_notifier;
unsigned long actions;
unsigned stamp;
 
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
# define COUNT(x) do { (x)++; } while (0)
#else
# define COUNT(x) do {} while (0)
#endif
};
 
/* unwrap an HCD pointer to get an EHCI_HCD pointer */
#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
 
/* NOTE: urb->transfer_flags expected to not use this bit !!! */
#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
 
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
 
static inline void
timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
clear_bit (action, &ehci->actions);
}
 
static inline void
timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
if (!test_and_set_bit (action, &ehci->actions)) {
unsigned long t;
 
switch (action) {
case TIMER_IAA_WATCHDOG:
t = EHCI_IAA_JIFFIES;
break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
case TIMER_ASYNC_OFF:
t = EHCI_ASYNC_JIFFIES;
break;
// case TIMER_ASYNC_SHRINK:
default:
t = EHCI_SHRINK_JIFFIES;
break;
}
t += jiffies;
// all timings except IAA watchdog can be overridden.
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
if (action != TIMER_IAA_WATCHDOG
&& t > ehci->watchdog.expires
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
}
}
 
/*-------------------------------------------------------------------------*/
 
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
 
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
u8 length; /* CAPLENGTH - size of this struct */
u8 reserved; /* offset 0x1 */
u16 hci_version; /* HCIVERSION - offset 0x2 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
 
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
u8 portroute [8]; /* nibbles for routing - offset 0xC */
} __attribute__ ((packed));
 
 
/* Section 2.3 Host Controller Operational Registers */
struct ehci_regs {
 
/* USBCMD: offset 0x00 */
u32 command;
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
 
/* USBSTS: offset 0x04 */
u32 status;
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
#define STS_HALT (1<<12) /* Not running (any reason) */
/* some bits reserved */
/* these STS_* flags are also intr_enable bits (USBINTR) */
#define STS_IAA (1<<5) /* Interrupted on async advance */
#define STS_FATAL (1<<4) /* such as some PCI access errors */
#define STS_FLR (1<<3) /* frame list rolled over */
#define STS_PCD (1<<2) /* port change detect */
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
 
/* USBINTR: offset 0x08 */
u32 intr_enable;
 
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */
/* ASYNCICLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
 
u32 reserved [9];
 
/* CONFIGFLAG: offset 0x40 */
u32 configured_flag;
#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
 
/* PORTSC: offset 0x44 */
u32 port_status [0]; /* up to N_PORTS */
/* 31:23 reserved */
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */
/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
#define PORT_POWER (1<<12) /* true: has power (see PPC) */
#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
#define PORT_OCC (1<<5) /* over current change */
#define PORT_OC (1<<4) /* over current active */
#define PORT_PEC (1<<3) /* port enable change */
#define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */
} __attribute__ ((packed));
 
 
/*-------------------------------------------------------------------------*/
 
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
 
/*
* EHCI Specification 0.95 Section 3.5
* QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
* used with control, bulk, and interrupt transfers.
*/
struct ehci_qtd {
/* first part defined by EHCI spec */
u32 hw_next; /* see EHCI 3.5.1 */
u32 hw_alt_next; /* see EHCI 3.5.2 */
u32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
#define QTD_PID(tok) (((tok)>>8) & 0x3)
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define QTD_STS_HALT (1 << 6) /* halted on error */
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
#define QTD_STS_STS (1 << 1) /* split transaction state */
#define QTD_STS_PING (1 << 0) /* issue PING? */
u32 hw_buf [5]; /* see EHCI 3.5.4 */
u32 hw_buf_hi [5]; /* Appendix B */
 
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __attribute__ ((aligned (32)));
 
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
 
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
 
/*-------------------------------------------------------------------------*/
 
/* type tag from {qh,itd,sitd,fstn}->hw_next */
#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
 
/* values for that type tag */
#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
 
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
 
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
 
/*
* Entries in periodic shadow table are pointers to one of four kinds
* of data structure. That's dictated by the hardware; a type tag is
* encoded in the low bits of the hardware's periodic schedule. Use
* Q_NEXT_TYPE to get the tag.
*
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
u32 *hw_next; /* (all types) */
void *ptr;
};
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI Specification 0.95 Section 3.6
* QH: describes control/bulk/interrupt endpoints
* See Fig 3-7 "Queue Head Structure Layout".
*
* These appear in both the async and (for interrupt) periodic schedules.
*/
 
struct ehci_qh {
/* first part defined by EHCI spec */
u32 hw_next; /* see EHCI 3.6.1 */
u32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
u32 hw_info2; /* see EHCI 3.6.2 */
u32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
u32 hw_qtd_next;
u32 hw_alt_next;
u32 hw_token;
u32 hw_buf [5];
u32 hw_buf_hi [5];
 
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct ehci_qtd *dummy;
struct ehci_qh *reclaim; /* next to reclaim */
 
atomic_t refcount;
unsigned stamp;
 
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
 
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
u8 gap_uf; /* uframes split/csplit gap */
u8 c_usecs; /* ... split completion bw */
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
 
} __attribute__ ((aligned (32)));
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI Specification 0.95 Section 3.3
* Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
*
* Schedule records for high speed iso xfers
*/
struct ehci_itd {
/* first part defined by EHCI spec */
u32 hw_next; /* see EHCI 3.3.1 */
u32 hw_transaction [8]; /* see EHCI 3.3.2 */
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
 
u32 hw_bufp [7]; /* see EHCI 3.3.3 */
u32 hw_bufp_hi [7]; /* Appendix B */
 
/* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */
union ehci_shadow itd_next; /* ptr to periodic q entry */
 
struct urb *urb;
struct list_head itd_list; /* list of urb frames' itds */
dma_addr_t buf_dma; /* frame's buffer address */
 
/* for now, only one hw_transaction per itd */
u32 transaction;
u16 index; /* in urb->iso_frame_desc */
u16 uframe; /* in periodic schedule */
u16 usecs;
} __attribute__ ((aligned (32)));
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI Specification 0.95 Section 3.4
* siTD, aka split-transaction isochronous Transfer Descriptor
* ... describe low/full speed iso xfers through TT in hubs
* see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
*/
struct ehci_sitd {
/* first part defined by EHCI spec */
u32 hw_next;
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
u32 hw_fullspeed_ep; /* see EHCI table 3-9 */
u32 hw_uframe; /* see EHCI table 3-10 */
u32 hw_tx_results1; /* see EHCI table 3-11 */
u32 hw_tx_results2; /* see EHCI table 3-12 */
u32 hw_tx_results3; /* see EHCI table 3-12 */
u32 hw_backpointer; /* see EHCI table 3-13 */
u32 hw_buf_hi [2]; /* Appendix B */
 
/* the rest is HCD-private */
dma_addr_t sitd_dma;
union ehci_shadow sitd_next; /* ptr to periodic q entry */
struct urb *urb;
dma_addr_t buf_dma; /* buffer address */
 
unsigned short usecs; /* start bandwidth */
unsigned short c_usecs; /* completion bandwidth */
} __attribute__ ((aligned (32)));
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI Specification 0.96 Section 3.7
* Periodic Frame Span Traversal Node (FSTN)
*
* Manages split interrupt transactions (using TT) that span frame boundaries
* into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
* makes the HC jump (back) to a QH to scan for fs/ls QH completions until
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
*/
struct ehci_fstn {
u32 hw_next; /* any periodic q entry */
u32 hw_prev; /* qh or EHCI_LIST_END */
 
/* the rest is HCD-private */
dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __attribute__ ((aligned (32)));
 
/*-------------------------------------------------------------------------*/
 
#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
 
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
 
/*-------------------------------------------------------------------------*/
 
#endif /* __LINUX_EHCI_HCD_H */
/shark/trunk/drivers/usb/host/ohci-hcd.c
0,0 → 1,691
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
*
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
* interfaces (though some non-x86 Intel chips use it). It supports
* smarter hardware than UHCI. A download link for the spec available
* through the http://www.usb.org website.
*
* History:
*
* 2003/02/24 show registers in sysfs (Kevin Brosius)
*
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
* bandwidth accounting; if debugging, show schedules in driverfs
* 2002/07/19 fixes to management of ED and schedule state.
* 2002/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
* to fix urb unlink races caused by interrupt latency assumptions;
* minor ED field and function naming updates
* 2002/01/18 package as a patch for 2.5.3; this should match the
* 2.4.17 kernel modulo some bugs being fixed.
*
* 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
* from post-2.4.5 patches.
* 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
* 2001/09/07 match PCI PM changes, errnos from Linus' tree
* 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
* pbook pci quirks gone (please fix pbook pci sw!) (db)
*
* 2001/04/08 Identify version on module load (gb)
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
pci_map_single (db)
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
* 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
*
* 2000/09/26 fixed races in removing the private portion of the urb
* 2000/09/07 disable bulk and control lists when unlinking the last
* endpoint descriptor in order to avoid unrecoverable errors on
* the Lucent chips. (rwc@sgi)
* 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
* urb unlink probs, indentation fixes
* 2000/08/11 various oops fixes mostly affecting iso and cleanup from
* device unplugs.
* 2000/06/28 use PCI hotplug framework, for better power management
* and for Cardbus support (David Brownell)
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
* when the controller loses power; handle UE; cleanup; ...
*
* v5.2 1999/12/07 URB 3rd preview,
* v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
* v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
* i386: HUB, Keyboard, Mouse, Printer
*
* v4.3 1999/10/27 multiple HCs, bulk_request
* v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
* v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
* v4.0 1999/08/18
* v3.0 1999/06/25
* v2.1 1999/05/09 code clean up
* v2.0 1999/05/04
* v1.0 1999/04/27 initial release
*
* This file is licenced under the GPL.
*/
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#ifdef CONFIG_USB_DEBUG
# define DEBUG
#else
# undef DEBUG
#endif
 
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h> /* for in_interrupt () */
#include <linux/usb.h>
#include "../core/hcd.h"
 
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
 
 
#define DRIVER_VERSION "2003 Oct 13"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
/*-------------------------------------------------------------------------*/
 
//#define OHCI_VERBOSE_DEBUG /* not always helpful */
 
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
 
#define OHCI_UNLINK_TIMEOUT (HZ / 10)
 
/*-------------------------------------------------------------------------*/
 
static const char hcd_name [] = "ohci_hcd";
 
#include "ohci.h"
 
static inline void disable (struct ohci_hcd *ohci)
{
ohci->hcd.state = USB_STATE_HALT;
}
 
#include "ohci-hub.c"
#include "ohci-dbg.c"
#include "ohci-mem.c"
#include "ohci-q.c"
 
/*-------------------------------------------------------------------------*/
 
/*
* queue up an urb for anything except the root hub
*/
static int ohci_urb_enqueue (
struct usb_hcd *hcd,
struct urb *urb,
int mem_flags
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ed *ed;
urb_priv_t *urb_priv;
unsigned int pipe = urb->pipe;
int i, size = 0;
unsigned long flags;
int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
/* every endpoint has a ed, locate and maybe (re)initialize it */
if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval)))
return -ENOMEM;
 
/* for the private part of the URB we need the number of TDs (size) */
switch (ed->type) {
case PIPE_CONTROL:
/* td_submit_urb() doesn't yet handle these */
if (urb->transfer_buffer_length > 4096)
return -EMSGSIZE;
 
/* 1 TD for setup, 1 for ACK, plus ... */
size = 2;
/* FALLTHROUGH */
// case PIPE_INTERRUPT:
// case PIPE_BULK:
default:
/* one TD for every 4096 Bytes (can be upto 8K) */
size += urb->transfer_buffer_length / 4096;
/* ... and for any remaining bytes ... */
if ((urb->transfer_buffer_length % 4096) != 0)
size++;
/* ... and maybe a zero length packet to wrap it up */
if (size == 0)
size++;
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
&& (urb->transfer_buffer_length
% usb_maxpacket (urb->dev, pipe,
usb_pipeout (pipe))) == 0)
size++;
break;
case PIPE_ISOCHRONOUS: /* number of packets from URB */
size = urb->number_of_packets;
break;
}
 
/* allocate the private part of the URB */
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
mem_flags);
if (!urb_priv)
return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
/* fill the private part of the URB */
urb_priv->length = size;
urb_priv->ed = ed;
 
/* allocate the TDs (deferring hash chain updates) */
for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, mem_flags);
if (!urb_priv->td [i]) {
urb_priv->length = i;
urb_free_priv (ohci, urb_priv);
return -ENOMEM;
}
}
 
spin_lock_irqsave (&ohci->lock, flags);
 
/* don't submit to a dead HC */
if (!HCD_IS_RUNNING(ohci->hcd.state)) {
retval = -ENODEV;
goto fail;
}
 
/* schedule the ed if needed */
if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed);
if (retval < 0)
goto fail;
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = le16_to_cpu (ohci->hcca->frame_no);
 
/* delay a few frames before the first TD */
frame += max_t (u16, 8, ed->interval);
frame &= ~(ed->interval - 1);
frame |= ed->branch;
urb->start_frame = frame;
 
/* yes, only URB_ISO_ASAP is supported, and
* urb->start_frame is never used as input.
*/
}
} else if (ed->type == PIPE_ISOCHRONOUS)
urb->start_frame = ed->last_iso + ed->interval;
 
/* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed
* and update count of queued periodic urbs
*/
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb);
 
fail:
if (retval)
urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&ohci->lock, flags);
return retval;
}
 
/*
* decouple the URB from the HC queues (TDs, urb_priv); it's
* already marked using urb->status. reporting is always done
* asynchronously, and we might be dealing with an urb that's
* partially transferred, or an ED with other urbs being unlinked.
*/
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "UNLINK", 1);
#endif
 
spin_lock_irqsave (&ohci->lock, flags);
if (HCD_IS_RUNNING(ohci->hcd.state)) {
urb_priv_t *urb_priv;
 
/* Unless an IRQ completed the unlink while it was being
* handed to us, flag it for unlink and giveback, and force
* some upcoming INTR_SF to call finish_unlinks()
*/
urb_priv = urb->hcpriv;
if (urb_priv) {
if (urb_priv->ed->state == ED_OPER)
start_urb_unlink (ohci, urb_priv->ed);
}
} else {
/*
* with HC dead, we won't respect hc queue pointers
* any more ... just clean up every urb's memory.
*/
if (urb->hcpriv) {
spin_unlock (&ohci->lock);
finish_urb (ohci, urb, NULL);
spin_lock (&ohci->lock);
}
}
spin_unlock_irqrestore (&ohci->lock, flags);
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/* frees config/altsetting state for endpoints,
* including ED memory, dummy TD, and bulk/intr data toggle
*/
 
static void
ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int epnum = ep & USB_ENDPOINT_NUMBER_MASK;
unsigned long flags;
struct ed *ed;
unsigned limit = 1000;
 
/* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */
 
epnum <<= 1;
if (epnum != 0 && !(ep & USB_DIR_IN))
epnum |= 1;
 
rescan:
spin_lock_irqsave (&ohci->lock, flags);
ed = dev->ep [epnum];
if (!ed)
goto done;
 
if (!HCD_IS_RUNNING (ohci->hcd.state))
ed->state = ED_IDLE;
switch (ed->state) {
case ED_UNLINK: /* wait for hw to finish? */
/* major IRQ delivery trouble loses INTR_SF too... */
WARN_ON (limit-- == 0);
spin_unlock_irqrestore (&ohci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1);
goto rescan;
case ED_IDLE: /* fully unlinked */
if (list_empty (&ed->td_list)) {
td_free (ohci, ed->dummy);
ed_free (ohci, ed);
break;
}
/* else FALL THROUGH */
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. can't recover; must leak ed.
*/
ohci_err (ohci, "leak ed %p (#%d) state %d%s\n",
ed, epnum, ed->state,
list_empty (&ed->td_list) ? "" : " (has tds)");
td_free (ohci, ed->dummy);
break;
}
dev->ep [epnum] = 0;
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return;
}
 
static int ohci_get_frame (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
 
return le16_to_cpu (ohci->hcca->frame_no);
}
 
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
 
/* reset the HC and BUS */
 
static int hc_reset (struct ohci_hcd *ohci)
{
u32 temp;
 
/* SMM owns the HC? not for long!
* On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
*/
#ifndef __hppa__
if (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
 
/* this timeout is arbitrary. we make it long, so systems
* depending on usb keyboards may be usable even if the
* BIOS/SMM code seems pretty broken.
*/
temp = 500; /* arbitrary: five seconds */
 
writel (OHCI_INTR_OC, &ohci->regs->intrenable);
writel (OHCI_OCR, &ohci->regs->cmdstatus);
while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
wait_ms (10);
if (--temp == 0) {
ohci_err (ohci, "USB HC TakeOver failed!\n");
return -1;
}
}
}
#endif
 
/* Disable HC interrupts */
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
 
ohci_dbg (ohci, "reset, control = 0x%x\n",
readl (&ohci->regs->control));
 
/* Reset USB (needed by some controllers); RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
*/
ohci->hc_control = readl (&ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
writel (ohci->hc_control, &ohci->regs->control);
// flush those pci writes
(void) readl (&ohci->regs->control);
wait_ms (50);
 
/* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus);
temp = 30; /* ... allow extra time */
while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--temp == 0) {
ohci_err (ohci, "USB HC reset timed out!\n");
return -1;
}
udelay (1);
}
 
/* now we're in the SUSPEND state ... must go OPERATIONAL
* within 2msec else HC enters RESUME
*
* ... but some hardware won't init fmInterval "by the book"
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
*/
writel (ohci->hc_control, &ohci->regs->control);
// flush those pci writes
(void) readl (&ohci->regs->control);
 
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define LSTHRESH 0x628 /* lowspeed bit threshold */
 
/* Start an OHCI controller, set the BUS operational
* enable interrupts
* connect the virtual root hub
*/
static int hc_start (struct ohci_hcd *ohci)
{
u32 mask, tmp;
struct usb_device *udev;
struct usb_bus *bus;
 
spin_lock_init (&ohci->lock);
disable (ohci);
 
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
writel (0, &ohci->regs->ed_controlhead);
writel (0, &ohci->regs->ed_bulkhead);
 
/* a reset clears this */
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
 
/* force default fmInterval (we won't adjust it); init thresholds
* for last FS and LS packets, reserve 90% for periodic.
*/
writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval);
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
writel (LSTHRESH, &ohci->regs->lsthresh);
 
/* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work.
*/
if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0
|| !readl (&ohci->regs->periodicstart)) {
ohci_err (ohci, "init err\n");
return -EOVERFLOW;
}
 
/* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
writel (ohci->hc_control, &ohci->regs->control);
ohci->hcd.state = USB_STATE_RUNNING;
 
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH;
writel (mask, &ohci->regs->intrstatus);
writel (mask, &ohci->regs->intrenable);
 
/* handle root hub init quirks ... */
tmp = roothub_a (ohci);
tmp &= ~(RH_A_PSM | RH_A_OCPM);
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
/* NSC 87560 and maybe others */
tmp |= RH_A_NOCP;
tmp &= ~(RH_A_POTPGT | RH_A_NPS);
} else {
/* hub power always on; required for AMD-756 and some
* Mac platforms, use this mode everywhere by default
*/
tmp |= RH_A_NPS;
}
writel (tmp, &ohci->regs->roothub.a);
writel (RH_HS_LPSC, &ohci->regs->roothub.status);
writel (0, &ohci->regs->roothub.b);
// flush those pci writes
(void) readl (&ohci->regs->control);
 
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
/* connect the virtual root hub */
bus = hcd_to_bus (&ohci->hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus);
ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) {
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control);
return -ENOMEM;
}
 
udev->speed = USB_SPEED_FULL;
if (hcd_register_root (&ohci->hcd) != 0) {
usb_put_dev (udev);
bus->root_hub = NULL;
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
 
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/* an interrupt happens */
 
static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ohci_regs *regs = ohci->regs;
int ints;
 
/* we can eliminate a (slow) readl() if _only_ WDH caused this irq */
if ((ohci->hcca->done_head != 0)
&& ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
ints = OHCI_INTR_WDH;
 
/* cardbus/... hardware gone before remove() */
} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
disable (ohci);
ohci_dbg (ohci, "device removed!\n");
return;
 
/* interrupt for some other device? */
} else if ((ints &= readl (&regs->intrenable)) == 0) {
return;
}
 
if (ints & OHCI_INTR_UE) {
disable (ohci);
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
// e.g. due to PCI Master/Target Abort
 
ohci_dump (ohci, 1);
hc_reset (ohci);
}
if (ints & OHCI_INTR_WDH) {
if (HCD_IS_RUNNING(hcd->state))
writel (OHCI_INTR_WDH, &regs->intrdisable);
dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs);
if (HCD_IS_RUNNING(hcd->state))
writel (OHCI_INTR_WDH, &regs->intrenable);
}
/* could track INTR_SO to reduce available PCI/... bandwidth */
 
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
spin_lock (&ohci->lock);
if (ohci->ed_rm_list)
finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no),
ptregs);
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& HCD_IS_RUNNING(ohci->hcd.state))
writel (OHCI_INTR_SF, &regs->intrdisable);
spin_unlock (&ohci->lock);
 
if (HCD_IS_RUNNING(ohci->hcd.state)) {
writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable);
// flush those pci writes
(void) readl (&ohci->regs->control);
}
}
 
/*-------------------------------------------------------------------------*/
 
static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
 
ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
ohci->hcd.state);
ohci_dump (ohci, 1);
 
if (HCD_IS_RUNNING(ohci->hcd.state))
hc_reset (ohci);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
ohci->hcca = NULL;
ohci->hcca_dma = 0;
}
}
 
/*-------------------------------------------------------------------------*/
 
// FIXME: this restart logic should be generic,
// and handle full hcd state cleanup
 
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
 
#ifdef CONFIG_PM
static int hc_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
 
disable (ohci);
if (hcd_to_bus (&ohci->hcd)->root_hub)
usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub);
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
/* no EDs to remove */
ohci->ed_rm_list = NULL;
 
/* empty control and bulk lists */
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
 
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp);
return temp;
} else
ohci_dbg (ohci, "restart complete\n");
return 0;
}
#endif
 
/*-------------------------------------------------------------------------*/
 
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_LICENSE ("GPL");
 
#ifdef CONFIG_PCI
#include "ohci-pci.c"
#endif
 
#ifdef CONFIG_SA1111
#include "ohci-sa1111.c"
#endif
 
#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111))
#error "missing bus glue for ohci-hcd"
#endif
/shark/trunk/drivers/usb/host/ohci-q.c
0,0 → 1,1048
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
 
static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
{
int last = urb_priv->length - 1;
 
if (last >= 0) {
int i;
struct td *td;
 
for (i = 0; i <= last; i++) {
td = urb_priv->td [i];
if (td)
td_free (hc, td);
}
}
 
kfree (urb_priv);
}
 
/*-------------------------------------------------------------------------*/
 
/*
* URB goes back to driver, and isn't reissued.
* It's completely gone from HC data structures.
* PRECONDITION: no locks held, irqs blocked (Giveback can call into HCD.)
*/
static void
finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)
{
// ASSERT (urb->hcpriv != 0);
 
urb_free_priv (ohci, urb->hcpriv);
urb->hcpriv = NULL;
 
spin_lock (&urb->lock);
if (likely (urb->status == -EINPROGRESS))
urb->status = 0;
/* report short control reads right even though the data TD always
* has TD_R set. (much simpler, but creates the 1-td limit.)
*/
if (unlikely (urb->transfer_flags & URB_SHORT_NOT_OK)
&& unlikely (usb_pipecontrol (urb->pipe))
&& urb->actual_length < urb->transfer_buffer_length
&& usb_pipein (urb->pipe)
&& urb->status == 0) {
urb->status = -EREMOTEIO;
}
spin_unlock (&urb->lock);
 
// what lock protects these?
switch (usb_pipetype (urb->pipe)) {
case PIPE_ISOCHRONOUS:
hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs--;
break;
case PIPE_INTERRUPT:
hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs--;
break;
}
 
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "RET", usb_pipeout (urb->pipe));
#endif
usb_hcd_giveback_urb (&ohci->hcd, urb, regs);
}
 
 
/*-------------------------------------------------------------------------*
* ED handling functions
*-------------------------------------------------------------------------*/
 
/* search for the right schedule branch to use for a periodic ed.
* does some load balancing; returns the branch, or negative errno.
*/
static int balance (struct ohci_hcd *ohci, int interval, int load)
{
int i, branch = -ENOSPC;
 
/* iso periods can be huge; iso tds specify frame numbers */
if (interval > NUM_INTS)
interval = NUM_INTS;
 
/* search for the least loaded schedule branch of that period
* that has enough bandwidth left unreserved.
*/
for (i = 0; i < interval ; i++) {
if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
#if 1 /* CONFIG_USB_BANDWIDTH */
int j;
 
/* usb 1.1 says 90% of one frame */
for (j = i; j < NUM_INTS; j += interval) {
if ((ohci->load [j] + load) > 900)
break;
}
if (j < NUM_INTS)
continue;
#endif
branch = i;
}
}
return branch;
}
 
/*-------------------------------------------------------------------------*/
 
/* both iso and interrupt requests have periods; this routine puts them
* into the schedule tree in the apppropriate place. most iso devices use
* 1msec periods, but that's not required.
*/
static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)
{
unsigned i;
 
ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n",
(ed->hwINFO & ED_ISO) ? "iso " : "",
ed, ed->branch, ed->load, ed->interval);
 
for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
struct ed **prev = &ohci->periodic [i];
u32 *prev_p = &ohci->hcca->int_table [i];
struct ed *here = *prev;
 
/* sorting each branch by period (slow before fast)
* lets us share the faster parts of the tree.
* (plus maybe: put interrupt eds before iso)
*/
while (here && ed != here) {
if (ed->interval > here->interval)
break;
prev = &here->ed_next;
prev_p = &here->hwNextED;
here = *prev;
}
if (ed != here) {
ed->ed_next = here;
if (here)
ed->hwNextED = *prev_p;
wmb ();
*prev = ed;
*prev_p = cpu_to_le32p (&ed->dma);
}
ohci->load [i] += ed->load;
}
hcd_to_bus (&ohci->hcd)->bandwidth_allocated += ed->load / ed->interval;
}
 
/* link an ed into one of the HC chains */
 
static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
{
int branch;
 
ed->state = ED_OPER;
ed->ed_prev = 0;
ed->ed_next = 0;
ed->hwNextED = 0;
wmb ();
 
/* we care about rm_list when setting CLE/BLE in case the HC was at
* work on some TD when CLE/BLE was turned off, and isn't quiesced
* yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.
*
* control and bulk EDs are doubly linked (ed_next, ed_prev), but
* periodic ones are singly linked (ed_next). that's because the
* periodic schedule encodes a tree like figure 3-5 in the ohci
* spec: each qh can have several "previous" nodes, and the tree
* doesn't have unused/idle descriptors.
*/
switch (ed->type) {
case PIPE_CONTROL:
if (ohci->ed_controltail == NULL) {
writel (ed->dma, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->ed_next = ed;
ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
}
ed->ed_prev = ohci->ed_controltail;
if (!ohci->ed_controltail && !ohci->ed_rm_list) {
ohci->hc_control |= OHCI_CTRL_CLE;
writel (0, &ohci->regs->ed_controlcurrent);
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_controltail = ed;
break;
 
case PIPE_BULK:
if (ohci->ed_bulktail == NULL) {
writel (ed->dma, &ohci->regs->ed_bulkhead);
} else {
ohci->ed_bulktail->ed_next = ed;
ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
}
ed->ed_prev = ohci->ed_bulktail;
if (!ohci->ed_bulktail && !ohci->ed_rm_list) {
ohci->hc_control |= OHCI_CTRL_BLE;
writel (0, &ohci->regs->ed_bulkcurrent);
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_bulktail = ed;
break;
 
// case PIPE_INTERRUPT:
// case PIPE_ISOCHRONOUS:
default:
branch = balance (ohci, ed->interval, ed->load);
if (branch < 0) {
ohci_dbg (ohci,
"ERR %d, interval %d msecs, load %d\n",
branch, ed->interval, ed->load);
// FIXME if there are TDs queued, fail them!
return branch;
}
ed->branch = branch;
periodic_link (ohci, ed);
}
 
/* the HC may not see the schedule updates yet, but if it does
* then they'll be properly ordered.
*/
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
/* scan the periodic table to find and unlink this ED */
static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
{
int i;
 
for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
struct ed *temp;
struct ed **prev = &ohci->periodic [i];
u32 *prev_p = &ohci->hcca->int_table [i];
 
while (*prev && (temp = *prev) != ed) {
prev_p = &temp->hwNextED;
prev = &temp->ed_next;
}
if (*prev) {
*prev_p = ed->hwNextED;
*prev = ed->ed_next;
}
ohci->load [i] -= ed->load;
}
hcd_to_bus (&ohci->hcd)->bandwidth_allocated -= ed->load / ed->interval;
 
ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",
(ed->hwINFO & ED_ISO) ? "iso " : "",
ed, ed->branch, ed->load, ed->interval);
}
 
/* unlink an ed from one of the HC chains.
* just the link to the ed is unlinked.
* the link from the ed still points to another operational ed or 0
* so the HC can eventually finish the processing of the unlinked ed
*/
static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
{
ed->hwINFO |= ED_SKIP;
 
switch (ed->type) {
case PIPE_CONTROL:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
writel (0, &ohci->regs->ed_controlcurrent);
// post those pci writes
(void) readl (&ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED),
&ohci->regs->ed_controlhead);
} else {
ed->ed_prev->ed_next = ed->ed_next;
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
if (ohci->ed_controltail)
ohci->ed_controltail->ed_next = 0;
} else if (ed->ed_next) {
ed->ed_next->ed_prev = ed->ed_prev;
}
break;
 
case PIPE_BULK:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
writel (0, &ohci->regs->ed_bulkcurrent);
// post those pci writes
(void) readl (&ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED),
&ohci->regs->ed_bulkhead);
} else {
ed->ed_prev->ed_next = ed->ed_next;
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
if (ohci->ed_bulktail)
ohci->ed_bulktail->ed_next = 0;
} else if (ed->ed_next) {
ed->ed_next->ed_prev = ed->ed_prev;
}
break;
 
// case PIPE_INTERRUPT:
// case PIPE_ISOCHRONOUS:
default:
periodic_unlink (ohci, ed);
break;
}
 
/* NOTE: Except for a couple of exceptionally clean unlink cases
* (like unlinking the only c/b ED, with no TDs) HCs may still be
* caching this operational ED (or its address). Safe unlinking
* involves not marking it ED_IDLE till INTR_SF; we always do that
* if td_list isn't empty. Otherwise the race is small; but ...
*/
if (ed->state == ED_OPER) {
ed->state = ED_IDLE;
ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
ed->hwHeadP &= ~ED_H;
wmb ();
}
}
 
 
/*-------------------------------------------------------------------------*/
 
/* get and maybe (re)init an endpoint. init _should_ be done only as part
* of usb_set_configuration() or usb_set_interface() ... but the USB stack
* isn't very stateful, so we re-init whenever the HC isn't looking.
*/
static struct ed *ed_get (
struct ohci_hcd *ohci,
struct usb_device *udev,
unsigned int pipe,
int interval
) {
int is_out = !usb_pipein (pipe);
int type = usb_pipetype (pipe);
struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
struct ed *ed;
unsigned ep;
unsigned long flags;
 
ep = usb_pipeendpoint (pipe) << 1;
if (type != PIPE_CONTROL && is_out)
ep |= 1;
 
spin_lock_irqsave (&ohci->lock, flags);
 
if (!(ed = dev->ep [ep])) {
struct td *td;
 
ed = ed_alloc (ohci, SLAB_ATOMIC);
if (!ed) {
/* out of memory */
goto done;
}
dev->ep [ep] = ed;
 
/* dummy td; end of td list for ed */
td = td_alloc (ohci, SLAB_ATOMIC);
if (!td) {
/* out of memory */
ed_free (ohci, ed);
ed = 0;
goto done;
}
ed->dummy = td;
ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
ed->state = ED_IDLE;
ed->type = type;
}
 
/* NOTE: only ep0 currently needs this "re"init logic, during
* enumeration (after set_address, or if ep0 maxpacket >8).
*/
if (ed->state == ED_IDLE) {
u32 info;
 
info = usb_pipedevice (pipe);
info |= (ep >> 1) << 7;
info |= usb_maxpacket (udev, pipe, is_out) << 16;
info = cpu_to_le32 (info);
if (udev->speed == USB_SPEED_LOW)
info |= ED_LOWSPEED;
/* only control transfers store pids in tds */
if (type != PIPE_CONTROL) {
info |= is_out ? ED_OUT : ED_IN;
if (type != PIPE_BULK) {
/* periodic transfers... */
if (type == PIPE_ISOCHRONOUS)
info |= ED_ISO;
else if (interval > 32) /* iso can be bigger */
interval = 32;
ed->interval = interval;
ed->load = usb_calc_bus_time (
udev->speed, !is_out,
type == PIPE_ISOCHRONOUS,
usb_maxpacket (udev, pipe, is_out))
/ 1000;
}
}
ed->hwINFO = info;
}
 
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return ed;
}
 
/*-------------------------------------------------------------------------*/
 
/* request unlinking of an endpoint from an operational HC.
* put the ep on the rm_list
* real work is done at the next start frame (SF) hardware interrupt
*/
static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
{
ed->hwINFO |= ED_DEQUEUE;
ed->state = ED_UNLINK;
ed_deschedule (ohci, ed);
 
/* SF interrupt might get delayed; record the frame counter value that
* indicates when the HC isn't looking at it, so concurrent unlinks
* behave. frame_no wraps every 2^16 msec, and changes right before
* SF is triggered.
*/
ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
 
/* rm_list is just singly linked, for simplicity */
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = 0;
ohci->ed_rm_list = ed;
 
/* enable SOF interrupt */
if (HCD_IS_RUNNING (ohci->hcd.state)) {
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
// flush those pci writes
(void) readl (&ohci->regs->control);
}
}
 
/*-------------------------------------------------------------------------*
* TD handling functions
*-------------------------------------------------------------------------*/
 
/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
 
static void
td_fill (struct ohci_hcd *ohci, u32 info,
dma_addr_t data, int len,
struct urb *urb, int index)
{
struct td *td, *td_pt;
struct urb_priv *urb_priv = urb->hcpriv;
int is_iso = info & TD_ISO;
int hash;
 
// ASSERT (index < urb_priv->length);
 
/* aim for only one interrupt per urb. mostly applies to control
* and iso; other urbs rarely need more than one TD per urb.
* this way, only final tds (or ones with an error) cause IRQs.
* at least immediately; use DI=6 in case any control request is
* tempted to die part way through.
*
* NOTE: could delay interrupts even for the last TD, and get fewer
* interrupts ... increasing per-urb latency by sharing interrupts.
* Drivers that queue bulk urbs may request that behavior.
*/
if (index != (urb_priv->length - 1)
|| (urb->transfer_flags & URB_NO_INTERRUPT))
info |= TD_DI_SET (6);
 
/* use this td as the next dummy */
td_pt = urb_priv->td [index];
 
/* fill the old dummy TD */
td = urb_priv->td [index] = urb_priv->ed->dummy;
urb_priv->ed->dummy = td_pt;
 
td->ed = urb_priv->ed;
td->next_dl_td = NULL;
td->index = index;
td->urb = urb;
td->data_dma = data;
if (!len)
data = 0;
 
td->hwINFO = cpu_to_le32 (info);
if (is_iso) {
td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
td->ed->last_iso = info & 0xffff;
} else {
td->hwCBP = cpu_to_le32 (data);
}
if (data)
td->hwBE = cpu_to_le32 (data + len - 1);
else
td->hwBE = 0;
td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
 
/* append to queue */
list_add_tail (&td->td_list, &td->ed->td_list);
 
/* hash it for later reverse mapping */
hash = TD_HASH_FUNC (td->td_dma);
td->td_hash = ohci->td_hash [hash];
ohci->td_hash [hash] = td;
 
/* HC might read the TD (or cachelines) right away ... */
wmb ();
td->ed->hwTailP = td->hwNextTD;
}
 
/*-------------------------------------------------------------------------*/
 
/* Prepare all TDs of a transfer, and queue them onto the ED.
* Caller guarantees HC is active.
* Usually the ED is already on the schedule, so TDs might be
* processed as soon as they're queued.
*/
static void td_submit_urb (
struct ohci_hcd *ohci,
struct urb *urb
) {
struct urb_priv *urb_priv = urb->hcpriv;
dma_addr_t data;
int data_len = urb->transfer_buffer_length;
int cnt = 0;
u32 info = 0;
int is_out = usb_pipeout (urb->pipe);
 
/* OHCI handles the bulk/interrupt data toggles itself. We just
* use the device toggle bits for resetting, and rely on the fact
* that resetting toggle is meaningless if the endpoint is active.
*/
if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
is_out, 1);
urb_priv->ed->hwHeadP &= ~ED_C;
}
 
urb_priv->td_cnt = 0;
 
if (data_len)
data = urb->transfer_dma;
else
data = 0;
 
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by
* using TD_CC_GET, as well as by seeing them on the done list.
* (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)
*/
switch (urb_priv->ed->type) {
 
/* Bulk and interrupt are identical except for where in the schedule
* their EDs live.
*/
case PIPE_INTERRUPT:
/* ... and periodic urbs have extra accounting */
hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++;
/* FALLTHROUGH */
case PIPE_BULK:
info = is_out
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
/* TDs _could_ transfer up to 8K each */
while (data_len > 4096) {
td_fill (ohci, info, data, 4096, urb, cnt);
data += 4096;
data_len -= 4096;
cnt++;
}
/* maybe avoid ED halt on final TD short read */
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
info |= TD_R;
td_fill (ohci, info, data, data_len, urb, cnt);
cnt++;
if ((urb->transfer_flags & URB_ZERO_PACKET)
&& cnt < urb_priv->length) {
td_fill (ohci, info, 0, 0, urb, cnt);
cnt++;
}
/* maybe kickstart bulk list */
if (urb_priv->ed->type == PIPE_BULK) {
wmb ();
writel (OHCI_BLF, &ohci->regs->cmdstatus);
}
break;
 
/* control manages DATA0/DATA1 toggle per-request; SETUP resets it,
* any DATA phase works normally, and the STATUS ack is special.
*/
case PIPE_CONTROL:
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
if (data_len > 0) {
info = TD_CC | TD_R | TD_T_DATA1;
info |= is_out ? TD_DP_OUT : TD_DP_IN;
/* NOTE: mishandles transfers >8K, some >4K */
td_fill (ohci, info, data, data_len, urb, cnt++);
}
info = is_out
? TD_CC | TD_DP_IN | TD_T_DATA1
: TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, urb, cnt++);
/* maybe kickstart control list */
wmb ();
writel (OHCI_CLF, &ohci->regs->cmdstatus);
break;
 
/* ISO has no retransmit, so no toggle; and it uses special TDs.
* Each TD could handle multiple consecutive frames (interval 1);
* we could often reduce the number of TDs here.
*/
case PIPE_ISOCHRONOUS:
for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
int frame = urb->start_frame;
 
// FIXME scheduling should handle frame counter
// roll-around ... exotic case (and OHCI has
// a 2^16 iso range, vs other HCs max of 2^10)
frame += cnt * urb->interval;
frame &= 0xffff;
td_fill (ohci, TD_CC | TD_ISO | frame,
data + urb->iso_frame_desc [cnt].offset,
urb->iso_frame_desc [cnt].length, urb, cnt);
}
hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++;
break;
}
// ASSERT (urb_priv->length == cnt);
}
 
/*-------------------------------------------------------------------------*
* Done List handling functions
*-------------------------------------------------------------------------*/
 
/* calculate transfer length/status and update the urb
* PRECONDITION: irqsafe (only for urb->status locking)
*/
static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
{
u32 tdINFO = le32_to_cpup (&td->hwINFO);
int cc = 0;
 
list_del (&td->td_list);
 
/* ISO ... drivers see per-TD length/status */
if (tdINFO & TD_ISO) {
u16 tdPSW = le16_to_cpu (td->hwPSW [0]);
int dlen = 0;
 
/* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */
 
cc = (tdPSW >> 12) & 0xF;
if (tdINFO & TD_CC) /* hc didn't touch? */
return;
 
if (usb_pipeout (urb->pipe))
dlen = urb->iso_frame_desc [td->index].length;
else {
/* short reads are always OK for ISO */
if (cc == TD_DATAUNDERRUN)
cc = TD_CC_NOERROR;
dlen = tdPSW & 0x3ff;
}
urb->actual_length += dlen;
urb->iso_frame_desc [td->index].actual_length = dlen;
urb->iso_frame_desc [td->index].status = cc_to_error [cc];
 
if (cc != TD_CC_NOERROR)
ohci_vdbg (ohci,
"urb %p iso td %p (%d) len %d cc %d\n",
urb, td, 1 + td->index, dlen, cc);
 
/* BULK, INT, CONTROL ... drivers see aggregate length/status,
* except that "setup" bytes aren't counted and "short" transfers
* might not be reported as errors.
*/
} else {
int type = usb_pipetype (urb->pipe);
u32 tdBE = le32_to_cpup (&td->hwBE);
 
cc = TD_CC_GET (tdINFO);
 
/* control endpoints only have soft stalls */
if (type != PIPE_CONTROL && cc == TD_CC_STALL)
usb_endpoint_halt (urb->dev,
usb_pipeendpoint (urb->pipe),
usb_pipeout (urb->pipe));
 
/* update packet status if needed (short is normally ok) */
if (cc == TD_DATAUNDERRUN
&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
cc = TD_CC_NOERROR;
if (cc != TD_CC_NOERROR && cc < 0x0E) {
spin_lock (&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = cc_to_error [cc];
spin_unlock (&urb->lock);
}
 
/* count all non-empty packets except control SETUP packet */
if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
if (td->hwCBP == 0)
urb->actual_length += tdBE - td->data_dma + 1;
else
urb->actual_length +=
le32_to_cpup (&td->hwCBP)
- td->data_dma;
}
 
if (cc != TD_CC_NOERROR && cc < 0x0E)
ohci_vdbg (ohci,
"urb %p td %p (%d) cc %d, len=%d/%d\n",
urb, td, 1 + td->index, cc,
urb->actual_length,
urb->transfer_buffer_length);
}
}
 
/*-------------------------------------------------------------------------*/
 
static inline struct td *
ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
{
struct urb *urb = td->urb;
struct ed *ed = td->ed;
struct list_head *tmp = td->td_list.next;
u32 toggle = ed->hwHeadP & ED_C;
 
/* clear ed halt; this is the td that caused it, but keep it inactive
* until its urb->complete() has a chance to clean up.
*/
ed->hwINFO |= ED_SKIP;
wmb ();
ed->hwHeadP &= ~ED_H;
 
/* put any later tds from this urb onto the donelist, after 'td',
* order won't matter here: no errors, and nothing was transferred.
* also patch the ed so it looks as if those tds completed normally.
*/
while (tmp != &ed->td_list) {
struct td *next;
u32 info;
 
next = list_entry (tmp, struct td, td_list);
tmp = next->td_list.next;
 
if (next->urb != urb)
break;
 
/* NOTE: if multi-td control DATA segments get supported,
* this urb had one of them, this td wasn't the last td
* in that segment (TD_R clear), this ed halted because
* of a short read, _and_ URB_SHORT_NOT_OK is clear ...
* then we need to leave the control STATUS packet queued
* and clear ED_SKIP.
*/
info = next->hwINFO;
info |= cpu_to_le32 (TD_DONE);
info &= ~cpu_to_le32 (TD_CC);
next->hwINFO = info;
 
next->next_dl_td = rev;
rev = next;
 
if (ed->hwTailP == cpu_to_le32 (next->td_dma))
ed->hwTailP = next->hwNextTD;
ed->hwHeadP = next->hwNextTD | toggle;
}
 
/* help for troubleshooting: report anything that
* looks odd ... that doesn't include protocol stalls
* (or maybe some other things)
*/
switch (cc) {
case TD_DATAUNDERRUN:
if ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)
break;
/* fallthrough */
case TD_CC_STALL:
if (usb_pipecontrol (urb->pipe))
break;
/* fallthrough */
default:
ohci_dbg (ohci,
"urb %p path %s ep%d%s %08x cc %d --> status %d\n",
urb, urb->dev->devpath,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
le32_to_cpu (td->hwINFO),
cc, cc_to_error [cc]);
}
 
return rev;
}
 
/* replies to the request have to be on a FIFO basis so
* we unreverse the hc-reversed done-list
*/
static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
{
u32 td_dma;
struct td *td_rev = NULL;
struct td *td = NULL;
unsigned long flags;
 
spin_lock_irqsave (&ohci->lock, flags);
 
td_dma = le32_to_cpup (&ohci->hcca->done_head);
ohci->hcca->done_head = 0;
 
/* get TD from hc's singly linked list, and
* prepend to ours. ed->td_list changes later.
*/
while (td_dma) {
int cc;
 
td = dma_to_td (ohci, td_dma);
if (!td) {
ohci_err (ohci, "bad entry %8x\n", td_dma);
break;
}
 
td->hwINFO |= cpu_to_le32 (TD_DONE);
cc = TD_CC_GET (le32_to_cpup (&td->hwINFO));
 
/* Non-iso endpoints can halt on error; un-halt,
* and dequeue any other TDs from this urb.
* No other TD could have caused the halt.
*/
if (cc != TD_CC_NOERROR && (td->ed->hwHeadP & ED_H))
td_rev = ed_halted (ohci, td, cc, td_rev);
 
td->next_dl_td = td_rev;
td_rev = td;
td_dma = le32_to_cpup (&td->hwNextTD);
}
spin_unlock_irqrestore (&ohci->lock, flags);
return td_rev;
}
 
/*-------------------------------------------------------------------------*/
 
/* wrap-aware logic stolen from <linux/jiffies26.h> */
#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
 
/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
static void
finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)
{
struct ed *ed, **last;
 
rescan_all:
for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
struct list_head *entry, *tmp;
int completed, modified;
u32 *prev;
 
/* only take off EDs that the HC isn't using, accounting for
* frame counter wraps.
*/
if (tick_before (tick, ed->tick)
&& HCD_IS_RUNNING(ohci->hcd.state)) {
last = &ed->ed_next;
continue;
}
 
/* reentrancy: if we drop the schedule lock, someone might
* have modified this list. normally it's just prepending
* entries (which we'd ignore), but paranoia won't hurt.
*/
*last = ed->ed_next;
ed->ed_next = 0;
modified = 0;
 
/* unlink urbs as requested, but rescan the list after
* we call a completion since it might have unlinked
* another (earlier) urb
*/
rescan_this:
completed = 0;
prev = &ed->hwHeadP;
list_for_each_safe (entry, tmp, &ed->td_list) {
struct td *td;
struct urb *urb;
urb_priv_t *urb_priv;
u32 savebits;
 
td = list_entry (entry, struct td, td_list);
urb = td->urb;
urb_priv = td->urb->hcpriv;
 
if (urb->status == -EINPROGRESS) {
prev = &td->hwNextTD;
continue;
}
 
/* patch pointers hc uses ... tail, if we're removing
* an otherwise active td, and whatever td pointer
* points to this td
*/
if (ed->hwTailP == cpu_to_le32 (td->td_dma))
ed->hwTailP = td->hwNextTD;
savebits = *prev & ~cpu_to_le32 (TD_MASK);
*prev = td->hwNextTD | savebits;
 
/* HC may have partly processed this TD */
td_done (ohci, urb, td);
urb_priv->td_cnt++;
 
/* if URB is done, clean up */
if (urb_priv->td_cnt == urb_priv->length) {
modified = completed = 1;
spin_unlock (&ohci->lock);
finish_urb (ohci, urb, regs);
spin_lock (&ohci->lock);
}
}
if (completed && !list_empty (&ed->td_list))
goto rescan_this;
 
/* ED's now officially unlinked, hc doesn't see */
ed->state = ED_IDLE;
ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
ed->hwHeadP &= ~ED_H;
ed->hwNextED = 0;
 
/* but if there's work queued, reschedule */
if (!list_empty (&ed->td_list)) {
if (HCD_IS_RUNNING(ohci->hcd.state))
ed_schedule (ohci, ed);
}
 
if (modified)
goto rescan_all;
}
 
/* maybe reenable control and bulk lists */
if (HCD_IS_RUNNING(ohci->hcd.state) && !ohci->ed_rm_list) {
u32 command = 0, control = 0;
 
if (ohci->ed_controltail) {
command |= OHCI_CLF;
if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
control |= OHCI_CTRL_CLE;
writel (0, &ohci->regs->ed_controlcurrent);
}
}
if (ohci->ed_bulktail) {
command |= OHCI_BLF;
if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
control |= OHCI_CTRL_BLE;
writel (0, &ohci->regs->ed_bulkcurrent);
}
}
/* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
if (control) {
ohci->hc_control |= control;
writel (ohci->hc_control, &ohci->regs->control);
}
if (command)
writel (command, &ohci->regs->cmdstatus);
}
}
 
 
 
/*-------------------------------------------------------------------------*/
 
/*
* Process normal completions (error or success) and clean the schedules.
*
* This is the main path for handing urbs back to drivers. The only other
* path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of
* scanning the (re-reversed) donelist as this does.
*/
static void
dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs)
{
unsigned long flags;
 
spin_lock_irqsave (&ohci->lock, flags);
while (td) {
struct td *td_next = td->next_dl_td;
struct urb *urb = td->urb;
urb_priv_t *urb_priv = urb->hcpriv;
struct ed *ed = td->ed;
 
/* update URB's length and status from TD */
td_done (ohci, urb, td);
urb_priv->td_cnt++;
 
/* If all this urb's TDs are done, call complete() */
if (urb_priv->td_cnt == urb_priv->length) {
spin_unlock (&ohci->lock);
finish_urb (ohci, urb, regs);
spin_lock (&ohci->lock);
}
 
/* clean schedule: unlink EDs that are no longer busy */
if (list_empty (&ed->td_list))
ed_deschedule (ohci, ed);
/* ... reenabling halted EDs only after fault cleanup */
else if ((ed->hwINFO & (ED_SKIP | ED_DEQUEUE)) == ED_SKIP) {
td = list_entry (ed->td_list.next, struct td, td_list);
if (!(td->hwINFO & TD_DONE)) {
ed->hwINFO &= ~ED_SKIP;
/* ... hc may need waking-up */
switch (ed->type) {
case PIPE_CONTROL:
writel (OHCI_CLF,
&ohci->regs->cmdstatus);
break;
case PIPE_BULK:
writel (OHCI_BLF,
&ohci->regs->cmdstatus);
break;
}
}
}
 
td = td_next;
}
spin_unlock_irqrestore (&ohci->lock, flags);
}
/shark/trunk/drivers/usb/host/ehci-hub.c
0,0 → 1,344
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* this file is part of ehci-hcd.c */
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI Root Hub ... the nonsharable stuff
*
* Registers don't need cpu_to_le32, that happens transparently
*/
 
/*-------------------------------------------------------------------------*/
 
static int check_reset_complete (
struct ehci_hcd *ehci,
int index,
int port_status
) {
if (!(port_status & PORT_CONNECT)) {
ehci->reset_done [index] = 0;
return port_status;
}
 
/* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) {
ehci_dbg (ehci, "port %d full speed --> companion\n",
index + 1);
 
// what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER;
writel (port_status, &ehci->regs->port_status [index]);
 
} else
ehci_dbg (ehci, "port %d high speed\n", index + 1);
 
return port_status;
}
 
/*-------------------------------------------------------------------------*/
 
 
/* build "status change" packet (one or two bytes) from HC registers */
 
static int
ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp, status = 0;
int ports, i, retval = 1;
unsigned long flags;
 
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
if (ports > 7) {
buf [1] = 0;
retval++;
}
/* no hub change reports (bit 0) for now (power, ...) */
 
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ports; i++) {
temp = readl (&ehci->regs->port_status [i]);
if (temp & PORT_OWNER) {
/* don't report this in GetPortStatus */
if (temp & PORT_CSC) {
temp &= ~PORT_CSC;
writel (temp, &ehci->regs->port_status [i]);
}
continue;
}
if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
status = STS_PCD;
}
}
spin_unlock_irqrestore (&ehci->lock, flags);
return status ? retval : 0;
}
 
/*-------------------------------------------------------------------------*/
 
static void
ehci_hub_descriptor (
struct ehci_hcd *ehci,
struct usb_hub_descriptor *desc
) {
int ports = HCS_N_PORTS (ehci->hcs_params);
u16 temp;
 
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */
desc->bHubContrCurrent = 0;
 
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
 
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
memset (&desc->bitmap [0], 0, temp);
memset (&desc->bitmap [temp], 0xff, temp);
 
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; /* per-port power control */
if (HCS_INDICATOR (ehci->hcs_params))
temp |= 0x0080; /* per-port indicators (LEDs) */
desc->wHubCharacteristics = cpu_to_le16 (temp);
}
 
/*-------------------------------------------------------------------------*/
 
static int ehci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
u32 temp, status;
unsigned long flags;
int retval = 0;
 
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
* HCS_INDICATOR may say we can change LEDs to off/amber/green.
* (track current state ourselves) ... blink for diagnostics,
* power, "this is the one", etc. EHCI spec supports this.
*/
 
spin_lock_irqsave (&ehci->lock, flags);
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* no hub-wide feature/status flags */
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = readl (&ehci->regs->port_status [wIndex]);
if (temp & PORT_OWNER)
break;
 
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
writel (temp & ~PORT_PE,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_ENABLE:
writel (temp | PORT_PEC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_SUSPEND:
case USB_PORT_FEAT_C_SUSPEND:
/* ? */
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
writel (temp & ~PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_CONNECTION:
writel (temp | PORT_CSC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
writel (temp | PORT_OCC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_RESET:
/* GetPortStatus clears reset */
break;
default:
goto error;
}
readl (&ehci->regs->command); /* unblock posted write */
break;
case GetHubDescriptor:
ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
buf);
break;
case GetHubStatus:
/* no hub-wide feature/status flags */
memset (buf, 0, 4);
//cpu_to_le32s ((u32 *) buf);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
status = 0;
temp = readl (&ehci->regs->port_status [wIndex]);
 
// wPortChange bits
if (temp & PORT_CSC)
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC)
status |= 1 << USB_PORT_FEAT_C_ENABLE;
// USB_PORT_FEAT_C_SUSPEND
if (temp & PORT_OCC)
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
/* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET)
&& time_after (jiffies26,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_RESET;
 
/* force reset to complete */
writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]);
do {
temp = readl (
&ehci->regs->port_status [wIndex]);
udelay (10);
} while (temp & PORT_RESET);
 
/* see what we found out */
temp = check_reset_complete (ehci, wIndex, temp);
}
 
// don't show wPortStatus if it's owned by a companion hc
if (!(temp & PORT_OWNER)) {
if (temp & PORT_CONNECT) {
status |= 1 << USB_PORT_FEAT_CONNECTION;
status |= 1 << USB_PORT_FEAT_HIGHSPEED;
}
if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & PORT_SUSPEND)
status |= 1 << USB_PORT_FEAT_SUSPEND;
if (temp & PORT_OC)
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (temp & PORT_RESET)
status |= 1 << USB_PORT_FEAT_RESET;
if (temp & PORT_POWER)
status |= 1 << USB_PORT_FEAT_POWER;
}
 
#ifndef EHCI_VERBOSE_DEBUG
if (status & ~0xffff) /* only if wPortChange is interesting */
#endif
dbg_port (ehci, "GetStatus", wIndex + 1, temp);
// we "know" this alignment is good, caller used kmalloc()...
*((u32 *) buf) = cpu_to_le32 (status);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* no hub-wide feature/status flags */
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = readl (&ehci->regs->port_status [wIndex]);
if (temp & PORT_OWNER)
break;
 
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
writel (temp | PORT_SUSPEND,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
writel (temp | PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_RESET:
/* line status bits may report this as low speed */
if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& PORT_USB11 (temp)) {
ehci_dbg (ehci,
"port %d low speed --> companion\n",
wIndex + 1);
temp |= PORT_OWNER;
} else {
ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
temp |= PORT_RESET;
temp &= ~PORT_PE;
 
/*
* caller must wait, then call GetPortStatus
* usb 2.0 spec says 50 ms resets on root
*/
ehci->reset_done [wIndex] = jiffies26
+ ((50 /* msec */ * HZ) / 1000);
}
writel (temp, &ehci->regs->port_status [wIndex]);
break;
default:
goto error;
}
readl (&ehci->regs->command); /* unblock posted writes */
break;
 
default:
error:
/* "stall" on error */
retval = -EPIPE;
}
spin_unlock_irqrestore (&ehci->lock, flags);
return retval;
}
/shark/trunk/drivers/usb/host/ehci-mem.c
0,0 → 1,249
/*
* Copyright (c) 2001 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* this file is part of ehci-hcd.c */
 
/*-------------------------------------------------------------------------*/
 
/*
* There's basically three types of memory:
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use pci_pool or pci_alloc_consistent
* - driver buffers, read/written by HC ... single shot DMA mapped
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
*/
 
/*-------------------------------------------------------------------------*/
/*
* Allocator / cleanup for the per device structure
* Called by hcd init / removal code
*/
static struct usb_hcd *ehci_hcd_alloc (void)
{
struct ehci_hcd *ehci;
 
ehci = (struct ehci_hcd *)
kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL);
if (ehci != 0) {
memset (ehci, 0, sizeof (struct ehci_hcd));
ehci->hcd.product_desc = "EHCI Host Controller";
return &ehci->hcd;
}
return 0;
}
 
static void ehci_hcd_free (struct usb_hcd *hcd)
{
kfree (hcd_to_ehci (hcd));
}
 
/*-------------------------------------------------------------------------*/
 
/* Allocate the key transfer structures from the previously allocated pool */
 
static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
{
memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = dma;
qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
qtd->hw_next = EHCI_LIST_END;
qtd->hw_alt_next = EHCI_LIST_END;
INIT_LIST_HEAD (&qtd->qtd_list);
}
 
static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
{
struct ehci_qtd *qtd;
dma_addr_t dma;
 
qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
if (qtd != 0) {
ehci_qtd_init (qtd, dma);
}
return qtd;
}
 
static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
}
 
 
static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
{
struct ehci_qh *qh;
dma_addr_t dma;
 
qh = (struct ehci_qh *)
pci_pool_alloc (ehci->qh_pool, flags, &dma);
if (!qh)
return qh;
 
memset (qh, 0, sizeof *qh);
atomic_set (&qh->refcount, 1);
qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list);
 
/* dummy td enables safe urb queuing */
qh->dummy = ehci_qtd_alloc (ehci, flags);
if (qh->dummy == 0) {
ehci_dbg (ehci, "no dummy td\n");
pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
qh = 0;
}
return qh;
}
 
/* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
{
atomic_inc (&qh->refcount);
return qh;
}
 
static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
if (!atomic_dec_and_test (&qh->refcount))
return;
/* clean qtds first, and know this is not linked */
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
ehci_dbg (ehci, "unused qh not empty!\n");
BUG ();
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
 
/*-------------------------------------------------------------------------*/
 
/* The queue heads and transfer descriptors are managed from pools tied
* to each of the "per device" structures.
* This is the initialisation and cleanup code.
*/
 
static void ehci_mem_cleanup (struct ehci_hcd *ehci)
{
if (ehci->async)
qh_put (ehci, ehci->async);
ehci->async = 0;
 
/* PCI consistent memory and pools */
if (ehci->qtd_pool)
pci_pool_destroy (ehci->qtd_pool);
ehci->qtd_pool = 0;
 
if (ehci->qh_pool) {
pci_pool_destroy (ehci->qh_pool);
ehci->qh_pool = 0;
}
 
if (ehci->itd_pool)
pci_pool_destroy (ehci->itd_pool);
ehci->itd_pool = 0;
 
if (ehci->sitd_pool)
pci_pool_destroy (ehci->sitd_pool);
ehci->sitd_pool = 0;
 
if (ehci->periodic)
pci_free_consistent (ehci->hcd.pdev,
ehci->periodic_size * sizeof (u32),
ehci->periodic, ehci->periodic_dma);
ehci->periodic = 0;
 
/* shadow periodic table */
if (ehci->pshadow)
kfree (ehci->pshadow);
ehci->pshadow = 0;
}
 
/* remember to add cleanup code (above) if you add anything here */
static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
{
int i;
 
/* QTDs for control/bulk/intr transfers */
ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
sizeof (struct ehci_qtd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qtd_pool) {
goto fail;
}
 
/* QHs for control/bulk/intr transfers */
ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
sizeof (struct ehci_qh),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qh_pool) {
goto fail;
}
ehci->async = ehci_qh_alloc (ehci, flags);
if (!ehci->async) {
goto fail;
}
 
/* ITD for high speed ISO transfers */
ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
sizeof (struct ehci_itd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->itd_pool) {
goto fail;
}
 
/* SITD for full/low speed split ISO transfers */
ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
sizeof (struct ehci_sitd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->sitd_pool) {
goto fail;
}
 
/* Hardware periodic table */
ehci->periodic = (u32 *)
pci_alloc_consistent (ehci->hcd.pdev,
ehci->periodic_size * sizeof (u32),
&ehci->periodic_dma);
if (ehci->periodic == 0) {
goto fail;
}
for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic [i] = EHCI_LIST_END;
 
/* software shadow of hardware table */
ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
if (ehci->pshadow == 0) {
goto fail;
}
memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
 
return 0;
 
fail:
ehci_dbg (ehci, "couldn't init memory\n");
ehci_mem_cleanup (ehci);
return -ENOMEM;
}
/shark/trunk/drivers/usb/host/uhci-hcd.c
0,0 → 1,2670
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
* (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
*
* Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are
* people who decided that they want to do the same thing in a
* completely different way.
*
* WARNING! The USB documentation is downright evil. Most of it
* is just crap, written by a committee. You're better off ignoring
* most of it, the important stuff is:
* - the low-level protocol (fairly simple but lots of small details)
* - working around the horridness of the rest
*/
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
 
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
 
#include "../core/hcd.h"
#include "uhci-hcd.h"
 
#include <linux/pm.h>
 
/*
* Version Information
*/
#define DRIVER_VERSION "v2.1"
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
#define DRIVER_DESC "USB Universal Host Controller Interface driver"
 
/*
* debug = 0, no debugging messages
* debug = 1, dump failed URB's except for stalls
* debug = 2, dump all failed URB's (including stalls)
* show all queues in /proc/driver/uhci/[pci_addr]
* debug = 3, show all TD's in URB's when dumping
*/
#ifdef DEBUG
static int debug = 3;
#else
static int debug = 0;
#endif
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level");
static char *errbuf;
#define ERRBUF_LEN (PAGE_SIZE * 8)
 
#include "uhci-hub.c"
#include "uhci-debug.c"
 
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
 
static int uhci_get_current_frame_number(struct uhci_hcd *uhci);
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
 
static void hc_state_transitions(struct uhci_hcd *uhci);
 
/* If a transfer is still active after this much time, turn off FSBR */
#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */
#define FSBR_DELAY (HZ / 20) /* 50 ms */
 
/* When we timeout an idle transfer for FSBR, we'll switch it over to */
/* depth first traversal. We'll do it in groups of this number of TD's */
/* to make sure it doesn't hog all of the bandwidth */
#define DEPTH_INTERVAL 5
 
/*
* Technically, updating td->status here is a race, but it's not really a
* problem. The worst that can happen is that we set the IOC bit again
* generating a spurious interrupt. We could fix this by creating another
* QH and leaving the IOC bit always set, but then we would have to play
* games with the FSBR code to make sure we get the correct order in all
* the cases. I don't think it's worth the effort
*/
static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
{
unsigned long flags;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
{
unsigned long flags;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
static inline void uhci_add_complete(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
unsigned long flags;
 
spin_lock_irqsave(&uhci->complete_list_lock, flags);
list_add_tail(&urbp->complete_list, &uhci->complete_list);
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
}
 
static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
{
dma_addr_t dma_handle;
struct uhci_td *td;
 
td = pci_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
if (!td)
return NULL;
 
td->dma_handle = dma_handle;
 
td->link = UHCI_PTR_TERM;
td->buffer = 0;
 
td->frame = -1;
td->dev = dev;
 
INIT_LIST_HEAD(&td->list);
INIT_LIST_HEAD(&td->remove_list);
INIT_LIST_HEAD(&td->fl_list);
 
usb_get_dev(dev);
 
return td;
}
 
static inline void uhci_fill_td(struct uhci_td *td, __u32 status,
__u32 token, __u32 buffer)
{
td->status = cpu_to_le32(status);
td->token = cpu_to_le32(token);
td->buffer = cpu_to_le32(buffer);
}
 
/*
* We insert Isochronous URB's directly into the frame list at the beginning
*/
static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
{
unsigned long flags;
 
framenum %= UHCI_NUMFRAMES;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
td->frame = framenum;
 
/* Is there a TD already mapped there? */
if (uhci->fl->frame_cpu[framenum]) {
struct uhci_td *ftd, *ltd;
 
ftd = uhci->fl->frame_cpu[framenum];
ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
 
list_add_tail(&td->fl_list, &ftd->fl_list);
 
td->link = ltd->link;
mb();
ltd->link = cpu_to_le32(td->dma_handle);
} else {
td->link = uhci->fl->frame[framenum];
mb();
uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
uhci->fl->frame_cpu[framenum] = td;
}
 
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
unsigned long flags;
 
/* If it's not inserted, don't remove it */
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (td->frame == -1 && list_empty(&td->fl_list))
goto out;
 
if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
if (list_empty(&td->fl_list)) {
uhci->fl->frame[td->frame] = td->link;
uhci->fl->frame_cpu[td->frame] = NULL;
} else {
struct uhci_td *ntd;
 
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
uhci->fl->frame_cpu[td->frame] = ntd;
}
} else {
struct uhci_td *ptd;
 
ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
ptd->link = td->link;
}
 
mb();
td->link = UHCI_PTR_TERM;
 
list_del_init(&td->fl_list);
td->frame = -1;
 
out:
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
/*
* Inserts a td into qh list at the top.
*/
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, u32 breadth)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td, *ptd;
 
if (list_empty(&urbp->td_list))
return;
 
head = &urbp->td_list;
tmp = head->next;
 
/* Ordering isn't important here yet since the QH hasn't been */
/* inserted into the schedule yet */
td = list_entry(tmp, struct uhci_td, list);
 
/* Add the first TD to the QH element pointer */
qh->element = cpu_to_le32(td->dma_handle) | breadth;
 
ptd = td;
 
/* Then link the rest of the TD's */
tmp = tmp->next;
while (tmp != head) {
td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
ptd->link = cpu_to_le32(td->dma_handle) | breadth;
 
ptd = td;
}
 
ptd->link = UHCI_PTR_TERM;
}
 
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
if (!list_empty(&td->list))
dbg("td %p is still in list!", td);
if (!list_empty(&td->remove_list))
dbg("td %p still in remove_list!", td);
if (!list_empty(&td->fl_list))
dbg("td %p is still in fl_list!", td);
 
if (td->dev)
usb_put_dev(td->dev);
 
pci_pool_free(uhci->td_pool, td, td->dma_handle);
}
 
static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
{
dma_addr_t dma_handle;
struct uhci_qh *qh;
 
qh = pci_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
if (!qh)
return NULL;
 
qh->dma_handle = dma_handle;
 
qh->element = UHCI_PTR_TERM;
qh->link = UHCI_PTR_TERM;
 
qh->dev = dev;
qh->urbp = NULL;
 
INIT_LIST_HEAD(&qh->list);
INIT_LIST_HEAD(&qh->remove_list);
 
usb_get_dev(dev);
 
return qh;
}
 
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
if (!list_empty(&qh->list))
dbg("qh %p list not empty!", qh);
if (!list_empty(&qh->remove_list))
dbg("qh %p still in remove_list!", qh);
 
if (qh->dev)
usb_put_dev(qh->dev);
 
pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
}
 
/*
* Append this urb's qh after the last qh in skelqh->list
* MUST be called with uhci->frame_list_lock acquired
*
* Note that urb_priv.queue_list doesn't have a separate queue head;
* it's a ring with every element "live".
*/
static void _uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct list_head *tmp;
struct uhci_qh *lqh;
 
/* Grab the last QH */
lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
 
/*
* Patch this endpoint's URB's QHs to point to the next skelqh:
* skelqh --> ... lqh --> newqh --> next skelqh
* Do this first, so the HC always sees the right QH after this one.
*/
list_for_each (tmp, &urbp->queue_list) {
struct urb_priv *turbp =
list_entry(tmp, struct urb_priv, queue_list);
 
turbp->qh->link = lqh->link;
}
urbp->qh->link = lqh->link;
wmb(); /* Ordering is important */
 
/*
* Patch QHs for previous endpoint's queued URBs? HC goes
* here next, not to the next skelqh it now points to.
*
* lqh --> td ... --> qh ... --> td --> qh ... --> td
* | | |
* v v v
* +<----------------+-----------------+
* v
* newqh --> td ... --> td
* |
* v
* ...
*
* The HC could see (and use!) any of these as we write them.
*/
if (lqh->urbp) {
list_for_each (tmp, &lqh->urbp->queue_list) {
struct urb_priv *turbp =
list_entry(tmp, struct urb_priv, queue_list);
 
turbp->qh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
}
}
lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
 
list_add_tail(&urbp->qh->list, &skelqh->list);
}
 
static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
{
unsigned long flags;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
_uhci_insert_qh(uhci, skelqh, urb);
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
/*
* Start removal of QH from schedule; it finishes next frame.
* TDs should be unlinked before this is called.
*/
static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
unsigned long flags;
struct uhci_qh *pqh;
 
if (!qh)
return;
 
qh->urbp = NULL;
 
/*
* Only go through the hoops if it's actually linked in
* Queued QHs are removed in uhci_delete_queued_urb,
* since (for queued URBs) the pqh is pointed to the next
* QH in the queue, not the next endpoint's QH.
*/
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (!list_empty(&qh->list)) {
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
 
if (pqh->urbp) {
struct list_head *head, *tmp;
 
head = &pqh->urbp->queue_list;
tmp = head->next;
while (head != tmp) {
struct urb_priv *turbp =
list_entry(tmp, struct urb_priv, queue_list);
 
tmp = tmp->next;
 
turbp->qh->link = qh->link;
}
}
 
pqh->link = qh->link;
mb();
/* Leave qh->link in case the HC is on the QH now, it will */
/* continue the rest of the schedule */
qh->element = UHCI_PTR_TERM;
 
list_del_init(&qh->list);
}
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 
spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
 
/* Check to see if the remove list is empty. Set the IOC bit */
/* to force an interrupt so we can remove the QH */
if (list_empty(&uhci->qh_remove_list))
uhci_set_next_interrupt(uhci);
 
list_add(&qh->remove_list, &uhci->qh_remove_list);
 
spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
}
 
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct list_head *head, *tmp;
 
head = &urbp->td_list;
tmp = head->next;
while (head != tmp) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
if (toggle)
td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
else
td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
 
 
toggle ^= 1;
}
 
return toggle;
}
 
/* This function will append one URB's QH to another URB's QH. This is for */
/* queuing interrupt, control or bulk transfers */
static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
{
struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
struct list_head *tmp;
struct uhci_td *lltd;
unsigned long flags;
 
eurbp = eurb->hcpriv;
urbp = urb->hcpriv;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
/* Find the first URB in the queue */
if (eurbp->queued) {
struct list_head *head = &eurbp->queue_list;
 
tmp = head->next;
while (tmp != head) {
struct urb_priv *turbp =
list_entry(tmp, struct urb_priv, queue_list);
 
if (!turbp->queued)
break;
 
tmp = tmp->next;
}
} else
tmp = &eurbp->queue_list;
 
furbp = list_entry(tmp, struct urb_priv, queue_list);
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
 
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
 
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
uhci_fixup_toggle(urb, uhci_toggle(td_token(lltd)) ^ 1));
 
/* All qh's in the queue need to link to the next queue */
urbp->qh->link = eurbp->qh->link;
 
mb(); /* Make sure we flush everything */
 
lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
 
list_add_tail(&urbp->queue_list, &furbp->queue_list);
 
urbp->queued = 1;
 
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp, *nurbp;
struct list_head *head, *tmp;
struct urb_priv *purbp;
struct uhci_td *pltd;
unsigned int toggle;
unsigned long flags;
 
urbp = urb->hcpriv;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
if (list_empty(&urbp->queue_list))
goto out;
 
nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
 
/* Fix up the toggle for the next URB's */
if (!urbp->queued)
/* We just set the toggle in uhci_unlink_generic */
toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
else {
/* If we're in the middle of the queue, grab the toggle */
/* from the TD previous to us */
purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
queue_list);
 
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
 
toggle = uhci_toggle(td_token(pltd)) ^ 1;
}
 
head = &urbp->queue_list;
tmp = head->next;
while (head != tmp) {
struct urb_priv *turbp;
 
turbp = list_entry(tmp, struct urb_priv, queue_list);
 
tmp = tmp->next;
 
if (!turbp->queued)
break;
 
toggle = uhci_fixup_toggle(turbp->urb, toggle);
}
 
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), toggle);
 
if (!urbp->queued) {
struct uhci_qh *pqh;
 
nurbp->queued = 0;
 
/*
* Fixup the previous QH's queue to link to the new head
* of this queue.
*/
pqh = list_entry(urbp->qh->list.prev, struct uhci_qh, list);
 
if (pqh->urbp) {
struct list_head *head, *tmp;
 
head = &pqh->urbp->queue_list;
tmp = head->next;
while (head != tmp) {
struct urb_priv *turbp =
list_entry(tmp, struct urb_priv, queue_list);
 
tmp = tmp->next;
 
turbp->qh->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
}
}
 
pqh->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
 
list_add_tail(&nurbp->qh->list, &urbp->qh->list);
list_del_init(&urbp->qh->list);
} else {
/* We're somewhere in the middle (or end). A bit trickier */
/* than the head scenario */
purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
queue_list);
 
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
if (nurbp->queued)
pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
else
/* The next URB happens to be the beginning, so */
/* we're the last, end the chain */
pltd->link = UHCI_PTR_TERM;
}
 
list_del_init(&urbp->queue_list);
 
out:
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
extern void* malloc(int size);
 
static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp;
 
urbp = malloc(sizeof(struct urb_priv)); //**kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
if (!urbp) {
err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
return NULL;
}
 
memset((void *)urbp, 0, sizeof(*urbp));
 
urbp->inserttime = jiffies26;
urbp->fsbrtime = jiffies26;
urbp->urb = urb;
urbp->dev = urb->dev;
INIT_LIST_HEAD(&urbp->td_list);
INIT_LIST_HEAD(&urbp->queue_list);
INIT_LIST_HEAD(&urbp->complete_list);
INIT_LIST_HEAD(&urbp->urb_list);
 
list_add_tail(&urbp->urb_list, &uhci->urb_list);
 
urb->hcpriv = urbp;
 
return urbp;
}
 
/*
* MUST be called with urb->lock acquired
*/
static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
td->urb = urb;
 
list_add_tail(&td->list, &urbp->td_list);
}
 
/*
* MUST be called with urb->lock acquired
*/
static void uhci_remove_td_from_urb(struct uhci_td *td)
{
if (list_empty(&td->list))
return;
 
list_del_init(&td->list);
 
td->urb = NULL;
}
 
/*
* MUST be called with urb->lock acquired
*/
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *head, *tmp;
struct urb_priv *urbp;
unsigned long flags;
 
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
return;
 
if (!list_empty(&urbp->urb_list))
warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);
 
if (!list_empty(&urbp->complete_list))
warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
 
spin_lock_irqsave(&uhci->td_remove_list_lock, flags);
 
/* Check to see if the remove list is empty. Set the IOC bit */
/* to force an interrupt so we can remove the TD's*/
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
 
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list);
}
 
spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags);
 
urb->hcpriv = NULL;
//**kmem_cache_free(uhci_up_cachep, urbp);
free(urbp);
}
 
static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
{
unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
urbp->fsbr = 1;
if (!uhci->fsbr++ && !uhci->fsbrtimeout)
uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
}
 
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
{
unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
urbp->fsbr = 0;
if (!--uhci->fsbr)
uhci->fsbrtimeout = jiffies26 + FSBR_DELAY;
}
 
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
 
/*
* Map status to standard result codes
*
* <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
* <dir_out> is True for output TDs and False for input TDs.
*/
static int uhci_map_status(int status, int dir_out)
{
if (!status)
return 0;
if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */
return -EPROTO;
if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */
if (dir_out)
return -ETIMEDOUT;
else
return -EILSEQ;
}
if (status & TD_CTRL_NAK) /* NAK */
return -ETIMEDOUT;
if (status & TD_CTRL_BABBLE) /* Babble */
return -EOVERFLOW;
if (status & TD_CTRL_DBUFERR) /* Buffer error */
return -ENOSR;
if (status & TD_CTRL_STALLED) /* Stalled */
return -EPIPE;
if (status & TD_CTRL_ACTIVE) /* Active */
return 0;
 
return -EINVAL;
}
 
/*
* Control transfers
*/
static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
struct uhci_qh *qh, *skelqh;
unsigned long destination, status;
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
 
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
 
/* 3 errors */
status = TD_CTRL_ACTIVE | uhci_maxerr(3);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
 
/*
* Build the TD for the control request
*/
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(7),
urb->setup_dma);
 
/*
* If direction is "send", change the frame from SETUP (0x2D)
* to OUT (0xE1). Else change it from SETUP to IN (0x69).
*/
destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
 
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
status |= TD_CTRL_SPD;
 
/*
* Build the DATA TD's
*/
while (len > 0) {
int pktsze = len;
 
if (pktsze > maxsze)
pktsze = maxsze;
 
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
/* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
data);
 
data += pktsze;
len -= pktsze;
}
 
/*
* Build the final TD for control status
*/
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
/*
* It's IN if the pipe is an output pipe or we're not expecting
* data back.
*/
destination &= ~TD_TOKEN_PID_MASK;
if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
 
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
 
status &= ~TD_CTRL_SPD;
 
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status | TD_CTRL_IOC,
destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
 
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
return -ENOMEM;
 
urbp->qh = qh;
qh->urbp = urbp;
 
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
 
/* Low speed transfers get a different queue, and won't hog the bus */
if (urb->dev->speed == USB_SPEED_LOW)
skelqh = uhci->skel_ls_control_qh;
else {
skelqh = uhci->skel_hs_control_qh;
uhci_inc_fsbr(uhci, urb);
}
 
if (eurb)
uhci_append_queued_urb(uhci, eurb, urb);
else
uhci_insert_qh(uhci, skelqh, urb);
 
return -EINPROGRESS;
}
 
/*
* If control was short, then end status packet wasn't sent, so this
* reorganize s so it's sent to finish the transfer. The original QH is
* removed from the skel and discarded; all TDs except the last (status)
* are deleted; the last (status) TD is put on a new QH which is reinserted
* into the skel. Since the last TD and urb_priv are reused, the TD->link
* and urb_priv maintain any queued QHs.
*/
static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
urbp->short_control_packet = 1;
 
/* Create a new QH to avoid pointer overwriting problems */
uhci_remove_qh(uhci, urbp->qh);
 
/* Delete all of the TD's except for the status TD at the end */
head = &urbp->td_list;
tmp = head->next;
while (tmp != head && tmp->next != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
uhci_free_td(uhci, td);
}
 
urbp->qh = uhci_alloc_qh(uhci, urb->dev);
if (!urbp->qh) {
err("unable to allocate new QH for control retrigger");
return -ENOMEM;
}
 
urbp->qh->urbp = urbp;
 
/* One TD, who cares about Breadth first? */
uhci_insert_tds_in_qh(urbp->qh, urb, UHCI_PTR_DEPTH);
 
/* Low speed transfers get a different queue */
if (urb->dev->speed == USB_SPEED_LOW)
uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
else
uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
 
return -EINPROGRESS;
}
 
 
static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status;
int ret = 0;
 
if (list_empty(&urbp->td_list))
return -EINVAL;
 
head = &urbp->td_list;
if (urbp->short_control_packet) {
tmp = head->prev;
goto status_phase;
}
tmp = head->next;
td = list_entry(tmp, struct uhci_td, list);
 
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
 
if (status)
goto td_error;
 
urb->actual_length = 0;
 
/* The rest of the TD's (but the last) are data */
tmp = tmp->next;
while (tmp != head && tmp->next != head) {
td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
 
urb->actual_length += uhci_actual_length(td_status(td));
 
if (status)
goto td_error;
 
/* Check to see if we received a short packet */
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
}
 
if (uhci_packetid(td_token(td)) == USB_PID_IN)
return usb_control_retrigger_status(uhci, urb);
else
return 0;
}
}
 
status_phase:
td = list_entry(tmp, struct uhci_td, list);
 
/* Control status phase */
status = td_status(td);
 
#ifdef I_HAVE_BUGGY_APC_BACKUPS
/* APC BackUPS Pro kludge */
/* It tries to send all of the descriptor instead of the amount */
/* we requested */
if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
return 0;
#endif
 
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
 
if (uhci_status_bits(status))
goto td_error;
 
return 0;
 
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
 
err:
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dbg("uhci_result_control() failed with status %x", status);
 
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
 
lprintk(errbuf);
}
}
 
return ret;
}
 
/*
* Common submit for bulk and interrupt
*/
static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
{
struct uhci_td *td;
struct uhci_qh *qh;
unsigned long destination, status;
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
dma_addr_t data = urb->transfer_dma;
 
if (len < 0)
return -EINVAL;
 
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
 
status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
status |= TD_CTRL_SPD;
 
/*
* Build the DATA TD's
*/
do { /* Allow zero length packets */
int pktsze = len;
 
if (pktsze > maxsze)
pktsze = maxsze;
 
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
 
data += pktsze;
len -= maxsze;
 
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
} while (len > 0);
 
/*
* URB_ZERO_PACKET means adding a 0-length packet, if direction
* is OUT and the transfer_length was an exact multiple of maxsze,
* hence (len = transfer_length - N * maxsze) == 0
* however, if transfer_length == 0, the zero packet was already
* prepared above.
*/
if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
!len && urb->transfer_buffer_length) {
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
 
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
}
 
/* Set the flag on the last packet */
td->status |= cpu_to_le32(TD_CTRL_IOC);
 
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
return -ENOMEM;
 
urbp->qh = qh;
qh->urbp = urbp;
 
/* Always breadth first */
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
 
if (eurb)
uhci_append_queued_urb(uhci, eurb, urb);
else
uhci_insert_qh(uhci, skelqh, urb);
 
return -EINPROGRESS;
}
 
/*
* Common result for bulk and interrupt
*/
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status = 0;
int ret = 0;
 
urb->actual_length = 0;
 
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
 
urb->actual_length += uhci_actual_length(td_status(td));
 
if (status)
goto td_error;
 
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
} else
return 0;
}
}
 
return 0;
 
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
if (ret == -EPIPE)
/* endpoint has stalled - mark it halted */
usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)));
 
err:
/*
* Enable this chunk of code if you want to see some more debugging.
* But be careful, it has the tendancy to starve out khubd and prevent
* disconnects from happening successfully if you have a slow debug
* log interface (like a serial console.
*/
#if 0
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dbg("uhci_result_common() failed with status %x", status);
 
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
 
lprintk(errbuf);
}
}
#endif
return ret;
}
 
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
int ret;
 
/* Can't have low speed bulk transfers */
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
 
ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
if (ret == -EINPROGRESS)
uhci_inc_fsbr(uhci, urb);
 
return ret;
}
 
static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
/* USB 1.1 interrupt transfers only involve one packet per interval;
* that's the uhci_submit_common() "breadth first" policy. Drivers
* can submit urbs of any length, but longer ones might need many
* intervals to complete.
*/
return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
}
 
/*
* Bulk and interrupt use common result
*/
#define uhci_result_bulk uhci_result_common
#define uhci_result_interrupt uhci_result_common
 
/*
* Isochronous transfers
*/
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
struct list_head *tmp, *head;
int ret = 0;
 
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
 
tmp = tmp->next;
 
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
if (!last_urb)
*start = u->start_frame;
last_urb = u;
}
}
 
if (last_urb) {
*end = (last_urb->start_frame + last_urb->number_of_packets *
last_urb->interval) & (UHCI_NUMFRAMES-1);
ret = 0;
} else
ret = -1; /* no previous urb found */
 
return ret;
}
 
static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
{
int limits;
unsigned int start = 0, end = 0;
 
if (urb->number_of_packets > 900) /* 900? Why? */
return -EFBIG;
 
limits = isochronous_find_limits(uhci, urb, &start, &end);
 
if (urb->transfer_flags & URB_ISO_ASAP) {
if (limits) {
int curframe;
 
curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES;
urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
} else
urb->start_frame = end;
} else {
urb->start_frame %= UHCI_NUMFRAMES;
/* FIXME: Sanity check */
}
 
return 0;
}
 
/*
* Isochronous transfers
*/
static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
struct uhci_td *td;
int i, ret, frame;
int status, destination;
 
status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
 
ret = isochronous_find_start(uhci, urb);
if (ret)
return ret;
 
frame = urb->start_frame;
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
if (!urb->iso_frame_desc[i].length)
continue;
 
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
 
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
urb->transfer_dma + urb->iso_frame_desc[i].offset);
 
if (i + 1 >= urb->number_of_packets)
td->status |= cpu_to_le32(TD_CTRL_IOC);
 
uhci_insert_td_frame_list(uhci, td, frame);
}
 
return -EINPROGRESS;
}
 
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
 
urb->actual_length = 0;
 
i = 0;
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
int actlength;
 
tmp = tmp->next;
 
if (td_status(td) & TD_CTRL_ACTIVE)
return -EINPROGRESS;
 
actlength = uhci_actual_length(td_status(td));
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength;
 
status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe));
urb->iso_frame_desc[i].status = status;
if (status) {
urb->error_count++;
ret = status;
}
 
i++;
}
 
return ret;
}
 
/*
* MUST be called with uhci->urb_list_lock acquired
*/
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
 
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
 
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
 
tmp = tmp->next;
 
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
(u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
return u;
else if (u->pipe == urb->pipe)
return u;
}
}
 
return NULL;
}
 
static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags)
{
int ret = -EINVAL;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb *eurb;
int bustime;
 
spin_lock_irqsave(&uhci->urb_list_lock, flags);
 
eurb = uhci_find_urb_ep(uhci, urb);
 
if (!uhci_alloc_urb_priv(uhci, urb)) {
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return -ENOMEM;
}
 
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
ret = uhci_submit_control(uhci, urb, eurb);
break;
case PIPE_INTERRUPT:
if (!eurb) {
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
ret = bustime;
else {
ret = uhci_submit_interrupt(uhci, urb, eurb);
if (ret == -EINPROGRESS)
usb_claim_bandwidth(urb->dev, urb, bustime, 0);
}
} else { /* inherit from parent */
urb->bandwidth = eurb->bandwidth;
ret = uhci_submit_interrupt(uhci, urb, eurb);
}
break;
case PIPE_BULK:
ret = uhci_submit_bulk(uhci, urb, eurb);
break;
case PIPE_ISOCHRONOUS:
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) {
ret = bustime;
break;
}
 
ret = uhci_submit_isochronous(uhci, urb);
if (ret == -EINPROGRESS)
usb_claim_bandwidth(urb->dev, urb, bustime, 1);
break;
}
 
if (ret != -EINPROGRESS) {
/* Submit failed, so delete it from the urb_list */
struct urb_priv *urbp = urb->hcpriv;
 
list_del_init(&urbp->urb_list);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_destroy_urb_priv (uhci, urb);
 
return ret;
}
 
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
return 0;
}
 
/*
* Return the result of a transfer
*
* MUST be called with urb_list_lock acquired
*/
static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
{
int ret = -EINVAL;
unsigned long flags;
struct urb_priv *urbp;
 
spin_lock_irqsave(&urb->lock, flags);
 
urbp = (struct urb_priv *)urb->hcpriv;
 
if (urb->status != -EINPROGRESS) {
info("uhci_transfer_result: called for URB %p not in flight?", urb);
goto out;
}
 
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
ret = uhci_result_control(uhci, urb);
break;
case PIPE_INTERRUPT:
ret = uhci_result_interrupt(uhci, urb);
break;
case PIPE_BULK:
ret = uhci_result_bulk(uhci, urb);
break;
case PIPE_ISOCHRONOUS:
ret = uhci_result_isochronous(uhci, urb);
break;
}
 
urbp->status = ret;
 
if (ret == -EINPROGRESS)
goto out;
 
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
case PIPE_BULK:
case PIPE_ISOCHRONOUS:
/* Release bandwidth for Interrupt or Isoc. transfers */
/* Spinlock needed ? */
if (urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 1);
uhci_unlink_generic(uhci, urb);
break;
case PIPE_INTERRUPT:
/* Release bandwidth for Interrupt or Isoc. transfers */
/* Make sure we don't release if we have a queued URB */
spin_lock(&uhci->frame_list_lock);
/* Spinlock needed ? */
if (list_empty(&urbp->queue_list) && urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 0);
else
/* bandwidth was passed on to queued URB, */
/* so don't let usb_unlink_urb() release it */
urb->bandwidth = 0;
spin_unlock(&uhci->frame_list_lock);
uhci_unlink_generic(uhci, urb);
break;
default:
info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
usb_pipetype(urb->pipe), urb);
}
 
/* Remove it from uhci->urb_list */
list_del_init(&urbp->urb_list);
 
uhci_add_complete(uhci, urb);
 
out:
spin_unlock_irqrestore(&urb->lock, flags);
}
 
/*
* MUST be called with urb->lock acquired
*/
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *head, *tmp;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int prevactive = 1;
 
/* We can get called when urbp allocation fails, so check */
if (!urbp)
return;
 
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
 
/*
* Now we need to find out what the last successful toggle was
* so we can update the local data toggle for the next transfer
*
* There's 3 way's the last successful completed TD is found:
*
* 1) The TD is NOT active and the actual length < expected length
* 2) The TD is NOT active and it's the last TD in the chain
* 3) The TD is active and the previous TD is NOT active
*
* Control and Isochronous ignore the toggle, so this is safe
* for all types
*/
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
if (!(td_status(td) & TD_CTRL_ACTIVE) &&
(uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
tmp == head))
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)) ^ 1);
else if ((td_status(td) & TD_CTRL_ACTIVE) && !prevactive)
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)));
 
prevactive = td_status(td) & TD_CTRL_ACTIVE;
}
 
uhci_delete_queued_urb(uhci, urb);
 
/* The interrupt loop will reclaim the QH's */
uhci_remove_qh(uhci, urbp->qh);
urbp->qh = NULL;
}
 
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb_priv *urbp = urb->hcpriv;
 
/* If this is an interrupt URB that is being killed in urb->complete, */
/* then just set its status and return */
if (!urbp) {
urb->status = -ECONNRESET;
return 0;
}
 
spin_lock_irqsave(&uhci->urb_list_lock, flags);
 
list_del_init(&urbp->urb_list);
 
uhci_unlink_generic(uhci, urb);
 
spin_lock(&uhci->urb_remove_list_lock);
 
/* If we're the first, set the next interrupt bit */
if (list_empty(&uhci->urb_remove_list))
uhci_set_next_interrupt(uhci);
list_add(&urbp->urb_list, &uhci->urb_remove_list);
 
spin_unlock(&uhci->urb_remove_list_lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return 0;
}
 
static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct list_head *head, *tmp;
int count = 0;
 
uhci_dec_fsbr(uhci, urb);
 
urbp->fsbr_timeout = 1;
 
/*
* Ideally we would want to fix qh->element as well, but it's
* read/write by the HC, so that can introduce a race. It's not
* really worth the hassle
*/
 
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
tmp = tmp->next;
 
/*
* Make sure we don't do the last one (since it'll have the
* TERM bit set) as well as we skip every so many TD's to
* make sure it doesn't hog the bandwidth
*/
if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
td->link |= UHCI_PTR_DEPTH;
 
count++;
}
 
return 0;
}
 
/*
* uhci_get_current_frame_number()
*
* returns the current frame number for a USB bus/controller.
*/
static int uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
return inw(uhci->io_addr + USBFRNUM);
}
 
static int init_stall_timer(struct usb_hcd *hcd);
 
static void stall_callback(unsigned long ptr)
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
struct list_head list, *tmp, *head;
unsigned long flags;
 
INIT_LIST_HEAD(&list);
 
spin_lock_irqsave(&uhci->urb_list_lock, flags);
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
 
tmp = tmp->next;
 
spin_lock(&u->lock);
 
/* Check if the FSBR timed out */
if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies26, up->fsbrtime + IDLE_TIMEOUT))
uhci_fsbr_timeout(uhci, u);
 
/* Check if the URB timed out */
if (u->timeout && time_after_eq(jiffies26, up->inserttime + u->timeout))
list_move_tail(&up->urb_list, &list);
 
spin_unlock(&u->lock);
}
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
head = &list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
 
tmp = tmp->next;
 
uhci_urb_dequeue(hcd, u);
}
 
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies26, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
uhci->skel_term_qh->link = UHCI_PTR_TERM;
}
 
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
 
init_stall_timer(hcd);
}
 
static int init_stall_timer(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
init_timer(&uhci->stall_timer);
uhci->stall_timer.function = stall_callback;
uhci->stall_timer.data = (unsigned long)hcd;
uhci->stall_timer.expires = jiffies26 + (HZ / 10);
add_timer(&uhci->stall_timer);
 
return 0;
}
 
static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
{
struct list_head *tmp, *head;
unsigned long flags;
 
spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
head = &uhci->qh_remove_list;
tmp = head->next;
while (tmp != head) {
struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
 
tmp = tmp->next;
 
list_del_init(&qh->remove_list);
 
uhci_free_qh(uhci, qh);
}
spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
}
 
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
{
struct list_head *tmp, *head;
unsigned long flags;
 
spin_lock_irqsave(&uhci->td_remove_list_lock, flags);
head = &uhci->td_remove_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list);
 
tmp = tmp->next;
 
list_del_init(&td->remove_list);
 
uhci_free_td(uhci, td);
}
spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags);
}
 
static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int status;
unsigned long flags;
 
spin_lock_irqsave(&urb->lock, flags);
status = urbp->status;
uhci_destroy_urb_priv(uhci, urb);
 
if (urb->status != -ENOENT && urb->status != -ECONNRESET)
urb->status = status;
spin_unlock_irqrestore(&urb->lock, flags);
 
usb_hcd_giveback_urb(hcd, urb, regs);
}
 
static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
struct list_head *tmp, *head;
unsigned long flags;
 
spin_lock_irqsave(&uhci->complete_list_lock, flags);
head = &uhci->complete_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
struct urb *urb = urbp->urb;
 
list_del_init(&urbp->complete_list);
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
 
uhci_finish_urb(hcd, urb, regs);
 
spin_lock_irqsave(&uhci->complete_list_lock, flags);
head = &uhci->complete_list;
tmp = head->next;
}
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
}
 
static void uhci_remove_pending_qhs(struct uhci_hcd *uhci)
{
struct list_head *tmp, *head;
unsigned long flags;
 
spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
head = &uhci->urb_remove_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
struct urb *urb = urbp->urb;
 
tmp = tmp->next;
 
list_del_init(&urbp->urb_list);
 
urbp->status = urb->status = -ECONNRESET;
 
uhci_add_complete(uhci, urb);
}
spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
}
 
static void uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned int io_addr = uhci->io_addr;
unsigned short status;
struct list_head *tmp, *head;
static int count =0;
 
/*
* Read the interrupt status, and write it back to clear the
* interrupt cause
*/
status = inw(io_addr + USBSTS);
if (!status) /* shared interrupt, not mine */
return;
outw(status, io_addr + USBSTS); /* Clear it */
 
// printk("%x uhci_irq\n", io_addr);
 
if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
if (status & USBSTS_HSE)
{
err("%x: host system error, PCI problems?", io_addr);
}
if (status & USBSTS_HCPE)
err("%x: host controller process error. something bad happened", io_addr);
if ((status & USBSTS_HCH) && uhci->state > 0) {
err("%x: host controller halted. very bad", io_addr);
/* FIXME: Reset the controller, fix the offending TD */
}
}
 
if (status & USBSTS_RD)
uhci->resume_detect = 1;
 
uhci_free_pending_qhs(uhci);
 
uhci_free_pending_tds(uhci);
 
uhci_remove_pending_qhs(uhci);
 
uhci_clear_next_interrupt(uhci);
 
/* Walk the list of pending URB's to see which ones completed */
spin_lock(&uhci->urb_list_lock);
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
struct urb *urb = urbp->urb;
 
tmp = tmp->next;
 
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
spin_unlock(&uhci->urb_list_lock);
 
uhci_finish_completion(hcd, regs);
}
 
static void reset_hc(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
 
/* Global reset for 50ms */
uhci->state = UHCI_RESET;
outw(USBCMD_GRESET, io_addr + USBCMD);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((HZ*50+999) / 1000);
outw(0, io_addr + USBCMD);
 
/* Another 10ms delay */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((HZ*10+999) / 1000);
uhci->resume_detect = 0;
}
 
static void suspend_hc(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
 
dbg("%x: suspend_hc", io_addr);
uhci->state = UHCI_SUSPENDED;
uhci->resume_detect = 0;
outw(USBCMD_EGSM, io_addr + USBCMD);
}
 
static void wakeup_hc(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
 
switch (uhci->state) {
case UHCI_SUSPENDED: /* Start the resume */
dbg("%x: wakeup_hc", io_addr);
 
/* Global resume for >= 20ms */
outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
uhci->state = UHCI_RESUMING_1;
uhci->state_end = jiffies26 + (20*HZ+999) / 1000;
break;
 
case UHCI_RESUMING_1: /* End global resume */
uhci->state = UHCI_RESUMING_2;
outw(0, io_addr + USBCMD);
/* Falls through */
 
case UHCI_RESUMING_2: /* Wait for EOP to be sent */
if (inw(io_addr + USBCMD) & USBCMD_FGR)
break;
 
/* Run for at least 1 second, and
* mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies26 + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,
io_addr + USBCMD);
break;
 
case UHCI_RUNNING_GRACE: /* Now allowed to suspend */
uhci->state = UHCI_RUNNING;
break;
 
default:
break;
}
}
 
static int ports_active(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
int connection = 0;
int i;
 
for (i = 0; i < uhci->rh_numports; i++)
connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
 
return connection;
}
 
static int suspend_allowed(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
int i;
 
if (!uhci->hcd.pdev || uhci->hcd.pdev->vendor != PCI_VENDOR_ID_INTEL)
return 1;
 
/* Some of Intel's USB controllers have a bug that causes false
* resume indications if any port has an over current condition.
* To prevent problems, we will not allow a global suspend if
* any ports are OC.
*
* Some motherboards using Intel's chipsets (but not using all
* the USB ports) appear to hardwire the over current inputs active
* to disable the USB ports.
*/
 
/* check for over current condition on any port */
for (i = 0; i < uhci->rh_numports; i++) {
if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
return 0;
}
 
return 1;
}
 
static void hc_state_transitions(struct uhci_hcd *uhci)
{
switch (uhci->state) {
case UHCI_RUNNING:
 
/* global suspend if nothing connected for 1 second */
if (!ports_active(uhci) && suspend_allowed(uhci)) {
uhci->state = UHCI_SUSPENDING_GRACE;
uhci->state_end = jiffies26 + HZ;
}
break;
 
case UHCI_SUSPENDING_GRACE:
if (ports_active(uhci))
uhci->state = UHCI_RUNNING;
else if (time_after_eq(jiffies26, uhci->state_end))
suspend_hc(uhci);
break;
 
case UHCI_SUSPENDED:
 
/* wakeup if requested by a device */
if (uhci->resume_detect)
wakeup_hc(uhci);
break;
 
case UHCI_RESUMING_1:
case UHCI_RESUMING_2:
case UHCI_RUNNING_GRACE:
if (time_after_eq(jiffies26, uhci->state_end))
wakeup_hc(uhci);
break;
 
default:
break;
}
}
 
static void start_hc(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
int timeout = 1000;
/*
* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies.
*/
outw(USBCMD_HCRESET, io_addr + USBCMD);
while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
if (!--timeout) {
printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
break;
}
}
 
/* Turn on all interrupts */
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
io_addr + USBINTR);
 
/* Start at frame 0 */
outw(0, io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
 
/* Run and mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies26 + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 
uhci->hcd.state = USB_STATE_RUNNING;
 
#ifdef DEB
{
__u32 *tdp;
int i;
int status = inw(io_addr + USBSTS);
printk(KERN_INFO "[%x] Frame = %d Status =%x fl=%x\n", io_addr, inw(io_addr + USBFRNUM), status, uhci->fl->dma_handle);
for (i=0; i<20; i++)
{
int status = inw(io_addr + USBSTS);
wait_ms26(500);
tdp=(__u32*)uhci->fl->frame[i];
printk(KERN_INFO "[%x] Frame[%d] -> @%x = %x status=%x fl=%x\n", io_addr, i, uhci->fl->frame[i], *tdp, status, uhci->fl->dma_handle );
}
}
#endif
 
}
 
/*
* De-allocate all resources..
*/
static void release_uhci(struct uhci_hcd *uhci)
{
int i;
 
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
 
if (uhci->term_td) {
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
}
 
if (uhci->qh_pool) {
pci_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
}
 
if (uhci->td_pool) {
pci_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
}
 
if (uhci->fl) {
pci_free_consistent(uhci->hcd.pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
}
 
#ifdef CONFIG_PROC_FS
if (uhci->proc_entry) {
remove_proc_entry(uhci->hcd.self.bus_name, uhci_proc_root);
uhci->proc_entry = NULL;
}
#endif
}
 
static int uhci_reset(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
uhci->io_addr = (unsigned long) hcd->regs;
 
/* Maybe kick BIOS off this hardware. Then reset, so we won't get
* interrupts from any previous setup.
*/
reset_hc(uhci);
pci_write_config_word(hcd->pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
return 0;
}
 
/*
* Allocate a frame list, and then setup the skeleton
*
* The hardware doesn't really know any difference
* in the queues, but the order does matter for the
* protocols higher up. The order is:
*
* - any isochronous events handled before any
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
* - The first queue is the interrupt queue.
* - The second queue is the control queue, split into low and high speed
* - The third queue is bulk queue.
* - The fourth queue is the bandwidth reclamation queue, which loops back
* to the high speed control queue.
*/
static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i, port;
unsigned io_size;
dma_addr_t dma_handle;
struct usb_device *udev;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *ent;
#endif
 
io_size = pci_resource_len(hcd->pdev, hcd->region);
 
#ifdef CONFIG_PROC_FS
ent = create_proc_entry(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
if (!ent) {
err("couldn't create uhci proc entry");
retval = -ENOMEM;
goto err_create_proc_entry;
}
 
ent->data = uhci;
ent->proc_fops = &uhci_proc_operations;
ent->size = 0;
uhci->proc_entry = ent;
#endif
 
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
 
spin_lock_init(&uhci->qh_remove_list_lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
 
spin_lock_init(&uhci->td_remove_list_lock);
INIT_LIST_HEAD(&uhci->td_remove_list);
 
spin_lock_init(&uhci->urb_remove_list_lock);
INIT_LIST_HEAD(&uhci->urb_remove_list);
 
spin_lock_init(&uhci->urb_list_lock);
INIT_LIST_HEAD(&uhci->urb_list);
 
spin_lock_init(&uhci->complete_list_lock);
INIT_LIST_HEAD(&uhci->complete_list);
 
spin_lock_init(&uhci->frame_list_lock);
 
uhci->fl = pci_alloc_consistent(hcd->pdev, sizeof(*uhci->fl), &dma_handle);
if (!uhci->fl) {
err("unable to allocate consistent memory for frame list");
goto err_alloc_fl;
}
 
memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
uhci->fl->dma_handle = dma_handle;
 
uhci->td_pool = pci_pool_create("uhci_td", hcd->pdev,
sizeof(struct uhci_td), 16, 0);
if (!uhci->td_pool) {
err("unable to create td pci_pool");
goto err_create_td_pool;
}
 
uhci->qh_pool = pci_pool_create("uhci_qh", hcd->pdev,
sizeof(struct uhci_qh), 16, 0);
if (!uhci->qh_pool) {
err("unable to create qh pci_pool");
goto err_create_qh_pool;
}
 
/* Initialize the root hub */
 
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (io_size - 0x10) / 2; port++) {
unsigned int portstatus;
 
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
info("detected %d ports", port);
 
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > 8) {
info("port count misdetected? forcing to 2 ports");
port = 2;
}
 
uhci->rh_numports = port;
 
hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self);
if (!udev) {
err("unable to allocate root hub");
goto err_alloc_root_hub;
}
 
uhci->term_td = uhci_alloc_td(uhci, udev);
if (!uhci->term_td) {
err("unable to allocate terminating TD");
goto err_alloc_term_td;
}
 
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
if (!uhci->skelqh[i]) {
err("unable to allocate QH %d", i);
goto err_alloc_skelqh;
}
}
 
/*
* 8 Interrupt queues; link int2 to int1, int4 to int2, etc
* then link int1 to control and control to bulk
*/
uhci->skel_int128_qh->link = cpu_to_le32(uhci->skel_int64_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int64_qh->link = cpu_to_le32(uhci->skel_int32_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int32_qh->link = cpu_to_le32(uhci->skel_int16_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int16_qh->link = cpu_to_le32(uhci->skel_int8_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int8_qh->link = cpu_to_le32(uhci->skel_int4_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int4_qh->link = cpu_to_le32(uhci->skel_int2_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int2_qh->link = cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
 
uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
 
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
 
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
 
/*
* Fill the frame list: make all entries point to
* the proper interrupt queue.
*
* This is probably silly, but it's a simple way to
* scatter the interrupt queues in a way that gives
* us a reasonable dynamic range for irq latencies.
*/
for (i = 0; i < UHCI_NUMFRAMES; i++) {
int irq = 0;
 
if (i & 1) {
irq++;
if (i & 2) {
irq++;
if (i & 4) {
irq++;
if (i & 8) {
irq++;
if (i & 16) {
irq++;
if (i & 32) {
irq++;
if (i & 64)
irq++;
}
}
}
}
}
}
 
/* Only place we don't use the frame list routines */
uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[7 - irq]->dma_handle);
}
 
start_hc(uhci);
 
init_stall_timer(hcd);
 
udev->speed = USB_SPEED_FULL;
 
if (usb_register_root_hub(udev, &hcd->pdev->dev) != 0) {
err("unable to start root hub");
retval = -ENOMEM;
goto err_start_root_hub;
}
 
return 0;
 
/*
* error exits:
*/
err_start_root_hub:
reset_hc(uhci);
 
del_timer_sync(&uhci->stall_timer);
 
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
 
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
 
err_alloc_term_td:
usb_put_dev(udev);
hcd->self.root_hub = NULL;
 
err_alloc_root_hub:
pci_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
 
err_create_qh_pool:
pci_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
 
err_create_td_pool:
pci_free_consistent(hcd->pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
 
err_alloc_fl:
#ifdef CONFIG_PROC_FS
remove_proc_entry(hcd->self.bus_name, uhci_proc_root);
uhci->proc_entry = NULL;
 
err_create_proc_entry:
#endif
 
return retval;
}
 
static void uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
del_timer_sync(&uhci->stall_timer);
 
/*
* At this point, we're guaranteed that no new connects can be made
* to this bus since there are no more parents
*/
uhci_free_pending_qhs(uhci);
uhci_free_pending_tds(uhci);
uhci_remove_pending_qhs(uhci);
 
reset_hc(uhci);
 
uhci_free_pending_qhs(uhci);
uhci_free_pending_tds(uhci);
 
release_uhci(uhci);
}
 
#ifdef CONFIG_PM
static int uhci_suspend(struct usb_hcd *hcd, u32 state)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
/* Don't try to suspend broken motherboards, reset instead */
if (suspend_allowed(uhci))
suspend_hc(uhci);
else
reset_hc(uhci);
return 0;
}
 
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
pci_set_master(uhci->hcd.pdev);
 
if (uhci->state == UHCI_SUSPENDED)
uhci->resume_detect = 1;
else {
reset_hc(uhci);
start_hc(uhci);
}
uhci->hcd.state = USB_STATE_RUNNING;
return 0;
}
#endif
 
static struct usb_hcd *uhci_hcd_alloc(void)
{
struct uhci_hcd *uhci;
 
uhci = (struct uhci_hcd *)kmalloc(sizeof(*uhci), GFP_KERNEL);
if (!uhci)
return NULL;
 
memset(uhci, 0, sizeof(*uhci));
uhci->hcd.product_desc = "UHCI Host Controller";
return &uhci->hcd;
}
 
static void uhci_hcd_free(struct usb_hcd *hcd)
{
kfree(hcd_to_uhci(hcd));
}
 
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
return uhci_get_current_frame_number(hcd_to_uhci(hcd));
}
 
static const char hcd_name[] = "uhci_hcd";
 
static const struct hc_driver uhci_driver = {
.description = hcd_name,
 
/* Generic hardware linkage */
.irq = uhci_irq,
.flags = HCD_USB11,
 
/* Basic lifecycle operations */
.reset = uhci_reset,
.start = uhci_start,
#ifdef CONFIG_PM
.suspend = uhci_suspend,
.resume = uhci_resume,
#endif
.stop = uhci_stop,
 
.hcd_alloc = uhci_hcd_alloc,
.hcd_free = uhci_hcd_free,
 
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
 
.get_frame_number = uhci_hcd_get_frame_number,
 
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
 
static const struct pci_device_id uhci_pci_ids[] = { {
/* handle any USB UHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0),
.driver_data = (unsigned long) &uhci_driver,
}, { /* end: all zeroes */ }
};
 
MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
 
static struct pci_driver uhci_pci_driver = {
.name = (char *)hcd_name,
.id_table = uhci_pci_ids,
 
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
 
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif /* PM */
};
/*static*/ int __init uhci_hcd_init(void)
{
int retval = -ENOMEM;
 
info(DRIVER_DESC " " DRIVER_VERSION);
 
if (usb_disabled())
return -ENODEV;
 
if (debug) {
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
goto errbuf_failed;
}
 
#ifdef CONFIG_PROC_FS
uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0);
if (!uhci_proc_root)
goto proc_failed;
#endif
 
//** uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
//** sizeof(struct urb_priv), 0, 0, NULL, NULL);
//** if (!uhci_up_cachep)
//** goto up_failed;
 
retval = pci_module_init(&uhci_pci_driver);
if (retval)
goto init_failed;
 
return 0;
 
init_failed:
//** if (kmem_cache_destroy(uhci_up_cachep))
//** printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
 
up_failed:
 
#ifdef CONFIG_PROC_FS
remove_proc_entry("driver/uhci", 0);
 
proc_failed:
#endif
if (errbuf)
kfree(errbuf);
 
errbuf_failed:
 
return retval;
}
 
/*static*/ void __exit uhci_hcd_cleanup(void)
{
pci_unregister_driver(&uhci_pci_driver);
//** if (kmem_cache_destroy(uhci_up_cachep))
//** printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
 
#ifdef CONFIG_PROC_FS
remove_proc_entry("driver/uhci", 0);
#endif
 
if (errbuf)
kfree(errbuf);
}
 
module_init(uhci_hcd_init);
module_exit(uhci_hcd_cleanup);
 
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
 
/shark/trunk/drivers/usb/host/ohci.h
0,0 → 1,401
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*
* OHCI Endpoint Descriptor (ED) ... holds TD queue
* See OHCI spec, section 4.2
*
* This is a "Queue Head" for those transfers, which is why
* both EHCI and UHCI call similar structures a "QH".
*/
struct ed {
/* first fields are hardware-specified, le32 */
__u32 hwINFO; /* endpoint config bitmap */
/* info bits defined by hcd */
#define ED_DEQUEUE __constant_cpu_to_le32(1 << 27)
/* info bits defined by the hardware */
#define ED_ISO __constant_cpu_to_le32(1 << 15)
#define ED_SKIP __constant_cpu_to_le32(1 << 14)
#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)
#define ED_OUT __constant_cpu_to_le32(0x01 << 11)
#define ED_IN __constant_cpu_to_le32(0x02 << 11)
__u32 hwTailP; /* tail of TD list */
__u32 hwHeadP; /* head of TD list (hc r/w) */
#define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */
#define ED_H __constant_cpu_to_le32(0x01) /* halted */
__u32 hwNextED; /* next ED in list */
 
/* rest are purely for the driver's use */
dma_addr_t dma; /* addr of ED */
struct td *dummy; /* next TD to activate */
 
/* host's view of schedule */
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
 
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
* some special cases : OPER --> IDLE ...
*/
u8 state; /* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE 0x00 /* NOT linked to HC */
#define ED_UNLINK 0x01 /* being unlinked from hc */
#define ED_OPER 0x02 /* IS linked to hc */
 
u8 type; /* PIPE_{BULK,...} */
 
/* periodic scheduling params (for intr and iso) */
u8 branch;
u16 interval;
u16 load;
u16 last_iso; /* iso only */
 
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
} __attribute__ ((aligned(16)));
 
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
 
/*
* OHCI Transfer Descriptor (TD) ... one per transfer segment
* See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
* and 4.3.2 (iso)
*/
struct td {
/* first fields are hardware-specified, le32 */
__u32 hwINFO; /* transfer info bitmask */
 
/* hwINFO bits for both general and iso tds: */
#define TD_CC 0xf0000000 /* condition code */
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_DI 0x00E00000 /* frames before interrupt */
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
/* these two bits are available for definition/use by HCDs in both
* general and iso tds ... others are available for only one type
*/
#define TD_DONE 0x00020000 /* retired to donelist */
#define TD_ISO 0x00010000 /* copy of ED_ISO */
 
/* hwINFO bits for general tds: */
#define TD_EC 0x0C000000 /* error count */
#define TD_T 0x03000000 /* data toggle state */
#define TD_T_DATA0 0x02000000 /* DATA0 */
#define TD_T_DATA1 0x03000000 /* DATA1 */
#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
#define TD_DP 0x00180000 /* direction/pid */
#define TD_DP_SETUP 0x00000000 /* SETUP pid */
#define TD_DP_IN 0x00100000 /* IN pid */
#define TD_DP_OUT 0x00080000 /* OUT pid */
/* 0x00180000 rsvd */
#define TD_R 0x00040000 /* round: short packets OK? */
 
/* (no hwINFO #defines yet for iso tds) */
 
__u32 hwCBP; /* Current Buffer Pointer (or 0) */
__u32 hwNextTD; /* Next TD Pointer */
__u32 hwBE; /* Memory Buffer End Pointer */
 
/* PSW is only for ISO */
#define MAXPSW 1 /* hardware allows 8 */
__u16 hwPSW [MAXPSW];
 
/* rest are purely for the driver's use */
__u8 index;
struct ed *ed;
struct td *td_hash; /* dma-->td hashtable */
struct td *next_dl_td;
struct urb *urb;
 
dma_addr_t td_dma; /* addr of this TD */
dma_addr_t data_dma; /* addr of data it points to */
 
struct list_head td_list; /* "shadow list", TDs on same ED */
} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
 
#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
 
/*
* Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
*/
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
/* 0x0A, 0x0B reserved for hardware */
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
/* 0x0E, 0x0F reserved for HCD */
#define TD_NOTACCESSED 0x0F
 
 
/* map OHCI TD status codes (CC) to errno values */
static const int cc_to_error [16] = {
/* No Error */ 0,
/* CRC Error */ -EILSEQ,
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
/* DevNotResp */ -ETIMEDOUT,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
/* DataUnder */ -EREMOTEIO,
/* (for hw) */ -EIO,
/* (for hw) */ -EIO,
/* BufferOver */ -ECOMM,
/* BuffUnder */ -ENOSR,
/* (for HCD) */ -EALREADY,
/* (for HCD) */ -EALREADY
};
 
 
/*
* The HCCA (Host Controller Communications Area) is a 256 byte
* structure defined section 4.4.1 of the OHCI spec. The HC is
* told the base address of it. It must be 256-byte aligned.
*/
struct ohci_hcca {
#define NUM_INTS 32
__u32 int_table [NUM_INTS]; /* periodic schedule */
__u16 frame_no; /* current frame number */
__u16 pad1; /* set to 0 on each frame_no change */
__u32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc [116];
u8 what [4]; /* spec only identifies 252 bytes :) */
} __attribute__ ((aligned(256)));
 
/*
* This is the structure of the OHCI controller's memory mapped I/O region.
* You must use readl() and writel() (in <asm/io.h>) to access these fields!!
* Layout is in section 7 (and appendix B) of the spec.
*/
struct ohci_regs {
/* control and status registers (section 7.1) */
__u32 revision;
__u32 control;
__u32 cmdstatus;
__u32 intrstatus;
__u32 intrenable;
__u32 intrdisable;
 
/* memory pointers (section 7.2) */
__u32 hcca;
__u32 ed_periodcurrent;
__u32 ed_controlhead;
__u32 ed_controlcurrent;
__u32 ed_bulkhead;
__u32 ed_bulkcurrent;
__u32 donehead;
 
/* frame counters (section 7.3) */
__u32 fminterval;
__u32 fmremaining;
__u32 fmnumber;
__u32 periodicstart;
__u32 lsthresh;
 
/* Root hub ports (section 7.4) */
struct ohci_roothub_regs {
__u32 a;
__u32 b;
__u32 status;
#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
__u32 portstatus [MAX_ROOT_PORTS];
} roothub;
 
/* and optional "legacy support" registers (appendix B) at 0x0100 */
 
} __attribute__ ((aligned(32)));
 
 
/* OHCI CONTROL AND STATUS REGISTER MASKS */
 
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
 
/* pre-shifted values for HCFS */
# define OHCI_USB_RESET (0 << 6)
# define OHCI_USB_RESUME (1 << 6)
# define OHCI_USB_OPER (2 << 6)
# define OHCI_USB_SUSPEND (3 << 6)
 
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_CLF (1 << 1) /* control list filled */
#define OHCI_BLF (1 << 2) /* bulk list filled */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
 
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
#define OHCI_INTR_SF (1 << 2) /* start frame */
#define OHCI_INTR_RD (1 << 3) /* resume detect */
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
#define OHCI_INTR_OC (1 << 30) /* ownership change */
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
 
 
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
 
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
 
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
 
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
 
 
/* hcd-private per-urb state */
typedef struct urb_priv {
struct ed *ed;
__u16 length; // # tds in this request
__u16 td_cnt; // tds already serviced
struct td *td [0]; // all TDs in this request
 
} urb_priv_t;
 
#define TD_HASH_SIZE 64 /* power'o'two */
// sizeof (struct td) ~= 64 == 2^6 ...
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
 
 
/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
 
struct ohci_hcd {
spinlock_t lock;
 
/*
* I/O memory used to communicate with the HC (dma-consistent)
*/
struct ohci_regs *regs;
 
/*
* main memory used to communicate with the HC (dma-consistent).
* hcd adds to schedule for a live hc any time, but removals finish
* only at the start of the next frame.
*/
struct ohci_hcca *hcca;
dma_addr_t hcca_dma;
 
struct ed *ed_rm_list; /* to be removed */
 
struct ed *ed_bulktail; /* last in bulk list */
struct ed *ed_controltail; /* last in ctrl list */
struct ed *periodic [NUM_INTS]; /* shadow int_table */
 
/*
* memory management for queue data structures
*/
struct pci_pool *td_cache;
struct pci_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE];
 
/*
* driver state
*/
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
 
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
// there are also chip quirks/bugs in init logic
 
/*
* framework state
*/
struct usb_hcd hcd;
};
 
#define hcd_to_ohci(hcd_ptr) container_of(hcd_ptr, struct ohci_hcd, hcd)
 
/*-------------------------------------------------------------------------*/
 
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
 
#define ohci_dbg(ohci, fmt, args...) \
dev_dbg ((ohci)->hcd.controller , fmt , ## args )
#define ohci_err(ohci, fmt, args...) \
dev_err ((ohci)->hcd.controller , fmt , ## args )
#define ohci_info(ohci, fmt, args...) \
dev_info ((ohci)->hcd.controller , fmt , ## args )
#define ohci_warn(ohci, fmt, args...) \
dev_warn ((ohci)->hcd.controller , fmt , ## args )
 
#ifdef OHCI_VERBOSE_DEBUG
# define ohci_vdbg ohci_dbg
#else
# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
#endif
 
/shark/trunk/drivers/usb/host/uhci-hcd.h
0,0 → 1,422
#ifndef __LINUX_UHCI_HCD_H
#define __LINUX_UHCI_HCD_H
 
#include <linux/list.h>
#include <linux/usb.h>
 
#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
#define PIPE_DEVEP_MASK 0x0007ff00
 
/*
* Universal Host Controller Interface data structures and defines
*/
 
/* Command register */
#define USBCMD 0
#define USBCMD_RS 0x0001 /* Run/Stop */
#define USBCMD_HCRESET 0x0002 /* Host reset */
#define USBCMD_GRESET 0x0004 /* Global reset */
#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
#define USBCMD_FGR 0x0010 /* Force Global Resume */
#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
 
/* Status register */
#define USBSTS 2
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
 
/* Interrupt enable register */
#define USBINTR 4
#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
 
#define USBFRNUM 6
#define USBFLBASEADD 8
#define USBSOF 12
 
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
#define USBPORTSC_LS 0x0030 /* Line Status */
#define USBPORTSC_RD 0x0040 /* Resume Detect */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */
#define USBPORTSC_OC 0x0400 /* Over Current condition */
#define USBPORTSC_SUSP 0x1000 /* Suspend */
 
/* Legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
 
#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
 
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
 
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
 
struct uhci_frame_list {
__u32 frame[UHCI_NUMFRAMES];
 
void *frame_cpu[UHCI_NUMFRAMES];
 
dma_addr_t dma_handle;
};
 
struct urb_priv;
 
/*
* One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
* used with one URB, and qh->element (updated by the HC) is either:
* - the next unprocessed TD for the URB, or
* - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
* - the QH for the next URB queued to the same endpoint.
*
* The other role of a QH is to serve as a "skeleton" framelist entry, so we
* can easily splice a QH for some endpoint into the schedule at the right
* place. Then qh->element is UHCI_PTR_TERM.
*
* In the frame list, qh->link maintains a list of QHs seen by the HC:
* skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
*/
struct uhci_qh {
/* Hardware fields */
__u32 link; /* Next queue */
__u32 element; /* Queue element pointer */
 
/* Software fields */
dma_addr_t dma_handle;
 
struct usb_device *dev;
struct urb_priv *urbp;
 
struct list_head list; /* P: uhci->frame_list_lock */
struct list_head remove_list; /* P: uhci->remove_list_lock */
} __attribute__((aligned(16)));
 
/*
* for TD <status>:
*/
#define td_status(td) le32_to_cpu((td)->status)
#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
#define TD_CTRL_C_ERR_SHIFT 27
#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
#define TD_CTRL_NAK (1 << 19) /* NAK Received */
#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
 
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
 
#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xFE0000)
#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
 
/*
* for TD <info>: (a.k.a. Token)
*/
#define td_token(td) le32_to_cpu((td)->token)
#define TD_TOKEN_DEVADDR_SHIFT 8
#define TD_TOKEN_TOGGLE_SHIFT 19
#define TD_TOKEN_TOGGLE (1 << 19)
#define TD_TOKEN_EXPLEN_SHIFT 21
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
#define TD_TOKEN_PID_MASK 0xFF
 
#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT)
 
#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
#define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
 
/*
* The documentation says "4 words for hardware, 4 words for software".
*
* That's silly, the hardware doesn't care. The hardware only cares that
* the hardware words are 16-byte aligned, and we can have any amount of
* sw space after the TD entry as far as I can tell.
*
* But let's just go with the documentation, at least for 32-bit machines.
* On 64-bit machines we probably want to take advantage of the fact that
* hw doesn't really care about the size of the sw-only area.
*
* Alas, not anymore, we have more than 4 words for software, woops.
* Everything still works tho, surprise! -jerdfelt
*
* td->link points to either another TD (not necessarily for the same urb or
* even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs)
*/
struct uhci_td {
/* Hardware fields */
__u32 link;
__u32 status;
__u32 token;
__u32 buffer;
 
/* Software fields */
dma_addr_t dma_handle;
 
struct usb_device *dev;
struct urb *urb;
 
struct list_head list; /* P: urb->lock */
struct list_head remove_list; /* P: uhci->td_remove_list_lock */
 
int frame; /* for iso: what frame? */
struct list_head fl_list; /* P: uhci->frame_list_lock */
} __attribute__((aligned(16)));
 
/*
* The UHCI driver places Interrupt, Control and Bulk into QH's both
* to group together TD's for one transfer, and also to faciliate queuing
* of URB's. To make it easy to insert entries into the schedule, we have
* a skeleton of QH's for each predefined Interrupt latency, low speed
* control, high speed control and terminating QH (see explanation for
* the terminating QH below).
*
* When we want to add a new QH, we add it to the end of the list for the
* skeleton QH.
*
* For instance, the queue can look like this:
*
* skel int128 QH
* dev 1 interrupt QH
* dev 5 interrupt QH
* skel int64 QH
* skel int32 QH
* ...
* skel int1 QH
* skel low speed control QH
* dev 5 control QH
* skel high speed control QH
* skel bulk QH
* dev 1 bulk QH
* dev 2 bulk QH
* skel terminating QH
*
* The terminating QH is used for 2 reasons:
* - To place a terminating TD which is used to workaround a PIIX bug
* (see Intel errata for explanation)
* - To loop back to the high speed control queue for full speed bandwidth
* reclamation
*
* Isochronous transfers are stored before the start of the skeleton
* schedule and don't use QH's. While the UHCI spec doesn't forbid the
* use of QH's for Isochronous, it doesn't use them either. Since we don't
* need to use them either, we follow the spec diagrams in hope that it'll
* be more compatible with future UHCI implementations.
*/
 
#define UHCI_NUM_SKELQH 12
#define skel_int128_qh skelqh[0]
#define skel_int64_qh skelqh[1]
#define skel_int32_qh skelqh[2]
#define skel_int16_qh skelqh[3]
#define skel_int8_qh skelqh[4]
#define skel_int4_qh skelqh[5]
#define skel_int2_qh skelqh[6]
#define skel_int1_qh skelqh[7]
#define skel_ls_control_qh skelqh[8]
#define skel_hs_control_qh skelqh[9]
#define skel_bulk_qh skelqh[10]
#define skel_term_qh skelqh[11]
 
/*
* Search tree for determining where <interval> fits in the skelqh[]
* skeleton.
*
* An interrupt request should be placed into the slowest skelqh[]
* which meets the interval/period/frequency requirement.
* An interrupt request is allowed to be faster than <interval> but not slower.
*
* For a given <interval>, this function returns the appropriate/matching
* skelqh[] index value.
*/
static inline int __interval_to_skel(int interval)
{
if (interval < 16) {
if (interval < 4) {
if (interval < 2)
return 7; /* int1 for 0-1 ms */
return 6; /* int2 for 2-3 ms */
}
if (interval < 8)
return 5; /* int4 for 4-7 ms */
return 4; /* int8 for 8-15 ms */
}
if (interval < 64) {
if (interval < 32)
return 3; /* int16 for 16-31 ms */
return 2; /* int32 for 32-63 ms */
}
if (interval < 128)
return 1; /* int64 for 64-127 ms */
return 0; /* int128 for 128-255 ms (Max.) */
}
 
/*
* Device states for the host controller.
*
* To prevent "bouncing" in the presence of electrical noise,
* we insist on a 1-second "grace" period, before switching to
* the RUNNING or SUSPENDED states, during which the state is
* not allowed to change.
*
* The resume process is divided into substates in order to avoid
* potentially length delays during the timer handler.
*
* States in which the host controller is halted must have values <= 0.
*/
enum uhci_state {
UHCI_RESET,
UHCI_RUNNING_GRACE, /* Before RUNNING */
UHCI_RUNNING, /* The normal state */
UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */
UHCI_SUSPENDED = -10, /* When no devices are attached */
UHCI_RESUMING_1,
UHCI_RESUMING_2
};
 
#define hcd_to_uhci(hcd_ptr) container_of(hcd_ptr, struct uhci_hcd, hcd)
 
/*
* This describes the full uhci information.
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs.
*/
struct uhci_hcd {
struct usb_hcd hcd;
 
#ifdef CONFIG_PROC_FS
/* procfs */
struct proc_dir_entry *proc_entry;
#endif
 
/* Grabbed from PCI */
unsigned long io_addr;
 
struct pci_pool *qh_pool;
struct pci_pool *td_pool;
 
struct usb_bus *bus;
 
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
 
spinlock_t frame_list_lock;
struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */
int fsbr; /* Full speed bandwidth reclamation */
unsigned long fsbrtimeout; /* FSBR delay */
 
enum uhci_state state; /* FIXME: needs a spinlock */
unsigned long state_end; /* Time of next transition */
int resume_detect; /* Need a Global Resume */
 
/* Main list of URB's currently controlled by this HC */
spinlock_t urb_list_lock;
struct list_head urb_list; /* P: uhci->urb_list_lock */
 
/* List of QH's that are done, but waiting to be unlinked (race) */
spinlock_t qh_remove_list_lock;
struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */
 
/* List of TD's that are done, but waiting to be freed (race) */
spinlock_t td_remove_list_lock;
struct list_head td_remove_list; /* P: uhci->td_remove_list_lock */
 
/* List of asynchronously unlinked URB's */
spinlock_t urb_remove_list_lock;
struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */
 
/* List of URB's awaiting completion callback */
spinlock_t complete_list_lock;
struct list_head complete_list; /* P: uhci->complete_list_lock */
 
int rh_numports;
 
struct timer_list stall_timer;
};
 
struct urb_priv {
struct list_head urb_list;
 
struct urb *urb;
struct usb_device *dev;
 
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; /* P: urb->lock */
 
int fsbr : 1; /* URB turned on FSBR */
int fsbr_timeout : 1; /* URB timed out on FSBR */
int queued : 1; /* QH was queued (not linked in) */
int short_control_packet : 1; /* If we get a short packet during */
/* a control transfer, retrigger */
/* the status phase */
 
int status; /* Final status */
 
unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */
 
struct list_head queue_list; /* P: uhci->frame_list_lock */
struct list_head complete_list; /* P: uhci->complete_list_lock */
};
 
/*
* Locking in uhci.c
*
* spinlocks are used extensively to protect the many lists and data
* structures we have. It's not that pretty, but it's necessary. We
* need to be done with all of the locks (except complete_list_lock) when
* we call urb->complete. I've tried to make it simple enough so I don't
* have to spend hours racking my brain trying to figure out if the
* locking is safe.
*
* Here's the safe locking order to prevent deadlocks:
*
* #1 uhci->urb_list_lock
* #2 urb->lock
* #3 uhci->urb_remove_list_lock, uhci->frame_list_lock,
* uhci->qh_remove_list_lock
* #4 uhci->complete_list_lock
*
* If you're going to grab 2 or more locks at once, ALWAYS grab the lock
* at the lowest level FIRST and NEVER grab locks at the same level at the
* same time.
*
* So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
*/
 
#endif
 
/shark/trunk/drivers/usb/host/ohci-pci.c
0,0 → 1,396
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
* PCI Bus Glue
*
* This file is licenced under the GPL.
*/
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#ifndef CONFIG_PM
# define CONFIG_PM
#endif
#endif
 
#ifndef CONFIG_PCI
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
#endif
 
/*-------------------------------------------------------------------------*/
 
static int
ohci_pci_reset (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
 
ohci->regs = hcd->regs;
return hc_reset (ohci);
}
 
static int __devinit
ohci_pci_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
 
if (hcd->pdev) {
ohci->hcca = pci_alloc_consistent (hcd->pdev,
sizeof *ohci->hcca, &ohci->hcca_dma);
 
if (!ohci->hcca)
return -ENOMEM;
 
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
if (hcd->pdev->vendor == PCI_VENDOR_ID_AMD
&& hcd->pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
}
 
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
 
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
else if (hcd->pdev->vendor == PCI_VENDOR_ID_OPTI
&& hcd->pdev->device == 0xc861) {
ohci_info (ohci,
"WARNING: OPTi workarounds unavailable\n");
}
 
/* Check for NSC87560. We have to look at the bridge (fn1) to
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
else if (hcd->pdev->vendor == PCI_VENDOR_ID_NS) {
struct pci_dev *b, *hc;
 
hc = hcd->pdev;
b = pci_find_slot (hc->bus->number,
PCI_DEVFN (PCI_SLOT (hc->devfn), 1));
if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
&& b->vendor == PCI_VENDOR_ID_NS) {
ohci->flags |= OHCI_QUIRK_SUPERIO;
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
}
 
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
if ((ret = ohci_mem_init (ohci)) < 0) {
ohci_stop (hcd);
return ret;
}
 
if (hc_start (ohci) < 0) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
return -EBUSY;
}
create_debug_files (ohci);
 
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
return 0;
}
 
#ifdef CONFIG_PM
 
static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u16 cmd;
u32 tmp;
 
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
ohci_dbg (ohci, "can't suspend (state is %s)\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
return -EIO;
}
 
/* act as if usb suspend can always be used */
ohci_dbg (ohci, "suspend to %d\n", state);
/* First stop processing */
spin_lock_irq (&ohci->lock);
ohci->hc_control &=
~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
writel (ohci->hc_control, &ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
(void) readl (&ohci->regs->intrstatus);
spin_unlock_irq (&ohci->lock);
 
/* Wait a frame or two */
mdelay (1);
if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
mdelay (1);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
disable_irq (hcd->pdev->irq);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
 
/* Enable remote wakeup */
writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
&ohci->regs->intrenable);
 
/* Suspend chip and let things settle down a bit */
spin_lock_irq (&ohci->lock);
ohci->hc_control = OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
spin_unlock_irq (&ohci->lock);
 
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (HZ/2);
 
tmp = readl (&ohci->regs->control) | OHCI_CTRL_HCFS;
switch (tmp) {
case OHCI_USB_RESET:
case OHCI_USB_RESUME:
case OHCI_USB_OPER:
ohci_err (ohci, "can't suspend; hcfs %d\n", tmp);
break;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "suspended\n");
break;
}
 
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable its bus master bit during
* suspend
*/
pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (hcd->pdev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif
return 0;
}
 
 
static int ohci_pci_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int temp;
int retval = 0;
 
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
 
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (hcd->pdev);
if (of_node)
pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
}
#endif
/* did we suspend, or were we powered off? */
ohci->hc_control = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
 
#ifdef DEBUG
/* the registers may look crazy here */
ohci_dump_status (ohci, 0, 0);
#endif
 
/* Re-enable bus mastering */
pci_set_master (ohci->hcd.pdev);
switch (temp) {
 
case OHCI_USB_RESET: // lost power
restart:
ohci_info (ohci, "USB restart\n");
retval = hc_restart (ohci);
break;
 
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
ohci_info (ohci, "USB continue from %s wakeup\n",
(temp == OHCI_USB_SUSPEND)
? "host" : "remote");
 
/* we "should" only need RESUME if we're SUSPENDed ... */
ohci->hc_control = OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
/* Some controllers (lucent) need extra-long delays */
mdelay (35); /* no schedule here ! */
 
temp = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
/* maybe we can reset */
goto restart;
}
 
/* Then re-enable operations */
writel (OHCI_USB_OPER, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (3);
 
spin_lock_irq (&ohci->lock);
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
hcd->state = USB_STATE_RUNNING;
writel (ohci->hc_control, &ohci->regs->control);
 
/* trigger a start-frame interrupt (why?) */
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
 
writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
(void) readl (&ohci->regs->intrdisable);
spin_unlock_irq (&ohci->lock);
 
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
enable_irq (hcd->pdev->irq);
#endif
 
/* Check for a pending done list */
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci), NULL);
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
 
/* assume there are TDs on the bulk and control lists */
writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus);
break;
 
default:
ohci_warn (ohci, "odd PCI resume\n");
}
return retval;
}
 
#endif /* CONFIG_PM */
 
 
/*-------------------------------------------------------------------------*/
 
static const struct hc_driver ohci_pci_hc_driver = {
.description = hcd_name,
 
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_MEMORY | HCD_USB11,
 
/*
* basic lifecycle operations
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
#ifdef CONFIG_PM
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
.stop = ohci_stop,
 
/*
* memory lifecycle (except per-request)
*/
.hcd_alloc = ohci_hcd_alloc,
.hcd_free = ohci_hcd_free,
 
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
 
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
 
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
};
 
/*-------------------------------------------------------------------------*/
 
 
static const struct pci_device_id pci_ids [] = { {
/* handle any USB OHCI controller */
PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0),
.driver_data = (unsigned long) &ohci_pci_hc_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
 
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ohci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
 
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
 
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
};
 
/*static*/ int __init ohci_hcd_pci_init (void)
{
printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);
if (usb_disabled())
return -ENODEV;
 
printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
//*** printk (KERN_DEBUG "File: %s @Line:%d\n", __FILE__, __LINE__);
return pci_module_init (&ohci_pci_driver);
}
module_init (ohci_hcd_pci_init);
 
/*-------------------------------------------------------------------------*/
 
/*static*/ void /*__exit*/ ohci_hcd_pci_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_exit (ohci_hcd_pci_cleanup);
/shark/trunk/drivers/usb/host/ehci-sched.c
0,0 → 1,1123
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* this file is part of ehci-hcd.c */
 
/*-------------------------------------------------------------------------*/
 
/*
* EHCI scheduled transaction support: interrupt, iso, split iso
* These are called "periodic" transactions in the EHCI spec.
*
* Note that for interrupt transfers, the QH/QTD manipulation is shared
* with the "asynchronous" transaction support (control/bulk transfers).
* The only real difference is in how interrupt transfers are scheduled.
* We get some funky API restrictions from the current URB model, which
* works notably better for reading transfers than for writing. (And
* which accordingly needs to change before it'll work inside devices,
* or with "USB On The Go" additions to USB 2.0 ...)
*/
 
static int ehci_get_frame (struct usb_hcd *hcd);
 
/*-------------------------------------------------------------------------*/
 
/*
* periodic_next_shadow - return "next" pointer on shadow list
* @periodic: host pointer to qh/itd/sitd
* @tag: hardware tag for type of this record
*/
static union ehci_shadow *
periodic_next_shadow (union ehci_shadow *periodic, int tag)
{
switch (tag) {
case Q_TYPE_QH:
return &periodic->qh->qh_next;
case Q_TYPE_FSTN:
return &periodic->fstn->fstn_next;
case Q_TYPE_ITD:
return &periodic->itd->itd_next;
#ifdef have_split_iso
case Q_TYPE_SITD:
return &periodic->sitd->sitd_next;
#endif /* have_split_iso */
}
dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
// BUG ();
return 0;
}
 
/* returns true after successful unlink */
/* caller must hold ehci->lock */
static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
{
union ehci_shadow *prev_p = &ehci->pshadow [frame];
u32 *hw_p = &ehci->periodic [frame];
union ehci_shadow here = *prev_p;
union ehci_shadow *next_p;
 
/* find predecessor of "ptr"; hw and shadow lists are in sync */
while (here.ptr && here.ptr != ptr) {
prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
hw_p = &here.qh->hw_next;
here = *prev_p;
}
/* an interrupt entry (at list end) could have been shared */
if (!here.ptr) {
dbg ("entry %p no longer on frame [%d]", ptr, frame);
return 0;
}
// vdbg ("periodic unlink %p from frame %d", ptr, frame);
 
/* update hardware list ... HC may still know the old structure, so
* don't change hw_next until it'll have purged its cache
*/
next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
*hw_p = here.qh->hw_next;
 
/* unlink from shadow list; HCD won't see old structure again */
*prev_p = *next_p;
next_p->ptr = 0;
 
return 1;
}
 
/* how many of the uframe's 125 usecs are allocated? */
static unsigned short
periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
{
u32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame];
unsigned usecs = 0;
 
while (q->ptr) {
switch (Q_NEXT_TYPE (*hw_p)) {
case Q_TYPE_QH:
/* is it in the S-mask? */
if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
usecs += q->qh->usecs;
/* ... or C-mask? */
if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
usecs += q->qh->c_usecs;
q = &q->qh->qh_next;
break;
case Q_TYPE_FSTN:
/* for "save place" FSTNs, count the relevant INTR
* bandwidth from the previous frame
*/
if (q->fstn->hw_prev != EHCI_LIST_END) {
dbg ("not counting FSTN bandwidth yet ...");
}
q = &q->fstn->fstn_next;
break;
case Q_TYPE_ITD:
/* NOTE the "one uframe per itd" policy */
if (q->itd->hw_transaction [uframe] != 0)
usecs += q->itd->usecs;
q = &q->itd->itd_next;
break;
#ifdef have_split_iso
case Q_TYPE_SITD:
temp = q->sitd->hw_fullspeed_ep &
__constant_cpu_to_le32 (1 << 31);
 
// FIXME: this doesn't count data bytes right...
 
/* is it in the S-mask? (count SPLIT, DATA) */
if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
if (temp)
usecs += HS_USECS (188);
else
usecs += HS_USECS (1);
}
 
/* ... C-mask? (count CSPLIT, DATA) */
if (q->sitd->hw_uframe &
cpu_to_le32 (1 << (8 + uframe))) {
if (temp)
usecs += HS_USECS (0);
else
usecs += HS_USECS (188);
}
q = &q->sitd->sitd_next;
break;
#endif /* have_split_iso */
default:
BUG ();
}
}
#ifdef DEBUG
if (usecs > 100)
err ("overallocated uframe %d, periodic is %d usecs",
frame * 8 + uframe, usecs);
#endif
return usecs;
}
 
/*-------------------------------------------------------------------------*/
 
static int enable_periodic (struct ehci_hcd *ehci)
{
u32 cmd;
int status;
 
/* did clearing PSE did take effect yet?
* takes effect only at frame boundaries...
*/
status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125);
if (status != 0) {
ehci->hcd.state = USB_STATE_HALT;
return status;
}
 
cmd = readl (&ehci->regs->command) | CMD_PSE;
writel (cmd, &ehci->regs->command);
/* posted write ... PSS happens later */
ehci->hcd.state = USB_STATE_RUNNING;
 
/* make sure ehci_work scans these */
ehci->next_uframe = readl (&ehci->regs->frame_index)
% (ehci->periodic_size << 3);
return 0;
}
 
static int disable_periodic (struct ehci_hcd *ehci)
{
u32 cmd;
int status;
 
/* did setting PSE not take effect yet?
* takes effect only at frame boundaries...
*/
status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125);
if (status != 0) {
ehci->hcd.state = USB_STATE_HALT;
return status;
}
 
cmd = readl (&ehci->regs->command) & ~CMD_PSE;
writel (cmd, &ehci->regs->command);
/* posted write ... */
 
ehci->next_uframe = -1;
return 0;
}
 
/*-------------------------------------------------------------------------*/
 
// FIXME microframe periods not yet handled
 
static void intr_deschedule (
struct ehci_hcd *ehci,
struct ehci_qh *qh,
int wait
) {
int status;
unsigned frame = qh->start;
 
do {
periodic_unlink (ehci, frame, qh);
qh_put (ehci, qh);
frame += qh->period;
} while (frame < ehci->periodic_size);
 
qh->qh_state = QH_STATE_UNLINK;
qh->qh_next.ptr = 0;
ehci->periodic_sched--;
 
/* maybe turn off periodic schedule */
if (!ehci->periodic_sched)
status = disable_periodic (ehci);
else {
status = 0;
vdbg ("periodic schedule still enabled");
}
 
/*
* If the hc may be looking at this qh, then delay a uframe
* (yeech!) to be sure it's done.
* No other threads may be mucking with this qh.
*/
if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
if (wait) {
udelay (125);
qh->hw_next = EHCI_LIST_END;
} else {
/* we may not be IDLE yet, but if the qh is empty
* the race is very short. then if qh also isn't
* rescheduled soon, it won't matter. otherwise...
*/
vdbg ("intr_deschedule...");
}
} else
qh->hw_next = EHCI_LIST_END;
 
qh->qh_state = QH_STATE_IDLE;
 
/* update per-qh bandwidth utilization (for usbfs) */
hcd_to_bus (&ehci->hcd)->bandwidth_allocated -=
(qh->usecs + qh->c_usecs) / qh->period;
 
dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
qh, qh->period, frame,
atomic_read (&qh->refcount), ehci->periodic_sched);
}
 
static int check_period (
struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
unsigned period,
unsigned usecs
) {
/* complete split running into next frame?
* given FSTN support, we could sometimes check...
*/
if (uframe >= 8)
return 0;
 
/*
* 80% periodic == 100 usec/uframe available
* convert "usecs we need" to "max already claimed"
*/
usecs = 100 - usecs;
 
do {
int claimed;
 
// FIXME delete when intr_submit handles non-empty queues
// this gives us a one intr/frame limit (vs N/uframe)
// ... and also lets us avoid tracking split transactions
// that might collide at a given TT/hub.
if (ehci->pshadow [frame].ptr)
return 0;
 
claimed = periodic_usecs (ehci, frame, uframe);
if (claimed > usecs)
return 0;
 
// FIXME update to handle sub-frame periods
} while ((frame += period) < ehci->periodic_size);
 
// success!
return 1;
}
 
static int check_intr_schedule (
struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
const struct ehci_qh *qh,
u32 *c_maskp
)
{
int retval = -ENOSPC;
 
if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
goto done;
if (!qh->c_usecs) {
retval = 0;
*c_maskp = cpu_to_le32 (0);
goto done;
}
 
/* This is a split transaction; check the bandwidth available for
* the completion too. Check both worst and best case gaps: worst
* case is SPLIT near uframe end, and CSPLIT near start ... best is
* vice versa. Difference can be almost two uframe times, but we
* reserve unnecessary bandwidth (waste it) this way. (Actually
* even better cases exist, like immediate device NAK.)
*
* FIXME don't even bother unless we know this TT is idle in that
* range of uframes ... for now, check_period() allows only one
* interrupt transfer per frame, so needn't check "TT busy" status
* when scheduling a split (QH, SITD, or FSTN).
*
* FIXME ehci 0.96 and above can use FSTNs
*/
if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
qh->period, qh->c_usecs))
goto done;
if (!check_period (ehci, frame, uframe + qh->gap_uf,
qh->period, qh->c_usecs))
goto done;
 
*c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf));
retval = 0;
done:
return retval;
}
 
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
int status;
unsigned uframe;
u32 c_mask;
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
 
qh->hw_next = EHCI_LIST_END;
frame = qh->start;
 
/* reuse the previous schedule slots, if we can */
if (frame < qh->period) {
uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
status = check_intr_schedule (ehci, frame, --uframe,
qh, &c_mask);
} else {
uframe = 0;
c_mask = 0;
status = -ENOSPC;
}
 
/* else scan the schedule to find a group of slots such that all
* uframes have enough periodic bandwidth available.
*/
if (status) {
frame = qh->period - 1;
do {
for (uframe = 0; uframe < 8; uframe++) {
status = check_intr_schedule (ehci,
frame, uframe, qh,
&c_mask);
if (status == 0)
break;
}
} while (status && frame--);
if (status)
goto done;
qh->start = frame;
 
/* reset S-frame and (maybe) C-frame masks */
qh->hw_info2 &= ~0xffff;
qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
} else
dbg ("reused previous qh %p schedule", qh);
 
/* stuff into the periodic schedule */
qh->qh_state = QH_STATE_LINKED;
dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
qh, qh->usecs, qh->c_usecs,
qh->period, frame, uframe, qh->gap_uf);
do {
if (unlikely (ehci->pshadow [frame].ptr != 0)) {
 
// FIXME -- just link toward the end, before any qh with a shorter period,
// AND accommodate it already having been linked here (after some other qh)
// AS WELL AS updating the schedule checking logic
 
BUG ();
} else {
ehci->pshadow [frame].qh = qh_get (qh);
ehci->periodic [frame] =
QH_NEXT (qh->qh_dma);
}
wmb ();
frame += qh->period;
} while (frame < ehci->periodic_size);
 
/* update per-qh bandwidth for usbfs */
hcd_to_bus (&ehci->hcd)->bandwidth_allocated +=
(qh->usecs + qh->c_usecs) / qh->period;
 
/* maybe enable periodic schedule processing */
if (!ehci->periodic_sched++)
status = enable_periodic (ehci);
done:
return status;
}
 
static int intr_submit (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
int mem_flags
) {
unsigned epnum;
unsigned long flags;
struct ehci_qh *qh;
struct hcd_dev *dev;
int is_input;
int status = 0;
struct list_head empty;
 
/* get endpoint and transfer/schedule data */
epnum = usb_pipeendpoint (urb->pipe);
is_input = usb_pipein (urb->pipe);
if (is_input)
epnum |= 0x10;
 
spin_lock_irqsave (&ehci->lock, flags);
dev = (struct hcd_dev *)urb->dev->hcpriv;
 
/* get qh and force any scheduling errors */
INIT_LIST_HEAD (&empty);
qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]);
if (qh == 0) {
status = -ENOMEM;
goto done;
}
if (qh->qh_state == QH_STATE_IDLE) {
if ((status = qh_schedule (ehci, qh)) != 0)
goto done;
}
 
/* then queue the urb's tds to the qh */
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
BUG_ON (qh == 0);
 
/* ... update usbfs periodic stats */
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++;
 
done:
spin_unlock_irqrestore (&ehci->lock, flags);
if (status)
qtd_list_free (ehci, urb, qtd_list);
 
return status;
}
 
static unsigned
intr_complete (
struct ehci_hcd *ehci,
unsigned frame,
struct ehci_qh *qh,
struct pt_regs *regs
) {
unsigned count;
 
/* nothing to report? */
if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
!= 0))
return 0;
if (unlikely (list_empty (&qh->qtd_list))) {
dbg ("intr qh %p no TDs?", qh);
return 0;
}
/* handle any completions */
count = qh_completions (ehci, qh, regs);
 
if (unlikely (list_empty (&qh->qtd_list)))
intr_deschedule (ehci, qh, 0);
 
return count;
}
 
/*-------------------------------------------------------------------------*/
 
static void
itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
{
struct ehci_itd *first_itd = urb->hcpriv;
 
while (!list_empty (&first_itd->itd_list)) {
struct ehci_itd *itd;
 
itd = list_entry (
first_itd->itd_list.next,
struct ehci_itd, itd_list);
list_del (&itd->itd_list);
pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
}
pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
urb->hcpriv = 0;
}
 
static int
itd_fill (
struct ehci_hcd *ehci,
struct ehci_itd *itd,
struct urb *urb,
unsigned index, // urb->iso_frame_desc [index]
dma_addr_t dma // mapped transfer buffer
) {
u64 temp;
u32 buf1;
unsigned i, epnum, maxp, multi;
unsigned length;
int is_input;
 
itd->hw_next = EHCI_LIST_END;
itd->urb = urb;
itd->index = index;
 
/* tell itd about its transfer buffer, max 2 pages */
length = urb->iso_frame_desc [index].length;
dma += urb->iso_frame_desc [index].offset;
temp = dma & ~0x0fff;
for (i = 0; i < 2; i++) {
itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
temp += 0x1000;
}
itd->buf_dma = dma;
 
/*
* this might be a "high bandwidth" highspeed endpoint,
* as encoded in the ep descriptor's maxpacket field
*/
epnum = usb_pipeendpoint (urb->pipe);
is_input = usb_pipein (urb->pipe);
if (is_input) {
maxp = urb->dev->epmaxpacketin [epnum];
buf1 = (1 << 11);
} else {
maxp = urb->dev->epmaxpacketout [epnum];
buf1 = 0;
}
buf1 |= (maxp & 0x03ff);
multi = 1;
multi += (maxp >> 11) & 0x03;
maxp &= 0x03ff;
maxp *= multi;
 
/* transfer can't fit in any uframe? */
if (length < 0 || maxp < length) {
dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
length, maxp, urb, index,
urb->iso_frame_desc [index].length);
return -ENOSPC;
}
itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
 
/* "plus" info in low order bits of buffer pointers */
itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
itd->hw_bufp [1] |= cpu_to_le32 (buf1);
itd->hw_bufp [2] |= cpu_to_le32 (multi);
 
/* figure hw_transaction[] value (it's scheduled later) */
itd->transaction = EHCI_ISOC_ACTIVE;
itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */
if ((index + 1) == urb->number_of_packets)
itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */
itd->transaction |= length << 16;
cpu_to_le32s (&itd->transaction);
 
return 0;
}
 
static int
itd_urb_transaction (
struct ehci_hcd *ehci,
struct urb *urb,
int mem_flags
) {
int frame_index;
struct ehci_itd *first_itd, *itd;
int status;
dma_addr_t itd_dma;
 
/* allocate/init ITDs */
for (frame_index = 0, first_itd = 0;
frame_index < urb->number_of_packets;
frame_index++) {
itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
if (!itd) {
status = -ENOMEM;
goto fail;
}
memset (itd, 0, sizeof *itd);
itd->itd_dma = itd_dma;
 
status = itd_fill (ehci, itd, urb, frame_index,
urb->transfer_dma);
if (status != 0)
goto fail;
 
if (first_itd)
list_add_tail (&itd->itd_list,
&first_itd->itd_list);
else {
INIT_LIST_HEAD (&itd->itd_list);
urb->hcpriv = first_itd = itd;
}
}
urb->error_count = 0;
return 0;
 
fail:
if (urb->hcpriv)
itd_free_list (ehci, urb);
return status;
}
 
/*-------------------------------------------------------------------------*/
 
static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
{
/* always prepend ITD/SITD ... only QH tree is order-sensitive */
itd->itd_next = ehci->pshadow [frame];
itd->hw_next = ehci->periodic [frame];
ehci->pshadow [frame].itd = itd;
ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
}
 
/*
* return zero on success, else -errno
* - start holds first uframe to start scheduling into
* - max is the first uframe it's NOT (!) OK to start scheduling into
* math to be done modulo "mod" (ehci->periodic_size << 3)
*/
static int get_iso_range (
struct ehci_hcd *ehci,
struct urb *urb,
unsigned *start,
unsigned *max,
unsigned mod
) {
struct list_head *lh;
struct hcd_dev *dev = urb->dev->hcpriv;
int last = -1;
unsigned now, span, end;
 
span = urb->interval * urb->number_of_packets;
 
/* first see if we know when the next transfer SHOULD happen */
list_for_each (lh, &dev->urb_list) {
struct urb *u;
struct ehci_itd *itd;
unsigned s;
 
u = list_entry (lh, struct urb, urb_list);
if (u == urb || u->pipe != urb->pipe)
continue;
if (u->interval != urb->interval) { /* must not change! */
dbg ("urb %p interval %d ... != %p interval %d",
u, u->interval, urb, urb->interval);
return -EINVAL;
}
/* URB for this endpoint... covers through when? */
itd = urb->hcpriv;
s = itd->uframe + u->interval * u->number_of_packets;
if (last < 0)
last = s;
else {
/*
* So far we can only queue two ISO URBs...
*
* FIXME do interval math, figure out whether
* this URB is "before" or not ... also, handle
* the case where the URB might have completed,
* but hasn't yet been processed.
*/
dbg ("NYET: queue >2 URBs per ISO endpoint");
return -EDOM;
}
}
 
/* calculate the legal range [start,max) */
now = readl (&ehci->regs->frame_index) + 1; /* next uframe */
if (!ehci->periodic_sched)
now += 8; /* startup delay */
now %= mod;
end = now + mod;
if (last < 0) {
*start = now + ehci->i_thresh + /* paranoia */ 1;
*max = end - span;
if (*max < *start + 1)
*max = *start + 1;
} else {
*start = last % mod;
*max = (last + 1) % mod;
}
 
/* explicit start frame? */
if (!(urb->transfer_flags & URB_ISO_ASAP)) {
unsigned temp;
 
/* sanity check: must be in range */
urb->start_frame %= ehci->periodic_size;
temp = urb->start_frame << 3;
if (temp < *start)
temp += mod;
if (temp > *max)
return -EDOM;
 
/* use that explicit start frame */
*start = urb->start_frame << 3;
temp += 8;
if (temp < *max)
*max = temp;
}
 
// FIXME minimize wraparound to "now" ... insist max+span
// (and start+span) remains a few frames short of "end"
 
*max %= ehci->periodic_size;
if ((*start + span) < end)
return 0;
return -EFBIG;
}
 
static int
itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
{
unsigned start, max, i;
int status;
unsigned mod = ehci->periodic_size << 3;
 
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc [i].status = -EINPROGRESS;
urb->iso_frame_desc [i].actual_length = 0;
}
 
if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
return status;
 
do {
unsigned uframe;
unsigned usecs;
struct ehci_itd *itd;
 
/* check schedule: enough space? */
itd = urb->hcpriv;
uframe = start;
for (i = 0, uframe = start;
i < urb->number_of_packets;
i++, uframe += urb->interval) {
uframe %= mod;
 
/* can't commit more than 80% periodic == 100 usec */
if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
> (100 - itd->usecs)) {
itd = 0;
break;
}
itd = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
}
if (!itd)
continue;
/* that's where we'll schedule this! */
itd = urb->hcpriv;
urb->start_frame = start >> 3;
vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
urb, urb->number_of_packets, urb->interval,
urb->start_frame, start & 0x7);
for (i = 0, uframe = start, usecs = 0;
i < urb->number_of_packets;
i++, uframe += urb->interval) {
uframe %= mod;
 
itd->uframe = uframe;
itd->hw_transaction [uframe & 0x07] = itd->transaction;
itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
itd);
wmb ();
usecs += itd->usecs;
 
itd = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
}
 
/* update bandwidth utilization records (for usbfs)
*
* FIXME This claims each URB queued to an endpoint, as if
* transfers were concurrent, not sequential. So bandwidth
* typically gets double-billed ... comes from tying it to
* URBs rather than endpoints in the schedule. Luckily we
* don't use this usbfs data for serious decision making.
*/
usecs /= urb->number_of_packets;
usecs /= urb->interval;
usecs >>= 3;
if (usecs < 1)
usecs = 1;
usb_claim_bandwidth (urb->dev, urb, usecs, 1);
 
/* maybe enable periodic schedule processing */
if (!ehci->periodic_sched++) {
if ((status = enable_periodic (ehci)) != 0) {
// FIXME deschedule right away
err ("itd_schedule, enable = %d", status);
}
}
 
return 0;
 
} while ((start = ++start % mod) != max);
 
/* no room in the schedule */
dbg ("urb %p, CAN'T SCHEDULE", urb);
return -ENOSPC;
}
 
/*-------------------------------------------------------------------------*/
 
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
 
static unsigned
itd_complete (
struct ehci_hcd *ehci,
struct ehci_itd *itd,
unsigned uframe,
struct pt_regs *regs
) {
struct urb *urb = itd->urb;
struct usb_iso_packet_descriptor *desc;
u32 t;
 
/* update status for this uframe's transfers */
desc = &urb->iso_frame_desc [itd->index];
 
t = itd->hw_transaction [uframe];
itd->hw_transaction [uframe] = 0;
if (t & EHCI_ISOC_ACTIVE)
desc->status = -EXDEV;
else if (t & ISO_ERRS) {
urb->error_count++;
if (t & EHCI_ISOC_BUF_ERR)
desc->status = usb_pipein (urb->pipe)
? -ENOSR /* couldn't read */
: -ECOMM; /* couldn't write */
else if (t & EHCI_ISOC_BABBLE)
desc->status = -EOVERFLOW;
else /* (t & EHCI_ISOC_XACTERR) */
desc->status = -EPROTO;
 
/* HC need not update length with this error */
if (!(t & EHCI_ISOC_BABBLE))
desc->actual_length += EHCI_ITD_LENGTH (t);
} else {
desc->status = 0;
desc->actual_length += EHCI_ITD_LENGTH (t);
}
 
vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
itd, urb, itd->index + 1, urb->number_of_packets,
t, desc->status, desc->actual_length);
 
/* handle completion now? */
if ((itd->index + 1) != urb->number_of_packets)
return 0;
 
/*
* Always give the urb back to the driver ... expect it to submit
* a new urb (or resubmit this), and to have another already queued
* when un-interrupted transfers are needed.
*
* NOTE that for now we don't accelerate ISO unlinks; they just
* happen according to the current schedule. Means a delay of
* up to about a second (max).
*/
itd_free_list (ehci, urb);
if (urb->status == -EINPROGRESS)
urb->status = 0;
 
/* complete() can reenter this HCD */
spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
spin_lock (&ehci->lock);
 
/* defer stopping schedule; completion can submit */
ehci->periodic_sched--;
if (!ehci->periodic_sched)
(void) disable_periodic (ehci);
 
return 1;
}
 
/*-------------------------------------------------------------------------*/
 
static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
{
int status;
unsigned long flags;
 
dbg ("itd_submit urb %p", urb);
 
/* allocate ITDs w/o locking anything */
status = itd_urb_transaction (ehci, urb, mem_flags);
if (status < 0)
return status;
 
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
status = itd_schedule (ehci, urb);
spin_unlock_irqrestore (&ehci->lock, flags);
if (status < 0)
itd_free_list (ehci, urb);
 
return status;
}
 
#ifdef have_split_iso
 
/*-------------------------------------------------------------------------*/
 
/*
* "Split ISO TDs" ... used for USB 1.1 devices going through
* the TTs in USB 2.0 hubs.
*
* FIXME not yet implemented
*/
 
#endif /* have_split_iso */
 
/*-------------------------------------------------------------------------*/
 
static void
scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
{
unsigned frame, clock, now_uframe, mod;
unsigned count = 0;
 
mod = ehci->periodic_size << 3;
 
/*
* When running, scan from last scan point up to "now"
* else clean up by scanning everything that's left.
* Touches as few pages as possible: cache-friendly.
* Don't scan ISO entries more than once, though.
*/
frame = ehci->next_uframe >> 3;
if (HCD_IS_RUNNING (ehci->hcd.state))
now_uframe = readl (&ehci->regs->frame_index);
else
now_uframe = (frame << 3) - 1;
now_uframe %= mod;
clock = now_uframe >> 3;
 
for (;;) {
union ehci_shadow q, *q_p;
u32 type, *hw_p;
unsigned uframes;
 
restart:
/* scan schedule to _before_ current frame index */
if (frame == clock)
uframes = now_uframe & 0x07;
else
uframes = 8;
 
q_p = &ehci->pshadow [frame];
hw_p = &ehci->periodic [frame];
q.ptr = q_p->ptr;
type = Q_NEXT_TYPE (*hw_p);
 
/* scan each element in frame's queue for completions */
while (q.ptr != 0) {
int last;
unsigned uf;
union ehci_shadow temp;
 
switch (type) {
case Q_TYPE_QH:
last = (q.qh->hw_next == EHCI_LIST_END);
temp = q.qh->qh_next;
type = Q_NEXT_TYPE (q.qh->hw_next);
count += intr_complete (ehci, frame,
qh_get (q.qh), regs);
qh_put (ehci, q.qh);
q = temp;
break;
case Q_TYPE_FSTN:
last = (q.fstn->hw_next == EHCI_LIST_END);
/* for "save place" FSTNs, look at QH entries
* in the previous frame for completions.
*/
if (q.fstn->hw_prev != EHCI_LIST_END) {
dbg ("ignoring completions from FSTNs");
}
type = Q_NEXT_TYPE (q.fstn->hw_next);
q = q.fstn->fstn_next;
break;
case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END);
 
/* Unlink each (S)ITD we see, since the ISO
* URB model forces constant rescheduling.
* That complicates sharing uframes in ITDs,
* and means we need to skip uframes the HC
* hasn't yet processed.
*/
for (uf = 0; uf < uframes; uf++) {
if (q.itd->hw_transaction [uf] != 0) {
temp = q;
*q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next;
type = Q_NEXT_TYPE (*hw_p);
 
/* might free q.itd ... */
count += itd_complete (ehci,
temp.itd, uf, regs);
break;
}
}
/* we might skip this ITD's uframe ... */
if (uf == uframes) {
q_p = &q.itd->itd_next;
hw_p = &q.itd->hw_next;
type = Q_NEXT_TYPE (q.itd->hw_next);
}
 
q = *q_p;
break;
#ifdef have_split_iso
case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END);
sitd_complete (ehci, q.sitd);
type = Q_NEXT_TYPE (q.sitd->hw_next);
 
// FIXME unlink SITD after split completes
q = q.sitd->sitd_next;
break;
#endif /* have_split_iso */
default:
dbg ("corrupt type %d frame %d shadow %p",
type, frame, q.ptr);
// BUG ();
last = 1;
q.ptr = 0;
}
 
/* did completion remove an interior q entry? */
if (unlikely (q.ptr == 0 && !last))
goto restart;
}
 
/* stop when we catch up to the HC */
 
// FIXME: this assumes we won't get lapped when
// latencies climb; that should be rare, but...
// detect it, and just go all the way around.
// FLR might help detect this case, so long as latencies
// don't exceed periodic_size msec (default 1.024 sec).
 
// FIXME: likewise assumes HC doesn't halt mid-scan
 
if (frame == clock) {
unsigned now;
 
if (!HCD_IS_RUNNING (ehci->hcd.state))
break;
ehci->next_uframe = now_uframe;
now = readl (&ehci->regs->frame_index) % mod;
if (now_uframe == now)
break;
 
/* rescan the rest of this frame, then ... */
now_uframe = now;
clock = now_uframe >> 3;
} else
frame = (frame + 1) % ehci->periodic_size;
}
}
/shark/trunk/drivers/usb/makefile
0,0 → 1,34
# PCI support from linux 2.6.0
#_I386_SEMAPHORE_H
 
ifndef BASE
BASE=../..
endif
 
include $(BASE)/config/config.mk
 
LIBRARY = usb
 
OBJS_PATH = $(BASE)/demos/usb
 
OBJS = core/usb.o core/usb-debug.o core/hub.o core/hcd.o core/urb.o core/message.o \
core/config.o core/file.o core/buffer.o core/driverfs.o core/hcd-pci.o\
host/ohci-hcd.o host/ehci-hcd.o host/uhci-hcd.o \
shark_glue/shark_usb.o\
input/usbmouse.o input/usbkbd.o \
media/pwc-if.o media/pwc-ctrl.o media/pwc-misc.o media/pwc-uncompress.o \
input/hid-core.o input/hid-input.o serial/bus.o serial/generic.o \
serial/usb-serial.o serial/pl2303.o serial/tty_io.o
 
OTHERINCL += -I$(BASE)/drivers/linuxc26/include -I./core -I./host
 
#
# add -DCONFIG_USB_DEBUG to enable USB core debug messages
# -DCONFIG_USB_SERIAL_DEBUG to enable USB/serial converters debug messages
#
C_OPT += -D__KERNEL__ -DCONFIG_USB_HIDINPUT -DCONFIG_USB_SERIAL_PL2303 -DCONFIG_USB_DEBUG
 
include $(BASE)/config/lib.mk
 
clean::
rm -f $(OBJS)