D: Joystick driver
D: arcnet-hardware readme
D: Minor ARCnet hacking
-D: USB hacking
+D: USB (HID, ACM, Printer ...)
S: Ucitelska 1576
S: Prague 8
S: 182 00 Czech Republic
The module will be called usb-uhci.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
-OHCI-HCD (Compaq, iMacs, OPTi, SiS, ALi, ...) support?
-CONFIG_USB_OHCI_HCD
+OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support?
+CONFIG_USB_OHCI
The Open Host Controller Interface is a standard by
Compaq/Microsoft/National for accessing the USB PC hardware (also
called USB host controller). If your USB host controller conforms
chipsets - like SiS (actual 610, 610 and so on) or ALi (ALi IV, ALi V,
Aladdin Pro..) - conform to this standard.
- You may want to read the file Documentation/usb/ohci-hcd.txt.
+ You may want to read the file Documentation/usb/ohci.txt.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
- The module will be called usb-ohci-hcd.o. If you want to compile it
+ The module will be called usb-ohci.o. If you want to compile it
as a module, say M here and read Documentation/modules.txt.
USB Human Interface Device (HID) support
The module will be called cpia.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
+USB IBM (Xirlink) C-It Camera support
+CONFIG_USB_IBMCAM
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port.
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called ibmcam.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. This camera
+ has several configuration options which can be specified when you
+ load the module. Read Documentation/usb/ibmcam.txt to learn more.
+
USB OV511 Camera support
CONFIG_USB_OV511
Say Y here if you want to connect this type of camera to your
ham <ham@unsuave.com>
Bradley M Keryan <keryan@andrew.cmu.edu>
Greg Kroah-Hartman <greg@kroah.com>
+ Pavel Machek <pavel@suse.cz>
Paul Mackerras <paulus@cs.anu.edu.au>
David E. Nelson <dnelson@jump.net>
Vojtech Pavlik <vojtech@suse.cz>
--- /dev/null
+README for Linux device driver for the IBM "C-It" USB video camera
+
+INTRODUCTION:
+
+This driver does not use all features known to exist in
+the IBM camera. However most of needed features work well.
+
+This driver was developed using logs of observed USB traffic
+which was produced by standard Windows driver (c-it98.sys).
+I did not have any input from Xirlink. Some people asked about
+data sheets, but nothing came out of that. I didn't try.
+
+Video formats: 128x96, 176x144, 352x288
+Frame rate: 3 - 30 frames per second (FPS)
+External interface: USB
+Internal interface: Video For Linux (V4L)
+Supported controls:
+- by V4L: Contrast, Brightness, Color, Hue
+- by driver options: frame rate, lighting conditions, video format,
+ default picture settings, sharpness.
+
+SUPPORTED CAMERAS:
+
+IBM "C-It" camera, also known as "Xirlink PC Camera"
+The device uses proprietary ASIC (and compression method);
+it is manufactured by Xirlink. See http://www.xirlink.com
+
+WHAT YOU NEED:
+
+- A camera
+
+- A Linux box with USB support (2.3/2.4 or 2.2 w/backport)
+
+- A Video4Linux compatible frame grabber program such as xawtv.
+
+HOW TO COMPILE THE DRIVER:
+
+You need to compile the driver only if you are a developer
+or if you want to make changes to the code. Most distributions
+precompile all modules, so you can go directly to the next
+section "HOW TO USE THE DRIVER".
+
+The driver consists of two files in usb/ directory:
+ibmcam.c and ibmcam.h These files are included into the
+Linux kernel build process if you configure the kernel
+for CONFIG_USB_IBMCAM. Run "make xconfig" and in USB section
+you will find the IBM camera driver. Select it, save the
+configuration and recompile.
+
+HOW TO USE THE DRIVER:
+
+I recommend to compile driver as a module. This gives you an
+easier access to its configuration. The camera has many more
+settings than V4L can operate, so some settings are done using
+module options.
+
+Typically module is installed with command 'modprobe', like this:
+
+# modprobe ibmcam framerate=1
+
+Alternatively you can use 'insmod' in similar fashion:
+
+# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1
+
+Module can be inserted with camera connected or disconnected.
+
+The driver can have options, though some defaults are provided.
+
+Driver options:
+
+Name Type Range [default] Example
+-------------- -------------- -------------- ------------------
+debug Integer 0-9 [0] debug=1
+flags Integer 0-0xFF [0] flags=0x0d
+framerate Integer 0-6 [2] framerate=1
+init_brightness Integer 0-255 [128] init_brightness=100
+init_contrast Integer 0-255 [192] init_contrast=200
+init_color Integer 0-255 [128] init_color=130
+init_hue Integer 0-255 [128] init_hue=115
+lighting Integer 0-2 [1] lighting=2
+sharpness Integer 0-6 [4] sharpness=3
+videosize Integer 0-2 [2] videosize=1
+
+debug You don't need this option unless you are a developer.
+ If you are a developer then you will see in the code
+ what values do what. 0=off.
+
+flags This is a bit mask, and you can combine any number of
+ bits to produce what you want. Usually you don't want
+ any of extra features this option provides:
+
+ FLAGS_RETRY_VIDIOCSYNC 1 This bit allows to retry failed
+ VIDIOCSYNC ioctls without failing.
+ Will work with xawtv, will not
+ with xrealproducer. Default is
+ not set.
+ FLAGS_MONOCHROME 2 Forces monochrome (b/w) mode.
+ FLAGS_DISPLAY_HINTS 4 Shows colored pixels which have
+ magic meaning to developers.
+ FLAGS_OVERLAY_STATS 8 Shows tiny numbers on screen,
+ useful only for debugging.
+ FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers.
+
+framerate This setting controls frame rate of the camera. This is
+ an approximate setting (in terms of "worst" ... "best")
+ because camera changes frame rate depending on amount
+ of light available. Setting 0 is slowest, 6 is fastest.
+ Beware - fast settings are very demanding and may not
+ work well with all video sizes. Be conservative.
+
+init_brightness These settings specify _initial_ values which will be
+init_contrast used to set up the camera. If your V4L application has
+init_color its own controls to adjust the picture then these
+init_hue controls will be used too. These options allow you to
+ preconfigure the camera when it gets connected, before
+ any V4L application connects to it. Good for webcams.
+
+lighting This option selects one of three hardware-defined
+ photosensitivity settings of the camera. 0=bright light,
+ 1=Medium (default), 2=Low light. This setting affects
+ frame rate: the dimmer the lighting the lower the frame
+ rate (because longer exposition time is needed).
+
+sharpness This option controls smoothing (noise reduction)
+ made by camera. Setting 0 is most smooth, setting 6
+ is most sharp. Be aware that CMOS sensor used in the
+ camera is pretty noisy, so if you choose 6 you will
+ be greeted with "snowy" image. Default is 4.
+
+videosize This setting chooses one if three image sizes that are
+ supported by this driver. Camera supports more, but
+ it's difficult to reverse-engineer all formats.
+ Following video sizes are supported:
+
+ videosize=0 128x96
+ videosize=1 176x144
+ videosize=2 352x288
+
+ The last one (352x288) is the native size of the sensor
+ array, so it's the best resolution camera can yield.
+ Choose the image size you need. The smaller image can
+ support faster frame rate. Default is 352x288.
+
+WHAT NEEDS TO BE DONE:
+
+- The box freezes if working camera (with xawtv) is unplugged (OHCI).
+ Workaround: don't do that :) End the V4L application first.
+- Some USB frames are lost on high frame rates, though they shouldn't
+- ViCE compression (Xirlink proprietary) may improve frame rate
+- On occasion camera does not start properly; xawtv reports errors.
+ Workaround: reload the driver module. Reason: [1].
+- On occasion camera produces negative image (funny colors.)
+ Workaround: reload the driver module. Reason: [1].
+- The button on the camera is not used. I don't know how to get to it.
+- The LED on the camera goes off after video init.
+
+[1]
+- I2O interface does not read what camera reports back. Actually, it
+ reads it, but I don't know what returned data means. If camera fails
+ at some initialization stage then something should be done, and I don't
+ do that because I don't even know that some command failed.
+
+CREDITS:
+
+The code is based in no small part on the CPiA driver by Johannes Erdfelt,
+Randy Dunlap, and others. Big thanks to them for their pioneering work on that
+and the USB stack.
+++ /dev/null
-
-The OHCI HCD layer is a simple but nearly complete implementation of what the
-USB people would call a HCD for the OHCI.
- (ISO comming soon, Bulk, INT u. CTRL transfers enabled)
-It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree).
-The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers.
-
-- Roman Weissgaerber <weissg@vienna.at>
-
- * v4.0 1999/08/18 removed all dummy eds, unlink unused eds, code cleanup, bulk transfers
- * v2.1 1999/05/09 ep_addr correction, code cleanup
- * v0.2.0 1999/05/04
- * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers)
- * virtual root hub is now an option,
- * memory allocation based on kmalloc and kfree now, simple Bus error handling,
- * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
- *
- * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff
- * from Greg Smith (ohci.c): better reset ohci-controller handling, hub
- *
- * v0.1.0 1999/04/27 initial release
-
-to remove the module try:
-rmmod usb-ohci-hcd
-
-Features:
-- virtual root hub, all basic hub descriptors and commands (state: complete)
- this is an option now (v0.2.0)
- #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB)
- default is with.
- (at the moment: the Virtual Root Hub is included automatically)
-
- files: ohci-root-hub.c, ohci-root-hub.h
-
-
-- Endpoint Descriptor (ED) handling more static approach
- (EDs should be allocated in parallel to the SET CONFIGURATION command and they live
- as long as the function (device) is alive or another configuration is choosen.
- In the HCD layer the EDs has to be allocated manually either by calling a subroutine
- or by sending a USB root hub vendor specific command to the virtual root hub.
- At the alternate linux usb stack EDs will be added (allocated) at their first use.
- ED will be unlinked from the HC chains if they are not bussy.
-
- files: ohci-hcd.c ohci-hcd.h
- routines: (do not use for drivers, use the top layer alternate usb commands instead)
-
- int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1,
- int interval, int load, f_handler handler, int ep_size, int speed)
- adds an endpoint, (if the endpoint already exists some parameters will be updated)
-
- int usb_ohci_rm_ep( )
- removes an endpoint and all pending TDs of that EP
-
- usb_ohci_rm_function( )
- removes all Endpoints of a function (device)
-
-- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers
- The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has
- to take care of buffer allocation.
- files: ohci-hcd.c ohci-hcd.h
-
- There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL):
-
- int ohci_trans_req(struct ohci * ohci, hcd_ed, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1)
-
- CTRL: ctrl, ctrl_len ... cmd buffer
- data, data_len ... data buffer (in or out)
- INT, BULK: ctrl = NULL, ctrl_len=0,
- data, data_len ... data buffer (in or out)
- ISO: tbd
-
- There is no buffer reinsertion done by the internal HCD function.
- (The interface layer does this for a INT-pipe on request.)
- If you want a transfer then you have to
- provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED
- you can send as many as you like. They should come back by the callback f_handler in
- the same order (for each endpoint, not globally) If an error occurs all
- queued transfers of an endpoint will return unsent. They will be marked with an error status.
-
- e.g double-buffering for int transfers:
-
- ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
- ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
-
- and when a data0 packet returns by the callback f_handler requeue it:
- ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
- and when a data1 packet returns by the callback f_handler requeue it:
- ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
-
- lw0, lw1 are private fields for upper layers for ids or fine grained handlers.
- The alternate usb uses them for dev_id and usb_device_irq handler.
-
-
-- Done list handling: returns the requests (callback f_handler in ED) and does
- some error handling, root-hub request dequeuing
- (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0))
-
-
--- /dev/null
+
+The OHCI HCD layer is a simple but nearly complete implementation of what the
+USB people would call a HCD for the OHCI.
+ (ISO comming soon, Bulk, INT u. CTRL transfers enabled)
+It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree).
+The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers.
+
+- Roman Weissgaerber <weissg@vienna.at>
+
+ * v4.0 1999/08/18 removed all dummy eds, unlink unused eds, code cleanup, bulk transfers
+ * v2.1 1999/05/09 ep_addr correction, code cleanup
+ * v0.2.0 1999/05/04
+ * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers)
+ * virtual root hub is now an option,
+ * memory allocation based on kmalloc and kfree now, simple Bus error handling,
+ * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
+ *
+ * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff
+ * from Greg Smith (ohci.c): better reset ohci-controller handling, hub
+ *
+ * v0.1.0 1999/04/27 initial release
+
+to remove the module try:
+rmmod usb-ohci
+
+Features:
+- virtual root hub, all basic hub descriptors and commands (state: complete)
+ this is an option now (v0.2.0)
+ #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB)
+ default is with.
+ (at the moment: the Virtual Root Hub is included automatically)
+
+ files: ohci-root-hub.c, ohci-root-hub.h
+
+
+- Endpoint Descriptor (ED) handling more static approach
+ (EDs should be allocated in parallel to the SET CONFIGURATION command and they live
+ as long as the function (device) is alive or another configuration is choosen.
+ In the HCD layer the EDs has to be allocated manually either by calling a subroutine
+ or by sending a USB root hub vendor specific command to the virtual root hub.
+ At the alternate linux usb stack EDs will be added (allocated) at their first use.
+ ED will be unlinked from the HC chains if they are not bussy.
+
+ files: ohci-hcd.c ohci-hcd.h
+ routines: (do not use for drivers, use the top layer alternate usb commands instead)
+
+ int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1,
+ int interval, int load, f_handler handler, int ep_size, int speed)
+ adds an endpoint, (if the endpoint already exists some parameters will be updated)
+
+ int usb_ohci_rm_ep( )
+ removes an endpoint and all pending TDs of that EP
+
+ usb_ohci_rm_function( )
+ removes all Endpoints of a function (device)
+
+- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers
+ The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has
+ to take care of buffer allocation.
+ files: ohci-hcd.c ohci-hcd.h
+
+ There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL):
+
+ int ohci_trans_req(struct ohci * ohci, hcd_ed, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1)
+
+ CTRL: ctrl, ctrl_len ... cmd buffer
+ data, data_len ... data buffer (in or out)
+ INT, BULK: ctrl = NULL, ctrl_len=0,
+ data, data_len ... data buffer (in or out)
+ ISO: tbd
+
+ There is no buffer reinsertion done by the internal HCD function.
+ (The interface layer does this for a INT-pipe on request.)
+ If you want a transfer then you have to
+ provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED
+ you can send as many as you like. They should come back by the callback f_handler in
+ the same order (for each endpoint, not globally) If an error occurs all
+ queued transfers of an endpoint will return unsent. They will be marked with an error status.
+
+ e.g double-buffering for int transfers:
+
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+
+ and when a data0 packet returns by the callback f_handler requeue it:
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+ and when a data1 packet returns by the callback f_handler requeue it:
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+
+ lw0, lw1 are private fields for upper layers for ids or fine grained handlers.
+ The alternate usb uses them for dev_id and usb_device_irq handler.
+
+
+- Done list handling: returns the requests (callback f_handler in ED) and does
+ some error handling, root-hub request dequeuing
+ (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0))
+
+
M: vojtech@suse.cz
L: linux-joystick@atrey.karlin.mff.cuni.cz
W: http://www.suse.cz/development/joystick/
-S: Maintained
+S: Supported
KERNEL AUTOMOUNTER (AUTOFS)
P: H. Peter Anvin
W: http://www.linux-usb.org
S: Supported
+USB ACM DRIVER
+P: Vojtech Pavlik
+M: vojtech@suse.cz
+L: linux-usb@suse.com
+S: Supported
+
+USB HID/HIDBP/INPUT DRIVERS
+P: Vojtech Pavlik
+M: vojtech@suse.cz
+L: linux-usb@suse.com
+W: http://www.suse.cz/development/input/
+S: Supported
+
USB HUB
P: Johannes Erdfelt
M: jerdfelt@sventech.com
S: Maintained
USB OHCI DRIVER
-P: Gregory P. Smith
-M: greg@electricrain.com
-M: greg@suitenine.com
+P: Roman Weissgaerber
+M: weissg@vienna.at
L: linux-usb@suse.com
-S: Maintained (not yet usable)
-W: http://suitenine.com/usb/
+S: Maintained
+
+USB PRINTER DRIVER
+P: Vojtech Pavlik
+M: vojtech@suse.cz
+L: linux-usb@suse.com
+S: Supported
USB SERIAL DRIVER
P: Greg Kroah-Hartman
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 40
+SUBLEVEL = 41
EXTRAVERSION =
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
#include <linux/stddef.h>
#include <linux/binfmts.h>
#include <linux/tty.h>
-#include <linux/highuid.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
info.si_code = SI_USER;
info.si_pid = current->p_pptr->pid;
info.si_uid = current->p_pptr->uid;
- info.si_uid16 = high2lowuid(current->p_pptr->uid);
}
/* If the (new) signal is now blocked, requeue it. */
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
-#include <linux/highuid.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
info.si_code = SI_USER;
info.si_pid = current->p_pptr->pid;
info.si_uid = current->p_pptr->uid;
- info.si_uid16 = high2lowuid(current->p_pptr->uid);
}
/* If the (new) signal is now blocked, requeue it. */
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
-#include <linux/highuid.h>
#include <asm/setup.h>
#include <asm/uaccess.h>
info.si_code = SI_USER;
info.si_pid = current->p_pptr->pid;
info.si_uid = current->p_pptr->uid;
- info.si_uid16 = high2lowuid(current->p_pptr->uid);
}
/* If the (new) signal is now blocked, requeue it. */
#endif
}
+static unsigned char kbd_exists = 1;
+
static inline void handle_keyboard_event(unsigned char scancode)
{
+ kbd_exists = 1;
#ifdef CONFIG_VT
if (do_acknowledge(scancode))
handle_scancode(scancode, !(scancode & 0x80));
{
int retries = 3;
+ if (!kbd_exists) return 0;
+
do {
unsigned long timeout = KBD_TIMEOUT;
mdelay(1);
if (!--timeout) {
#ifdef KBD_REPORT_TIMEOUTS
- printk(KERN_WARNING "Keyboard timeout[2]\n");
+ printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?\n");
#endif
+ kbd_exists = 0;
return 0;
}
}
#ifdef KBD_REPORT_TIMEOUTS
printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n");
#endif
+ kbd_exists = 0;
return 0;
}
endif
L_OBJS += scsi_n_syms.o hosts.o scsi_ioctl.o constants.o scsicam.o
L_OBJS += scsi_error.o scsi_obsolete.o scsi_queue.o scsi_lib.o
- L_OBJS += scsi_merge.o scsi_proc.o
+ L_OBJS += scsi_merge.o scsi_proc.o scsi_dma.o scsi_scan.o
else
ifeq ($(CONFIG_SCSI),m)
MIX_OBJS += scsi_syms.o
scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o \
- scsi_queue.o scsi_lib.o scsi_merge.o
+ scsi_queue.o scsi_lib.o scsi_merge.o scsi_dma.o scsi_scan.o
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o \
constants.o scsicam.o scsi_proc.o scsi_merge.o \
- scsi_error.o scsi_obsolete.o scsi_queue.o scsi_lib.o
+ scsi_error.o scsi_obsolete.o scsi_queue.o scsi_lib.o \
+ scsi_dma.o scsi_scan.o
sr_mod.o: sr.o sr_ioctl.o sr_vendor.o
$(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o sr_vendor.o
* a later time. This problem cannot be resolved by holding a single entry
* in scratch ram since a reconnecting target can request sense and this will
* create yet another SCB waiting for selection. The solution used here is to
- * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
* of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
* SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
* this list everytime a request sense occurs or after completing a non-tagged
static ulong int_counter = 0;
static ulong queue_counter = 0;
-void eata_scsi_done (Scsi_Cmnd * scmd)
-{
- scmd->request.rq_status = RQ_SCSI_DONE;
-
- if (scmd->request.sem != NULL)
- up(scmd->request.sem);
-
- return;
-}
-
void eata_fake_int_handler(s32 irq, void *dev_id, struct pt_regs * regs)
{
fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
int hostno, int inout)
{
- Scsi_Device *scd, SDev;
+ Scsi_Device *scd, *SDev;
struct Scsi_Host *HBA_ptr;
- Scsi_Cmnd scmd;
- char cmnd[10];
+ Scsi_Cmnd * scmd;
+ char cmnd[MAX_COMMAND_SIZE];
static u8 buff[512];
static u8 buff2[512];
hst_cmd_stat *rhcs, *whcs;
pos = begin + len;
} else {
- memset(&SDev, 0, sizeof(Scsi_Device));
- memset(&scmd, 0, sizeof(Scsi_Cmnd));
-
- SDev.host = HBA_ptr;
- SDev.id = HBA_ptr->this_id;
- SDev.lun = 0;
- SDev.channel = 0;
+ SDev = scsi_get_host_dev(HBA_ptr);
+ scmd = scsi_allocate_device(SDev, 1, FALSE);
cmnd[0] = LOG_SENSE;
cmnd[1] = 0;
cmnd[8] = 0x66;
cmnd[9] = 0;
- scmd.cmd_len = 10;
+ scmd->cmd_len = 10;
- scmd.host = HBA_ptr;
- scmd.device = &SDev;
- scmd.target = HBA_ptr->this_id;
- scmd.lun = 0;
- scmd.channel = 0;
- scmd.use_sg = 0;
-
/*
* Do the command and wait for it to finish.
*/
- {
- DECLARE_MUTEX_LOCKED(sem);
- scmd.request.rq_status = RQ_SCSI_BUSY;
- scmd.request.sem = &sem;
- scsi_do_cmd (&scmd, cmnd, buff + 0x144, 0x66,
- eata_scsi_done, 1 * HZ, 1);
- down(&sem);
- }
+ scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,
+ 1 * HZ, 1);
size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
(cc->intt == TRUE)?"level":"edge");
cmnd[8] = 0x44;
cmnd[9] = 0;
- scmd.cmd_len = 10;
+ scmd->cmd_len = 10;
/*
* Do the command and wait for it to finish.
*/
- {
- DECLARE_MUTEX_LOCKED(sem);
- scmd.request.rq_status = RQ_SCSI_BUSY;
- scmd.request.sem = &sem;
- scsi_do_cmd (&scmd, cmnd, buff2, 0x144,
- eata_scsi_done, 1 * HZ, 1);
- down(&sem);
- }
+ scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+ 1 * HZ, 1);
swap_statistics(buff2);
rhcs = (hst_cmd_stat *)(buff2 + 0x2c);
len += size;
pos = begin + len;
}
+
+ scsi_release_command(scmd);
+ scsi_free_host_dev(SDev);
}
if (pos < offset) {
NUMDATA(shp)->busnum= 0;
ha->pccb = CMDDATA(shp);
- ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+ ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, GDTH_SCRATCH_ORD);
ha->scratch_busy = FALSE;
ha->req_first = NULL;
ha->tid_cnt = MAX_HDRIVES;
--gdth_ctr_count;
--gdth_ctr_vcount;
if (ha->pscratch != NULL)
- scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+ free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
free_irq(ha->irq,NULL);
scsi_unregister(shp);
continue;
NUMDATA(shp)->hanum));
ha->pccb = CMDDATA(shp);
- ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+ ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, GDTH_SCRATCH_ORD);
ha->scratch_busy = FALSE;
ha->req_first = NULL;
ha->tid_cnt = MAX_HDRIVES;
--gdth_ctr_count;
--gdth_ctr_vcount;
if (ha->pscratch != NULL)
- scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+ free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
free_irq(ha->irq,NULL);
scsi_unregister(shp);
continue;
NUMDATA(shp)->busnum= 0;
ha->pccb = CMDDATA(shp);
- ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+ ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, GDTH_SCRATCH_ORD);
ha->scratch_busy = FALSE;
ha->req_first = NULL;
ha->tid_cnt = pcistr[ctr].device_id >= 0x200 ? MAXID : MAX_HDRIVES;
--gdth_ctr_count;
--gdth_ctr_vcount;
if (ha->pscratch != NULL)
- scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+ free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
free_irq(ha->irq,NULL);
scsi_unregister(shp);
continue;
if (shp->dma_channel != 0xff) {
free_dma(shp->dma_channel);
}
- scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+ free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
gdth_ctr_released++;
TRACE2(("gdth_release(): HA %d of %d\n",
gdth_ctr_released, gdth_ctr_count));
{
int i;
gdth_ha_str *ha;
- Scsi_Cmnd scp;
- Scsi_Device sdev;
+ Scsi_Cmnd * scp;
+ Scsi_Device * sdev;
gdth_cmd_str gdtcmd;
TRACE2(("gdth_flush() hanum %d\n",hanum));
ha = HADATA(gdth_ctr_tab[hanum]);
- memset(&sdev,0,sizeof(Scsi_Device));
- memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_tab[hanum];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_tab[hanum];
- scp.target = sdev.host->this_id;
- scp.device = &sdev;
- scp.use_sg = 0;
+
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
for (i = 0; i < MAX_HDRIVES; ++i) {
if (ha->hdr[i].present) {
gdtcmd.u.cache.BlockNo = 1;
gdtcmd.u.cache.sg_canz = 0;
TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i));
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(scp, &gdtcmd, 30);
}
}
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
}
/* shutdown routine */
{
int hanum;
#ifndef __alpha__
- Scsi_Cmnd scp;
- Scsi_Device sdev;
+ Scsi_Cmnd * scp;
+ Scsi_Device * sdev;
gdth_cmd_str gdtcmd;
#endif
#ifndef __alpha__
/* controller reset */
- memset(&sdev,0,sizeof(Scsi_Device));
- memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_tab[hanum];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_tab[hanum];
- scp.target = sdev.host->this_id;
- scp.device = &sdev;
- scp.use_sg = 0;
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
gdtcmd.BoardNode = LOCALBOARD;
gdtcmd.Service = CACHESERVICE;
gdtcmd.OpCode = GDT_RESET;
TRACE2(("gdth_halt(): reset controller %d\n", hanum));
- gdth_do_cmd(&scp, &gdtcmd, 10);
+ gdth_do_cmd(scp, &gdtcmd, 10);
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
#endif
}
+
printk("Done.\n");
#ifdef GDTH_STATISTICS
#endif
/* limits */
-#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */
+#define GDTH_SCRATCH PAGE_SIZE /* 4KB scratch buffer */
+#define GDTH_SCRATCH_ORD 0 /* order 0 means 1 page */
#define GDTH_MAXCMDS 124
#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
#define GDTH_MAX_RAW 2 /* max. cmds per raw device */
static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
{
int ret_val;
- Scsi_Cmnd scp;
- Scsi_Device sdev;
+ Scsi_Cmnd * scp;
+ Scsi_Device * sdev;
gdth_iowr_str *piowr;
TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
piowr = (gdth_iowr_str *)buffer;
- memset(&sdev,0,sizeof(Scsi_Device));
- memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_vtab[vh];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_vtab[vh];
- scp.target = sdev.host->this_id;
- scp.device = &sdev;
- scp.use_sg = 0;
+ sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
if (length >= 4) {
if (strncmp(buffer,"gdth",4) == 0) {
} else {
ret_val = -EINVAL;
}
+
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+
return ret_val;
}
-static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd * scp)
{
int orig_length, drive, wb_mode;
int i, found;
gdtcmd.u.cache.DeviceNo = i;
gdtcmd.u.cache.BlockNo = 1;
gdtcmd.u.cache.sg_canz = 0;
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(scp, &gdtcmd, 30);
}
}
if (!found)
gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
pcpar->write_back = wb_mode==1 ? 0:1;
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(scp, &gdtcmd, 30);
gdth_ioctl_free(hanum);
printk("Done.\n");
return(orig_length);
return(-EINVAL);
}
-static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd * scp)
{
unchar i, j;
gdth_ha_str *ha;
*ppadd2 = virt_to_bus(piord->iu.general.data+add_size);
}
/* do IOCTL */
- gdth_do_cmd(&scp, pcmd, piowr->timeout);
- piord->status = (ulong32)scp.SCp.Message;
+ gdth_do_cmd(scp, pcmd, piowr->timeout);
+ piord->status = (ulong32)scp->SCp.Message;
break;
case GDTIOCTL_DRVERS:
gdth_cmd_str gdtcmd;
gdth_evt_str estr;
- Scsi_Cmnd scp;
- Scsi_Device sdev;
+ Scsi_Cmnd * scp;
+ Scsi_Device *sdev;
char hrec[161];
struct timeval tv;
ha = HADATA(gdth_ctr_tab[hanum]);
id = length;
- memset(&sdev,0,sizeof(Scsi_Device));
- memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_vtab[vh];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_vtab[vh];
- scp.target = sdev.host->this_id;
- scp.device = &sdev;
- scp.use_sg = 0;
+ sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
/* look for buffer ID in length */
if (id > 1) {
sizeof(pds->list[0]);
if (pds->entries > cnt)
pds->entries = cnt;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK)
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message != S_OK)
pds->count = 0;
TRACE2(("pdr_statistics() entries %d status %d\n",
- pds->count, scp.SCp.Message));
+ pds->count, scp->SCp.Message));
/* other IOCTLs must fit into area GDTH_SCRATCH/4 */
for (j = 0; j < ha->raw[i].pdev_cnt; ++j) {
gdtcmd.u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
gdtcmd.u.ioctl.channel =
ha->raw[i].address | ha->raw[i].id_list[j];
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message == S_OK) {
strncpy(hrec,pdi->vendor,8);
strncpy(hrec+8,pdi->product,16);
strncpy(hrec+24,pdi->revision,4);
gdtcmd.u.ioctl.channel =
ha->raw[i].address | ha->raw[i].id_list[j];
pdef->sddc_type = 0x08;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message == S_OK) {
size = sprintf(buffer+len,
" Grown Defects:\t%d\n",
pdef->sddc_cnt);
gdtcmd.u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
gdtcmd.u.ioctl.subfunc = CACHE_DRV_INFO;
gdtcmd.u.ioctl.channel = drv_no;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK)
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message != S_OK)
break;
pcdi->ld_dtype >>= 16;
j++;
gdtcmd.u.ioctl.param_size = sizeof(gdth_arrayinf_str);
gdtcmd.u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
gdtcmd.u.ioctl.channel = i;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message == S_OK) {
if (pai->ai_state == 0)
strcpy(hrec, "idle");
else if (pai->ai_state == 2)
gdtcmd.u.ioctl.channel = i;
phg->entries = MAX_HDRIVES;
phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]);
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK) {
+ gdth_do_cmd(scp, &gdtcmd, 30);
+ if (scp->SCp.Message != S_OK) {
ha->hdr[i].ldr_no = i;
ha->hdr[i].rw_attribs = 0;
ha->hdr[i].start_sec = 0;
}
}
TRACE2(("host_get entries %d status %d\n",
- phg->entries, scp.SCp.Message));
+ phg->entries, scp->SCp.Message));
}
gdth_ioctl_free(hanum);
}
stop_output:
+
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+
*start = buffer +(offset-begin);
len -= (offset-begin);
if (len > length)
static void gdth_do_cmd(Scsi_Cmnd *scp,gdth_cmd_str *gdtcmd,int timeout)
{
- char cmnd[12];
+ char cmnd[MAX_COMMAND_SIZE];
DECLARE_MUTEX_LOCKED(sem);
TRACE2(("gdth_do_cmd()\n"));
- memset(cmnd, 0, 12);
+ memset(cmnd, 0, MAX_COMMAND_SIZE);
scp->request.rq_status = RQ_SCSI_BUSY;
scp->request.sem = &sem;
scp->SCp.this_residual = IOCTL_PRI;
*/
static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
-static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
-static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd * scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd * scp);
static int gdth_get_info(char *buffer,char **start,off_t offset,
int length,int vh,int hanum,int busnum);
printk ("scsi : %d host%s.\n", next_scsi_host,
(next_scsi_host == 1) ? "" : "s");
-
/* Now attach the high level drivers */
#ifdef CONFIG_BLK_DEV_SD
scsi_register_device(&sd_template);
unsigned int max_lun;
unsigned int max_channel;
-
/* These parameters should be set by the detect routine */
unsigned long base;
unsigned long io_port;
extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
extern void scsi_unregister(struct Scsi_Host * i);
+extern request_fn_proc * scsi_get_request_handler(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
+
+/*
+ * Prototypes for functions/data in scsi_scan.c
+ */
+extern void scan_scsis(struct Scsi_Host *shpnt,
+ unchar hardcoded,
+ unchar hchannel,
+ unchar hid,
+ unchar hlun);
+
extern void scsi_mark_host_reset(struct Scsi_Host *Host);
#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
USHORT SupportLBA :1; // 49 LBA supported
USHORT SupportIORDYDisable :1; // 49 IORDY can be disabled
USHORT SupportIORDY :1; // 49 IORDY supported
- USHORT ReservedPsuedoDMA :1; // 49 reserved for pseudo DMA mode support
+ USHORT ReservedPseudoDMA :1; // 49 reserved for pseudo DMA mode support
USHORT Reserved3 :3; // 49
USHORT Reserved4; // 50
USHORT Reserved5 :8; // 51 Transfer Cycle Timing - PIO
* Definitions and constants.
*/
-/*
- * PAGE_SIZE must be a multiple of the sector size (512). True
- * for all reasonably recent architectures (even the VAX...).
- */
-#define SECTOR_SIZE 512
-#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)
-
-#if SECTORS_PER_PAGE <= 8
-typedef unsigned char FreeSectorBitmap;
-#elif SECTORS_PER_PAGE <= 32
-typedef unsigned int FreeSectorBitmap;
-#else
-#error You lose.
-#endif
-
#define MIN_RESET_DELAY (2*HZ)
/* Do not call reset on error if we just did a reset within 15 sec. */
#define MIN_RESET_PERIOD (15*HZ)
-/* The following devices are known not to tolerate a lun != 0 scan for
- * one reason or another. Some will respond to all luns, others will
- * lock up.
- */
-
-#define BLIST_NOLUN 0x001
-#define BLIST_FORCELUN 0x002
-#define BLIST_BORKEN 0x004
-#define BLIST_KEY 0x008
-#define BLIST_SINGLELUN 0x010
-#define BLIST_NOTQ 0x020
-#define BLIST_SPARSELUN 0x040
-#define BLIST_MAX5LUN 0x080
-#define BLIST_ISDISK 0x100
-#define BLIST_ISROM 0x200
-#define BLIST_GHOST 0x400
-
-
/*
* Data declarations.
static unsigned long serial_number = 0;
static Scsi_Cmnd *scsi_bh_queue_head = NULL;
static Scsi_Cmnd *scsi_bh_queue_tail = NULL;
-static FreeSectorBitmap *dma_malloc_freelist = NULL;
-static int need_isa_bounce_buffers;
-static unsigned int dma_sectors = 0;
-unsigned int scsi_dma_free_sectors = 0;
-unsigned int scsi_need_isa_buffer = 0;
-static unsigned char **dma_malloc_pages = NULL;
/*
* Note - the initial logging level can be set here to log events at boot time.
/*
* Function prototypes.
*/
-static void resize_dma_pool(void);
-static void print_inquiry(unsigned char *data);
extern void scsi_times_out(Scsi_Cmnd * SCpnt);
-static int scan_scsis_single(int channel, int dev, int lun, int *max_scsi_dev,
- int *sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
- struct Scsi_Host *shpnt, char *scsi_result);
void scsi_build_commandblocks(Scsi_Device * SDpnt);
static int scsi_unregister_device(struct Scsi_Device_Template *tpnt);
extern void scsi_old_done(Scsi_Cmnd * SCpnt);
extern void scsi_old_times_out(Scsi_Cmnd * SCpnt);
-struct dev_info {
- const char *vendor;
- const char *model;
- const char *revision; /* Latest revision known to be bad. Not used yet */
- unsigned flags;
-};
-
-/*
- * This is what was previously known as the blacklist. The concept
- * has been expanded so that we can specify other types of things we
- * need to be aware of.
- */
-static struct dev_info device_list[] =
-{
- {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
- {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
- {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */
- {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
- {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
- {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* guess what? */
- {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /*Responds to all lun */
- {"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */
- {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
- {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* Responds to all lun */
- {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- * for aha152x controller, which causes
- * SCSI code to reset bus.*/
- {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- * for aha152x controller, which causes
- * SCSI code to reset bus.*/
- {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* Responds to all lun */
- {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* Responds to all lun */
- {"SONY", "CD-ROM CDU-541", "4.3d", BLIST_NOLUN},
- {"SONY", "CD-ROM CDU-55S", "1.0i", BLIST_NOLUN},
- {"SONY", "CD-ROM CDU-561", "1.7x", BLIST_NOLUN},
- {"SONY", "CD-ROM CDU-8012", "*", BLIST_NOLUN},
- {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- * for seagate controller, which causes
- * SCSI code to reset bus.*/
- {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* Responds to all lun */
- {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
- * for seagate controller, which causes
- * SCSI code to reset bus.*/
- {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
- {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
- {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* Locks up when polled for lun != 0 */
- {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
- {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
- {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
- {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
- {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
- {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0
- * extra reset */
- {"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */
- {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all LUN */
/*
- * Other types of devices that have special flags.
+ * Function: scsi_get_request_handler()
+ *
+ * Purpose: Selects queue handler function for a device.
+ *
+ * Arguments: SDpnt - device for which we need a handler function.
+ *
+ * Returns: Nothing
+ *
+ * Lock status: No locking assumed or required.
+ *
+ * Notes: Most devices will end up using scsi_request_fn for the
+ * handler function (at least as things are done now).
+ * The "block" feature basically ensures that only one of
+ * the blocked hosts is active at one time, mainly to work around
+ * buggy DMA chipsets where the memory gets starved.
+ * For this case, we have a special handler function, which
+ * does some checks and ultimately calls scsi_request_fn.
+ *
+ * As a future enhancement, it might be worthwhile to add support
+ * for stacked handlers - there might get to be too many permutations
+ * otherwise. Then again, we might just have one handler that does
+ * all of the special cases (a little bit slower), and those devices
+ * that don't need the special case code would directly call
+ * scsi_request_fn.
+ *
+ * As it stands, I can think of a number of special cases that
+ * we might need to handle. This would not only include the blocked
+ * case, but single_lun (for changers), and any special handling
+ * we might need for a spun-down disk to spin it back up again.
*/
- {"SONY", "CD-ROM CDU-8001", "*", BLIST_BORKEN},
- {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN},
- {"IOMEGA", "Io20S *F", "*", BLIST_KEY},
- {"INSITE", "Floptical F*8I", "*", BLIST_KEY},
- {"INSITE", "I325VM", "*", BLIST_KEY},
- {"NRC", "MBR-7", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"NRC", "MBR-7.4", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"REGAL", "CDC-4X", "*", BLIST_MAX5LUN | BLIST_SINGLELUN},
- {"NAKAMICH", "MJ-4.8S", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"NAKAMICH", "MJ-5.16S", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"PIONEER", "CD-ROM DRM-600", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"PIONEER", "CD-ROM DRM-602X", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"PIONEER", "CD-ROM DRM-604X", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN},
- {"CANON", "IPUBJD", "*", BLIST_SPARSELUN},
- {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN},
- {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
- {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
- {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST},
- {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST},
- {"HITACHI", "GF-1050","*", BLIST_GHOST}, /* Hitachi SCSI DVD-RAM */
- {"TOSHIBA","CDROM","*", BLIST_ISROM},
- {"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST},
- {"TOSHIBA","DVD-RAM SD-W1111","*", BLIST_GHOST},
-
- /*
- * Must be at end of list...
- */
- {NULL, NULL, NULL}
-};
-
-static int get_device_flags(unsigned char *response_data)
-{
- int i = 0;
- unsigned char *pnt;
- for (i = 0; 1; i++) {
- if (device_list[i].vendor == NULL)
- return 0;
- pnt = &response_data[8];
- while (*pnt && *pnt == ' ')
- pnt++;
- if (memcmp(device_list[i].vendor, pnt,
- strlen(device_list[i].vendor)))
- continue;
- pnt = &response_data[16];
- while (*pnt && *pnt == ' ')
- pnt++;
- if (memcmp(device_list[i].model, pnt,
- strlen(device_list[i].model)))
- continue;
- return device_list[i].flags;
- }
- return 0;
-}
-
-
-static void scan_scsis_done(Scsi_Cmnd * SCpnt)
-{
-
- SCSI_LOG_MLCOMPLETE(1, printk("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result));
- SCpnt->request.rq_status = RQ_SCSI_DONE;
-
- if (SCpnt->request.sem != NULL)
- up(SCpnt->request.sem);
+request_fn_proc * scsi_get_request_handler(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) {
+ return scsi_request_fn;
}
#ifdef MODULE
#endif
-#ifdef CONFIG_SCSI_MULTI_LUN
-static int max_scsi_luns = 8;
-#else
-static int max_scsi_luns = 1;
-#endif
-
-#ifdef MODULE
-
-MODULE_PARM(max_scsi_luns, "i");
-MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 8)");
-
-#else
-
-static int __init scsi_luns_setup(char *str)
+/*
+ * Issue a command and wait for it to complete
+ */
+
+static void scsi_wait_done(Scsi_Cmnd * SCpnt)
{
- int tmp;
+ struct request *req;
- if (get_option(&str, &tmp) == 1) {
- max_scsi_luns = tmp;
- return 1;
- } else {
- printk("scsi_luns_setup : usage max_scsi_luns=n "
- "(n should be between 1 and 8)\n");
- return 0;
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
}
}
-__setup("max_scsi_luns=", scsi_luns_setup);
-
-#endif
-
-/*
- * Issue a command and wait for it to complete
- */
-
void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
- void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
+ void *buffer, unsigned bufflen,
int timeout, int retries)
{
DECLARE_MUTEX_LOCKED(sem);
SCpnt->request.sem = &sem;
SCpnt->request.rq_status = RQ_SCSI_BUSY;
scsi_do_cmd (SCpnt, (void *) cmnd,
- buffer, bufflen, done, timeout, retries);
+ buffer, bufflen, scsi_wait_done, timeout, retries);
down (&sem);
SCpnt->request.sem = NULL;
}
-/*
- * Detecting SCSI devices :
- * We scan all present host adapter's busses, from ID 0 to ID (max_id).
- * We use the INQUIRY command, determine device type, and pass the ID /
- * lun address of all sequential devices to the tape driver, all random
- * devices to the disk driver.
- */
-static void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun)
-{
- int channel;
- int dev;
- int lun;
- int max_dev_lun;
- Scsi_Cmnd *SCpnt;
- unsigned char *scsi_result;
- unsigned char scsi_result0[256];
- Scsi_Device *SDpnt;
- Scsi_Device *SDtail;
- int sparse_lun;
-
- scsi_result = NULL;
- SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd),
- GFP_ATOMIC | GFP_DMA);
- if (SCpnt) {
- SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof(Scsi_Device),
- GFP_ATOMIC);
- if (SDpnt) {
- /*
- * Register the queue for the device. All I/O requests will come
- * in through here. We also need to register a pointer to
- * ourselves, since the queue handler won't know what device
- * the queue actually represents. We could look it up, but it
- * is pointless work.
- */
- blk_init_queue(&SDpnt->request_queue, scsi_request_fn);
- blk_queue_headactive(&SDpnt->request_queue, 0);
- SDpnt->request_queue.queuedata = (void *) SDpnt;
- /* Make sure we have something that is valid for DMA purposes */
- scsi_result = ((!shpnt->unchecked_isa_dma)
- ? &scsi_result0[0] : scsi_init_malloc(512, GFP_DMA));
- }
- }
- if (scsi_result == NULL) {
- printk("Unable to obtain scsi_result buffer\n");
- goto leave;
- }
- /*
- * We must chain ourself in the host_queue, so commands can time out
- */
- SCpnt->next = NULL;
- SDpnt->device_queue = SCpnt;
- SDpnt->host = shpnt;
- SDpnt->online = TRUE;
-
- initialize_merge_fn(SDpnt);
-
- /*
- * Initialize the object that we will use to wait for command blocks.
- */
- init_waitqueue_head(&SDpnt->scpnt_wait);
-
- /*
- * Next, hook the device to the host in question.
- */
- SDpnt->prev = NULL;
- SDpnt->next = NULL;
- if (shpnt->host_queue != NULL) {
- SDtail = shpnt->host_queue;
- while (SDtail->next != NULL)
- SDtail = SDtail->next;
-
- SDtail->next = SDpnt;
- SDpnt->prev = SDtail;
- } else {
- shpnt->host_queue = SDpnt;
- }
-
- /*
- * We need to increment the counter for this one device so we can track when
- * things are quiet.
- */
- atomic_inc(&shpnt->host_active);
- atomic_inc(&SDpnt->device_active);
-
- if (hardcoded == 1) {
- Scsi_Device *oldSDpnt = SDpnt;
- struct Scsi_Device_Template *sdtpnt;
- channel = hchannel;
- if (channel > shpnt->max_channel)
- goto leave;
- dev = hid;
- if (dev >= shpnt->max_id)
- goto leave;
- lun = hlun;
- if (lun >= shpnt->max_lun)
- goto leave;
- scan_scsis_single(channel, dev, lun, &max_dev_lun, &sparse_lun,
- &SDpnt, SCpnt, shpnt, scsi_result);
- if (SDpnt != oldSDpnt) {
-
- /* it could happen the blockdevice hasn't yet been inited */
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->init && sdtpnt->dev_noticed)
- (*sdtpnt->init) ();
-
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
- if (sdtpnt->attach) {
- (*sdtpnt->attach) (oldSDpnt);
- if (oldSDpnt->attached) {
- scsi_build_commandblocks(oldSDpnt);
- if (0 == oldSDpnt->has_cmdblocks) {
- printk("scan_scsis: DANGER, no command blocks\n");
- /* What to do now ?? */
- }
- }
- }
- }
- resize_dma_pool();
-
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
- if (sdtpnt->finish && sdtpnt->nr_dev) {
- (*sdtpnt->finish) ();
- }
- }
- }
- } else {
- /* Actual LUN. PC ordering is 0->n IBM/spec ordering is n->0 */
- int order_dev;
-
- for (channel = 0; channel <= shpnt->max_channel; channel++) {
- for (dev = 0; dev < shpnt->max_id; ++dev) {
- if (shpnt->reverse_ordering)
- /* Shift to scanning 15,14,13... or 7,6,5,4, */
- order_dev = shpnt->max_id - dev - 1;
- else
- order_dev = dev;
-
- if (shpnt->this_id != order_dev) {
-
- /*
- * We need the for so our continue, etc. work fine. We put this in
- * a variable so that we can override it during the scan if we
- * detect a device *KNOWN* to have multiple logical units.
- */
- max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
- max_scsi_luns : shpnt->max_lun);
- sparse_lun = 0;
- for (lun = 0; lun < max_dev_lun; ++lun) {
- if (!scan_scsis_single(channel, order_dev, lun, &max_dev_lun,
- &sparse_lun, &SDpnt, SCpnt, shpnt,
- scsi_result)
- && !sparse_lun)
- break; /* break means don't probe further for luns!=0 */
- } /* for lun ends */
- } /* if this_id != id ends */
- } /* for dev ends */
- } /* for channel ends */
- } /* if/else hardcoded */
-
- /*
- * We need to decrement the counter for this one device
- * so we know when everything is quiet.
- */
- atomic_dec(&shpnt->host_active);
- atomic_dec(&SDpnt->device_active);
-
- leave:
-
- { /* Unchain SCpnt from host_queue */
- Scsi_Device *prev, *next;
- Scsi_Device *dqptr;
-
- for (dqptr = shpnt->host_queue; dqptr != SDpnt; dqptr = dqptr->next)
- continue;
- if (dqptr) {
- prev = dqptr->prev;
- next = dqptr->next;
- if (prev)
- prev->next = next;
- else
- shpnt->host_queue = next;
- if (next)
- next->prev = prev;
- }
- }
-
- /* Last device block does not exist. Free memory. */
- if (SDpnt != NULL)
- scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
-
- if (SCpnt != NULL)
- scsi_init_free((char *) SCpnt, sizeof(Scsi_Cmnd));
-
- /* If we allocated a buffer so we could do DMA, free it now */
- if (scsi_result != &scsi_result0[0] && scsi_result != NULL) {
- scsi_init_free(scsi_result, 512);
- } {
- Scsi_Device *sdev;
- Scsi_Cmnd *scmd;
-
- SCSI_LOG_SCAN_BUS(4, printk("Host status for host %p:\n", shpnt));
- for (sdev = shpnt->host_queue; sdev; sdev = sdev->next) {
- SCSI_LOG_SCAN_BUS(4, printk("Device %d %p: ", sdev->id, sdev));
- for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
- SCSI_LOG_SCAN_BUS(4, printk("%p ", scmd));
- }
- SCSI_LOG_SCAN_BUS(4, printk("\n"));
- }
- }
-}
-
-/*
- * The worker for scan_scsis.
- * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
- * Global variables used : scsi_devices(linked list)
- */
-int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
- int *sparse_lun, Scsi_Device ** SDpnt2, Scsi_Cmnd * SCpnt,
- struct Scsi_Host *shpnt, char *scsi_result)
-{
- unsigned char scsi_cmd[12];
- struct Scsi_Device_Template *sdtpnt;
- Scsi_Device *SDtail, *SDpnt = *SDpnt2;
- int bflags, type = -1;
- static int ghost_channel=-1, ghost_dev=-1;
- int org_lun = lun;
-
- SDpnt->host = shpnt;
- SDpnt->id = dev;
- SDpnt->lun = lun;
- SDpnt->channel = channel;
- SDpnt->online = TRUE;
-
-
- if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
- SDpnt->lun = 0;
- } else {
- ghost_channel = ghost_dev = -1;
- }
-
-
- /* Some low level driver could use device->type (DB) */
- SDpnt->type = -1;
-
- /*
- * Assume that the device will have handshaking problems, and then fix this
- * field later if it turns out it doesn't
- */
- SDpnt->borken = 1;
- SDpnt->was_reset = 0;
- SDpnt->expecting_cc_ua = 0;
- SDpnt->starved = 0;
-
- scsi_cmd[0] = TEST_UNIT_READY;
- scsi_cmd[1] = lun << 5;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
-
- SCpnt->host = SDpnt->host;
- SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
-
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
- (void *) NULL,
- 0, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
-
- SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
- dev, lun, SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, printk("\n"));
-
- if (SCpnt->result) {
- if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
- (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
- ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
- if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
- ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
- ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0))
- return 1;
- } else
- return 0;
- }
- SCSI_LOG_SCAN_BUS(3, printk("scsi: performing INQUIRY\n"));
- /*
- * Build an INQUIRY command block.
- */
- scsi_cmd[0] = INQUIRY;
- scsi_cmd[1] = (lun << 5) & 0xe0;
- scsi_cmd[2] = 0;
- scsi_cmd[3] = 0;
- scsi_cmd[4] = 255;
- scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
-
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
- (void *) scsi_result,
- 256, scan_scsis_done, SCSI_TIMEOUT, 3);
-
- SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
- SCpnt->result ? "failed" : "successful", SCpnt->result));
-
- if (SCpnt->result)
- return 0; /* assume no peripheral if any sort of error */
-
- /*
- * Check the peripheral qualifier field - this tells us whether LUNS
- * are supported here or not.
- */
- if ((scsi_result[0] >> 5) == 3) {
- return 0; /* assume no peripheral if any sort of error */
- }
-
- /*
- * Get any flags for this device.
- */
- bflags = get_device_flags (scsi_result);
-
-
- /* The Toshiba ROM was "gender-changed" here as an inline hack.
- This is now much more generic.
- This is a mess: What we really want is to leave the scsi_result
- alone, and just change the SDpnt structure. And the SDpnt is what
- we want print_inquiry to print. -- REW
- */
- if (bflags & BLIST_ISDISK) {
- scsi_result[0] = TYPE_DISK;
- scsi_result[1] |= 0x80; /* removable */
- }
-
- if (bflags & BLIST_ISROM) {
- scsi_result[0] = TYPE_ROM;
- scsi_result[1] |= 0x80; /* removable */
- }
-
- if (bflags & BLIST_GHOST) {
- if ((ghost_channel == channel) && (ghost_dev == dev) && (org_lun == 1)) {
- lun=1;
- } else {
- ghost_channel = channel;
- ghost_dev = dev;
- scsi_result[0] = TYPE_MOD;
- scsi_result[1] |= 0x80; /* removable */
- }
- }
-
-
- memcpy(SDpnt->vendor, scsi_result + 8, 8);
- memcpy(SDpnt->model, scsi_result + 16, 16);
- memcpy(SDpnt->rev, scsi_result + 32, 4);
-
- SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
- SDpnt->online = TRUE;
- SDpnt->lockable = SDpnt->removable;
- SDpnt->changed = 0;
- SDpnt->access_count = 0;
- SDpnt->busy = 0;
- SDpnt->has_cmdblocks = 0;
- /*
- * Currently, all sequential devices are assumed to be tapes, all random
- * devices disk, with the appropriate read only flags set for ROM / WORM
- * treated as RO.
- */
- switch (type = (scsi_result[0] & 0x1f)) {
- case TYPE_TAPE:
- case TYPE_DISK:
- case TYPE_MOD:
- case TYPE_PROCESSOR:
- case TYPE_SCANNER:
- case TYPE_MEDIUM_CHANGER:
- case TYPE_ENCLOSURE:
- SDpnt->writeable = 1;
- break;
- case TYPE_WORM:
- case TYPE_ROM:
- SDpnt->writeable = 0;
- break;
- default:
- printk("scsi: unknown type %d\n", type);
- }
-
- SDpnt->device_blocked = FALSE;
- SDpnt->device_busy = 0;
- SDpnt->single_lun = 0;
- SDpnt->soft_reset =
- (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
- SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
- SDpnt->type = (type & 0x1f);
-
- print_inquiry(scsi_result);
-
- for (sdtpnt = scsi_devicelist; sdtpnt;
- sdtpnt = sdtpnt->next)
- if (sdtpnt->detect)
- SDpnt->attached +=
- (*sdtpnt->detect) (SDpnt);
-
- SDpnt->scsi_level = scsi_result[2] & 0x07;
- if (SDpnt->scsi_level >= 2 ||
- (SDpnt->scsi_level == 1 &&
- (scsi_result[3] & 0x0f) == 1))
- SDpnt->scsi_level++;
-
- /*
- * Accommodate drivers that want to sleep when they should be in a polling
- * loop.
- */
- SDpnt->disconnect = 0;
-
-
- /*
- * Set the tagged_queue flag for SCSI-II devices that purport to support
- * tagged queuing in the INQUIRY data.
- */
- SDpnt->tagged_queue = 0;
- if ((SDpnt->scsi_level >= SCSI_2) &&
- (scsi_result[7] & 2) &&
- !(bflags & BLIST_NOTQ)) {
- SDpnt->tagged_supported = 1;
- SDpnt->current_tag = 0;
- }
- /*
- * Some revisions of the Texel CD ROM drives have handshaking problems when
- * used with the Seagate controllers. Before we know what type of device
- * we're talking to, we assume it's borken and then change it here if it
- * turns out that it isn't a TEXEL drive.
- */
- if ((bflags & BLIST_BORKEN) == 0)
- SDpnt->borken = 0;
-
- /*
- * If we want to only allow I/O to one of the luns attached to this device
- * at a time, then we set this flag.
- */
- if (bflags & BLIST_SINGLELUN)
- SDpnt->single_lun = 1;
-
- /*
- * These devices need this "key" to unlock the devices so we can use it
- */
- if ((bflags & BLIST_KEY) != 0) {
- printk("Unlocked floptical drive.\n");
- SDpnt->lockable = 0;
- scsi_cmd[0] = MODE_SENSE;
- scsi_cmd[1] = (lun << 5) & 0xe0;
- scsi_cmd[2] = 0x2e;
- scsi_cmd[3] = 0;
- scsi_cmd[4] = 0x2a;
- scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
- (void *) scsi_result, 0x2a,
- scan_scsis_done, SCSI_TIMEOUT, 3);
- }
- /*
- * Detach the command from the device. It was just a temporary to be used while
- * scanning the bus - the real ones will be allocated later.
- */
- SDpnt->device_queue = NULL;
-
- /*
- * This device was already hooked up to the host in question,
- * so at this point we just let go of it and it should be fine. We do need to
- * allocate a new one and attach it to the host so that we can further scan the bus.
- */
- SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof(Scsi_Device), GFP_ATOMIC);
- *SDpnt2 = SDpnt;
- if (!SDpnt) {
- printk("scsi: scan_scsis_single: Cannot malloc\n");
- return 0;
- }
- /*
- * Register the queue for the device. All I/O requests will come
- * in through here. We also need to register a pointer to
- * ourselves, since the queue handler won't know what device
- * the queue actually represents. We could look it up, but it
- * is pointless work.
- */
- blk_init_queue(&SDpnt->request_queue, scsi_request_fn);
- blk_queue_headactive(&SDpnt->request_queue, 0);
- SDpnt->request_queue.queuedata = (void *) SDpnt;
- SDpnt->host = shpnt;
- initialize_merge_fn(SDpnt);
-
- /*
- * And hook up our command block to the new device we will be testing
- * for.
- */
- SDpnt->device_queue = SCpnt;
- SDpnt->online = TRUE;
-
- /*
- * Initialize the object that we will use to wait for command blocks.
- */
- init_waitqueue_head(&SDpnt->scpnt_wait);
-
- /*
- * Since we just found one device, there had damn well better be one in the list
- * already.
- */
- if (shpnt->host_queue == NULL)
- panic("scan_scsis_single: Host queue == NULL\n");
-
- SDtail = shpnt->host_queue;
- while (SDtail->next) {
- SDtail = SDtail->next;
- }
-
- /* Add this device to the linked list at the end */
- SDtail->next = SDpnt;
- SDpnt->prev = SDtail;
- SDpnt->next = NULL;
-
- /*
- * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
- */
- if (bflags & BLIST_NOLUN)
- return 0; /* break; */
-
- /*
- * If this device is known to support sparse multiple units, override the
- * other settings, and scan all of them.
- */
- if (bflags & BLIST_SPARSELUN) {
- *max_dev_lun = 8;
- *sparse_lun = 1;
- return 1;
- }
- /*
- * If this device is known to support multiple units, override the other
- * settings, and scan all of them.
- */
- if (bflags & BLIST_FORCELUN) {
- *max_dev_lun = 8;
- return 1;
- }
- /*
- * REGAL CDC-4X: avoid hang after LUN 4
- */
- if (bflags & BLIST_MAX5LUN) {
- *max_dev_lun = 5;
- return 1;
- }
-
- /*
- * If this device is Ghosted, scan upto two luns. (It physically only
- * has one). -- REW
- */
- if (bflags & BLIST_GHOST) {
- *max_dev_lun = 2;
- return 1;
- }
-
-
- /*
- * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
- * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
- * (ANSI SCSI Revision 1) and Response Data Format 0
- */
- if (((scsi_result[2] & 0x07) == 0)
- ||
- ((scsi_result[2] & 0x07) == 1 &&
- (scsi_result[3] & 0x0f) == 0))
- return 0;
- return 1;
-}
-
-/*
- * Flag bits for the internal_timeout array
- */
-#define NORMAL_TIMEOUT 0
-#define IN_ABORT 1
-#define IN_RESET 2
-#define IN_RESET2 4
-#define IN_RESET3 8
-
-
/*
* This lock protects the freelist for all devices on the system.
* We could make this finer grained by having a single lock per
*/
static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED;
-/*
- * Used for access to internal allocator used for DMA safe buffers.
- */
-static spinlock_t allocator_request_lock = SPIN_LOCK_UNLOCKED;
-
/*
* Used to protect insertion into and removal from the queue of
* commands to be processed by the bottom half handler.
* the completion function for the high level driver.
*/
- memcpy((void *) SCpnt->data_cmnd, (const void *) cmnd, 12);
+ memcpy((void *) SCpnt->data_cmnd, (const void *) cmnd,
+ sizeof(SCpnt->data_cmnd));
SCpnt->reset_chain = NULL;
SCpnt->serial_number = 0;
SCpnt->serial_number_at_timeout = 0;
SCpnt->done = done;
SCpnt->timeout_per_command = timeout;
- memcpy((void *) SCpnt->cmnd, (const void *) cmnd, 12);
+ memcpy((void *) SCpnt->cmnd, (const void *) cmnd,
+ sizeof(SCpnt->cmnd));
/* Zero the sense buffer. Some host adapters automatically request
* sense on error. 0 is not a valid sense code.
*/
static void scsi_unregister_host(Scsi_Host_Template *);
#endif
-/*
- * Function: scsi_malloc
- *
- * Purpose: Allocate memory from the DMA-safe pool.
- *
- * Arguments: len - amount of memory we need.
- *
- * Lock status: No locks assumed to be held. This function is SMP-safe.
- *
- * Returns: Pointer to memory block.
- *
- * Notes: Prior to the new queue code, this function was not SMP-safe.
- * This function can only allocate in units of sectors
- * (i.e. 512 bytes).
- *
- * We cannot use the normal system allocator becuase we need
- * to be able to guarantee that we can process a complete disk
- * I/O request without touching the system allocator. Think
- * about it - if the system were heavily swapping, and tried to
- * write out a block of memory to disk, and the SCSI code needed
- * to allocate more memory in order to be able to write the
- * data to disk, you would wedge the system.
- */
-void *scsi_malloc(unsigned int len)
-{
- unsigned int nbits, mask;
- unsigned long flags;
-
- int i, j;
- if (len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
- return NULL;
-
- nbits = len >> 9;
- mask = (1 << nbits) - 1;
-
- spin_lock_irqsave(&allocator_request_lock, flags);
-
- for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++)
- for (j = 0; j <= SECTORS_PER_PAGE - nbits; j++) {
- if ((dma_malloc_freelist[i] & (mask << j)) == 0) {
- dma_malloc_freelist[i] |= (mask << j);
- scsi_dma_free_sectors -= nbits;
-#ifdef DEBUG
- SCSI_LOG_MLQUEUE(3, printk("SMalloc: %d %p [From:%p]\n", len, dma_malloc_pages[i] + (j << 9)));
- printk("SMalloc: %d %p [From:%p]\n", len, dma_malloc_pages[i] + (j << 9));
-#endif
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
- }
- }
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- return NULL; /* Nope. No more */
-}
-
-/*
- * Function: scsi_free
- *
- * Purpose: Free memory into the DMA-safe pool.
- *
- * Arguments: ptr - data block we are freeing.
- * len - size of block we are freeing.
- *
- * Lock status: No locks assumed to be held. This function is SMP-safe.
- *
- * Returns: Nothing
- *
- * Notes: This function *must* only be used to free memory
- * allocated from scsi_malloc().
- *
- * Prior to the new queue code, this function was not SMP-safe.
- * This function can only allocate in units of sectors
- * (i.e. 512 bytes).
- */
-int scsi_free(void *obj, unsigned int len)
-{
- unsigned int page, sector, nbits, mask;
- unsigned long flags;
-
-#ifdef DEBUG
- unsigned long ret = 0;
-
-#ifdef __mips__
- __asm__ __volatile__("move\t%0,$31":"=r"(ret));
-#else
- ret = __builtin_return_address(0);
-#endif
- printk("scsi_free %p %d\n", obj, len);
- SCSI_LOG_MLQUEUE(3, printk("SFree: %p %d\n", obj, len));
-#endif
-
- spin_lock_irqsave(&allocator_request_lock, flags);
-
- for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
- unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
- if ((unsigned long) obj >= page_addr &&
- (unsigned long) obj < page_addr + PAGE_SIZE) {
- sector = (((unsigned long) obj) - page_addr) >> 9;
-
- nbits = len >> 9;
- mask = (1 << nbits) - 1;
-
- if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
- panic("scsi_free:Bad memory alignment");
-
- if ((dma_malloc_freelist[page] &
- (mask << sector)) != (mask << sector)) {
-#ifdef DEBUG
- printk("scsi_free(obj=%p, len=%d) called from %08lx\n",
- obj, len, ret);
-#endif
- panic("scsi_free:Trying to free unused memory");
- }
- scsi_dma_free_sectors += nbits;
- dma_malloc_freelist[page] &= ~(mask << sector);
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- return 0;
- }
- }
- panic("scsi_free:Bad offset");
-}
-
int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
/*
* This should build the DMA pool.
*/
- resize_dma_pool();
+ scsi_resize_dma_pool();
/*
* OK, now we finish the initialization by doing spin-up, read
}
#endif /* MODULE */ /* } */
-static void print_inquiry(unsigned char *data)
-{
- int i;
-
- printk(" Vendor: ");
- for (i = 8; i < 16; i++) {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
-
- printk(" Model: ");
- for (i = 16; i < 32; i++) {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
-
- printk(" Rev: ");
- for (i = 32; i < 36; i++) {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
-
- printk("\n");
-
- i = data[0] & 0x1f;
-
- printk(" Type: %s ",
- i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown ");
- printk(" ANSI SCSI revision: %02x", data[2] & 0x07);
- if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
- printk(" CCS\n");
- else
- printk("\n");
-}
-
#ifdef CONFIG_PROC_FS
static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
{
}
#endif
-/*
- * Function: resize_dma_pool
- *
- * Purpose: Ensure that the DMA pool is sufficiently large to be
- * able to guarantee that we can always process I/O requests
- * without calling the system allocator.
- *
- * Arguments: None.
- *
- * Lock status: No locks assumed to be held. This function is SMP-safe.
- *
- * Returns: Nothing
- *
- * Notes: Prior to the new queue code, this function was not SMP-safe.
- * Go through the device list and recompute the most appropriate
- * size for the dma pool. Then grab more memory (as required).
- */
-static void resize_dma_pool(void)
-{
- int i, k;
- unsigned long size;
- unsigned long flags;
- struct Scsi_Host *shpnt;
- struct Scsi_Host *host = NULL;
- Scsi_Device *SDpnt;
- FreeSectorBitmap *new_dma_malloc_freelist = NULL;
- unsigned int new_dma_sectors = 0;
- unsigned int new_need_isa_buffer = 0;
- unsigned char **new_dma_malloc_pages = NULL;
- int out_of_space = 0;
-
- spin_lock_irqsave(&allocator_request_lock, flags);
-
- if (!scsi_hostlist) {
- /*
- * Free up the DMA pool.
- */
- if (scsi_dma_free_sectors != dma_sectors)
- panic("SCSI DMA pool memory leak %d %d\n", scsi_dma_free_sectors, dma_sectors);
-
- for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++)
- scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
- if (dma_malloc_pages)
- scsi_init_free((char *) dma_malloc_pages,
- (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages));
- dma_malloc_pages = NULL;
- if (dma_malloc_freelist)
- scsi_init_free((char *) dma_malloc_freelist,
- (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_freelist));
- dma_malloc_freelist = NULL;
- dma_sectors = 0;
- scsi_dma_free_sectors = 0;
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- return;
- }
- /* Next, check to see if we need to extend the DMA buffer pool */
-
- new_dma_sectors = 2 * SECTORS_PER_PAGE; /* Base value we use */
-
- if (__pa(high_memory) - 1 > ISA_DMA_THRESHOLD)
- need_isa_bounce_buffers = 1;
- else
- need_isa_bounce_buffers = 0;
-
- if (scsi_devicelist)
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
- new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
-
- for (host = scsi_hostlist; host; host = host->next) {
- for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
- /*
- * sd and sr drivers allocate scatterlists.
- * sr drivers may allocate for each command 1x2048 or 2x1024 extra
- * buffers for 2k sector size and 1k fs.
- * sg driver allocates buffers < 4k.
- * st driver does not need buffers from the dma pool.
- * estimate 4k buffer/command for devices of unknown type (should panic).
- */
- if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
- SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
- new_dma_sectors += ((host->sg_tablesize *
- sizeof(struct scatterlist) + 511) >> 9) *
- SDpnt->queue_depth;
- if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
- new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
- } else if (SDpnt->type == TYPE_SCANNER ||
- SDpnt->type == TYPE_PROCESSOR ||
- SDpnt->type == TYPE_MEDIUM_CHANGER ||
- SDpnt->type == TYPE_ENCLOSURE) {
- new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
- } else {
- if (SDpnt->type != TYPE_TAPE) {
- printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
- new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
- }
- }
-
- if (host->unchecked_isa_dma &&
- need_isa_bounce_buffers &&
- SDpnt->type != TYPE_TAPE) {
- new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
- SDpnt->queue_depth;
- new_need_isa_buffer++;
- }
- }
- }
-
-#ifdef DEBUG_INIT
- printk("resize_dma_pool: needed dma sectors = %d\n", new_dma_sectors);
-#endif
-
- /* limit DMA memory to 32MB: */
- new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
-
- /*
- * We never shrink the buffers - this leads to
- * race conditions that I would rather not even think
- * about right now.
- */
-#if 0 /* Why do this? No gain and risks out_of_space */
- if (new_dma_sectors < dma_sectors)
- new_dma_sectors = dma_sectors;
-#endif
- if (new_dma_sectors <= dma_sectors) {
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- return; /* best to quit while we are in front */
- }
-
- for (k = 0; k < 20; ++k) { /* just in case */
- out_of_space = 0;
- size = (new_dma_sectors / SECTORS_PER_PAGE) *
- sizeof(FreeSectorBitmap);
- new_dma_malloc_freelist = (FreeSectorBitmap *)
- scsi_init_malloc(size, GFP_ATOMIC);
- if (new_dma_malloc_freelist) {
- size = (new_dma_sectors / SECTORS_PER_PAGE) *
- sizeof(*new_dma_malloc_pages);
- new_dma_malloc_pages = (unsigned char **)
- scsi_init_malloc(size, GFP_ATOMIC);
- if (!new_dma_malloc_pages) {
- size = (new_dma_sectors / SECTORS_PER_PAGE) *
- sizeof(FreeSectorBitmap);
- scsi_init_free((char *) new_dma_malloc_freelist, size);
- out_of_space = 1;
- }
- } else
- out_of_space = 1;
-
- if ((!out_of_space) && (new_dma_sectors > dma_sectors)) {
- for (i = dma_sectors / SECTORS_PER_PAGE;
- i < new_dma_sectors / SECTORS_PER_PAGE; i++) {
- new_dma_malloc_pages[i] = (unsigned char *)
- scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
- if (!new_dma_malloc_pages[i])
- break;
- }
- if (i != new_dma_sectors / SECTORS_PER_PAGE) { /* clean up */
- int k = i;
-
- out_of_space = 1;
- for (i = 0; i < k; ++i)
- scsi_init_free(new_dma_malloc_pages[i], PAGE_SIZE);
- }
- }
- if (out_of_space) { /* try scaling down new_dma_sectors request */
- printk("scsi::resize_dma_pool: WARNING, dma_sectors=%u, "
- "wanted=%u, scaling\n", dma_sectors, new_dma_sectors);
- if (new_dma_sectors < (8 * SECTORS_PER_PAGE))
- break; /* pretty well hopeless ... */
- new_dma_sectors = (new_dma_sectors * 3) / 4;
- new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
- if (new_dma_sectors <= dma_sectors)
- break; /* stick with what we have got */
- } else
- break; /* found space ... */
- } /* end of for loop */
- if (out_of_space) {
- spin_unlock_irqrestore(&allocator_request_lock, flags);
- scsi_need_isa_buffer = new_need_isa_buffer; /* some useful info */
- printk(" WARNING, not enough memory, pool not expanded\n");
- return;
- }
- /* When we dick with the actual DMA list, we need to
- * protect things
- */
- if (dma_malloc_freelist) {
- size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(FreeSectorBitmap);
- memcpy(new_dma_malloc_freelist, dma_malloc_freelist, size);
- scsi_init_free((char *) dma_malloc_freelist, size);
- }
- dma_malloc_freelist = new_dma_malloc_freelist;
-
- if (dma_malloc_pages) {
- size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages);
- memcpy(new_dma_malloc_pages, dma_malloc_pages, size);
- scsi_init_free((char *) dma_malloc_pages, size);
- }
- scsi_dma_free_sectors += new_dma_sectors - dma_sectors;
- dma_malloc_pages = new_dma_malloc_pages;
- dma_sectors = new_dma_sectors;
- scsi_need_isa_buffer = new_need_isa_buffer;
-
- spin_unlock_irqrestore(&allocator_request_lock, flags);
-
-#ifdef DEBUG_INIT
- printk("resize_dma_pool: dma free sectors = %d\n", scsi_dma_free_sectors);
- printk("resize_dma_pool: dma sectors = %d\n", dma_sectors);
- printk("resize_dma_pool: need isa buffers = %d\n", scsi_need_isa_buffer);
-#endif
-}
-
#ifdef CONFIG_MODULES /* a big #ifdef block... */
/*
* Now that we have all of the devices, resize the DMA pool,
* as required. */
if (!out_of_space)
- resize_dma_pool();
+ scsi_resize_dma_pool();
/* This does any final handling that is required. */
* do the right thing and free everything.
*/
if (!scsi_hosts)
- resize_dma_pool();
+ scsi_resize_dma_pool();
printk("scsi : %d host%s.\n", next_scsi_host,
(next_scsi_host == 1) ? "" : "s");
(scsi_memory_upper_value - scsi_init_memory_start) / 1024);
#endif
-
/* There were some hosts that were loaded at boot time, so we cannot
do any more than this */
if (tpnt->present)
if (tpnt->finish && tpnt->nr_dev)
(*tpnt->finish) ();
if (!out_of_space)
- resize_dma_pool();
+ scsi_resize_dma_pool();
MOD_INC_USE_COUNT;
if (out_of_space) {
scsi_loadable_module_flag = 1;
- dma_sectors = PAGE_SIZE / SECTOR_SIZE;
- scsi_dma_free_sectors = dma_sectors;
- /*
- * Set up a minimal DMA buffer list - this will be used during scan_scsis
- * in some cases.
- */
+ if( scsi_init_minimal_dma_pool() == 0 )
+ {
+ return 1;
+ }
- /* One bit per sector to indicate free/busy */
- size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(FreeSectorBitmap);
- dma_malloc_freelist = (FreeSectorBitmap *)
- scsi_init_malloc(size, GFP_ATOMIC);
- if (dma_malloc_freelist) {
- /* One pointer per page for the page list */
- dma_malloc_pages = (unsigned char **) scsi_init_malloc(
- (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages),
- GFP_ATOMIC);
- if (dma_malloc_pages) {
- dma_malloc_pages[0] = (unsigned char *)
- scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
- if (dma_malloc_pages[0])
- has_space = 1;
- }
- }
- if (!has_space) {
- if (dma_malloc_freelist) {
- scsi_init_free((char *) dma_malloc_freelist, size);
- if (dma_malloc_pages)
- scsi_init_free((char *) dma_malloc_pages,
- (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages));
- }
- printk("scsi::init_module: failed, out of memory\n");
- return 1;
- }
/*
* This is where the processing takes place for most everything
* when commands are completed.
/*
* Free up the DMA pool.
*/
- resize_dma_pool();
+ scsi_resize_dma_pool();
}
SDpnt->device_queue = SCpnt;
- blk_init_queue(&SDpnt->request_queue, scsi_request_fn);
+ blk_init_queue(&SDpnt->request_queue, scsi_get_request_handler(SDpnt, SDpnt->host));
blk_queue_headactive(&SDpnt->request_queue, 0);
SDpnt->request_queue.queuedata = (void *) SDpnt;
*/
void scsi_free_host_dev(Scsi_Device * SDpnt)
{
- if( SDpnt->id != SDpnt->host->this_id )
+ if( (unsigned char) SDpnt->id != (unsigned char) SDpnt->host->this_id )
{
panic("Attempt to delete wrong device\n");
}
* Initializes all SCSI devices. This scans all scsi busses.
*/
-extern int scsi_dev_init(void);
-
-
-
-void *scsi_malloc(unsigned int);
-int scsi_free(void *, unsigned int);
extern unsigned int scsi_logging_level; /* What do we log? */
extern unsigned int scsi_dma_free_sectors; /* How much room do we have left */
extern unsigned int scsi_need_isa_buffer; /* True if some devices need indirection
* buffers */
-extern void scsi_make_blocked_list(void);
extern volatile int in_scan_scsis;
extern const unsigned char scsi_command_size[8];
+
/*
* These are the error handling functions defined in scsi_error.c
*/
+extern void scsi_times_out(Scsi_Cmnd * SCpnt);
extern void scsi_add_timer(Scsi_Cmnd * SCset, int timeout,
void (*complete) (Scsi_Cmnd *));
-extern void scsi_done(Scsi_Cmnd * SCpnt);
extern int scsi_delete_timer(Scsi_Cmnd * SCset);
extern void scsi_error_handler(void *host);
-extern int scsi_retry_command(Scsi_Cmnd *);
-extern void scsi_finish_command(Scsi_Cmnd *);
extern int scsi_sense_valid(Scsi_Cmnd *);
extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt);
extern int scsi_block_when_processing_errors(Scsi_Device *);
extern void scsi_sleep(int);
+
+/*
+ * Prototypes for functions in scsicam.c
+ */
extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
unsigned int *cyls, unsigned int *hds,
unsigned int *secs);
+/*
+ * Prototypes for functions in scsi_dma.c
+ */
+void scsi_resize_dma_pool(void);
+int scsi_init_minimal_dma_pool(void);
+void *scsi_malloc(unsigned int);
+int scsi_free(void *, unsigned int);
+
/*
* Prototypes for functions in scsi_merge.c
*/
extern void recount_segments(Scsi_Cmnd * SCpnt);
+extern void initialize_merge_fn(Scsi_Device * SDpnt);
+
+/*
+ * Prototypes for functions in scsi_queue.c
+ */
+extern int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason);
/*
* Prototypes for functions in scsi_lib.c
*/
-extern void initialize_merge_fn(Scsi_Device * SDpnt);
-extern void scsi_request_fn(request_queue_t * q);
+extern void scsi_maybe_unblock_host(Scsi_Device * SDpnt);
+extern void scsi_blocked_request_fn(request_queue_t * q);
+extern Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate,
+ int sectors);
+extern struct Scsi_Device_Template *scsi_get_request_dev(struct request *);
+extern int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt);
+extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int);
+extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
+ int block_sectors);
extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt);
+extern void scsi_request_fn(request_queue_t * q);
-extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int);
-extern int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt);
/*
* Prototypes for functions in scsi.c
*/
-
-/*
- * scsi_abort aborts the current command that is executing on host host.
- * The error code, if non zero is returned in the host byte, otherwise
- * DID_ABORT is returned in the hostbyte.
- */
-
+extern int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt);
+extern void scsi_bottom_half_handler(void);
+extern void scsi_build_commandblocks(Scsi_Device * SDpnt);
+extern void scsi_done(Scsi_Cmnd * SCpnt);
+extern void scsi_finish_command(Scsi_Cmnd *);
+extern int scsi_retry_command(Scsi_Cmnd *);
+extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int);
+extern void scsi_release_command(Scsi_Cmnd *);
extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
void *buffer, unsigned bufflen,
void (*done) (struct scsi_cmnd *),
int timeout, int retries);
-
extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
void *buffer, unsigned bufflen,
- void (*done) (struct scsi_cmnd *),
int timeout, int retries);
+extern int scsi_dev_init(void);
-extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int);
-
-extern void scsi_release_command(Scsi_Cmnd *);
+/*
+ * Prototypes for functions/data in hosts.c
+ */
extern int max_scsi_hosts;
+/*
+ * Prototypes for functions in scsi_proc.c
+ */
extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
extern struct proc_dir_entry *proc_scsi;
+/*
+ * Prototypes for functions in constants.c
+ */
extern void print_command(unsigned char *);
extern void print_sense(const char *, Scsi_Cmnd *);
extern void print_driverbyte(int scsiresult);
extern void print_hostbyte(int scsiresult);
+extern void print_status (int status);
/*
* The scsi_device struct contains what we know about each given scsi
* device.
+ *
+ * FIXME(eric) - one of the great regrets that I have is that I failed to define
+ * these structure elements as something like sdev_foo instead of foo. This would
+ * make it so much easier to grep through sources and so forth. I propose that
+ * all new elements that get added to these structures follow this convention.
+ * As time goes on and as people have the stomach for it, it should be possible to
+ * go back and retrofit at least some of the elements here with with the prefix.
*/
struct scsi_device {
} Scsi_Pointer;
+/*
+ * FIXME(eric) - one of the great regrets that I have is that I failed to define
+ * these structure elements as something like sc_foo instead of foo. This would
+ * make it so much easier to grep through sources and so forth. I propose that
+ * all new elements that get added to these structures follow this convention.
+ * As time goes on and as people have the stomach for it, it should be possible to
+ * go back and retrofit at least some of the elements here with with the prefix.
+ */
struct scsi_cmnd {
/* private: */
/*
unsigned char old_cmd_len;
/* These elements define the operation we are about to perform */
- unsigned char cmnd[12];
+ unsigned char cmnd[MAX_COMMAND_SIZE];
unsigned request_bufflen; /* Actual request size */
struct timer_list eh_timeout; /* Used to time out the command. */
void *request_buffer; /* Actual requested buffer */
/* These elements define the operation we ultimately want to perform */
- unsigned char data_cmnd[12];
+ unsigned char data_cmnd[MAX_COMMAND_SIZE];
unsigned short old_use_sg; /* We save use_sg here when requesting
* sense info */
unsigned short use_sg; /* Number of pieces of scatter-gather */
};
/*
- * Flag bits for the internal_timeout array
+ * Flag bit for the internal_timeout array
*/
#define NORMAL_TIMEOUT 0
-#define IN_ABORT 1
-#define IN_RESET 2
-#define IN_RESET2 4
-#define IN_RESET3 8
/*
* Definitions and prototypes used for scsi mid-level queue.
#define SCSI_MLQUEUE_HOST_BUSY 0x1055
#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
-extern int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason);
-
-extern Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate,
- int sectors);
-
-extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
- int block_sectors);
-
-extern struct Scsi_Device_Template *scsi_get_request_dev(struct request *);
-
#define SCSI_SLEEP(QUEUE, CONDITION) { \
if (CONDITION) { \
DECLARE_WAITQUEUE(wait, current); \
return 0;
}
-static void sd_test_done(Scsi_Cmnd * SCpnt)
-{
- struct request *req;
-
- req = &SCpnt->request;
- req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
-
- if (req->sem != NULL) {
- up(req->sem);
- }
-}
-
static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
{
static unsigned char cmd[6] =
printk("Sending command\n");
scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
- 0, sd_test_done, 100, 3);
+ 0, 100, 3);
printk("Releasing command\n");
scsi_release_command(scp);
--- /dev/null
+/*
+ * scsi_dma.c Copyright (C) 2000 Eric Youngdale
+ *
+ * mid-level SCSI DMA bounce buffer allocator
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/blk.h>
+
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+/*
+ * PAGE_SIZE must be a multiple of the sector size (512). True
+ * for all reasonably recent architectures (even the VAX...).
+ */
+#define SECTOR_SIZE 512
+#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)
+
+#if SECTORS_PER_PAGE <= 8
+typedef unsigned char FreeSectorBitmap;
+#elif SECTORS_PER_PAGE <= 32
+typedef unsigned int FreeSectorBitmap;
+#else
+#error You lose.
+#endif
+
+/*
+ * Used for access to internal allocator used for DMA safe buffers.
+ */
+static spinlock_t allocator_request_lock = SPIN_LOCK_UNLOCKED;
+
+static FreeSectorBitmap *dma_malloc_freelist = NULL;
+static int need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int scsi_dma_free_sectors = 0;
+unsigned int scsi_need_isa_buffer = 0;
+static unsigned char **dma_malloc_pages = NULL;
+
+/*
+ * Function: scsi_malloc
+ *
+ * Purpose: Allocate memory from the DMA-safe pool.
+ *
+ * Arguments: len - amount of memory we need.
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ *
+ * Returns: Pointer to memory block.
+ *
+ * Notes: Prior to the new queue code, this function was not SMP-safe.
+ * This function can only allocate in units of sectors
+ * (i.e. 512 bytes).
+ *
+ * We cannot use the normal system allocator becuase we need
+ * to be able to guarantee that we can process a complete disk
+ * I/O request without touching the system allocator. Think
+ * about it - if the system were heavily swapping, and tried to
+ * write out a block of memory to disk, and the SCSI code needed
+ * to allocate more memory in order to be able to write the
+ * data to disk, you would wedge the system.
+ */
+void *scsi_malloc(unsigned int len)
+{
+ unsigned int nbits, mask;
+ unsigned long flags;
+
+ int i, j;
+ if (len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
+ return NULL;
+
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ spin_lock_irqsave(&allocator_request_lock, flags);
+
+ for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++)
+ for (j = 0; j <= SECTORS_PER_PAGE - nbits; j++) {
+ if ((dma_malloc_freelist[i] & (mask << j)) == 0) {
+ dma_malloc_freelist[i] |= (mask << j);
+ scsi_dma_free_sectors -= nbits;
+#ifdef DEBUG
+ SCSI_LOG_MLQUEUE(3, printk("SMalloc: %d %p [From:%p]\n", len, dma_malloc_pages[i] + (j << 9)));
+ printk("SMalloc: %d %p [From:%p]\n", len, dma_malloc_pages[i] + (j << 9));
+#endif
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+ }
+ }
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return NULL; /* Nope. No more */
+}
+
+/*
+ * Function: scsi_free
+ *
+ * Purpose: Free memory into the DMA-safe pool.
+ *
+ * Arguments: ptr - data block we are freeing.
+ * len - size of block we are freeing.
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ *
+ * Returns: Nothing
+ *
+ * Notes: This function *must* only be used to free memory
+ * allocated from scsi_malloc().
+ *
+ * Prior to the new queue code, this function was not SMP-safe.
+ * This function can only allocate in units of sectors
+ * (i.e. 512 bytes).
+ */
+int scsi_free(void *obj, unsigned int len)
+{
+ unsigned int page, sector, nbits, mask;
+ unsigned long flags;
+
+#ifdef DEBUG
+ unsigned long ret = 0;
+
+#ifdef __mips__
+ __asm__ __volatile__("move\t%0,$31":"=r"(ret));
+#else
+ ret = __builtin_return_address(0);
+#endif
+ printk("scsi_free %p %d\n", obj, len);
+ SCSI_LOG_MLQUEUE(3, printk("SFree: %p %d\n", obj, len));
+#endif
+
+ spin_lock_irqsave(&allocator_request_lock, flags);
+
+ for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
+ unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
+ if ((unsigned long) obj >= page_addr &&
+ (unsigned long) obj < page_addr + PAGE_SIZE) {
+ sector = (((unsigned long) obj) - page_addr) >> 9;
+
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
+ panic("scsi_free:Bad memory alignment");
+
+ if ((dma_malloc_freelist[page] &
+ (mask << sector)) != (mask << sector)) {
+#ifdef DEBUG
+ printk("scsi_free(obj=%p, len=%d) called from %08lx\n",
+ obj, len, ret);
+#endif
+ panic("scsi_free:Trying to free unused memory");
+ }
+ scsi_dma_free_sectors += nbits;
+ dma_malloc_freelist[page] &= ~(mask << sector);
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return 0;
+ }
+ }
+ panic("scsi_free:Bad offset");
+}
+
+
+/*
+ * Function: scsi_resize_dma_pool
+ *
+ * Purpose: Ensure that the DMA pool is sufficiently large to be
+ * able to guarantee that we can always process I/O requests
+ * without calling the system allocator.
+ *
+ * Arguments: None.
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ *
+ * Returns: Nothing
+ *
+ * Notes: Prior to the new queue code, this function was not SMP-safe.
+ * Go through the device list and recompute the most appropriate
+ * size for the dma pool. Then grab more memory (as required).
+ */
+void scsi_resize_dma_pool(void)
+{
+ int i, k;
+ unsigned long size;
+ unsigned long flags;
+ struct Scsi_Host *shpnt;
+ struct Scsi_Host *host = NULL;
+ Scsi_Device *SDpnt;
+ FreeSectorBitmap *new_dma_malloc_freelist = NULL;
+ unsigned int new_dma_sectors = 0;
+ unsigned int new_need_isa_buffer = 0;
+ unsigned char **new_dma_malloc_pages = NULL;
+ int out_of_space = 0;
+
+ spin_lock_irqsave(&allocator_request_lock, flags);
+
+ if (!scsi_hostlist) {
+ /*
+ * Free up the DMA pool.
+ */
+ if (scsi_dma_free_sectors != dma_sectors)
+ panic("SCSI DMA pool memory leak %d %d\n", scsi_dma_free_sectors, dma_sectors);
+
+ for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++)
+ free_pages((unsigned long) dma_malloc_pages[i], 0);
+ if (dma_malloc_pages)
+ kfree((char *) dma_malloc_pages);
+ dma_malloc_pages = NULL;
+ if (dma_malloc_freelist)
+ kfree((char *) dma_malloc_freelist);
+ dma_malloc_freelist = NULL;
+ dma_sectors = 0;
+ scsi_dma_free_sectors = 0;
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return;
+ }
+ /* Next, check to see if we need to extend the DMA buffer pool */
+
+ new_dma_sectors = 2 * SECTORS_PER_PAGE; /* Base value we use */
+
+ if (__pa(high_memory) - 1 > ISA_DMA_THRESHOLD)
+ need_isa_bounce_buffers = 1;
+ else
+ need_isa_bounce_buffers = 0;
+
+ if (scsi_devicelist)
+ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
+
+ for (host = scsi_hostlist; host; host = host->next) {
+ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+ /*
+ * sd and sr drivers allocate scatterlists.
+ * sr drivers may allocate for each command 1x2048 or 2x1024 extra
+ * buffers for 2k sector size and 1k fs.
+ * sg driver allocates buffers < 4k.
+ * st driver does not need buffers from the dma pool.
+ * estimate 4k buffer/command for devices of unknown type (should panic).
+ */
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
+ SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ SDpnt->queue_depth;
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
+ new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
+ } else if (SDpnt->type == TYPE_SCANNER ||
+ SDpnt->type == TYPE_PROCESSOR ||
+ SDpnt->type == TYPE_MEDIUM_CHANGER ||
+ SDpnt->type == TYPE_ENCLOSURE) {
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ } else {
+ if (SDpnt->type != TYPE_TAPE) {
+ printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ }
+
+ if (host->unchecked_isa_dma &&
+ need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ SDpnt->queue_depth;
+ new_need_isa_buffer++;
+ }
+ }
+ }
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: needed dma sectors = %d\n", new_dma_sectors);
+#endif
+
+ /* limit DMA memory to 32MB: */
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+
+ /*
+ * We never shrink the buffers - this leads to
+ * race conditions that I would rather not even think
+ * about right now.
+ */
+#if 0 /* Why do this? No gain and risks out_of_space */
+ if (new_dma_sectors < dma_sectors)
+ new_dma_sectors = dma_sectors;
+#endif
+ if (new_dma_sectors <= dma_sectors) {
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return; /* best to quit while we are in front */
+ }
+
+ for (k = 0; k < 20; ++k) { /* just in case */
+ out_of_space = 0;
+ size = (new_dma_sectors / SECTORS_PER_PAGE) *
+ sizeof(FreeSectorBitmap);
+ new_dma_malloc_freelist = (FreeSectorBitmap *)
+ kmalloc(size, GFP_ATOMIC);
+ if (new_dma_malloc_freelist) {
+ memset(new_dma_malloc_freelist, 0, size);
+ size = (new_dma_sectors / SECTORS_PER_PAGE) *
+ sizeof(*new_dma_malloc_pages);
+ new_dma_malloc_pages = (unsigned char **)
+ kmalloc(size, GFP_ATOMIC);
+ if (!new_dma_malloc_pages) {
+ size = (new_dma_sectors / SECTORS_PER_PAGE) *
+ sizeof(FreeSectorBitmap);
+ kfree((char *) new_dma_malloc_freelist);
+ out_of_space = 1;
+ } else {
+ memset(new_dma_malloc_pages, 0, size);
+ }
+ } else
+ out_of_space = 1;
+
+ if ((!out_of_space) && (new_dma_sectors > dma_sectors)) {
+ for (i = dma_sectors / SECTORS_PER_PAGE;
+ i < new_dma_sectors / SECTORS_PER_PAGE; i++) {
+ new_dma_malloc_pages[i] = (unsigned char *)
+ __get_free_pages(GFP_ATOMIC | GFP_DMA, 0);
+ if (!new_dma_malloc_pages[i])
+ break;
+ }
+ if (i != new_dma_sectors / SECTORS_PER_PAGE) { /* clean up */
+ int k = i;
+
+ out_of_space = 1;
+ for (i = 0; i < k; ++i)
+ free_pages((unsigned long) new_dma_malloc_pages[i], 0);
+ }
+ }
+ if (out_of_space) { /* try scaling down new_dma_sectors request */
+ printk("scsi::resize_dma_pool: WARNING, dma_sectors=%u, "
+ "wanted=%u, scaling\n", dma_sectors, new_dma_sectors);
+ if (new_dma_sectors < (8 * SECTORS_PER_PAGE))
+ break; /* pretty well hopeless ... */
+ new_dma_sectors = (new_dma_sectors * 3) / 4;
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+ if (new_dma_sectors <= dma_sectors)
+ break; /* stick with what we have got */
+ } else
+ break; /* found space ... */
+ } /* end of for loop */
+ if (out_of_space) {
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ scsi_need_isa_buffer = new_need_isa_buffer; /* some useful info */
+ printk(" WARNING, not enough memory, pool not expanded\n");
+ return;
+ }
+ /* When we dick with the actual DMA list, we need to
+ * protect things
+ */
+ if (dma_malloc_freelist) {
+ size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(FreeSectorBitmap);
+ memcpy(new_dma_malloc_freelist, dma_malloc_freelist, size);
+ kfree((char *) dma_malloc_freelist);
+ }
+ dma_malloc_freelist = new_dma_malloc_freelist;
+
+ if (dma_malloc_pages) {
+ size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages);
+ memcpy(new_dma_malloc_pages, dma_malloc_pages, size);
+ kfree((char *) dma_malloc_pages);
+ }
+ scsi_dma_free_sectors += new_dma_sectors - dma_sectors;
+ dma_malloc_pages = new_dma_malloc_pages;
+ dma_sectors = new_dma_sectors;
+ scsi_need_isa_buffer = new_need_isa_buffer;
+
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: dma free sectors = %d\n", scsi_dma_free_sectors);
+ printk("resize_dma_pool: dma sectors = %d\n", dma_sectors);
+ printk("resize_dma_pool: need isa buffers = %d\n", scsi_need_isa_buffer);
+#endif
+}
+
+/*
+ * Function: scsi_init_minimal_dma_pool
+ *
+ * Purpose: Allocate a minimal (1-page) DMA pool.
+ *
+ * Arguments: None.
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ *
+ * Returns: Nothing
+ *
+ * Notes:
+ */
+int scsi_init_minimal_dma_pool(void)
+{
+ unsigned long size;
+ unsigned long flags;
+ int has_space = 0;
+
+ spin_lock_irqsave(&allocator_request_lock, flags);
+
+ dma_sectors = PAGE_SIZE / SECTOR_SIZE;
+ scsi_dma_free_sectors = dma_sectors;
+ /*
+ * Set up a minimal DMA buffer list - this will be used during scan_scsis
+ * in some cases.
+ */
+
+ /* One bit per sector to indicate free/busy */
+ size = (dma_sectors / SECTORS_PER_PAGE) * sizeof(FreeSectorBitmap);
+ dma_malloc_freelist = (FreeSectorBitmap *)
+ kmalloc(size, GFP_ATOMIC);
+ if (dma_malloc_freelist) {
+ memset(dma_malloc_freelist, 0, size);
+ /* One pointer per page for the page list */
+ dma_malloc_pages = (unsigned char **) kmalloc(
+ (dma_sectors / SECTORS_PER_PAGE) * sizeof(*dma_malloc_pages),
+ GFP_ATOMIC);
+ if (dma_malloc_pages) {
+ memset(dma_malloc_pages, 0, size);
+ dma_malloc_pages[0] = (unsigned char *)
+ __get_free_pages(GFP_ATOMIC | GFP_DMA, 0);
+ if (dma_malloc_pages[0])
+ has_space = 1;
+ }
+ }
+ if (!has_space) {
+ if (dma_malloc_freelist) {
+ kfree((char *) dma_malloc_freelist);
+ if (dma_malloc_pages)
+ kfree((char *) dma_malloc_pages);
+ }
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ printk("scsi::init_module: failed, out of memory\n");
+ return 1;
+ }
+
+ spin_unlock_irqrestore(&allocator_request_lock, flags);
+ return 0;
+}
* The output area is then filled in starting from the command byte.
*/
-static void scsi_ioctl_done(Scsi_Cmnd * SCpnt)
-{
- struct request *req;
-
- req = &SCpnt->request;
- req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
-
- if (req->sem != NULL) {
- up(req->sem);
- }
-}
-
static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
int timeout, int retries)
{
return -EINTR;
}
- scsi_wait_cmd(SCpnt, cmd, NULL, 0, scsi_ioctl_done, timeout, retries);
+ scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result));
int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
{
char *buf;
- unsigned char cmd[12];
+ unsigned char cmd[MAX_COMMAND_SIZE];
char *cmd_in;
Scsi_Cmnd *SCpnt;
Scsi_Device *SDpnt;
return -EINTR;
}
- scsi_wait_cmd(SCpnt, cmd, buf, needed, scsi_ioctl_done,
- timeout, retries);
+ scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
/*
* If there was an error condition, pass the info back to the user.
int scsi_ioctl(Scsi_Device * dev, int cmd, void *arg)
{
int result;
- char scsi_cmd[12];
+ char scsi_cmd[MAX_COMMAND_SIZE];
/* No idea how this happens.... */
if (!dev)
* This entire source file deals with the new queueing code.
*/
+/*
+ * For hosts that request single-file access to the ISA bus, this is a pointer to
+ * the currently active host.
+ */
+volatile struct Scsi_Host *host_active = NULL;
+
+
/*
* Function: scsi_insert_special_cmd()
*
return 1;
}
+
/*
* Function: scsi_queue_next_request()
*
* If SCpnt is NULL, it means that the previous command
* was completely finished, and we should simply start
* a new command, if possible.
+ *
+ * This is where a lot of special case code has begun to
+ * accumulate. It doesn't really affect readability or
+ * anything, but it might be considered architecturally
+ * inelegant. If more of these special cases start to
+ * accumulate, I am thinking along the lines of implementing
+ * an atexit() like technology that gets run when commands
+ * complete. I am not convinced that it is worth the
+ * added overhead, however. Right now as things stand,
+ * there are simple conditional checks, and most hosts
+ * would skip past.
+ *
+ * Another possible solution would be to tailor different
+ * handler functions, sort of like what we did in scsi_merge.c.
+ * This is probably a better solution, but the number of different
+ * permutations grows as 2**N, and if too many more special cases
+ * get added, we start to get screwed.
*/
void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt)
{
/*
* Flag bits for the internal_timeout array
*/
-#define NORMAL_TIMEOUT 0
#define IN_ABORT 1
#define IN_RESET 2
#define IN_RESET2 4
--- /dev/null
+/*
+ * scsi_scan.c Copyright (C) 2000 Eric Youngdale
+ *
+ * Bus scan logic.
+ *
+ * This used to live in scsi.c, but that file was just a laundry basket
+ * full of misc stuff. This got separated out in order to make things
+ * clearer.
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/blk.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another. Some will respond to all luns, others will
+ * lock up.
+ */
+
+#define BLIST_NOLUN 0x001
+#define BLIST_FORCELUN 0x002
+#define BLIST_BORKEN 0x004
+#define BLIST_KEY 0x008
+#define BLIST_SINGLELUN 0x010
+#define BLIST_NOTQ 0x020
+#define BLIST_SPARSELUN 0x040
+#define BLIST_MAX5LUN 0x080
+#define BLIST_ISDISK 0x100
+#define BLIST_ISROM 0x200
+#define BLIST_GHOST 0x400
+
+static void print_inquiry(unsigned char *data);
+static int scan_scsis_single(int channel, int dev, int lun, int *max_scsi_dev,
+ int *sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char *scsi_result);
+
+struct dev_info {
+ const char *vendor;
+ const char *model;
+ const char *revision; /* Latest revision known to be bad. Not used yet */
+ unsigned flags;
+};
+
+/*
+ * This is what was previously known as the blacklist. The concept
+ * has been expanded so that we can specify other types of things we
+ * need to be aware of.
+ */
+static struct dev_info device_list[] =
+{
+ {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
+ {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
+ {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */
+ {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+ {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+ {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+ {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
+ {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
+ {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* guess what? */
+ {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /*Responds to all lun */
+ {"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */
+ {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+ {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* Responds to all lun */
+ {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+ {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+ {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* Responds to all lun */
+ {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* Responds to all lun */
+ {"SONY", "CD-ROM CDU-541", "4.3d", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-55S", "1.0i", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-561", "1.7x", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-8012", "*", BLIST_NOLUN},
+ {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+ {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* Responds to all lun */
+ {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+ {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+ {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+ {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* Locks up when polled for lun != 0 */
+ {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
+ {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
+ {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
+ {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
+ {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0
+ * extra reset */
+ {"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */
+ {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all LUN */
+
+/*
+ * Other types of devices that have special flags.
+ */
+ {"SONY", "CD-ROM CDU-8001", "*", BLIST_BORKEN},
+ {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN},
+ {"IOMEGA", "Io20S *F", "*", BLIST_KEY},
+ {"INSITE", "Floptical F*8I", "*", BLIST_KEY},
+ {"INSITE", "I325VM", "*", BLIST_KEY},
+ {"NRC", "MBR-7", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NRC", "MBR-7.4", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"REGAL", "CDC-4X", "*", BLIST_MAX5LUN | BLIST_SINGLELUN},
+ {"NAKAMICH", "MJ-4.8S", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NAKAMICH", "MJ-5.16S", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-600", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-602X", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-604X", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN},
+ {"CANON", "IPUBJD", "*", BLIST_SPARSELUN},
+ {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN},
+ {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
+ {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST},
+ {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST},
+ {"HITACHI", "GF-1050","*", BLIST_GHOST}, /* Hitachi SCSI DVD-RAM */
+ {"TOSHIBA","CDROM","*", BLIST_ISROM},
+ {"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST},
+ {"TOSHIBA","DVD-RAM SD-W1111","*", BLIST_GHOST},
+
+ /*
+ * Must be at end of list...
+ */
+ {NULL, NULL, NULL}
+};
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
+#endif
+
+#ifdef MODULE
+
+MODULE_PARM(max_scsi_luns, "i");
+MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 8)");
+
+#else
+
+static int __init scsi_luns_setup(char *str)
+{
+ int tmp;
+
+ if (get_option(&str, &tmp) == 1) {
+ max_scsi_luns = tmp;
+ return 1;
+ } else {
+ printk("scsi_luns_setup : usage max_scsi_luns=n "
+ "(n should be between 1 and 8)\n");
+ return 0;
+ }
+}
+
+__setup("max_scsi_luns=", scsi_luns_setup);
+
+#endif
+
+static void print_inquiry(unsigned char *data)
+{
+ int i;
+
+ printk(" Vendor: ");
+ for (i = 8; i < 16; i++) {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Model: ");
+ for (i = 16; i < 32; i++) {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Rev: ");
+ for (i = 32; i < 36; i++) {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk("\n");
+
+ i = data[0] & 0x1f;
+
+ printk(" Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown ");
+ printk(" ANSI SCSI revision: %02x", data[2] & 0x07);
+ if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+ printk(" CCS\n");
+ else
+ printk("\n");
+}
+
+static int get_device_flags(unsigned char *response_data)
+{
+ int i = 0;
+ unsigned char *pnt;
+ for (i = 0; 1; i++) {
+ if (device_list[i].vendor == NULL)
+ return 0;
+ pnt = &response_data[8];
+ while (*pnt && *pnt == ' ')
+ pnt++;
+ if (memcmp(device_list[i].vendor, pnt,
+ strlen(device_list[i].vendor)))
+ continue;
+ pnt = &response_data[16];
+ while (*pnt && *pnt == ' ')
+ pnt++;
+ if (memcmp(device_list[i].model, pnt,
+ strlen(device_list[i].model)))
+ continue;
+ return device_list[i].flags;
+ }
+ return 0;
+}
+
+/*
+ * Detecting SCSI devices :
+ * We scan all present host adapter's busses, from ID 0 to ID (max_id).
+ * We use the INQUIRY command, determine device type, and pass the ID /
+ * lun address of all sequential devices to the tape driver, all random
+ * devices to the disk driver.
+ */
+void scan_scsis(struct Scsi_Host *shpnt,
+ unchar hardcoded,
+ unchar hchannel,
+ unchar hid,
+ unchar hlun)
+{
+ int channel;
+ int dev;
+ int lun;
+ int max_dev_lun;
+ Scsi_Cmnd *SCpnt;
+ unsigned char *scsi_result;
+ unsigned char scsi_result0[256];
+ Scsi_Device *SDpnt;
+ Scsi_Device *SDtail;
+ int sparse_lun;
+
+ scsi_result = NULL;
+ SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd),
+ GFP_ATOMIC | GFP_DMA);
+ if (SCpnt) {
+ memset(SCpnt, 0, sizeof(Scsi_Cmnd));
+ SDpnt = (Scsi_Device *) kmalloc(sizeof(Scsi_Device),
+ GFP_ATOMIC);
+ if (SDpnt) {
+ memset(SDpnt, 0, sizeof(Scsi_Device));
+ /*
+ * Register the queue for the device. All I/O requests will come
+ * in through here. We also need to register a pointer to
+ * ourselves, since the queue handler won't know what device
+ * the queue actually represents. We could look it up, but it
+ * is pointless work.
+ */
+ blk_init_queue(&SDpnt->request_queue, scsi_get_request_handler(SDpnt, shpnt));
+ blk_queue_headactive(&SDpnt->request_queue, 0);
+ SDpnt->request_queue.queuedata = (void *) SDpnt;
+ /* Make sure we have something that is valid for DMA purposes */
+ scsi_result = ((!shpnt->unchecked_isa_dma)
+ ? &scsi_result0[0] : kmalloc(512, GFP_DMA));
+ }
+ }
+ if (scsi_result == NULL) {
+ printk("Unable to obtain scsi_result buffer\n");
+ goto leave;
+ }
+ /*
+ * We must chain ourself in the host_queue, so commands can time out
+ */
+ SCpnt->next = NULL;
+ SDpnt->device_queue = SCpnt;
+ SDpnt->host = shpnt;
+ SDpnt->online = TRUE;
+
+ initialize_merge_fn(SDpnt);
+
+ /*
+ * Initialize the object that we will use to wait for command blocks.
+ */
+ init_waitqueue_head(&SDpnt->scpnt_wait);
+
+ /*
+ * Next, hook the device to the host in question.
+ */
+ SDpnt->prev = NULL;
+ SDpnt->next = NULL;
+ if (shpnt->host_queue != NULL) {
+ SDtail = shpnt->host_queue;
+ while (SDtail->next != NULL)
+ SDtail = SDtail->next;
+
+ SDtail->next = SDpnt;
+ SDpnt->prev = SDtail;
+ } else {
+ shpnt->host_queue = SDpnt;
+ }
+
+ /*
+ * We need to increment the counter for this one device so we can track when
+ * things are quiet.
+ */
+ atomic_inc(&shpnt->host_active);
+ atomic_inc(&SDpnt->device_active);
+
+ if (hardcoded == 1) {
+ Scsi_Device *oldSDpnt = SDpnt;
+ struct Scsi_Device_Template *sdtpnt;
+ channel = hchannel;
+ if (channel > shpnt->max_channel)
+ goto leave;
+ dev = hid;
+ if (dev >= shpnt->max_id)
+ goto leave;
+ lun = hlun;
+ if (lun >= shpnt->max_lun)
+ goto leave;
+ scan_scsis_single(channel, dev, lun, &max_dev_lun, &sparse_lun,
+ &SDpnt, SCpnt, shpnt, scsi_result);
+ if (SDpnt != oldSDpnt) {
+
+ /* it could happen the blockdevice hasn't yet been inited */
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->init && sdtpnt->dev_noticed)
+ (*sdtpnt->init) ();
+
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if (sdtpnt->attach) {
+ (*sdtpnt->attach) (oldSDpnt);
+ if (oldSDpnt->attached) {
+ scsi_build_commandblocks(oldSDpnt);
+ if (0 == oldSDpnt->has_cmdblocks) {
+ printk("scan_scsis: DANGER, no command blocks\n");
+ /* What to do now ?? */
+ }
+ }
+ }
+ }
+ scsi_resize_dma_pool();
+
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if (sdtpnt->finish && sdtpnt->nr_dev) {
+ (*sdtpnt->finish) ();
+ }
+ }
+ }
+ } else {
+ /* Actual LUN. PC ordering is 0->n IBM/spec ordering is n->0 */
+ int order_dev;
+
+ for (channel = 0; channel <= shpnt->max_channel; channel++) {
+ for (dev = 0; dev < shpnt->max_id; ++dev) {
+ if (shpnt->reverse_ordering)
+ /* Shift to scanning 15,14,13... or 7,6,5,4, */
+ order_dev = shpnt->max_id - dev - 1;
+ else
+ order_dev = dev;
+
+ if (shpnt->this_id != order_dev) {
+
+ /*
+ * We need the for so our continue, etc. work fine. We put this in
+ * a variable so that we can override it during the scan if we
+ * detect a device *KNOWN* to have multiple logical units.
+ */
+ max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+ max_scsi_luns : shpnt->max_lun);
+ sparse_lun = 0;
+ for (lun = 0; lun < max_dev_lun; ++lun) {
+ if (!scan_scsis_single(channel, order_dev, lun, &max_dev_lun,
+ &sparse_lun, &SDpnt, SCpnt, shpnt,
+ scsi_result)
+ && !sparse_lun)
+ break; /* break means don't probe further for luns!=0 */
+ } /* for lun ends */
+ } /* if this_id != id ends */
+ } /* for dev ends */
+ } /* for channel ends */
+ } /* if/else hardcoded */
+
+ /*
+ * We need to decrement the counter for this one device
+ * so we know when everything is quiet.
+ */
+ atomic_dec(&shpnt->host_active);
+ atomic_dec(&SDpnt->device_active);
+
+ leave:
+
+ { /* Unchain SCpnt from host_queue */
+ Scsi_Device *prev, *next;
+ Scsi_Device *dqptr;
+
+ for (dqptr = shpnt->host_queue; dqptr != SDpnt; dqptr = dqptr->next)
+ continue;
+ if (dqptr) {
+ prev = dqptr->prev;
+ next = dqptr->next;
+ if (prev)
+ prev->next = next;
+ else
+ shpnt->host_queue = next;
+ if (next)
+ next->prev = prev;
+ }
+ }
+
+ /* Last device block does not exist. Free memory. */
+ if (SDpnt != NULL)
+ kfree((char *) SDpnt);
+
+ if (SCpnt != NULL)
+ kfree((char *) SCpnt);
+
+ /* If we allocated a buffer so we could do DMA, free it now */
+ if (scsi_result != &scsi_result0[0] && scsi_result != NULL) {
+ kfree(scsi_result);
+ } {
+ Scsi_Device *sdev;
+ Scsi_Cmnd *scmd;
+
+ SCSI_LOG_SCAN_BUS(4, printk("Host status for host %p:\n", shpnt));
+ for (sdev = shpnt->host_queue; sdev; sdev = sdev->next) {
+ SCSI_LOG_SCAN_BUS(4, printk("Device %d %p: ", sdev->id, sdev));
+ for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
+ SCSI_LOG_SCAN_BUS(4, printk("%p ", scmd));
+ }
+ SCSI_LOG_SCAN_BUS(4, printk("\n"));
+ }
+ }
+}
+
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list)
+ */
+int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
+ int *sparse_lun, Scsi_Device ** SDpnt2, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char *scsi_result)
+{
+ unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+ struct Scsi_Device_Template *sdtpnt;
+ Scsi_Device *SDtail, *SDpnt = *SDpnt2;
+ int bflags, type = -1;
+ static int ghost_channel=-1, ghost_dev=-1;
+ int org_lun = lun;
+
+ SDpnt->host = shpnt;
+ SDpnt->id = dev;
+ SDpnt->lun = lun;
+ SDpnt->channel = channel;
+ SDpnt->online = TRUE;
+
+
+ if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
+ SDpnt->lun = 0;
+ } else {
+ ghost_channel = ghost_dev = -1;
+ }
+
+
+ /* Some low level driver could use device->type (DB) */
+ SDpnt->type = -1;
+
+ /*
+ * Assume that the device will have handshaking problems, and then fix this
+ * field later if it turns out it doesn't
+ */
+ SDpnt->borken = 1;
+ SDpnt->was_reset = 0;
+ SDpnt->expecting_cc_ua = 0;
+ SDpnt->starved = 0;
+
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+
+ scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) NULL,
+ 0, SCSI_TIMEOUT + 4 * HZ, 5);
+
+ SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
+ dev, lun, SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3, printk("\n"));
+
+ if (SCpnt->result) {
+ if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
+ (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
+ ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0))
+ return 1;
+ } else
+ return 0;
+ }
+ SCSI_LOG_SCAN_BUS(3, printk("scsi: performing INQUIRY\n"));
+ /*
+ * Build an INQUIRY command block.
+ */
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 255;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+
+ scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, SCSI_TIMEOUT, 3);
+
+ SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
+ SCpnt->result ? "failed" : "successful", SCpnt->result));
+
+ if (SCpnt->result)
+ return 0; /* assume no peripheral if any sort of error */
+
+ /*
+ * Check the peripheral qualifier field - this tells us whether LUNS
+ * are supported here or not.
+ */
+ if ((scsi_result[0] >> 5) == 3) {
+ return 0; /* assume no peripheral if any sort of error */
+ }
+
+ /*
+ * Get any flags for this device.
+ */
+ bflags = get_device_flags (scsi_result);
+
+
+ /* The Toshiba ROM was "gender-changed" here as an inline hack.
+ This is now much more generic.
+ This is a mess: What we really want is to leave the scsi_result
+ alone, and just change the SDpnt structure. And the SDpnt is what
+ we want print_inquiry to print. -- REW
+ */
+ if (bflags & BLIST_ISDISK) {
+ scsi_result[0] = TYPE_DISK;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+
+ if (bflags & BLIST_ISROM) {
+ scsi_result[0] = TYPE_ROM;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+
+ if (bflags & BLIST_GHOST) {
+ if ((ghost_channel == channel) && (ghost_dev == dev) && (org_lun == 1)) {
+ lun=1;
+ } else {
+ ghost_channel = channel;
+ ghost_dev = dev;
+ scsi_result[0] = TYPE_MOD;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+ }
+
+
+ memcpy(SDpnt->vendor, scsi_result + 8, 8);
+ memcpy(SDpnt->model, scsi_result + 16, 16);
+ memcpy(SDpnt->rev, scsi_result + 32, 4);
+
+ SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+ SDpnt->online = TRUE;
+ SDpnt->lockable = SDpnt->removable;
+ SDpnt->changed = 0;
+ SDpnt->access_count = 0;
+ SDpnt->busy = 0;
+ SDpnt->has_cmdblocks = 0;
+ /*
+ * Currently, all sequential devices are assumed to be tapes, all random
+ * devices disk, with the appropriate read only flags set for ROM / WORM
+ * treated as RO.
+ */
+ switch (type = (scsi_result[0] & 0x1f)) {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ case TYPE_MOD:
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+ case TYPE_ENCLOSURE:
+ SDpnt->writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ SDpnt->writeable = 0;
+ break;
+ default:
+ printk("scsi: unknown type %d\n", type);
+ }
+
+ SDpnt->device_blocked = FALSE;
+ SDpnt->device_busy = 0;
+ SDpnt->single_lun = 0;
+ SDpnt->soft_reset =
+ (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+ SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+ SDpnt->type = (type & 0x1f);
+
+ print_inquiry(scsi_result);
+
+ for (sdtpnt = scsi_devicelist; sdtpnt;
+ sdtpnt = sdtpnt->next)
+ if (sdtpnt->detect)
+ SDpnt->attached +=
+ (*sdtpnt->detect) (SDpnt);
+
+ SDpnt->scsi_level = scsi_result[2] & 0x07;
+ if (SDpnt->scsi_level >= 2 ||
+ (SDpnt->scsi_level == 1 &&
+ (scsi_result[3] & 0x0f) == 1))
+ SDpnt->scsi_level++;
+
+ /*
+ * Accommodate drivers that want to sleep when they should be in a polling
+ * loop.
+ */
+ SDpnt->disconnect = 0;
+
+
+ /*
+ * Set the tagged_queue flag for SCSI-II devices that purport to support
+ * tagged queuing in the INQUIRY data.
+ */
+ SDpnt->tagged_queue = 0;
+ if ((SDpnt->scsi_level >= SCSI_2) &&
+ (scsi_result[7] & 2) &&
+ !(bflags & BLIST_NOTQ)) {
+ SDpnt->tagged_supported = 1;
+ SDpnt->current_tag = 0;
+ }
+ /*
+ * Some revisions of the Texel CD ROM drives have handshaking problems when
+ * used with the Seagate controllers. Before we know what type of device
+ * we're talking to, we assume it's borken and then change it here if it
+ * turns out that it isn't a TEXEL drive.
+ */
+ if ((bflags & BLIST_BORKEN) == 0)
+ SDpnt->borken = 0;
+
+ /*
+ * If we want to only allow I/O to one of the luns attached to this device
+ * at a time, then we set this flag.
+ */
+ if (bflags & BLIST_SINGLELUN)
+ SDpnt->single_lun = 1;
+
+ /*
+ * These devices need this "key" to unlock the devices so we can use it
+ */
+ if ((bflags & BLIST_KEY) != 0) {
+ printk("Unlocked floptical drive.\n");
+ SDpnt->lockable = 0;
+ scsi_cmd[0] = MODE_SENSE;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0x2e;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 0x2a;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result, 0x2a,
+ SCSI_TIMEOUT, 3);
+ }
+ /*
+ * Detach the command from the device. It was just a temporary to be used while
+ * scanning the bus - the real ones will be allocated later.
+ */
+ SDpnt->device_queue = NULL;
+
+ /*
+ * This device was already hooked up to the host in question,
+ * so at this point we just let go of it and it should be fine. We do need to
+ * allocate a new one and attach it to the host so that we can further scan the bus.
+ */
+ SDpnt = (Scsi_Device *) kmalloc(sizeof(Scsi_Device), GFP_ATOMIC);
+ *SDpnt2 = SDpnt;
+ if (!SDpnt) {
+ printk("scsi: scan_scsis_single: Cannot malloc\n");
+ return 0;
+ }
+ memset(SDpnt, 0, sizeof(Scsi_Device));
+
+ /*
+ * Register the queue for the device. All I/O requests will come
+ * in through here. We also need to register a pointer to
+ * ourselves, since the queue handler won't know what device
+ * the queue actually represents. We could look it up, but it
+ * is pointless work.
+ */
+ blk_init_queue(&SDpnt->request_queue, scsi_get_request_handler(SDpnt, shpnt));
+ blk_queue_headactive(&SDpnt->request_queue, 0);
+ SDpnt->request_queue.queuedata = (void *) SDpnt;
+ SDpnt->host = shpnt;
+ initialize_merge_fn(SDpnt);
+
+ /*
+ * And hook up our command block to the new device we will be testing
+ * for.
+ */
+ SDpnt->device_queue = SCpnt;
+ SDpnt->online = TRUE;
+
+ /*
+ * Initialize the object that we will use to wait for command blocks.
+ */
+ init_waitqueue_head(&SDpnt->scpnt_wait);
+
+ /*
+ * Since we just found one device, there had damn well better be one in the list
+ * already.
+ */
+ if (shpnt->host_queue == NULL)
+ panic("scan_scsis_single: Host queue == NULL\n");
+
+ SDtail = shpnt->host_queue;
+ while (SDtail->next) {
+ SDtail = SDtail->next;
+ }
+
+ /* Add this device to the linked list at the end */
+ SDtail->next = SDpnt;
+ SDpnt->prev = SDtail;
+ SDpnt->next = NULL;
+
+ /*
+ * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ */
+ if (bflags & BLIST_NOLUN)
+ return 0; /* break; */
+
+ /*
+ * If this device is known to support sparse multiple units, override the
+ * other settings, and scan all of them.
+ */
+ if (bflags & BLIST_SPARSELUN) {
+ *max_dev_lun = 8;
+ *sparse_lun = 1;
+ return 1;
+ }
+ /*
+ * If this device is known to support multiple units, override the other
+ * settings, and scan all of them.
+ */
+ if (bflags & BLIST_FORCELUN) {
+ *max_dev_lun = 8;
+ return 1;
+ }
+ /*
+ * REGAL CDC-4X: avoid hang after LUN 4
+ */
+ if (bflags & BLIST_MAX5LUN) {
+ *max_dev_lun = 5;
+ return 1;
+ }
+
+ /*
+ * If this device is Ghosted, scan upto two luns. (It physically only
+ * has one). -- REW
+ */
+ if (bflags & BLIST_GHOST) {
+ *max_dev_lun = 2;
+ return 1;
+ }
+
+
+ /*
+ * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+ * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+ * (ANSI SCSI Revision 1) and Response Data Format 0
+ */
+ if (((scsi_result[2] & 0x07) == 0)
+ ||
+ ((scsi_result[2] & 0x07) == 1 &&
+ (scsi_result[3] & 0x0f) == 0))
+ return 0;
+ return 1;
+}
+
return retval;
}
-static void sd_init_done(Scsi_Cmnd * SCpnt)
-{
- struct request *req;
-
- req = &SCpnt->request;
- req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
-
- if (req->sem != NULL) {
- up(req->sem);
- }
-}
static int sd_init_onedisk(int i)
{
unsigned char cmd[10];
SCpnt->sense_buffer[2] = 0;
scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
- 0/*512*/, sd_init_done, SD_TIMEOUT, MAX_RETRIES);
+ 0/*512*/, SD_TIMEOUT, MAX_RETRIES);
the_result = SCpnt->result;
retries++;
SCpnt->sense_buffer[2] = 0;
scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
- 512, sd_init_done, SD_TIMEOUT, MAX_RETRIES);
+ 512, SD_TIMEOUT, MAX_RETRIES);
}
spintime = 1;
spintime_value = jiffies;
SCpnt->sense_buffer[2] = 0;
scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
- 8, sd_init_done, SD_TIMEOUT, MAX_RETRIES);
+ 8, SD_TIMEOUT, MAX_RETRIES);
the_result = SCpnt->result;
retries--;
/* same code as READCAPA !! */
scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
- 512, sd_init_done, SD_TIMEOUT, MAX_RETRIES);
+ 512, SD_TIMEOUT, MAX_RETRIES);
the_result = SCpnt->result;
}
-static void sr_init_done(Scsi_Cmnd * SCpnt)
-{
- struct request *req;
-
- req = &SCpnt->request;
- req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
-
- if (req->sem != NULL) {
- up(req->sem);
- }
-}
-
void get_sectorsize(int i)
{
unsigned char cmd[10];
/* Do the command and wait.. */
scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
- 512, sr_init_done, SR_TIMEOUT, MAX_RETRIES);
+ 512, SR_TIMEOUT, MAX_RETRIES);
the_result = SCpnt->result;
retries--;
/* do the locking and issue the command */
SCpnt->request.rq_dev = cdi->dev;
- /* scsi_do_cmd sets the command length */
+ /* scsi_wait_cmd sets the command length */
SCpnt->cmd_len = 0;
scsi_wait_cmd(SCpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen,
- sr_init_done, SR_TIMEOUT, MAX_RETRIES);
+ SR_TIMEOUT, MAX_RETRIES);
if ((cgc->stat = SCpnt->result))
cgc->sense = (struct request_sense *) SCpnt->sense_buffer;
/* In fact, it is very slow if it has to spin up first */
#define IOCTL_TIMEOUT 30*HZ
-static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
-{
- struct request *req;
-
- req = &SCpnt->request;
- req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
-
- if (SCpnt->buffer && req->buffer && SCpnt->buffer != req->buffer) {
- memcpy(req->buffer, SCpnt->buffer, SCpnt->bufflen);
- scsi_free(SCpnt->buffer, (SCpnt->bufflen + 511) & ~511);
- SCpnt->buffer = req->buffer;
- }
- if (req->sem != NULL) {
- up(req->sem);
- }
-}
-
/* We do our own retries because we want to know what the specific
error code is. Normally the UNIT_ATTENTION code will automatically
clear after one error */
{
Scsi_Cmnd *SCpnt;
Scsi_Device *SDev;
+ struct request *req;
int result, err = 0, retries = 0;
char *bounce_buffer;
scsi_wait_cmd(SCpnt, (void *) sr_cmd, (void *) buffer, buflength,
- sr_ioctl_done, IOCTL_TIMEOUT, IOCTL_RETRIES);
+ IOCTL_TIMEOUT, IOCTL_RETRIES);
+
+ req = &SCpnt->request;
+ if (SCpnt->buffer && req->buffer && SCpnt->buffer != req->buffer) {
+ memcpy(req->buffer, SCpnt->buffer, SCpnt->bufflen);
+ scsi_free(SCpnt->buffer, (SCpnt->bufflen + 511) & ~511);
+ SCpnt->buffer = req->buffer;
+ }
result = SCpnt->result;
int sr_select_speed(struct cdrom_device_info *cdi, int speed)
{
- u_char sr_cmd[12];
+ u_char sr_cmd[MAX_COMMAND_SIZE];
if (speed == 0)
speed = 0xffff; /* set to max */
else
speed *= 177; /* Nx to kbyte/s */
- memset(sr_cmd, 0, 12);
+ memset(sr_cmd, 0, MAX_COMMAND_SIZE);
sr_cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */
sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->lun) << 5;
sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
int sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize)
{
- unsigned char cmd[12];
+ unsigned char cmd[MAX_COMMAND_SIZE];
#ifdef DEBUG
printk("sr%d: sr_read_cd lba=%d format=%d blksize=%d\n",
minor, lba, format, blksize);
#endif
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = GPCMD_READ_CD; /* READ_CD */
cmd[1] = (scsi_CDs[minor].device->lun << 5) | ((format & 7) << 2);
cmd[2] = (unsigned char) (lba >> 24) & 0xff;
int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
{
- unsigned char cmd[12]; /* the scsi-command */
+ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */
int rc;
/* we try the READ CD command first... */
printk("sr%d: sr_read_sector lba=%d blksize=%d\n", minor, lba, blksize);
#endif
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = GPCMD_READ_10;
cmd[1] = (scsi_CDs[minor].device->lun << 5);
cmd[2] = (unsigned char) (lba >> 24) & 0xff;
int sr_set_blocklength(int minor, int blocklength)
{
unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char cmd[12]; /* the scsi-command */
+ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */
struct ccs_modesel_head *modesel;
int rc, density = 0;
#ifdef DEBUG
printk("sr%d: MODE SELECT 0x%x/%d\n", minor, density, blocklength);
#endif
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SELECT;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | (1 << 4);
cmd[4] = 12;
{
unsigned long sector;
unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char cmd[12]; /* the scsi-command */
+ unsigned char cmd[MAX_COMMAND_SIZE]; /* the scsi-command */
int rc, no_multi, minor;
minor = MINOR(cdi->dev);
switch (VENDOR_ID) {
case VENDOR_SCSI3:
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_TOC;
cmd[1] = (scsi_CDs[minor].device->lun << 5);
cmd[8] = 12;
#ifdef CONFIG_BLK_DEV_SR_VENDOR
case VENDOR_NEC:{
unsigned long min, sec, frame;
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = 0xde;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 0x03;
cmd[2] = 0xb0;
/* we request some disc information (is it a XA-CD ?,
* where starts the last session ?) */
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = 0xc7;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 3;
rc = sr_do_ioctl(minor, cmd, buffer, 4, 1);
}
case VENDOR_WRITER:
- memset(cmd, 0, 12);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_TOC;
cmd[1] = (scsi_CDs[minor].device->lun << 5);
cmd[8] = 0x04;
comment 'USB Controllers'
dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
- dep_tristate ' OHCI-HCD (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI_HCD $CONFIG_USB
+ dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
comment 'Miscellaneous USB options'
bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
bool ' USB Peracom Single Port Serial Driver' CONFIG_USB_SERIAL_PERACOM
fi
dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB
+ dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB
dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
dep_tristate ' USB SCSI (mass storage) support' CONFIG_USB_SCSI $CONFIG_USB
# Multipart objects.
-list-multi := usbcore.o usb-uhci.o usb-ohci-hcd.o
+list-multi := usbcore.o
usbcore-objs := usb.o usb-debug.o usb-core.o hub.o
-usb-uhci-objs := uhci.o uhci-debug.o
-usb-ohci-hcd-objs := ohci-hcd.o
usb-scsi-objs := usb_scsi.o
# Optional parts of multipart objects.
obj-$(CONFIG_USB) += usbcore.o
obj-$(CONFIG_USB_UHCI) += usb-uhci.o
-obj-$(CONFIG_USB_OHCI_HCD) += usb-ohci-hcd.o
+obj-$(CONFIG_USB_OHCI) += usb-ohci.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o
obj-$(CONFIG_USB_HID) += hid.o input.o
obj-$(CONFIG_USB_SERIAL) += usb-serial.o
obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_CPIA) += cpia.o
+obj-$(CONFIG_USB_IBMCAM) += ibmcam.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
obj-$(CONFIG_USB_SCSI) += usb-scsi.o
obj-$(CONFIG_USB_USS720) += uss720.o
usbcore.o: $(usbcore-objs)
$(LD) -r -o $@ $(usbcore-objs)
-usb-uhci.o: $(usb-uhci-objs)
- $(LD) -r -o $@ $(usb-uhci-objs)
-
-usb-ohci-hcd.o: $(usb-ohci-hcd-objs)
- $(LD) -r -o $@ $(usb-ohci-hcd-objs)
-
usb-scsi.o: $(usb-scsi-objs)
$(LD) -r -o $@ $(usb-scsi-objs)
#include "usb.h"
#include "dabusb.h"
-#include "bitstream.h"
-#include "firmware.h"
+#include "dabfirmware.h"
/* --------------------------------------------------------------------- */
#define NRDABUSB 4
/*
- * graphire.c Version 0.1
+ * graphire.c Version 0.2
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
*
* USB Wacom Graphire tablet support
*
* Sponsored by SuSE
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
*/
/*
* There are also two single-byte feature reports (2 and 3).
*/
-#define USB_VENDOR_ID_WACOM 0xffff /* FIXME */
-#define USB_DEVICE_ID_WACOM_GRAPHIRE 0xffff /* FIXME */
+#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
struct graphire {
signed char data[8];
- int oldx, oldy;
struct input_dev dev;
struct urb irq;
};
input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
input_report_abs(dev, ABS_Y, data[4] | ((__u32)data[5] << 8));
- input_report_key(dev, BTN_NEAR, !!(data[1] & 0x80));
-
switch ((data[1] >> 5) & 3) {
case 0: /* Pen */
- input_report_key(dev, BTN_PEN, !!(data[1] & 0x01));
- input_report_key(dev, BTN_PEN_SIDE, !!(data[1] & 0x02));
- input_report_key(dev, BTN_PEN_SIDE2, !!(data[1] & 0x04));
+ input_report_key(dev, BTN_TOOL_PEN, !!(data[1] & 0x80));
+ input_report_key(dev, BTN_TOUCH, !!(data[1] & 0x01));
+ input_report_key(dev, BTN_STYLUS, !!(data[1] & 0x02));
+ input_report_key(dev, BTN_STYLUS2, !!(data[1] & 0x04));
input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
break;
case 1: /* Rubber */
- input_report_key(dev, BTN_RUBBER, !!(data[1] & 0x01));
- input_report_key(dev, BTN_PEN_SIDE, !!(data[1] & 0x02));
- input_report_key(dev, BTN_PEN_SIDE2, !!(data[1] & 0x04));
+ input_report_key(dev, BTN_TOOL_RUBBER, !!(data[1] & 0x80));
+ input_report_key(dev, BTN_TOUCH, !!(data[1] & 0x01));
+ input_report_key(dev, BTN_STYLUS, !!(data[1] & 0x02));
+ input_report_key(dev, BTN_STYLUS2, !!(data[1] & 0x04));
input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
break;
case 2: /* Mouse */
- 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_TOOL_MOUSE, data[7] > 27);
+ input_report_key(dev, BTN_LEFT, !!(data[1] & 0x01));
+ input_report_key(dev, BTN_RIGHT, !!(data[1] & 0x02));
+ input_report_key(dev, BTN_MIDDLE, !!(data[1] & 0x04));
input_report_abs(dev, ABS_DISTANCE, data[7]);
-
- if (data[1] & 0x80) {
- input_report_rel(dev, REL_X, dev->abs[ABS_X] - graphire->oldx);
- input_report_rel(dev, REL_Y, dev->abs[ABS_Y] - graphire->oldy);
- }
-
input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
break;
}
-
- graphire->oldx = dev->abs[ABS_X];
- graphire->oldy = dev->abs[ABS_Y];
}
static void *graphire_probe(struct usb_device *dev, unsigned int ifnum)
graphire->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
graphire->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
- graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_PEN) | BIT(BTN_RUBBER);
- graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_PEN_SIDE) | BIT(BTN_PEN_SIDE2) | BIT(BTN_NEAR);
- graphire->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
+ graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE);
+ graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
+ graphire->dev.relbit[0] |= BIT(REL_WHEEL);
graphire->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+ graphire->dev.absmax[ABS_X] = 10000;
+ graphire->dev.absmax[ABS_Y] = 7500;
+ graphire->dev.absmax[ABS_PRESSURE] = 500;
+ graphire->dev.absmax[ABS_DISTANCE] = 32;
+
FILL_INT_URB(&graphire->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
graphire->data, 8, graphire_irq, graphire, endpoint->bInterval);
input_register_device(&graphire->dev);
- printk(KERN_INFO "input%d: Wacom Graphire USB\n", graphire->dev.number);
+ printk(KERN_INFO "input%d: Wacom Graphire\n", graphire->dev.number);
return graphire;
}
switch (device->application) {
case HID_GD_GAMEPAD: usage->code += 0x10;
case HID_GD_JOYSTICK: usage->code += 0x10;
- case HID_GD_MOUSE: usage->code += 0x10;
+ case HID_GD_MOUSE: usage->code += 0x10; break;
+ default:
+ if (field->physical == HID_GD_POINTER)
+ usage->code += 0x10;
+ break;
}
break;
usage->type = EV_LED; bit = input->ledbit; max = LED_MAX;
break;
+ case HID_UP_DIGITIZER:
+
+ switch (usage->hid & 0xff) {
+
+ case 0x30: /* TipPressure */
+
+ 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 0x33: /* Touch */
+ case 0x42: /* TipSwitch */
+ case 0x43: /* TipSwitch2 */
+
+ 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;
+
default:
+ unknown:
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
usage->code = REL_MISC;
break;
}
- if (field->logical_minimum == 0 && field->logical_maximum == 1) {
+ if (field->report_size == 1) {
usage->code = BTN_MISC;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break;
#define HID_USAGE 0x0000ffff
+#define HID_GD_POINTER 0x00010001
#define HID_GD_MOUSE 0x00010002
#define HID_GD_JOYSTICK 0x00010004
#define HID_GD_GAMEPAD 0x00010005
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
*
- * $Id: hub.c,v 1.15 1999/12/27 15:17:45 acher Exp $
+ * $Id: hub.c,v 1.21 2000/01/16 21:19:44 acher Exp $
*/
#include <linux/kernel.h>
#include "usb.h"
#include "hub.h"
-#ifdef __alpha
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-extern long __kernel_thread(unsigned long, int (*)(void *), void *);
-static inline long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
- return __kernel_thread(flags | CLONE_VM, fn, arg);
-}
-#endif
-#endif
/* Wakes up khubd */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
return 1;
}
+static void usb_hub_power_on(struct usb_hub *hub)
+{
+ int i;
+
+ /* Enable power to the ports */
+ dbg("enabling power on all ports");
+ for (i = 0; i < hub->nports; i++)
+ usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER);
+}
+
static int usb_hub_configure(struct usb_hub *hub)
{
struct usb_device *dev = hub->dev;
dbg("%sover-current condition exists",
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
- /* Enable power to the ports */
- dbg("enabling power on all ports");
- for (i = 0; i < hub->nports; i++)
- usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+ usb_hub_power_on(hub);
+
return 0;
}
portchange = le16_to_cpu(portsts.wPortChange);
dbg("portstatus %x, change %x, %s", portstatus, portchange,
portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
- /* If it's not in CONNECT and ENABLE state, we're done */
- if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
- (!(portstatus & USB_PORT_STAT_ENABLE))) {
- /* Disconnect anything that may have been there */
+
+ /* Clear the connection change status */
+ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+ /* Disconnect any existing devices under this port */
+ if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+ (!(portstatus & USB_PORT_STAT_ENABLE)))|| (hub->children[port])) {
usb_disconnect(&hub->children[port]);
- /* We're done now, we already disconnected the device */
- return;
+ /* Return now if nothing is connected */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return;
}
wait_ms(400);
dbg("portstatus %x, change %x, %s", portstatus ,portchange,
portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
- if ((portstatus&(1<<USB_PORT_FEAT_ENABLE)))
+ if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
+ !(portstatus & USB_PORT_STAT_CONNECTION))
+ return;
+
+ if (portstatus & USB_PORT_STAT_ENABLE)
break;
wait_ms(200);
err("Maybe the USB cable is bad?");
return;
}
+
+ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
+
/* Allocate a new device struct for it */
usb = usb_alloc_dev(hub, hub->bus);
if (portchange & USB_PORT_STAT_C_CONNECTION) {
dbg("port %d connection change", i + 1);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_CONNECTION);
-
usb_hub_port_connect_change(dev, i);
}
usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE);
}
- if (portchange & USB_PORT_STAT_C_SUSPEND)
+ if (portstatus & USB_PORT_STAT_SUSPEND) {
dbg("port %d suspend change", i + 1);
-
+ usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND);
+ }
+
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- dbg("port %d over-current change", i + 1);
+ err("port %d over-current change", i + 1);
usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+ usb_hub_power_on(hub);
}
if (portchange & USB_PORT_STAT_C_RESET) {
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dbg("hub overcurrent change");
+ wait_ms(500); //Cool down
usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
+ usb_hub_power_on(hub);
}
}
} /* end while (1) */
* This thread doesn't need any user-level access,
* so get rid of all our resources
*/
+ exit_files(current); /* daemonize doesn't do exit_files */
daemonize();
/* Setup a nice name */
--- /dev/null
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports IBM C-It Video Camera.
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/wrapper.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+
+#include "usb.h"
+#include "ibmcam.h"
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+static int debug = 0;
+
+/* Completion states of the data parser */
+typedef enum {
+ scan_Continue, /* Just parse next item */
+ scan_NextFrame, /* Frame done, send it to V4L */
+ scan_Out, /* Not enough data for frame */
+ scan_EndParse /* End parsing */
+} scan_state_t;
+
+/* Bit flags (options) */
+#define FLAGS_RETRY_VIDIOCSYNC (1 << 0)
+#define FLAGS_MONOCHROME (1 << 1)
+#define FLAGS_DISPLAY_HINTS (1 << 2)
+#define FLAGS_OVERLAY_STATS (1 << 3)
+#define FLAGS_FORCE_TESTPATTERN (1 << 4)
+
+static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+
+/* This is the size of V4L frame that we provide */
+static const int imgwidth = V4L_FRAME_WIDTH_USED;
+static const int imgheight = V4L_FRAME_HEIGHT;
+static const int min_imgwidth = 8;
+static const int min_imgheight = 4;
+
+#define LIGHTING_MIN 0 /* 0=Bright 1=Med 2=Low */
+#define LIGHTING_MAX 2
+static int lighting = (LIGHTING_MIN + LIGHTING_MAX) / 2; /* Medium */
+
+#define SHARPNESS_MIN 0
+#define SHARPNESS_MAX 6
+static int sharpness = 4; /* Low noise, good details */
+
+#define FRAMERATE_MIN 0
+#define FRAMERATE_MAX 6
+static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */
+
+enum {
+ VIDEOSIZE_128x96 = 0,
+ VIDEOSIZE_176x144,
+ VIDEOSIZE_352x288
+};
+
+static int videosize = VIDEOSIZE_352x288;
+
+/*
+ * The value of 'scratchbufsize' affects quality of the picture
+ * in many ways. Shorter buffers may cause loss of data when client
+ * is too slow. Larger buffers are memory-consuming and take longer
+ * to work with. This setting can be adjusted, but the default value
+ * should be OK for most desktop users.
+ */
+#define DEFAULT_SCRATCH_BUF_SIZE (0x10000) /* 64 KB */
+static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE;
+
+/*
+ * Here we define several initialization variables. They may
+ * be used to automatically set color, hue, brightness and
+ * contrast to desired values. This is particularly useful in
+ * case of webcams (which have no controls and no on-screen
+ * output) and also when a client V4L software is used that
+ * does not have some of those controls. In any case it's
+ * good to have startup values as options.
+ *
+ * These values are all in [0..255] range. This simplifies
+ * operation. Note that actual values of V4L variables may
+ * be scaled up (as much as << 8). User can see that only
+ * on overlay output, however, or through a V4L client.
+ */
+static int init_brightness = 128;
+static int init_contrast = 192;
+static int init_color = 128;
+static int init_hue = 128;
+
+MODULE_PARM(debug, "i");
+MODULE_PARM(flags, "i");
+MODULE_PARM(framerate, "i");
+MODULE_PARM(lighting, "i");
+MODULE_PARM(sharpness, "i");
+MODULE_PARM(videosize, "i");
+MODULE_PARM(init_brightness, "i");
+MODULE_PARM(init_contrast, "i");
+MODULE_PARM(init_color, "i");
+MODULE_PARM(init_hue, "i");
+
+/* Still mysterious i2o commands */
+static const unsigned short unknown_88 = 0x0088;
+static const unsigned short unknown_89 = 0x0089;
+static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 };
+static const unsigned short contrast_14 = 0x0014;
+static const unsigned short light_27 = 0x0027;
+static const unsigned short sharp_13 = 0x0013;
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+#define MDEBUG(x) do { } while(0) /* Debug memory management */
+
+static struct usb_driver ibmcam_driver;
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+ unsigned long ret = 0UL;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+ if (!pgd_none(*pgd)) {
+ pmd = pmd_offset(pgd, adr);
+ if (!pmd_none(*pmd)) {
+ ptep = pte_offset(pmd, adr);
+ pte = *ptep;
+ if (pte_present(pte))
+ ret = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1));
+ }
+ }
+ MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));
+ return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+ ret = virt_to_bus((void *)kva);
+ MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
+ return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+ unsigned long va, kva, ret;
+
+ va = VMALLOC_VMADDR(adr);
+ kva = uvirt_to_kva(pgd_offset_k(va), va);
+ ret = virt_to_bus((void *)kva);
+ MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));
+ return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long va, kva, ret;
+
+ va = VMALLOC_VMADDR(adr);
+ kva = uvirt_to_kva(pgd_offset_k(va), va);
+ ret = __pa(kva);
+ MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
+ return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr, page;
+
+ /* Round it off to PAGE_SIZE */
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ mem = vmalloc(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_pa(adr);
+ mem_map_reserve(MAP_NR(__va(page)));
+ adr += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ if (!mem)
+ return;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ adr=(unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_pa(adr);
+ mem_map_unreserve(MAP_NR(__va(page)));
+ adr += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ vfree(mem);
+}
+
+/*
+ * usb_ibmcam_overlaychar()
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+void usb_ibmcam_overlaychar(
+ struct usb_ibmcam *ibmcam,
+ struct ibmcam_frame *frame,
+ int x, int y, int ch)
+{
+ static const unsigned short digits[16] = {
+ 0xF6DE, /* 0 */
+ 0x2492, /* 1 */
+ 0xE7CE, /* 2 */
+ 0xE79E, /* 3 */
+ 0xB792, /* 4 */
+ 0xF39E, /* 5 */
+ 0xF3DE, /* 6 */
+ 0xF492, /* 7 */
+ 0xF7DE, /* 8 */
+ 0xF79E, /* 9 */
+ 0x77DA, /* a */
+ 0xD75C, /* b */
+ 0xF24E, /* c */
+ 0xD6DC, /* d */
+ 0xF34E, /* e */
+ 0xF348 /* f */
+ };
+ unsigned short digit;
+ int ix, iy;
+
+ if ((ibmcam == NULL) || (frame == NULL))
+ return;
+
+ if (ch >= '0' && ch <= '9')
+ ch -= '0';
+ else if (ch >= 'A' && ch <= 'F')
+ ch = 10 + (ch - 'A');
+ else if (ch >= 'a' && ch <= 'f')
+ ch = 10 + (ch - 'a');
+ else
+ return;
+ digit = digits[ch];
+
+ for (iy=0; iy < 5; iy++) {
+ for (ix=0; ix < 3; ix++) {
+ if (digit & 0x8000) {
+ IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF);
+ }
+ digit = digit << 1;
+ }
+ }
+}
+
+/*
+ * usb_ibmcam_overlaystring()
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+void usb_ibmcam_overlaystring(
+ struct usb_ibmcam *ibmcam,
+ struct ibmcam_frame *frame,
+ int x, int y, const char *str)
+{
+ while (*str) {
+ usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str);
+ str++;
+ x += 4; /* 3 pixels character + 1 space */
+ }
+}
+
+/*
+ * usb_ibmcam_overlaystats()
+ *
+ * Overlays important debugging information.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame)
+{
+ const int y_diff = 8;
+ char tmp[16];
+ int x = 10;
+ int y = 10;
+
+ sprintf(tmp, "%8x", ibmcam->frame_num);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->urb_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->urb_length);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->data_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->header_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->iso_skip_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", ibmcam->iso_err_count);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", ibmcam->vpic.colour);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", ibmcam->vpic.hue);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8);
+ usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+ y += y_diff;
+}
+
+/*
+ * usb_ibmcam_testpattern()
+ *
+ * Procedure forms a test pattern (yellow grid on blue background).
+ *
+ * Parameters:
+ * fullframe: if TRUE then entire frame is filled, otherwise the procedure
+ * continues from the current scanline.
+ * pmode 0: fill the frame with solid blue color (like on VCR or TV)
+ * 1: Draw a colored grid
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode)
+{
+ static const char proc[] = "usb_ibmcam_testpattern";
+ struct ibmcam_frame *frame;
+ unsigned char *f;
+ int num_cell = 0;
+ int scan_length = 0;
+ static int num_pass = 0;
+
+ if (ibmcam == NULL) {
+ printk(KERN_ERR "%s: ibmcam == NULL\n", proc);
+ return;
+ }
+ if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) {
+ printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe);
+ return;
+ }
+
+ /* Grab the current frame */
+ frame = &ibmcam->frame[ibmcam->curframe];
+
+ /* Optionally start at the beginning */
+ if (fullframe) {
+ frame->curline = 0;
+ frame->scanlength = 0;
+ }
+
+ /* Form every scan line */
+ for (; frame->curline < imgheight; frame->curline++) {
+ int i;
+
+ f = frame->data + (imgwidth * 3 * frame->curline);
+ for (i=0; i < imgwidth; i++) {
+ unsigned char cb=0x80;
+ unsigned char cg = 0;
+ unsigned char cr = 0;
+
+ if (pmode == 1) {
+ if (frame->curline % 32 == 0)
+ cb = 0, cg = cr = 0xFF;
+ else if (i % 32 == 0) {
+ if (frame->curline % 32 == 1)
+ num_cell++;
+ cb = 0, cg = cr = 0xFF;
+ } else {
+ cb = ((num_cell*7) + num_pass) & 0xFF;
+ cg = ((num_cell*5) + num_pass*2) & 0xFF;
+ cr = ((num_cell*3) + num_pass*3) & 0xFF;
+ }
+ } else {
+ /* Just the blue screen */
+ }
+
+ *f++ = cb;
+ *f++ = cg;
+ *f++ = cr;
+ scan_length += 3;
+ }
+ }
+
+ frame->grabstate = FRAME_DONE;
+ frame->scanlength += scan_length;
+ ++num_pass;
+
+ /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */
+ usb_ibmcam_overlaystats(ibmcam, frame);
+}
+
+static unsigned char *ibmcam_find_header(const unsigned char hdr_sig, unsigned char *data, int len)
+{
+ while (len >= 4)
+ {
+ if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00))
+ {
+#if 0
+ /* This code helps to detect new frame markers */
+ printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]);
+#endif
+ if (data[3] == hdr_sig) {
+ if (debug > 2)
+ printk(KERN_DEBUG "Header found.\n");
+ return data;
+ }
+ }
+ ++data;
+ --len;
+ }
+ return NULL;
+}
+
+/* How much data is left in the scratch buf? */
+#define scratch_left(x) (ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch))
+
+/* Grab the remaining */
+static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data)
+{
+ unsigned long left;
+
+ left = scratch_left(data);
+ memmove(ibmcam->scratch, data, left);
+ ibmcam->scratchlen = left;
+}
+
+/*
+ * usb_ibmcam_find_header()
+ *
+ * Locate one of supported header markers in the scratch buffer.
+ * Once found, remove all preceding bytes AND the marker (4 bytes)
+ * from the scratch buffer. Whatever follows must be video lines.
+ *
+ * History:
+ * 1/21/00 Created.
+ */
+static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam)
+{
+ struct ibmcam_frame *frame;
+ unsigned char *data, *tmp;
+
+ data = ibmcam->scratch;
+ frame = &ibmcam->frame[ibmcam->curframe];
+ tmp = ibmcam_find_header(frame->hdr_sig, data, scratch_left(data));
+
+ if (tmp == NULL) {
+ /* No header - entire scratch buffer is useless! */
+ if (debug > 2)
+ printk(KERN_DEBUG "Skipping frame, no header\n");
+ ibmcam->scratchlen = 0;
+ return scan_EndParse;
+ }
+ /* Header found */
+ data = tmp+4;
+
+ ibmcam->has_hdr = 1;
+ ibmcam->header_count++;
+ frame->scanstate = STATE_LINES;
+ frame->curline = 0;
+
+ if (flags & FLAGS_FORCE_TESTPATTERN) {
+ usb_ibmcam_testpattern(ibmcam, 1, 1);
+ return scan_NextFrame;
+ }
+ usb_ibmcam_align_scratch(ibmcam, data);
+ return scan_Continue;
+}
+
+/*
+ * usb_ibmcam_parse_lines()
+ *
+ * Parse one line (TODO: more than one!) from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ * History:
+ * 1/21/00 Created.
+ */
+static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen)
+{
+ struct ibmcam_frame *frame;
+ unsigned char *data, *f, *chromaLine;
+ unsigned int len;
+ const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */
+ int y, u, v, i, frame_done=0, mono_plane, hue_corr, color_corr;
+
+ hue_corr = (ibmcam->vpic.hue - 0x8000) >> 10; /* -32..+31 */
+ color_corr = (ibmcam->vpic.colour - 0x8000) >> 10; /* -32..+31 */
+
+ data = ibmcam->scratch;
+ frame = &ibmcam->frame[ibmcam->curframe];
+
+ len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */
+ /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/
+
+ mono_plane = ((frame->curline & 1) == 0);
+
+ /*
+ * Lines are organized this way (or are they?)
+ *
+ * I420:
+ * ~~~~
+ * ___________________________________
+ * |-----Y-----|---UVUVUV...UVUV-----| \
+ * |-----------+---------------------| \
+ * |<-- 176 -->|<------ 176*2 ------>| Total 72. pairs of lines
+ * |... ... ...| /
+ * |___________|_____________________| /
+ * - odd line- ------- even line ---
+ *
+ * another format:
+ * ~~~~~~~~~~~~~~
+ * ___________________________________
+ * |-----Y-----|---UVUVUV...UVUV-----| \
+ * |-----------+---------------------| \
+ * |<-- 352 -->|<------ 352*2 ------>| Total 144. pairs of lines
+ * |... ... ...| /
+ * |___________|_____________________| /
+ * - odd line- ------- even line ---
+ */
+
+ /* Make sure there's enough data for the entire line */
+ if (scratch_left(data) < (len+1024)) {
+ /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/
+ return scan_Out;
+ }
+
+ /*
+ * Make sure that our writing into output buffer
+ * will not exceed the buffer. Mind that we may write
+ * not into current output scanline but in several after
+ * it as well (if we enlarge image vertically.)
+ */
+ if ((frame->curline + 1) >= V4L_FRAME_HEIGHT)
+ return scan_NextFrame;
+
+ /*
+ * Now we are sure that entire line (representing all 'frame->frmwidth'
+ * pixels from the camera) is available in the scratch buffer. We
+ * start copying the line left-aligned to the V4L buffer (which
+ * might be larger - not smaller, hopefully). If the camera
+ * line is shorter then we should pad the V4L buffer with something
+ * (black in this case) to complete the line.
+ */
+ f = frame->data + (v4l_linesize * frame->curline);
+
+ /*
+ * chromaLine points to 1st pixel of the line with chrominance.
+ * If current line is monochrome then chromaLine points to next
+ * line after monochrome one. If current line has chrominance
+ * then chromaLine points to this very line. Such method allows
+ * to access chrominance data uniformly.
+ *
+ * To obtain chrominance data from the 'chromaLine' use this:
+ * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]...
+ * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]...
+ *
+ * Indices must be calculated this way:
+ * v_index = (i >> 1) << 2;
+ * u_index = (i >> 1) << 2 + 2;
+ *
+ * where 'i' is the column number [0..frame->frmwidth-1]
+ */
+ chromaLine = data;
+ if (mono_plane)
+ chromaLine += frame->frmwidth;
+
+ for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2))
+ {
+ unsigned char rv, gv, bv; /* RGB components */
+
+ /*
+ * Search for potential Start-Of-Frame marker. It should
+ * not be here, of course, but if your formats don't match
+ * you might exceed the frame. We must match the marker to
+ * each byte of multi-byte data element if it is multi-byte.
+ */
+#if 1
+ if (scratch_left(data) >= (4+2)) {
+ unsigned char *dp;
+ int j;
+
+ for (j=0, dp=data; j < 2; j++, dp++) {
+ if ((dp[0] == 0x00) && (dp[1] == 0xFF) &&
+ (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) {
+ ibmcam->has_hdr = 2;
+ frame_done++;
+ break;
+ }
+ }
+ }
+#endif
+
+ /* Check for various visual debugging hints (colorized pixels) */
+ if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) {
+ /*
+ * This is bad and should not happen. This means that
+ * we somehow overshoot the line and encountered new
+ * frame! Obviously our camera/V4L frame size is out
+ * of whack. This cyan dot will help you to figure
+ * out where exactly the new frame arrived.
+ */
+ if (ibmcam->has_hdr == 1) {
+ bv = 0; /* Yellow marker */
+ gv = 0xFF;
+ rv = 0xFF;
+ } else {
+ bv = 0xFF; /* Cyan marker */
+ gv = 0xFF;
+ rv = 0;
+ }
+ ibmcam->has_hdr = 0;
+ goto make_pixel;
+ }
+
+ y = mono_plane ? data[0] : data[1];
+
+ if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
+ rv = gv = bv = y;
+ else {
+ if (frame->order_uv) {
+ u = chromaLine[(i >> 1) << 2] + hue_corr;
+ v = chromaLine[((i >> 1) << 2) + 2] + color_corr;
+ } else {
+ v = chromaLine[(i >> 1) << 2] + color_corr;
+ u = chromaLine[((i >> 1) << 2) + 2] + hue_corr;
+ }
+ YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv);
+ }
+
+ make_pixel:
+ /*
+ * The purpose of creating the pixel here, in one,
+ * dedicated place is that we may need to make the
+ * pixel wider and taller than it actually is. This
+ * may be used if camera generates small frames for
+ * sake of frame rate (or any other reason.)
+ *
+ * The output data consists of B, G, R bytes
+ * (in this order).
+ */
+#if USES_IBMCAM_PUTPIXEL
+ IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+#else
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+#endif
+ /*
+ * Typically we do not decide within a legitimate frame
+ * that we want to end the frame. However debugging code
+ * may detect marker of new frame within the data. Then
+ * this condition activates. The 'data' pointer is already
+ * pointing at the new marker, so we'd better leave it as is.
+ */
+ if (frame_done)
+ break; /* End scanning of lines */
+ }
+ /*
+ * Account for number of bytes that we wrote into output V4L frame.
+ * We do it here, after we are done with the scanline, because we
+ * may fill more than one output scanline if we do vertical
+ * enlargement.
+ */
+ frame->curline++;
+ *pcopylen += v4l_linesize;
+ usb_ibmcam_align_scratch(ibmcam, data);
+
+ if (frame_done || (frame->curline >= frame->frmheight))
+ return scan_NextFrame;
+ else
+ return scan_Continue;
+}
+
+/*
+ * ibmcam_parse_data()
+ *
+ * Generic routine to parse the scratch buffer. It employs either
+ * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most
+ * of work.
+ *
+ * History:
+ * 1/21/00 Created.
+ */
+static void ibmcam_parse_data(struct usb_ibmcam *ibmcam)
+{
+ struct ibmcam_frame *frame;
+ unsigned char *data = ibmcam->scratch;
+ scan_state_t newstate;
+ long copylen = 0;
+
+ /* Grab the current frame and the previous frame */
+ frame = &ibmcam->frame[ibmcam->curframe];
+
+ /* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */
+
+ while (1) {
+
+ newstate = scan_Out;
+ if (scratch_left(data)) {
+ if (frame->scanstate == STATE_SCANNING)
+ newstate = usb_ibmcam_find_header(ibmcam);
+ else if (frame->scanstate == STATE_LINES)
+ newstate = usb_ibmcam_parse_lines(ibmcam, ©len);
+ }
+ if (newstate == scan_Continue)
+ continue;
+ else if ((newstate == scan_NextFrame) || (newstate == scan_Out))
+ break;
+ else
+ return; /* scan_EndParse */
+ }
+
+ if (newstate == scan_NextFrame) {
+ frame->grabstate = FRAME_DONE;
+ ibmcam->curframe = -1;
+ ibmcam->frame_num++;
+
+ /* Optionally display statistics on the screen */
+ if (flags & FLAGS_OVERLAY_STATS)
+ usb_ibmcam_overlaystats(ibmcam, frame);
+
+ /* This will cause the process to request another frame. */
+ if (waitqueue_active(&frame->wq))
+ wake_up_interruptible(&frame->wq);
+ }
+
+ /* Update the frame's uncompressed length. */
+ frame->scanlength += copylen;
+}
+
+#if ENABLE_HEXDUMP
+static void ibmcam_hexdump(const unsigned char *data, int len)
+{
+ char tmp[80];
+ int i, k;
+
+ for (i=k=0; len > 0; i++, len--) {
+ if (i > 0 && (i%16 == 0)) {
+ printk("%s\n", tmp);
+ k=0;
+ }
+ k += sprintf(&tmp[k], "%02x ", data[i]);
+ }
+ if (k > 0)
+ printk("%s\n", tmp);
+}
+#endif
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb)
+{
+ unsigned char *cdata, *data, *data0;
+ int i, totlen = 0;
+
+ data = data0 = ibmcam->scratch + ibmcam->scratchlen;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+
+ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (st < 0) {
+ if (debug >= 1) {
+ printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n",
+ i, n, st);
+ }
+ ibmcam->iso_err_count++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (n <= 0) {
+ ibmcam->iso_skip_count++;
+ continue;
+ }
+
+ /*
+ * If camera continues to feed us with data but there is no
+ * consumption (if, for example, V4L client fell asleep) we
+ * may overflow the buffer. We have to move old data over to
+ * free room for new data. This is bad for old data. If we
+ * just drop new data then it's bad for new data... choose
+ * your favorite evil here.
+ */
+ if ((ibmcam->scratchlen + n) > scratchbufsize) {
+#if 0
+ ibmcam->scratch_ovf_count++;
+ if (debug >= 3)
+ printk(KERN_ERR "ibmcam: scratch buf overflow! "
+ "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
+ return totlen;
+#else
+ int mv;
+
+ ibmcam->scratch_ovf_count++;
+ if (debug >= 3) {
+ printk(KERN_ERR "ibmcam: scratch buf overflow! "
+ "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
+ }
+ mv = (ibmcam->scratchlen + n) - scratchbufsize;
+ if (ibmcam->scratchlen >= mv) {
+ int newslen = ibmcam->scratchlen - mv;
+ memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen);
+ ibmcam->scratchlen = newslen;
+ data = data0 = ibmcam->scratch + ibmcam->scratchlen;
+ } else {
+ printk(KERN_ERR "ibmcam: scratch buf too small\n");
+ return totlen;
+ }
+#endif
+ }
+
+ /* Now we know that there is enough room in scratch buffer */
+ memmove(data, cdata, n);
+ data += n;
+ totlen += n;
+ ibmcam->scratchlen += n;
+ }
+#if 0
+ if (totlen > 0) {
+ static int foo=0;
+ if (foo < 1) {
+ printk(KERN_DEBUG "+%d.\n", totlen);
+ ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen);
+ ++foo;
+ }
+ }
+#endif
+ return totlen;
+}
+
+static void ibmcam_isoc_irq(struct urb *urb)
+{
+ int len;
+ struct usb_ibmcam *ibmcam = urb->context;
+ struct ibmcam_sbuf *sbuf;
+ int i;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (ibmcam->remove_pending)
+ return;
+
+#if 0
+ if (urb->actual_length > 0) {
+ printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, "
+ " errcount = %d, length = %d\n", urb, urb->status,
+ urb->error_count, urb->actual_length);
+ } else {
+ static int c = 0;
+ if (c++ % 100 == 0)
+ printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n");
+ }
+#endif
+
+ if (!ibmcam->streaming) {
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n");
+ return;
+ }
+
+ sbuf = &ibmcam->sbuf[ibmcam->cursbuf];
+
+ /* Copy the data received into our scratch buffer */
+ len = ibmcam_compress_isochronous(ibmcam, urb);
+
+ ibmcam->urb_count++;
+ ibmcam->urb_length = len;
+ ibmcam->data_count += len;
+
+#if 0 /* This code prints few initial bytes of ISO data: used to decode markers */
+ if (ibmcam->urb_count % 64 == 1) {
+ if (ibmcam->urb_count == 1) {
+ ibmcam_hexdump(ibmcam->scratch,
+ (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen);
+ }
+ }
+#endif
+
+ /* If we collected enough data let's parse! */
+ if (ibmcam->scratchlen) {
+ /* If we don't have a frame we're current working on, complain */
+ if (ibmcam->curframe >= 0)
+ ibmcam_parse_data(ibmcam);
+ else {
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam: received data, but no frame available\n");
+ }
+ }
+
+ for (i = 0; i < FRAMES_PER_DESC; i++) {
+ sbuf->urb->iso_frame_desc[i].status = 0;
+ sbuf->urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ /* Move to the next sbuf */
+ ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF;
+
+ return;
+}
+
+static int usb_ibmcam_veio(
+ struct usb_device *dev,
+ unsigned char req,
+ unsigned short value,
+ unsigned short index)
+{
+ static const char proc[] = "usb_ibmcam_veio";
+ unsigned char cp[8] = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef };
+ const unsigned short len = sizeof(cp);
+ int i;
+
+ if (req == 1) {
+ i = usb_control_msg(
+ dev,
+ usb_rcvctrlpipe(dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value,
+ index,
+ cp,
+ len,
+ HZ);
+#if 0
+ printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+ "(req=$%02x val=$%04x ind=$%04x len=%d.)\n",
+ cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+ req, value, index, len);
+#endif
+ } else {
+ i = usb_control_msg(
+ dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value,
+ index,
+ NULL,
+ 0,
+ HZ);
+ }
+ if (i < 0)
+ printk(KERN_ERR "%s: ERROR=%d.\n", proc, i);
+ return i;
+}
+
+/*
+ * usb_ibmcam_calculate_fps()
+ *
+ * This procedure roughly calculates the real frame rate based
+ * on FPS code (framerate=NNN option). Actual FPS differs
+ * slightly depending on lighting conditions, so that actual frame
+ * rate is determined by the camera. Since I don't know how to ask
+ * the camera what FPS is now I have to use the FPS code instead.
+ *
+ * The FPS code is in range [0..6], 0 is slowest, 6 is fastest.
+ * Corresponding real FPS should be in range [3..30] frames per second.
+ * The conversion formula is obvious:
+ *
+ * real_fps = 3 + (fps_code * 4.5)
+ *
+ * History:
+ * 1/18/99 Created.
+ */
+static int usb_ibmcam_calculate_fps(void)
+{
+ return 3 + framerate*4 + framerate/2;
+}
+
+/*
+ * usb_ibmcam_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void usb_ibmcam_send_FF_04_02(struct usb_device *dev)
+{
+ usb_ibmcam_veio(dev, 0, 0x00FF, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0004, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0002, 0x0124);
+}
+
+static void usb_ibmcam_send_00_04_06(struct usb_device *dev)
+{
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0004, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0006, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_veio(dev, 0, x, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_send_x_00(dev, x);
+ usb_ibmcam_veio(dev, 0, 0x0005, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_veio(dev, 0, x, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0005, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0002, 0x0124);
+}
+
+static void usb_ibmcam_send_x_01_00_05(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_veio(dev, 0, x, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0001, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0005, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02_01(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_veio(dev, 0, x, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0005, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0002, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0001, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_device *dev, unsigned short x)
+{
+ usb_ibmcam_veio(dev, 0, x, 0x0127);
+ usb_ibmcam_veio(dev, 0, 0x0000, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0005, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0002, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0008, 0x0124);
+ usb_ibmcam_veio(dev, 0, 0x0001, 0x0124);
+}
+
+static void usb_ibmcam_Packet_Format1(struct usb_device *dev, unsigned char fkey, unsigned char val)
+{
+ usb_ibmcam_send_x_01_00_05 (dev, unknown_88);
+ usb_ibmcam_send_x_00_05 (dev, fkey);
+ usb_ibmcam_send_x_00_05_02_08_01(dev, val);
+ usb_ibmcam_send_x_00_05 (dev, unknown_88);
+ usb_ibmcam_send_x_00_05_02_01 (dev, fkey);
+ usb_ibmcam_send_x_00_05 (dev, unknown_89);
+ usb_ibmcam_send_x_00 (dev, fkey);
+ usb_ibmcam_send_00_04_06 (dev);
+ usb_ibmcam_veio (dev, 1, 0x0000, 0x0126);
+ usb_ibmcam_send_FF_04_02 (dev);
+}
+
+static void usb_ibmcam_PacketFormat2(struct usb_device *dev, unsigned char fkey, unsigned char val)
+{
+ usb_ibmcam_send_x_01_00_05 (dev, unknown_88);
+ usb_ibmcam_send_x_00_05 (dev, fkey);
+ usb_ibmcam_send_x_00_05_02 (dev, val);
+}
+
+/*
+ * usb_ibmcam_adjust_contrast()
+ *
+ * The contrast value changes from 0 (high contrast) to 15 (low contrast).
+ * This is in reverse to usual order of things (such as TV controls), so
+ * we reverse it again here.
+ *
+ * TODO: we probably don't need to send the setup 5 times...
+ *
+ * History:
+ * 1/2/99 Created.
+ */
+static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam)
+{
+ struct usb_device *dev = ibmcam->dev;
+ unsigned char new_contrast = ibmcam->vpic.contrast >> 12;
+ const int ntries = 5;
+
+ if (new_contrast >= 16)
+ new_contrast = 15;
+ new_contrast = 15 - new_contrast;
+ if (new_contrast != ibmcam->vpic_old.contrast) {
+ int i;
+ ibmcam->vpic_old.contrast = new_contrast;
+ for (i=0; i < ntries; i++) {
+ usb_ibmcam_Packet_Format1(dev, contrast_14, new_contrast);
+ usb_ibmcam_send_FF_04_02(dev);
+ }
+ /*usb_ibmcam_veio(dev, 0, 0x00FF, 0x0127);*/
+ }
+}
+
+/*
+ * usb_ibmcam_change_lighting_conditions()
+ *
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ * Low lighting forces slower FPS. Lighting is set as a module parameter.
+ *
+ * History:
+ * 1/5/99 Created.
+ */
+static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam)
+{
+ static const char proc[] = "usb_ibmcam_change_lighting_conditions";
+ struct usb_device *dev = ibmcam->dev;
+ const int ntries = 5;
+ int i;
+
+ RESTRICT_TO_RANGE(lighting, LIGHTING_MIN, LIGHTING_MAX);
+ if (debug > 0)
+ printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting);
+
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, light_27, (unsigned short) lighting);
+}
+
+static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam)
+{
+ static const char proc[] = "usb_ibmcam_set_sharpness";
+ struct usb_device *dev = ibmcam->dev;
+ static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+ unsigned short i, sv;
+
+ RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+ if (debug > 0)
+ printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness);
+
+ sv = sa[sharpness - SHARPNESS_MIN];
+ for (i=0; i < 2; i++) {
+ usb_ibmcam_send_x_01_00_05 (dev, unknown_88);
+ usb_ibmcam_send_x_00_05 (dev, sharp_13);
+ usb_ibmcam_send_x_00_05_02 (dev, sv);
+ }
+}
+
+static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam)
+{
+ static const char proc[] = "usb_ibmcam_set_brightness";
+ struct usb_device *dev = ibmcam->dev;
+ static const unsigned short n = 1;
+ unsigned short i, j, bv[3];
+
+ bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10;
+ if (bv[0] == (ibmcam->vpic_old.brightness >> 10))
+ return;
+ ibmcam->vpic_old.brightness = ibmcam->vpic.brightness;
+
+ if (debug > 0)
+ printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n",
+ proc, bv[0], bv[1], bv[2]);
+
+ for (j=0; j < 3; j++)
+ for (i=0; i < n; i++)
+ usb_ibmcam_Packet_Format1(dev, bright_3x[j], bv[j]);
+}
+
+static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam)
+{
+ usb_ibmcam_adjust_contrast(ibmcam);
+ usb_ibmcam_set_brightness(ibmcam);
+}
+
+static void usb_ibmcam_setup_before_if1(struct usb_device *dev)
+{
+ switch (videosize) {
+ case VIDEOSIZE_128x96:
+ case VIDEOSIZE_176x144:
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0108);
+ break;
+ case VIDEOSIZE_352x288:
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0100);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x81, 0x0100);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0108);
+ break;
+ }
+}
+
+static void usb_ibmcam_setup_before_if2(struct usb_device *dev)
+{
+ const int ntries = 5;
+ int i;
+
+ usb_ibmcam_veio(dev, 0, 0x03, 0x0112);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0115);
+ usb_ibmcam_veio(dev, 0, 0x06, 0x0115);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0116);
+ usb_ibmcam_veio(dev, 0, 0x44, 0x0116);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0116);
+ usb_ibmcam_veio(dev, 0, 0x40, 0x0116);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0115);
+ usb_ibmcam_veio(dev, 0, 0x0e, 0x0115);
+ usb_ibmcam_veio(dev, 0, 0x19, 0x012c);
+
+ usb_ibmcam_Packet_Format1(dev, 0x00, 0x1e);
+ usb_ibmcam_Packet_Format1(dev, 0x39, 0x0d);
+ usb_ibmcam_Packet_Format1(dev, 0x39, 0x09);
+ usb_ibmcam_Packet_Format1(dev, 0x3b, 0x00);
+ usb_ibmcam_Packet_Format1(dev, 0x28, 0x22);
+ usb_ibmcam_Packet_Format1(dev, light_27, 0);
+ usb_ibmcam_Packet_Format1(dev, 0x2b, 0x1f);
+ usb_ibmcam_Packet_Format1(dev, 0x39, 0x08);
+
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x2c, 0x00);
+
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x30, 0x14);
+
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x02);
+ usb_ibmcam_PacketFormat2(dev, 0x01, 0xe1);
+ usb_ibmcam_PacketFormat2(dev, 0x02, 0xcd);
+ usb_ibmcam_PacketFormat2(dev, 0x03, 0xcd);
+ usb_ibmcam_PacketFormat2(dev, 0x04, 0xfa);
+ usb_ibmcam_PacketFormat2(dev, 0x3f, 0xff);
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x00);
+
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x02);
+ usb_ibmcam_PacketFormat2(dev, 0x0a, 0x37);
+ usb_ibmcam_PacketFormat2(dev, 0x0b, 0xb8);
+ usb_ibmcam_PacketFormat2(dev, 0x0c, 0xf3);
+ usb_ibmcam_PacketFormat2(dev, 0x0d, 0xe3);
+ usb_ibmcam_PacketFormat2(dev, 0x0e, 0x0d);
+ usb_ibmcam_PacketFormat2(dev, 0x0f, 0xf2);
+ usb_ibmcam_PacketFormat2(dev, 0x10, 0xd5);
+ usb_ibmcam_PacketFormat2(dev, 0x11, 0xba);
+ usb_ibmcam_PacketFormat2(dev, 0x12, 0x53);
+ usb_ibmcam_PacketFormat2(dev, 0x3f, 0xff);
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x00);
+
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x02);
+ usb_ibmcam_PacketFormat2(dev, 0x16, 0x00);
+ usb_ibmcam_PacketFormat2(dev, 0x17, 0x28);
+ usb_ibmcam_PacketFormat2(dev, 0x18, 0x7d);
+ usb_ibmcam_PacketFormat2(dev, 0x19, 0xbe);
+ usb_ibmcam_PacketFormat2(dev, 0x3f, 0xff);
+ usb_ibmcam_PacketFormat2(dev, 0x39, 0x00);
+
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x00, 0x18);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x13, 0x18);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x14, 0x06);
+
+ /* This is default brightness */
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x31, 0x37);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x32, 0x46);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x33, 0x55);
+
+ usb_ibmcam_Packet_Format1(dev, 0x2e, 0x04);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x2d, 0x04);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x29, 0x80);
+ usb_ibmcam_Packet_Format1(dev, 0x2c, 0x01);
+ usb_ibmcam_Packet_Format1(dev, 0x30, 0x17);
+ usb_ibmcam_Packet_Format1(dev, 0x39, 0x08);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x34, 0x00);
+
+ usb_ibmcam_veio(dev, 0, 0x00, 0x0101);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x010a);
+
+ switch (videosize) {
+ case VIDEOSIZE_128x96:
+ usb_ibmcam_veio(dev, 0, 0x80, 0x0103);
+ usb_ibmcam_veio(dev, 0, 0x60, 0x0105);
+ usb_ibmcam_veio(dev, 0, 0x0c, 0x010b);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011b); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x0b, 0x011d);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x011e); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x00, 0x0129);
+ break;
+ case VIDEOSIZE_176x144:
+ usb_ibmcam_veio(dev, 0, 0xb0, 0x0103);
+ usb_ibmcam_veio(dev, 0, 0x8f, 0x0105);
+ usb_ibmcam_veio(dev, 0, 0x06, 0x010b);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011b); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x0d, 0x011d);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x011e); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x03, 0x0129);
+ break;
+ case VIDEOSIZE_352x288:
+ usb_ibmcam_veio(dev, 0, 0xb0, 0x0103);
+ usb_ibmcam_veio(dev, 0, 0x90, 0x0105);
+ usb_ibmcam_veio(dev, 0, 0x02, 0x010b);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011b); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x05, 0x011d);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x011e); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x00, 0x0129);
+ break;
+ }
+
+ usb_ibmcam_veio(dev, 0, 0xff, 0x012b);
+
+ /* This is another brightness - don't know why */
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x31, 0xc3);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x32, 0xd2);
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, 0x33, 0xe1);
+
+ /* Default contrast */
+ for (i=0; i < ntries; i++)
+ usb_ibmcam_Packet_Format1(dev, contrast_14, 0x0a);
+
+ /* Default sharpness */
+ for (i=0; i < 2; i++)
+ usb_ibmcam_PacketFormat2(dev, sharp_13, 0x1a); /* Level 4 FIXME */
+
+ /* Default lighting conditions */
+ usb_ibmcam_Packet_Format1(dev, light_27, lighting); /* 0=Bright 2=Low */
+
+ /* Assorted init */
+
+ switch (videosize) {
+ case VIDEOSIZE_128x96:
+ usb_ibmcam_Packet_Format1(dev, 0x2b, 0x1e);
+ usb_ibmcam_veio(dev, 0, 0xc9, 0x0119); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x80, 0x0109); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x36, 0x0102);
+ usb_ibmcam_veio(dev, 0, 0x1a, 0x0104);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011a); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x2b, 0x011c);
+ usb_ibmcam_veio(dev, 0, 0x23, 0x012a); /* Same everywhere */
+#if 0
+ usb_ibmcam_veio(dev, 0, 0x00, 0x0106);
+ usb_ibmcam_veio(dev, 0, 0x38, 0x0107);
+#else
+ usb_ibmcam_veio(dev, 0, 0x02, 0x0106);
+ usb_ibmcam_veio(dev, 0, 0x2a, 0x0107);
+#endif
+ break;
+ case VIDEOSIZE_176x144:
+ usb_ibmcam_Packet_Format1(dev, 0x2b, 0x1e);
+ usb_ibmcam_veio(dev, 0, 0xc9, 0x0119); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x80, 0x0109); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x04, 0x0102);
+ usb_ibmcam_veio(dev, 0, 0x02, 0x0104);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011a); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x2b, 0x011c);
+ usb_ibmcam_veio(dev, 0, 0x23, 0x012a); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0106);
+ usb_ibmcam_veio(dev, 0, 0xca, 0x0107);
+ break;
+ case VIDEOSIZE_352x288:
+ usb_ibmcam_Packet_Format1(dev, 0x2b, 0x1f);
+ usb_ibmcam_veio(dev, 0, 0xc9, 0x0119); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x80, 0x0109); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x08, 0x0102);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0104);
+ usb_ibmcam_veio(dev, 0, 0x04, 0x011a); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x2f, 0x011c);
+ usb_ibmcam_veio(dev, 0, 0x23, 0x012a); /* Same everywhere */
+ usb_ibmcam_veio(dev, 0, 0x03, 0x0106);
+ usb_ibmcam_veio(dev, 0, 0xf6, 0x0107);
+ break;
+ }
+}
+
+static void usb_ibmcam_setup_after_video_if(struct usb_device *dev)
+{
+ unsigned short internal_frame_rate;
+
+ RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
+ internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */
+ usb_ibmcam_veio(dev, 0, internal_frame_rate, 0x0111);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0114);
+ usb_ibmcam_veio(dev, 0, 0xc0, 0x010c);
+}
+
+static void usb_ibmcam_setup_video_stop(struct usb_device *dev)
+{
+ usb_ibmcam_veio(dev, 0, 0x00, 0x010c);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x010c);
+ usb_ibmcam_veio(dev, 0, 0x01, 0x0114);
+ usb_ibmcam_veio(dev, 0, 0xc0, 0x010c);
+ usb_ibmcam_veio(dev, 0, 0x00, 0x010c);
+ usb_ibmcam_send_FF_04_02(dev);
+ usb_ibmcam_veio(dev, 1, 0x00, 0x0100);
+ usb_ibmcam_veio(dev, 0, 0x81, 0x0100);
+}
+
+/*
+ * usb_ibmcam_reinit_iso()
+ *
+ * This procedure sends couple of commands to the camera and then
+ * resets the video pipe. This sequence was observed to reinit the
+ * camera or, at least, to initiate ISO data stream.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop)
+{
+ if (do_stop)
+ usb_ibmcam_setup_video_stop(ibmcam->dev);
+
+ usb_ibmcam_veio(ibmcam->dev, 0, 0x0001, 0x0114);
+ usb_ibmcam_veio(ibmcam->dev, 0, 0x00c0, 0x010c);
+ usb_clear_halt(ibmcam->dev, ibmcam->video_endp);
+ usb_ibmcam_setup_after_video_if(ibmcam->dev);
+}
+
+static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam)
+{
+ struct usb_device *dev = ibmcam->dev;
+ urb_t *urb;
+ int fx, err;
+
+ ibmcam->compress = 0;
+ ibmcam->curframe = -1;
+ ibmcam->cursbuf = 0;
+ ibmcam->scratchlen = 0;
+
+ /* Alternate interface 1 is is the biggest frame size */
+ if (usb_set_interface(ibmcam->dev, 2, 1) < 0) {
+ printk(KERN_ERR "usb_set_interface error\n");
+ return -EBUSY;
+ }
+ usb_ibmcam_change_lighting_conditions(ibmcam);
+ usb_ibmcam_set_sharpness(ibmcam);
+ usb_ibmcam_reinit_iso(ibmcam, 0);
+
+ /* We double buffer the Iso lists */
+ urb = usb_alloc_urb(FRAMES_PER_DESC);
+
+ if (!urb) {
+ printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc ret %d\n",
+ 0);
+ return -ENOMEM;
+ }
+ ibmcam->sbuf[0].urb = urb;
+ urb->dev = dev;
+ urb->context = ibmcam;
+ urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = ibmcam->sbuf[0].data;
+ urb->complete = ibmcam_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ibmcam->iso_packet_len * fx;
+ urb->iso_frame_desc[fx].length = ibmcam->iso_packet_len;
+ }
+ urb = usb_alloc_urb(FRAMES_PER_DESC);
+ if (!urb) {
+ printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc ret %d\n",
+ 0);
+ return -ENOMEM;
+ }
+ ibmcam->sbuf[1].urb = urb;
+ urb->dev = dev;
+ urb->context = ibmcam;
+ urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = ibmcam->sbuf[1].data;
+ urb->complete = ibmcam_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ibmcam->iso_packet_len * fx;
+ urb->iso_frame_desc[fx].length = ibmcam->iso_packet_len;
+ }
+
+ ibmcam->sbuf[1].urb->next = ibmcam->sbuf[0].urb;
+ ibmcam->sbuf[0].urb->next = ibmcam->sbuf[1].urb;
+
+ err = usb_submit_urb(ibmcam->sbuf[0].urb);
+ if (err)
+ printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(0) ret %d\n",
+ err);
+ err = usb_submit_urb(ibmcam->sbuf[1].urb);
+ if (err)
+ printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(1) ret %d\n",
+ err);
+
+ ibmcam->streaming = 1;
+ // printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp);
+ return 0;
+}
+
+static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam)
+{
+ if (!ibmcam->streaming)
+ return;
+
+ usb_ibmcam_setup_video_stop(ibmcam->dev);
+
+ /* Set packet size to 0 */
+ if (usb_set_interface(ibmcam->dev, 2, 0) < 0) {
+ printk(KERN_ERR "usb_set_interface error\n");
+ return /* -EINVAL */;
+ }
+
+ /* Unschedule all of the iso td's */
+ usb_unlink_urb(ibmcam->sbuf[1].urb);
+ usb_unlink_urb(ibmcam->sbuf[0].urb);
+
+ // printk(KERN_DEBUG "streaming=0\n");
+ ibmcam->streaming = 0;
+
+ /* Delete them all */
+ usb_free_urb(ibmcam->sbuf[1].urb);
+ usb_free_urb(ibmcam->sbuf[0].urb);
+}
+
+static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum)
+{
+ struct ibmcam_frame *frame;
+ int n, width, height;
+
+ /* If we're not grabbing a frame right now and the other frame is */
+ /* ready to be grabbed into, then use it instead */
+ if (ibmcam->curframe != -1)
+ return 0;
+
+ n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES;
+ if (ibmcam->frame[n].grabstate == FRAME_READY)
+ framenum = n;
+
+ frame = &ibmcam->frame[framenum];
+
+ frame->grabstate = FRAME_GRABBING;
+ frame->scanstate = STATE_SCANNING;
+ frame->scanlength = 0; /* Accumulated in ibmcam_parse_data() */
+ ibmcam->curframe = framenum;
+#if 0
+ /* This provides a "clean" frame but slows things down */
+ memset(frame->data, 0, MAX_FRAME_SIZE);
+#endif
+ switch (videosize) {
+ case VIDEOSIZE_128x96:
+ frame->frmwidth = 128;
+ frame->frmheight = 96;
+ frame->order_uv = 1; /* U Y V Y ... */
+ frame->hdr_sig = 0x06; /* 00 FF 00 06 */
+ break;
+ case VIDEOSIZE_176x144:
+ frame->frmwidth = 176;
+ frame->frmheight = 144;
+ frame->order_uv = 1; /* U Y V Y ... */
+ frame->hdr_sig = 0x0E; /* 00 FF 00 0E */
+ break;
+ case VIDEOSIZE_352x288:
+ frame->frmwidth = 352;
+ frame->frmheight = 288;
+ frame->order_uv = 0; /* V Y U Y ... */
+ frame->hdr_sig = 0x00; /* 00 FF 00 00 */
+ break;
+ }
+
+ width = frame->width;
+ RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth);
+ width &= ~7; /* Multiple of 8 */
+
+ height = frame->height;
+ RESTRICT_TO_RANGE(height, min_imgheight, imgheight);
+ height &= ~3; /* Multiple of 4 */
+
+ return 0;
+}
+
+/* Video 4 Linux API */
+static int ibmcam_open(struct video_device *dev, int flags)
+{
+ int err = -EBUSY;
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+
+ if (ibmcam->remove_pending)
+ return -EFAULT;
+
+ down(&ibmcam->lock);
+ if (ibmcam->user)
+ goto out_unlock;
+
+ ibmcam->frame[0].grabstate = FRAME_UNUSED;
+ ibmcam->frame[1].grabstate = FRAME_UNUSED;
+
+ err = -ENOMEM;
+
+ /* Allocate memory for the frame buffers */
+ ibmcam->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
+ if (!ibmcam->fbuf)
+ goto open_err_ret;
+
+ ibmcam->frame[0].data = ibmcam->fbuf;
+ ibmcam->frame[1].data = ibmcam->fbuf + MAX_FRAME_SIZE;
+
+ ibmcam->sbuf[0].data = kmalloc (FRAMES_PER_DESC * ibmcam->iso_packet_len, GFP_KERNEL);
+ if (!ibmcam->sbuf[0].data)
+ goto open_err_on0;
+
+ ibmcam->sbuf[1].data = kmalloc (FRAMES_PER_DESC * ibmcam->iso_packet_len, GFP_KERNEL);
+ if (!ibmcam->sbuf[1].data)
+ goto open_err_on1;
+
+ /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+ * (using read() instead). */
+ ibmcam->frame[0].width = imgwidth;
+ ibmcam->frame[0].height = imgheight;
+ ibmcam->frame[0].bytes_read = 0;
+ ibmcam->frame[1].width = imgwidth;
+ ibmcam->frame[1].height = imgheight;
+ ibmcam->frame[1].bytes_read = 0;
+
+ err = ibmcam_init_isoc(ibmcam);
+ if (err)
+ goto open_err_on2;
+
+ ibmcam->user++;
+ up(&ibmcam->lock);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+
+open_err_on2:
+ kfree (ibmcam->sbuf[1].data);
+open_err_on1:
+ kfree (ibmcam->sbuf[0].data);
+open_err_on0:
+ rvfree(ibmcam->fbuf, 2 * MAX_FRAME_SIZE);
+open_err_ret:
+ return err;
+
+out_unlock:
+ up(&ibmcam->lock);
+ return err;
+}
+
+static void ibmcam_close(struct video_device *dev)
+{
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+
+ down(&ibmcam->lock);
+ ibmcam->user--;
+
+ MOD_DEC_USE_COUNT;
+
+ ibmcam_stop_isoc(ibmcam);
+
+ rvfree(ibmcam->fbuf, 2 * MAX_FRAME_SIZE);
+
+ kfree(ibmcam->sbuf[1].data);
+ kfree(ibmcam->sbuf[0].data);
+
+ up(&ibmcam->lock);
+}
+
+static int ibmcam_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+ return -EINVAL;
+}
+
+static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+
+ if (ibmcam->remove_pending)
+ return -EFAULT;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */
+ return -EINVAL;
+ if (v != ibmcam->vchan.channel) {
+ ibmcam->vchan.channel = v;
+ usb_ibmcam_change_lighting_conditions(ibmcam);
+ }
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ if (copy_from_user(&ibmcam->vpic, arg, sizeof(ibmcam->vpic)))
+ return -EFAULT;
+ usb_ibmcam_adjust_picture(ibmcam);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+
+ if (copy_from_user(&vw, arg, sizeof(vw)))
+ return -EFAULT;
+ if (vw.flags)
+ return -EINVAL;
+ if (vw.clipcount)
+ return -EINVAL;
+ if (vw.height != imgheight)
+ return -EINVAL;
+ if (vw.width != imgwidth)
+ return -EINVAL;
+
+ ibmcam->compress = 0;
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+
+ vw.x = 0;
+ vw.y = 0;
+ vw.width = imgwidth;
+ vw.height = imgheight;
+ vw.chromakey = 0;
+ vw.flags = usb_ibmcam_calculate_fps();
+
+ if (copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+
+ memset(&vm, 0, sizeof(vm));
+ vm.size = MAX_FRAME_SIZE * 2;
+ vm.frames = 2;
+ vm.offsets[0] = 0;
+ vm.offsets[1] = MAX_FRAME_SIZE;
+
+ if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+ return -EFAULT;
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n",
+ vm.frame, vm.width, vm.height, vm.format);
+
+ if (vm.format != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+
+ if ((vm.frame != 0) && (vm.frame != 1))
+ return -EINVAL;
+
+ if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING)
+ return -EBUSY;
+
+ /* Don't compress if the size changed */
+ if ((ibmcam->frame[vm.frame].width != vm.width) ||
+ (ibmcam->frame[vm.frame].height != vm.height))
+ ibmcam->compress = 0;
+
+ ibmcam->frame[vm.frame].width = vm.width;
+ ibmcam->frame[vm.frame].height = vm.height;
+
+ /* Mark it as ready */
+ ibmcam->frame[vm.frame].grabstate = FRAME_READY;
+
+ return ibmcam_new_frame(ibmcam, vm.frame);
+ }
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int)))
+ return -EFAULT;
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame);
+
+ switch (ibmcam->frame[frame].grabstate) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ case FRAME_ERROR:
+ {
+ int ntries;
+ redo:
+ ntries = 0;
+ do {
+ interruptible_sleep_on(&ibmcam->frame[frame].wq);
+ if (signal_pending(current)) {
+ if (flags & FLAGS_RETRY_VIDIOCSYNC) {
+ /* Polling apps will destroy frames with that! */
+ ibmcam_new_frame(ibmcam, frame);
+ usb_ibmcam_testpattern(ibmcam, 1, 0);
+ ibmcam->curframe = -1;
+ ibmcam->frame_num++;
+
+ /* This will request another frame. */
+ if (waitqueue_active(&ibmcam->frame[frame].wq))
+ wake_up_interruptible(&ibmcam->frame[frame].wq);
+ return 0;
+ } else {
+ /* Standard answer: not ready yet! */
+ return -EINTR;
+ }
+ }
+ } while (ibmcam->frame[frame].grabstate == FRAME_GRABBING);
+
+ if (ibmcam->frame[frame].grabstate == FRAME_ERROR) {
+ int ret = ibmcam_new_frame(ibmcam, frame);
+ if (ret < 0)
+ return ret;
+ goto redo;
+ }
+ }
+ case FRAME_DONE:
+ ibmcam->frame[frame].grabstate = FRAME_UNUSED;
+ break;
+ }
+
+ ibmcam->frame[frame].grabstate = FRAME_UNUSED;
+
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer vb;
+
+ memset(&vb, 0, sizeof(vb));
+ vb.base = NULL; /* frame buffer not supported, not used */
+
+ if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+
+ case VIDIOCSFBUF:
+
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+ int frmx = -1;
+ volatile struct ibmcam_frame *frame;
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock);
+
+ if (ibmcam->remove_pending)
+ return -EFAULT;
+
+ if (!dev || !buf)
+ return -EFAULT;
+
+ /* See if a frame is completed, then use it. */
+ if (ibmcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */
+ frmx = 0;
+ else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
+ frmx = 1;
+
+ if (noblock && (frmx == -1))
+ return -EAGAIN;
+
+ /* If no FRAME_DONE, look for a FRAME_GRABBING state. */
+ /* See if a frame is in process (grabbing), then use it. */
+ if (frmx == -1) {
+ if (ibmcam->frame[0].grabstate == FRAME_GRABBING)
+ frmx = 0;
+ else if (ibmcam->frame[1].grabstate == FRAME_GRABBING)
+ frmx = 1;
+ }
+
+ /* If no frame is active, start one. */
+ if (frmx == -1)
+ ibmcam_new_frame(ibmcam, frmx = 0);
+
+ frame = &ibmcam->frame[frmx];
+
+restart:
+ while (frame->grabstate == FRAME_GRABBING) {
+ interruptible_sleep_on((void *)&frame->wq);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+
+ if (frame->grabstate == FRAME_ERROR) {
+ frame->bytes_read = 0;
+ if (ibmcam_new_frame(ibmcam, frmx))
+ printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n");
+ goto restart;
+ }
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
+ frmx, frame->bytes_read, frame->scanlength);
+
+ /* copy bytes to user space; we allow for partials reads */
+ if ((count + frame->bytes_read) > frame->scanlength)
+ count = frame->scanlength - frame->bytes_read;
+
+ if (copy_to_user(buf, frame->data + frame->bytes_read, count))
+ return -EFAULT;
+
+ frame->bytes_read += count;
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n",
+ count, frame->bytes_read);
+
+ if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+ frame->bytes_read = 0;
+
+ /* Mark it as available to be used again. */
+ ibmcam->frame[frmx].grabstate = FRAME_UNUSED;
+ if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1))
+ printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n");
+ }
+
+ return count;
+}
+
+static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+ unsigned long start = (unsigned long)adr;
+ unsigned long page, pos;
+
+ if (ibmcam->remove_pending)
+ return -EFAULT;
+
+ if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ pos = (unsigned long)ibmcam->fbuf;
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+ if (remap_page_range(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;
+}
+
+static struct video_device ibmcam_template = {
+ "CPiA USB Camera",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_CPIA,
+ ibmcam_open,
+ ibmcam_close,
+ ibmcam_read,
+ ibmcam_write,
+ NULL,
+ ibmcam_ioctl,
+ ibmcam_mmap,
+ ibmcam_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam)
+{
+ if (ibmcam == NULL)
+ return;
+
+ RESTRICT_TO_RANGE(init_brightness, 0, 255);
+ RESTRICT_TO_RANGE(init_contrast, 0, 255);
+ RESTRICT_TO_RANGE(init_color, 0, 255);
+ RESTRICT_TO_RANGE(init_hue, 0, 255);
+
+ memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic));
+ memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old));
+
+ ibmcam->vpic.colour = init_color << 8;
+ ibmcam->vpic.hue = init_hue << 8;
+ ibmcam->vpic.brightness = init_brightness << 8;
+ ibmcam->vpic.contrast = init_contrast << 8;
+ ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */
+ ibmcam->vpic.depth = 24;
+ ibmcam->vpic.palette = VIDEO_PALETTE_RGB24;
+
+ memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap));
+ strcpy(ibmcam->vcap.name, "IBM USB Camera");
+ ibmcam->vcap.type = VID_TYPE_CAPTURE /*| VID_TYPE_SUBCAPTURE*/;
+ ibmcam->vcap.channels = 1;
+ ibmcam->vcap.audios = 0;
+ ibmcam->vcap.maxwidth = imgwidth;
+ ibmcam->vcap.maxheight = imgheight;
+ ibmcam->vcap.minwidth = min_imgwidth;
+ ibmcam->vcap.minheight = min_imgheight;
+
+ memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan));
+ ibmcam->vchan.flags = 0;
+ ibmcam->vchan.tuners = 0;
+ ibmcam->vchan.channel = 0;
+ ibmcam->vchan.type = VIDEO_TYPE_CAMERA;
+ strcpy(ibmcam->vchan.name, "Camera");
+}
+
+static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ struct usb_ibmcam *ibmcam = NULL;
+ struct usb_interface_descriptor *interface;
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum);
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return NULL;
+
+ /* Is it an IBM camera? */
+ if ((dev->descriptor.idVendor != 0x0545) ||
+ (dev->descriptor.idProduct != 0x8080))
+ return NULL;
+
+ /* Camera confirmed. We claim only interface 2 (video data) */
+ if (ifnum != 2)
+ return NULL;
+
+ /* We found an IBM camera */
+ printk(KERN_INFO "IBM USB camera found (interface %u.)\n", ifnum);
+
+ if (debug >= 1)
+ printk(KERN_DEBUG "ibmcam_probe: new ibmcam alloc\n");
+ ibmcam = kmalloc(sizeof(*ibmcam), GFP_KERNEL);
+ if (ibmcam == NULL) {
+ printk(KERN_ERR "couldn't kmalloc ibmcam struct\n");
+ return NULL;
+ }
+ memset(ibmcam, 0, sizeof(struct usb_ibmcam));
+ ibmcam->dev = dev;
+ interface = &dev->actconfig->interface[ifnum].altsetting[0];
+ ibmcam->iface = interface->bInterfaceNumber;
+ ibmcam->video_endp = 0x82;
+ ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL);
+ if (ibmcam->scratch == NULL) {
+ printk(KERN_ERR "couldn't kmalloc ibmcam->scratch\n");
+ kfree(ibmcam);
+ return NULL;
+ }
+ init_waitqueue_head (&ibmcam->remove_ok);
+ ibmcam->iso_packet_len = 1014;
+
+ /* Camera setup */
+ usb_ibmcam_veio(dev, 1, 0, 0x128);
+ usb_ibmcam_setup_before_if1(dev);
+ usb_ibmcam_setup_before_if2(dev);
+
+ memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template));
+ usb_ibmcam_configure_video(ibmcam);
+
+ init_waitqueue_head(&ibmcam->frame[0].wq);
+ init_waitqueue_head(&ibmcam->frame[1].wq);
+
+ if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) {
+ printk(KERN_ERR "video_register_device failed\n");
+ return NULL;
+ }
+ if (debug > 1)
+ printk(KERN_DEBUG "video_register_device() successful\n");
+
+ ibmcam->compress = 0;
+ ibmcam->user=0;
+ init_MUTEX(&ibmcam->lock); /* to 1 == available */
+
+ return ibmcam;
+}
+
+static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr)
+{
+ static const char proc[] = "usb_ibmcam_disconnect";
+ struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr;
+
+ if (debug > 0)
+ printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr);
+
+ ibmcam->remove_pending = 1;
+ /* Now all ISO data will be ignored */
+
+ /* At this time we ask to cancel outstanding URBs */
+ ibmcam_stop_isoc(ibmcam);
+
+ /* sleep_on(&s->remove_ok); */
+ /* Now it should be safe to remove */
+
+ video_unregister_device(&ibmcam->vdev);
+
+ /* Free the memory */
+ if (debug > 0)
+ printk(KERN_DEBUG "%s: freeing ibmcam=%p\n", proc, ibmcam);
+ if (ibmcam->scratch != NULL)
+ kfree(ibmcam->scratch);
+ kfree(ibmcam);
+}
+
+static struct usb_driver ibmcam_driver = {
+ "ibmcam",
+ usb_ibmcam_probe,
+ usb_ibmcam_disconnect,
+ { NULL, NULL }
+};
+
+int usb_ibmcam_init(void)
+{
+ return usb_register(&ibmcam_driver);
+}
+
+void usb_ibmcam_cleanup(void)
+{
+ usb_deregister(&ibmcam_driver);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_ibmcam_init();
+}
+
+void cleanup_module(void)
+{
+ usb_ibmcam_cleanup();
+}
+#endif
--- /dev/null
+/*
+ * Header file for USB IBM C-It Video Camera driver.
+ *
+ * Supports IBM C-It Video Camera.
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ */
+
+#ifndef __LINUX_IBMCAM_H
+#define __LINUX_IBMCAM_H
+
+#include <linux/list.h>
+
+#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */
+
+/* Video Size 384 x 288 x 3 bytes for RGB */
+/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */
+#define V4L_FRAME_WIDTH 384
+#define V4L_FRAME_WIDTH_USED 352
+#define V4L_FRAME_HEIGHT 288
+#define V4L_BYTES_PER_PIXEL 3
+#define MAX_FRAME_SIZE (V4L_FRAME_WIDTH * V4L_FRAME_HEIGHT * V4L_BYTES_PER_PIXEL)
+
+/* Camera capabilities (maximum) */
+#define CAMERA_IMAGE_WIDTH 352
+#define CAMERA_IMAGE_HEIGHT 288
+#define CAMERA_IMAGE_LINE_SZ ((CAMERA_IMAGE_WIDTH * 3) / 2) /* Bytes */
+#define CAMERA_URB_FRAMES 32
+#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */
+
+#define IBMCAM_NUMFRAMES 2
+#define IBMCAM_NUMSBUF 2
+
+#define FRAMES_PER_DESC (CAMERA_URB_FRAMES)
+#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET)
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+/*
+ * This macro performs bounds checking - use it when working with
+ * new formats, or else you may get oopses all over the place.
+ * If pixel falls out of bounds then it gets shoved back (as close
+ * to place of offence as possible) and is painted bright red.
+ */
+#define IBMCAM_PUTPIXEL(fr, ix, iy, vr, vg, vb) { \
+ register unsigned char *pf; \
+ int limiter = 0, mx, my; \
+ mx = ix; \
+ my = iy; \
+ if (mx < 0) { \
+ mx=0; \
+ limiter++; \
+ } else if (mx >= 352) { \
+ mx=351; \
+ limiter++; \
+ } \
+ if (my < 0) { \
+ my = 0; \
+ limiter++; \
+ } else if (my >= V4L_FRAME_HEIGHT) { \
+ my = V4L_FRAME_HEIGHT - 1; \
+ limiter++; \
+ } \
+ pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*352 + (ix)); \
+ if (limiter) { \
+ *pf++ = 0; \
+ *pf++ = 0; \
+ *pf++ = 0xFF; \
+ } else { \
+ *pf++ = (vb); \
+ *pf++ = (vg); \
+ *pf++ = (vr); \
+ } \
+}
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16) + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16) + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
+ int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+ mm_y = (my) - 16; \
+ mm_u = (mu) - 128; \
+ mm_v = (mv) - 128; \
+ mm_yc= mm_y * 76284; \
+ mm_b = (mm_yc + 132252*mm_v ) >> 16; \
+ mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \
+ mm_r = (mm_yc + 104595*mm_u ) >> 16; \
+ mb = LIMIT_RGB(mm_b); \
+ mg = LIMIT_RGB(mm_g); \
+ mr = LIMIT_RGB(mm_r); \
+}
+
+enum {
+ STATE_SCANNING, /* Scanning for header */
+ STATE_LINES, /* Parsing lines */
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+struct usb_device;
+
+struct ibmcam_sbuf {
+ char *data;
+ urb_t *urb;
+};
+
+struct ibmcam_frame {
+ char *data; /* Frame buffer */
+ int order_uv; /* True=UV False=VU */
+ unsigned char hdr_sig; /* "00 FF 00 ??" where 'hdr_sig' is '??' */
+
+ int width; /* Width application is expecting */
+ int height; /* Height */
+
+ int frmwidth; /* Width the frame actually is */
+ int frmheight; /* Height */
+
+ volatile int grabstate; /* State of grabbing */
+ int scanstate; /* State of scanning */
+
+ int curline; /* Line of frame we're working on */
+
+ long scanlength; /* uncompressed, raw data length of frame */
+ long bytes_read; /* amount of scanlength that has been read from *data */
+
+ wait_queue_head_t wq; /* Processes waiting */
+};
+
+struct usb_ibmcam {
+ struct video_device vdev;
+
+ /* Device structure */
+ struct usb_device *dev;
+
+ unsigned char iface;
+
+ struct semaphore lock;
+ int user; /* user count for exclusive use */
+
+ int streaming; /* Are we streaming Isochronous? */
+ int grabbing; /* Are we grabbing? */
+
+ int compress; /* Should the next frame be compressed? */
+
+ char *fbuf; /* Videodev buffer area */
+
+ int curframe;
+ struct ibmcam_frame frame[IBMCAM_NUMFRAMES]; /* Double buffering */
+
+ int cursbuf; /* Current receiving sbuf */
+ struct ibmcam_sbuf sbuf[IBMCAM_NUMSBUF]; /* Double buffering */
+ volatile int remove_pending; /* If set then about to exit */
+ wait_queue_head_t remove_ok; /* Wait here until removal is safe */
+
+ /*
+ * Scratch space from the Isochronous pipe.
+ * Scratch buffer should contain at least one pair of lines
+ * (CAMERA_IMAGE_LINE_SZ). We set it to two pairs here.
+ * This will be approximately 2 KB. HOWEVER in reality this
+ * buffer must be as large as hundred of KB because otherwise
+ * you'll get lots of overflows because V4L client may request
+ * frames not as uniformly as USB sources them.
+ */
+ unsigned char *scratch;
+ int scratchlen;
+
+ struct video_picture vpic, vpic_old; /* Picture settings */
+ struct video_capability vcap; /* Video capabilities */
+ struct video_channel vchan; /* May be used for tuner support */
+ unsigned char video_endp; /* 0x82 for IBM camera */
+ int has_hdr;
+ int frame_num;
+ int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */
+
+ /* Statistics that can be overlayed on screen */
+ unsigned long urb_count; /* How many URBs we received so far */
+ unsigned long urb_length; /* Length of last URB */
+ unsigned long data_count; /* How many bytes we received */
+ unsigned long header_count; /* How many frame headers we found */
+ unsigned long scratch_ovf_count;/* How many times we overflowed scratch */
+ unsigned long iso_skip_count; /* How many empty ISO packets received */
+ unsigned long iso_err_count; /* How many bad ISO packets received */
+};
+
+#endif /* __LINUX_IBMCAM_H */
{ 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128, 1,
2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
- 11, 45, 46, 43, 47, 44,123, 67, 55, 49, 57,122,120, 99,118, 96,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
- 84, 85, 82, 65, 42, 0, 10,103,111, 0, 0, 0, 0, 0, 0, 0,
+ 84, 85, 82, 65, 42,105, 10,103,111, 0, 0, 0, 0, 0, 0, 0,
76,125, 75, 0,124, 0,115, 62,116, 59, 60,119, 61,121,114,117,
- 0, 0, 0, 0,127, 81, 0,113 };
+ 0, 0, 0, 0,127, 24, 0,113, 0, 0, 0, 0, 0, 55, 55, 0 };
#endif
struct fasync_struct *fasync;
struct mousedev *mousedev;
struct mousedev_list *next;
- int dx, dy, dz;
+ int dx, dy, dz, oldx, oldy;
unsigned char ps2[6];
unsigned long buttons;
- unsigned char ready, buffer, bufsiz;
+ unsigned char ready, inrange, buffer, bufsiz;
unsigned char mode, genseq, impseq;
};
while (list) {
switch (type) {
+ case EV_ABS:
+ if (test_bit(EV_REL, handle->dev->evbit) && test_bit(REL_X, handle->dev->relbit))
+ return;
+ switch (code) {
+ case ABS_X:
+ if (list->inrange) list->dx += (value - list->oldx) * 2000 /
+ (handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]);
+ list->oldx = value;
+ break;
+ case ABS_Y:
+ if (list->inrange) list->dy += (value - list->oldy) * 2000 /
+ (handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]);
+ list->oldy = value;
+ break;
+ }
+ break;
+
case EV_REL:
switch (code) {
case REL_X: list->dx += value; break;
case EV_KEY:
switch (code) {
+ case BTN_TOUCH:
case BTN_LEFT: index = 0; break;
case BTN_EXTRA: if (list->mode > 1) { index = 4; break; }
+ case BTN_STYLUS:
case BTN_RIGHT: index = 1; break;
case BTN_SIDE: if (list->mode > 1) { index = 3; break; }
+ case BTN_STYLUS2:
case BTN_MIDDLE: index = 2; break;
- default: index = 0;
+ default:
+ if (code >= BTN_TOOL_PEN && code <= BTN_TOOL_MOUSE) {
+ list->inrange = value;
+ return;
+ }
+ index = 0;
}
switch (value) {
case 0: clear_bit(index, &list->buttons); break;
static int mousedev_connect(struct input_handler *handler, struct input_dev *dev)
{
- if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_REL, dev->evbit))) /* The device must have both rels and keys */
+ if (!test_bit(EV_KEY, dev->evbit) ||
+ (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit)))
return -1;
- if (!(test_bit(REL_X, dev->relbit) && test_bit(REL_Y, dev->relbit))) /* It must be a pointer device */
+ if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) &&
+ (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit)))
return -1;
- if (!test_bit(BTN_LEFT, dev->keybit)) /* And have at least one mousebutton */
- return -1;
-
#ifdef CONFIG_INPUT_MOUSEDEV_MIX
{
struct input_handle *handle;
+++ /dev/null
-/*
- * URB OHCI HCD (Host Controller Driver) for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- *
- * [ Initialisation is based on Linus' ]
- * [ uhci code and gregs ohci fragments ]
- * [ (C) Copyright 1999 Linus Torvalds ]
- * [ (C) Copyright 1999 Gregory P. Smith]
- *
- *
- * History:
- *
- * 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
- *
-
- */
-
-#include <linux/config.h>
-#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/malloc.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h> /* for in_interrupt() */
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#undef DEBUG
-#define OHCI_USE_NPS
-
-#include "usb.h"
-#include "ohci-hcd.h"
-
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-static int handle_apm_event (apm_event_t event);
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#endif
-
-static DECLARE_WAIT_QUEUE_HEAD (op_wakeup);
-static LIST_HEAD (ohci_hcd_list);
-spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
-
-/*-------------------------------------------------------------------------*
- * URB support functions
- *-------------------------------------------------------------------------*/
-
-/* free the private part of an URB */
-
-static void urb_rm_priv (urb_t * urb)
-{
- urb_priv_t * urb_priv = urb->hcpriv;
- int i;
- void * wait;
-
- if (!urb_priv) return;
-
- wait = urb_priv->wait;
-
- for (i = 0; i < urb_priv->length; i++) {
- if (urb_priv->td [i]) {
- OHCI_FREE (urb_priv->td [i]);
- }
- }
- kfree (urb->hcpriv);
- urb->hcpriv = NULL;
-
- if (wait) {
- add_wait_queue (&op_wakeup, wait);
- wake_up (&op_wakeup);
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef DEBUG
-static int sohci_get_current_frame_number (struct usb_device * dev);
-
-/* debug| print the main components of an URB
- * small: 0) header + data packets 1) just header */
-
-static void urb_print (urb_t * urb, char * str, int small)
-{
- unsigned int pipe= urb->pipe;
- int i, len;
-
- if (!urb->dev || !urb->dev->bus) {
- dbg("%s URB: no dev", str);
- return;
- }
-
- dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)",
- str,
- sohci_get_current_frame_number (urb->dev),
- usb_pipedevice (pipe),
- usb_pipeendpoint (pipe),
- usb_pipeout (pipe)? 'O': 'I',
- usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
- (usb_pipecontrol (pipe)? "CTRL": "BULK"),
- urb->transfer_flags,
- urb->actual_length,
- urb->transfer_buffer_length,
- urb->status, urb->status);
- if (!small) {
- if (usb_pipecontrol (pipe)) {
- printk (KERN_DEBUG __FILE__ ": cmd(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);
- }
- }
-
-}
-/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/
-void ep_print_int_eds (ohci_t * ohci, char * str) {
- int i, j;
- __u32 * ed_p;
- for (i= 0; i < 32; i++) {
- j = 5;
- printk (KERN_DEBUG __FILE__ " %s branch int %2d(%2x):", str, i, i);
- ed_p = &(ohci->hcca.int_table [i]);
- while (*ed_p != 0 && j--) {
- ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p));
- printk (" ed: %4x;", ed->hwINFO);
- ed_p = &ed->hwNextED;
- }
- printk ("\n");
- }
-}
-
-
-#endif
-
-/*-------------------------------------------------------------------------*
- * Interface functions (URB)
- *-------------------------------------------------------------------------*/
-
-/* return a request to the completion handler */
-
-static int sohci_return_urb (urb_t * urb)
-{
- urb_priv_t * urb_priv = urb->hcpriv;
- urb_t * urbt;
- unsigned long flags;
- int i;
-
- /* just to be sure */
- if (!urb->complete) {
- urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
- return -1;
- }
-
- if (!urb_priv) return -1; /* urb already unlinked */
-
-#ifdef DEBUG
- urb_print (urb, "RET", usb_pipeout (urb->pipe));
-#endif
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_INTERRUPT:
- urb->complete (urb); /* call complete and requeue URB */
- urb->actual_length = 0;
- urb->status = USB_ST_URB_PENDING;
- if (urb_priv->state != URB_DEL)
- td_submit_urb (urb);
- break;
-
- case PIPE_ISOCHRONOUS:
- for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
- if (urbt) { /* send the reply and requeue URB */
- urb->complete (urb);
-
- spin_lock_irqsave (&usb_ed_lock, flags);
- urb->actual_length = 0;
- urb->status = USB_ST_URB_PENDING;
- urb->start_frame = urb_priv->ed->last_iso + 1;
- if (urb_priv->state != URB_DEL) {
- for (i = 0; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].actual_length = 0;
- urb->iso_frame_desc[i].status = -EXDEV;
- }
- td_submit_urb (urb);
- }
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-
- } else { /* unlink URB, call complete */
- urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
- urb->complete (urb);
- }
- break;
-
- case PIPE_BULK:
- case PIPE_CONTROL: /* unlink URB, call complete */
- urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
- urb->complete (urb);
- break;
- }
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* get a transfer request */
-
-static int sohci_submit_urb (urb_t * urb)
-{
- ohci_t * ohci;
- ed_t * ed;
- urb_priv_t * urb_priv;
- unsigned int pipe = urb->pipe;
- int i, size = 0;
- unsigned long flags;
-
- if (!urb->dev || !urb->dev->bus) return -EINVAL;
-
- if (urb->hcpriv) return -EINVAL; /* urb already in use */
-
- usb_inc_dev_use (urb->dev);
- ohci = (ohci_t *) urb->dev->bus->hcpriv;
-
-#ifdef DEBUG
- urb_print (urb, "SUB", usb_pipein (pipe));
-#endif
-
- if (usb_pipedevice (pipe) == ohci->rh.devnum)
- return rh_submit_urb (urb); /* a request to the virtual root hub */
-
- /* every endpoint has a ed, locate and fill it */
- if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
- usb_dec_dev_use (urb->dev);
- return -ENOMEM;
- }
-
- /* for the private part of the URB we need the number of TDs (size) */
- switch (usb_pipetype (pipe)) {
- case PIPE_BULK: /* one TD for every 4096 Byte */
- size = (urb->transfer_buffer_length - 1) / 4096 + 1;
- break;
- case PIPE_ISOCHRONOUS: /* number of packets from URB */
- size = urb->number_of_packets;
- for (i = 0; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].actual_length = 0;
- urb->iso_frame_desc[i].status = -EXDEV;
- }
- break;
- case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
- size = (urb->transfer_buffer_length == 0)? 2:
- (urb->transfer_buffer_length - 1) / 4096 + 3;
- break;
- case PIPE_INTERRUPT: /* one TD */
- size = 1;
-
- break;
- }
-
- /* allocate the private part or the URB */
- urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *),
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
- if (!urb_priv) {
- usb_dec_dev_use (urb->dev);
- return -ENOMEM;
- }
- memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
-
- /* fill the private part of the URB */
- urb->hcpriv = urb_priv;
- urb_priv->length = size;
- urb_priv->td_cnt = 0;
- urb_priv->state = 0;
- urb_priv->ed = ed;
- urb_priv->wait = NULL;
-
- /* allocate the TDs */
- for (i = 0; i < size; i++) {
- OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
- if (!urb_priv->td[i]) {
- usb_dec_dev_use (urb->dev);
- urb_rm_priv (urb);
- return -ENOMEM;
- }
- }
- spin_lock_irqsave (&usb_ed_lock, flags);
- if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
- urb_rm_priv(urb);
- usb_dec_dev_use (urb->dev);
- return -EINVAL;
- }
-
- /* for ISOC transfers calculate start frame index */
- if (urb->transfer_flags & USB_ISO_ASAP) {
- urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1):
- (le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
- }
-
- td_submit_urb (urb); /* fill the TDs and link it to the ed */
-
- if (ed->state != ED_OPER) /* link the ed into a chain if is not already */
- ep_link (ohci, ed);
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-
- urb->status = USB_ST_URB_PENDING;
- // queue_urb(s, &urb->urb_list);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* deactivate all TDs and remove the private part of the URB */
-
-static int sohci_unlink_urb (urb_t * urb)
-{
- unsigned long flags;
- ohci_t * ohci;
- DECLARE_WAITQUEUE (wait, current);
-
- if (!urb) /* just to be sure */
- return -EINVAL;
-
-#ifdef DEBUG
- urb_print (urb, "UNLINK", 1);
-#endif
-
- ohci = (ohci_t *) urb->dev->bus->hcpriv;
-
- if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
- return rh_unlink_urb (urb); /* a request to the virtual root hub */
-
- if (urb->hcpriv) {
- if (urb->status == USB_ST_URB_PENDING) { /* URB active? */
- urb_priv_t * urb_priv = urb->hcpriv;
- urb_priv->state = URB_DEL;
- /* we want to delete the TDs of an URB from an ed
- * request the deletion, it will be handled at the next USB-frame */
- urb_priv->wait = &wait;
-
- spin_lock_irqsave (&usb_ed_lock, flags);
- ep_rm_ed (urb->dev, urb_priv->ed);
- urb_priv->ed->state |= ED_URB_DEL;
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-
- current->state = TASK_UNINTERRUPTIBLE;
- if(schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */
- remove_wait_queue (&op_wakeup, &wait);
- else
- err("unlink URB timeout!");
- } else
- urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
- }
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* allocate private data space for a usb device */
-
-static int sohci_alloc_dev (struct usb_device *usb_dev)
-{
- struct ohci_device * dev;
-
- dev = kmalloc (sizeof (*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- memset (dev, 0, sizeof (*dev));
-
- usb_dev->hcpriv = dev;
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* free private data space of usb device */
-
-static int sohci_free_dev (struct usb_device * usb_dev)
-{
- unsigned long flags;
- int i, cnt = 0;
- ed_t * ed;
- DECLARE_WAITQUEUE (wait, current);
- struct ohci_device * dev = usb_to_ohci (usb_dev);
- ohci_t * ohci = usb_dev->bus->hcpriv;
-
- if (!dev) return 0;
-
- if (usb_dev->devnum >= 0) {
-
- /* delete all TDs of all EDs */
- spin_lock_irqsave (&usb_ed_lock, flags);
- for(i = 0; i < NUM_EDS; i++) {
- ed = &(dev->ed[i]);
- if (ed->state != ED_NEW) {
- if (ed->state == ED_OPER) ep_unlink (ohci, ed);
- ep_rm_ed (usb_dev, ed);
- ed->state = ED_DEL;
- cnt++;
- }
- }
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-
- if (cnt > 0) {
- dev->wait = &wait;
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout (HZ / 10);
- remove_wait_queue (&op_wakeup, &wait);
- }
- }
- kfree (dev);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* tell us the current USB frame number */
-
-static int sohci_get_current_frame_number (struct usb_device *usb_dev)
-{
- ohci_t * ohci = usb_dev->bus->hcpriv;
-
- return le16_to_cpu (ohci->hcca.frame_no);
-}
-
-/*-------------------------------------------------------------------------*/
-
-struct usb_operations sohci_device_operations = {
- sohci_alloc_dev,
- sohci_free_dev,
- sohci_get_current_frame_number,
- sohci_submit_urb,
- sohci_unlink_urb
-};
-
-/*-------------------------------------------------------------------------*
- * ED handling functions
- *-------------------------------------------------------------------------*/
-
-/* search for the right branch to insert an interrupt ed into the int tree
- * do some load ballancing;
- * returns the branch and
- * sets the interval to interval = 2^integer (ld (interval)) */
-
-static int ep_int_ballance (ohci_t * ohci, int interval, int load)
-{
- int i, branch = 0;
-
- /* search for the least loaded interrupt endpoint branch of all 32 branches */
- for (i = 0; i < 32; i++)
- if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i;
-
- branch = branch % interval;
- for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
-
- return branch;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* 2^int( ld (inter)) */
-
-static int ep_2_n_interval (int inter)
-{
- int i;
- for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
- return 1 << i;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* the int tree is a binary tree
- * in order to process it sequentially the indexes of the branches have to be mapped
- * the mapping reverses the bits of a word of num_bits length */
-
-static int ep_rev (int num_bits, int word)
-{
- int i, wout = 0;
-
- for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
- return wout;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* link an ed into one of the HC chains */
-
-static int ep_link (ohci_t * ohci, ed_t * edi)
-{
- int int_branch;
- int i;
- int inter;
- int interval;
- int load;
- __u32 * ed_p;
- volatile ed_t * ed = edi;
-
- ed->state = ED_OPER;
-
- switch (ed->type) {
- case CTRL:
- ed->hwNextED = 0;
- if (ohci->ed_controltail == NULL) {
- writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
- } else {
- ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
- }
- ed->ed_prev = ohci->ed_controltail;
- ohci->ed_controltail = ed;
- break;
-
- case BULK:
- ed->hwNextED = 0;
- if (ohci->ed_bulktail == NULL) {
- writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
- } else {
- ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
- }
- ed->ed_prev = ohci->ed_bulktail;
- ohci->ed_bulktail = ed;
- break;
-
- case INT:
- load = ed->int_load;
- interval = ep_2_n_interval (ed->int_period);
- ed->int_interval = interval;
- int_branch = ep_int_ballance (ohci, interval, load);
- ed->int_branch = int_branch;
-
- for (i = 0; i < ep_rev (6, interval); i += inter) {
- inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]);
- (*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval);
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED))
- inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
- ed->hwNextED = *ed_p;
- *ed_p = cpu_to_le32 (virt_to_bus (ed));
- }
-#ifdef DEBUG
- ep_print_int_eds (ohci, "LINK_INT");
-#endif
- break;
-
- case ISO:
- ed->hwNextED = 0;
- ed->int_interval = 1;
- if (ohci->ed_isotail != NULL) {
- ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
- ed->ed_prev = ohci->ed_isotail;
- } else {
- for ( i = 0; i < 32; i += inter) {
- inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
- *ed_p != 0;
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED))
- inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
- *ed_p = cpu_to_le32 (virt_to_bus (ed));
- }
- ed->ed_prev = NULL;
- }
- ohci->ed_isotail = ed;
-#ifdef DEBUG
- ep_print_int_eds (ohci, "LINK_ISO");
-#endif
- break;
- }
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* 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 int ep_unlink (ohci_t * ohci, ed_t * ed)
-{
- int int_branch;
- int i;
- int inter;
- int interval;
- __u32 * ed_p;
-
-
- switch (ed->type) {
- case CTRL:
- if (ed->ed_prev == NULL) {
- writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
- } else {
- ed->ed_prev->hwNextED = ed->hwNextED;
- }
- if(ohci->ed_controltail == ed) {
- ohci->ed_controltail = ed->ed_prev;
- } else {
- ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
- }
- break;
-
- case BULK:
- if (ed->ed_prev == NULL) {
- writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
- } else {
- ed->ed_prev->hwNextED = ed->hwNextED;
- }
- if (ohci->ed_bulktail == ed) {
- ohci->ed_bulktail = ed->ed_prev;
- } else {
- ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
- }
- break;
-
- case INT:
- int_branch = ed->int_branch;
- interval = ed->int_interval;
-
- for (i = 0; i < ep_rev (6, interval); i += inter) {
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1;
- (*ed_p != 0) && (*ed_p != ed->hwNextED);
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED),
- inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) {
- if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
- *ed_p = ed->hwNextED;
- break;
- }
- }
- }
- for (i = int_branch; i < 32; i += interval) ohci->ohci_int_load[i] -= ed->int_load;
-#ifdef DEBUG
- ep_print_int_eds (ohci, "UNLINK_INT");
-#endif break;
-
- case ISO:
- if (ohci->ed_isotail == ed)
- ohci->ed_isotail = ed->ed_prev;
- if (ed->hwNextED != 0)
- ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
-
- if (ed->ed_prev != NULL) {
- ed->ed_prev->hwNextED = ed->hwNextED;
- } else {
- for (i = 0; i < 32; i += inter) {
- inter = 1;
- for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
- *ed_p != 0;
- ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) {
- inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
- if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
- *ed_p = ed->hwNextED;
- break;
- }
- }
- }
- }
-#ifdef DEBUG
- ep_print_int_eds (ohci, "UNLINK_ISO");
-#endif
- break;
- }
- ed->state = ED_UNLINK;
- return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
- * but the USB stack is a little bit stateless so we do it at every transaction
- * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
- * in all other cases the state is left unchanged
- * the ed info fields are setted anyway even though most of them should not change */
-
-static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load)
-{
- ohci_t * ohci = usb_dev->bus->hcpriv;
- td_t * td;
- volatile ed_t * ed;
-
-
- spin_lock (&usb_ed_lock);
-
- ed = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
- (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
-
- if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
- return NULL; /* pending delete request */
-
- if (ed->state == ED_NEW) {
- ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
- OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
- if(!td) return NULL; /* out of memory */
- ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
- ed->hwHeadP = ed->hwTailP;
- ed->state = ED_UNLINK;
- ed->type = usb_pipetype (pipe);
- usb_to_ohci (usb_dev)->ed_cnt++;
- }
-
- ohci->dev[usb_pipedevice (pipe)] = usb_dev;
-
- ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
- | usb_pipeendpoint (pipe) << 7
- | (usb_pipeisoc (pipe)? 0x8000: 0)
- | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
- | usb_pipeslow (pipe) << 13
- | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
-
- if (ed->type == INT && ed->state == ED_UNLINK) {
- ed->int_period = interval;
- ed->int_load = load;
- }
-
- spin_unlock(&usb_ed_lock);
- return ed;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* request the removal of an endpoint
- * put the ep on the rm_list and request a stop of the bulk or ctrl list
- * real removal is done at the next start of frame (SOF) hardware interrupt */
-
-static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
-{
- unsigned int frame;
- ohci_t * ohci = usb_dev->bus->hcpriv;
-
- if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return;
-
- ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
-
- writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
- writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
-
- frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
- ed->ed_rm_list = ohci->ed_rm_list[frame];
- ohci->ed_rm_list[frame] = ed;
-
- switch (ed->type) {
- case CTRL: /* stop CTRL list */
- writel (ohci->hc_control &= ~(0x01 << 4), &ohci->regs->control);
- break;
- case BULK: /* stop BULK list */
- writel (ohci->hc_control &= ~(0x01 << 5), &ohci->regs->control);
- break;
- }
-}
-
-/*-------------------------------------------------------------------------*
- * TD handling functions
- *-------------------------------------------------------------------------*/
-
-/* prepare a TD */
-
-static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, int index)
-{
- volatile td_t * td, * td_pt;
- urb_priv_t * urb_priv = urb->hcpriv;
-
- if (index >= urb_priv->length) {
- err("internal OHCI error: TD index > length");
- return;
- }
-
- td_pt = urb_priv->td [index];
- /* fill the old dummy TD */
- td = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
- td->ed = urb_priv->ed;
- td->index = index;
- td->urb = urb;
- td->hwINFO = cpu_to_le32 (info);
- td->type = type;
- if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
- td->hwCBP = cpu_to_le32 (((!data || !len)?
- 0 : virt_to_bus (data)) & 0xFFFFF000);
- td->ed->last_iso = info & 0xffff;
- } else {
- td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)));
- }
- td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1));
- td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
- td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
- td_pt->hwNextTD = 0;
- td->ed->hwTailP = td->hwNextTD;
- urb_priv->td [index] = td;
-
- td->next_dl_td = NULL; //td_pt;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* prepare all TDs of a transfer */
-
-static void td_submit_urb (urb_t * urb)
-{
- urb_priv_t * urb_priv = urb->hcpriv;
- ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
- void * ctrl = urb->setup_packet;
- void * data = urb->transfer_buffer;
- int data_len = urb->transfer_buffer_length;
- int cnt = 0;
- __u32 info = 0;
-
-
- urb_priv->td_cnt = 0;
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_BULK:
- info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE;
- while(data_len > 4096) {
- td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
- data += 4096; data_len -= 4096; cnt++;
- }
- info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
- td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
- cnt++;
- writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
- break;
-
- case PIPE_INTERRUPT:
- info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
- td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++);
- break;
-
- case PIPE_CONTROL:
- info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
- td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++);
- if (data_len > 0) {
- info = usb_pipeout (urb->pipe)?
- TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
- td_fill (info, data, data_len, urb, ADD_LEN, cnt++);
- }
- info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
- td_fill (info, NULL, 0, urb, 0, cnt++);
- writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
- break;
-
- case PIPE_ISOCHRONOUS:
- for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
- td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff),
- (__u8 *) data + urb->iso_frame_desc[cnt].offset,
- urb->iso_frame_desc[cnt].length, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
- }
- break;
- }
- if (urb_priv->length != cnt)
- dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
-}
-
-/*-------------------------------------------------------------------------*
- * Done List handling functions
- *-------------------------------------------------------------------------*/
-
-/* replies to the request have to be on a FIFO basis so
- * we reverse the reversed done-list */
-
-static td_t * dl_reverse_done_list (ohci_t * ohci)
-{
- __u32 td_list_hc;
- td_t * td_rev = NULL;
- td_t * td_list = NULL;
- urb_priv_t * urb_priv = NULL;
- unsigned long flags;
-
- spin_lock_irqsave (&usb_ed_lock, flags);
-
- td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;
- ohci->hcca.done_head = 0;
-
- while (td_list_hc) {
- td_list = (td_t *) bus_to_virt (td_list_hc);
-
- if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
- urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
- dbg(" USB-error/status: %x : %p",
- TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
- if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
- if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
- td_list->ed->hwHeadP =
- (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
- (td_list->ed->hwHeadP & cpu_to_le32 (0x2));
- urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
- } else
- td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
- }
- }
-
- td_list->next_dl_td = td_rev;
- td_rev = td_list;
- td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;
- }
- spin_unlock_irqrestore (&usb_ed_lock, flags);
- return td_list;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* there are some pending requests to remove
- * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
- * - some URBs/TDs if urb_priv->state == URB_DEL */
-
-static void dl_del_list (ohci_t * ohci, unsigned int frame)
-{
- unsigned long flags;
- ed_t * ed;
- __u32 edINFO;
- td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
- __u32 * td_p;
- int ctrl = 0, bulk = 0;
-
- spin_lock_irqsave (&usb_ed_lock, flags);
- for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
-
- tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
- tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
- edINFO = le32_to_cpup (&ed->hwINFO);
- td_p = &ed->hwHeadP;
-
- for (td = tdHeadP; td != tdTailP; td = td_next) {
- urb_t * urb = td->urb;
- urb_priv_t * urb_priv = td->urb->hcpriv;
-
- td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
- if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
- *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
- if(++ (urb_priv->td_cnt) == urb_priv->length)
- urb_rm_priv (urb);
- } else {
- td_p = &td->hwNextTD;
- }
-
- }
- if (ed->state & ED_DEL) { /* set by sohci_free_dev */
- struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
- OHCI_FREE (tdTailP); /* free dummy td */
- ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
- ed->state = ED_NEW;
- /* if all eds are removed wake up sohci_free_dev */
- if ((! --dev->ed_cnt) && dev->wait) {
- add_wait_queue (&op_wakeup, dev->wait);
- wake_up (&op_wakeup);
- }
- }
- else {
- ed->state &= ~ED_URB_DEL;
- ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
- }
-
- if ((ed->type & 3) == CTRL) ctrl |= 1;
- if ((ed->type & 3) == BULK) bulk |= 1;
- }
-
- if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
- if (bulk) writel (0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
- if (!ohci->ed_rm_list[!frame]) /* start CTRL u. BULK list */
- writel (ohci->hc_control |= (0x03<<4), &ohci->regs->control);
- ohci->ed_rm_list[frame] = NULL;
-
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* td done list */
-
-static void dl_done_list (ohci_t * ohci, td_t * td_list)
-{
- td_t * td_list_next = NULL;
- ed_t * ed;
- int dlen = 0;
- int cc = 0;
- urb_t * urb;
- urb_priv_t * urb_priv;
- __u32 tdINFO, tdBE, tdCBP, edHeadP, edTailP;
- __u16 tdPSW;
- unsigned long flags;
-
- while (td_list) {
- td_list_next = td_list->next_dl_td;
-
- urb = td_list->urb;
- urb_priv = urb->hcpriv;
- tdINFO = le32_to_cpup (&td_list->hwINFO);
- tdBE = le32_to_cpup (&td_list->hwBE);
- tdCBP = le32_to_cpup (&td_list->hwCBP);
-
- ed = td_list->ed;
-
- if (td_list->type & ST_ADDR)
- urb->actual_length = 0;
-
- if (td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */
- if (tdINFO & TD_ISO) {
- tdPSW = le16_to_cpu (td_list->hwPSW[0]);
- cc = (tdPSW >> 12) & 0xF;
- if (cc < 0xE) {
- if (usb_pipeout(urb->pipe)) {
- dlen = urb->iso_frame_desc[td_list->index].length;
- } else {
- dlen = tdPSW & 0x3ff;
- }
- urb->actual_length += dlen;
- urb->iso_frame_desc[td_list->index].actual_length = dlen;
- if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
- cc = TD_CC_NOERROR;
-
- urb->iso_frame_desc[td_list->index].status = cc_to_error[cc];
- }
- } else {
- if (tdBE != 0) {
- dlen = (bus_to_virt (tdBE) - urb->transfer_buffer + 1);
- if (td_list->hwCBP == 0)
- urb->actual_length += dlen;
- else
- urb->actual_length += (bus_to_virt(tdCBP) - urb->transfer_buffer);
- }
- }
- }
- /* error code of transfer */
- cc = TD_CC_GET (tdINFO);
- if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
- cc = TD_CC_NOERROR;
- if (++(urb_priv->td_cnt) == urb_priv->length) {
- if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) {
- urb->status = cc_to_error[cc];
- sohci_return_urb (urb);
- } else {
- urb_rm_priv (urb);
- }
- }
-
- spin_lock_irqsave (&usb_ed_lock, flags);
- if (ed->state != ED_NEW) {
- edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
- edTailP = le32_to_cpup (&ed->hwTailP);
-
- if((edHeadP == edTailP) && (ed->state == ED_OPER))
- ep_unlink (ohci, ed); /* unlink eds if they are not busy */
-
- }
- spin_unlock_irqrestore (&usb_ed_lock, flags);
-
- td_list = td_list_next;
- }
-}
-
-
-
-
-/*-------------------------------------------------------------------------*
- * Virtual Root Hub
- *-------------------------------------------------------------------------*/
-
-static __u8 root_hub_dev_des[] =
-{
- 0x12, /* __u8 bLength; */
- 0x01, /* __u8 bDescriptorType; Device */
- 0x00, /* __u16 bcdUSB; v1.0 */
- 0x01,
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; */
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- 0x00, /* __u16 idVendor; */
- 0x00,
- 0x00, /* __u16 idProduct; */
- 0x00,
- 0x00, /* __u16 bcdDevice; */
- 0x00,
- 0x00, /* __u8 iManufacturer; */
- 0x00, /* __u8 iProduct; */
- 0x00, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, /* __u16 wTotalLength; */
- 0x00,
- 0x01, /* __u8 bNumInterfaces; */
- 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; */
-
- /* 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; */
- 0x00, /* __u8 if_iInterface; */
-
- /* endpoint */
- 0x07, /* __u8 ep_bLength; */
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
- 0x00,
- 0xff /* __u8 ep_bInterval; 255 ms */
-};
-
-/*
-For OHCI we need just the 2nd Byte, so we
-don't need this constant byte-array
-
-static __u8 root_hub_hub_des[] =
-{
- 0x00, * __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; *** 8 Ports max *** *
- 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** *
-};
-*/
-
-/*-------------------------------------------------------------------------*/
-
-/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */
-
-static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
-{
- int num_ports;
- int i;
- int ret;
- int len;
-
- __u8 data[8];
-
- num_ports = readl (&ohci->regs->roothub.a) & 0xff;
- *(__u8 *) data = (readl (&ohci->regs->roothub.status) & 0x00030000) > 0? 1: 0;
- ret = *(__u8 *) data;
-
- for ( i = 0; i < num_ports; i++) {
- *(__u8 *) (data + (i + 1) / 8) |=
- ((readl (&ohci->regs->roothub.portstatus[i]) & 0x001f0000) > 0? 1: 0) << ((i + 1) % 8);
- ret += *(__u8 *) (data + (i + 1) / 8);
- }
- len = i/8 + 1;
-
- if (ret > 0) {
- memcpy (rh_data, data, min (len, min (rh_len, sizeof(data))));
- return len;
- }
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-
-static void rh_int_timer_do (unsigned long ptr)
-{
- int len;
-
- urb_t * urb = (urb_t *) ptr;
- ohci_t * ohci = urb->dev->bus->hcpriv;
-
- if(ohci->rh.send) {
- len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
- if (len > 0) {
- urb->actual_length = len;
-#ifdef DEBUG
- urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
-#endif
- if (urb->complete) urb->complete (urb);
- }
- }
- rh_init_int_timer (urb);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Root Hub INTs are polled by this timer */
-
-static int rh_init_int_timer (urb_t * urb)
-{
- ohci_t * ohci = urb->dev->bus->hcpriv;
-
- ohci->rh.interval = urb->interval;
- init_timer (&ohci->rh.rh_int_timer);
- ohci->rh.rh_int_timer.function = rh_int_timer_do;
- ohci->rh.rh_int_timer.data = (unsigned long) urb;
- ohci->rh.rh_int_timer.expires =
- jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
- add_timer (&ohci->rh.rh_int_timer);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define OK(x) len = (x); break
-#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status)
-#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
-#define RD_RH_STAT readl(&ohci->regs->roothub.status)
-#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1])
-
-/* request to virtual root hub */
-
-static int rh_submit_urb (urb_t * urb)
-{
- struct usb_device * usb_dev = urb->dev;
- ohci_t * ohci = usb_dev->bus->hcpriv;
- unsigned int pipe = urb->pipe;
- devrequest * cmd = (devrequest *) urb->setup_packet;
- void * data = urb->transfer_buffer;
- int leni = urb->transfer_buffer_length;
- int len = 0;
- int status = TD_CC_NOERROR;
-
- __u8 datab[16];
- __u8 * data_buf = datab;
-
- __u16 bmRType_bReq;
- __u16 wValue;
- __u16 wIndex;
- __u16 wLength;
-
- if (usb_pipeint(pipe)) {
-
- ohci->rh.urb = urb;
- ohci->rh.send = 1;
- ohci->rh.interval = urb->interval;
- rh_init_int_timer(urb);
- urb->status = cc_to_error [TD_CC_NOERROR];
-
- return 0;
- }
-
- bmRType_bReq = cmd->requesttype | (cmd->request << 8);
- wValue = le16_to_cpu (cmd->value);
- wIndex = le16_to_cpu (cmd->index);
- wLength = le16_to_cpu (cmd->length);
- switch (bmRType_bReq) {
- /* 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 RH_GET_STATUS:
- *(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
- case RH_GET_STATUS | RH_INTERFACE:
- *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
- case RH_GET_STATUS | RH_ENDPOINT:
- *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
- case RH_GET_STATUS | RH_CLASS:
- *(__u32 *) data_buf = cpu_to_le32 (RD_RH_STAT & 0x7fff7fff); OK (4);
- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
-
- case RH_CLEAR_FEATURE | RH_ENDPOINT:
- switch (wValue) {
- case (RH_ENDPOINT_STALL): OK (0);
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_CLASS:
- switch (wValue) {
- case RH_C_HUB_LOCAL_POWER:
- OK(0);
- case (RH_C_HUB_OVER_CURRENT):
- WR_RH_STAT(RH_HS_OCIC); OK (0);
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_ENABLE):
- WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
- case (RH_PORT_SUSPEND):
- WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
- case (RH_PORT_POWER):
- WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
- case (RH_C_PORT_CONNECTION):
- WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
- case (RH_C_PORT_ENABLE):
- WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
- case (RH_C_PORT_SUSPEND):
- WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
- case (RH_C_PORT_OVER_CURRENT):
- WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
- case (RH_C_PORT_RESET):
- WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
- }
- break;
-
- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_SUSPEND):
- WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
- case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
- if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PRS ); OK (0);
- case (RH_PORT_POWER):
- WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
- case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
- if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PES ); OK (0);
- }
- break;
-
- case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
-
- case RH_GET_DESCRIPTOR:
- switch ((wValue & 0xff00) >> 8) {
- case (0x01): /* device descriptor */
- len = min (leni, min (sizeof (root_hub_dev_des), wLength));
- data_buf = root_hub_dev_des; OK(len);
- case (0x02): /* configuration descriptor */
- len = min (leni, min (sizeof (root_hub_config_des), wLength));
- data_buf = root_hub_config_des; OK(len);
- case (0x03): /* string descriptors */
- default:
- status = TD_CC_STALL;
- }
- break;
-
- case RH_GET_DESCRIPTOR | RH_CLASS:
- *(__u8 *) (data_buf+1) = 0x29;
- *(__u32 *) (data_buf+2) = cpu_to_le32 (readl (&ohci->regs->roothub.a));
- *(__u8 *) data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */
-
- len = min (leni, min(*(__u8 *) data_buf, wLength));
- *(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */
- if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */
- *(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff;
- *(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16;
- } else {
- *(__u32 *) (data_buf+7) = cpu_to_le32 (readl(&ohci->regs->roothub.b));
- }
- OK (len);
-
- case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
-
- case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
-
- default:
- status = TD_CC_STALL;
- }
-
- dbg("USB HC roothubstat1: %x", readl ( &(ohci->regs->roothub.portstatus[0]) ));
- dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
-
- len = min(len, leni);
- memcpy (data, data_buf, len);
- urb->actual_length = len;
- urb->status = cc_to_error [status];
-
-#ifdef DEBUG
- urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
-#endif
-
- if (urb->complete) urb->complete (urb);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int rh_unlink_urb (urb_t * urb)
-{
- ohci_t * ohci = urb->dev->bus->hcpriv;
-
- ohci->rh.send = 0;
- del_timer (&ohci->rh.rh_int_timer);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*
- * HC functions
- *-------------------------------------------------------------------------*/
-
-/* reset the HC not the BUS */
-
-static void hc_reset (ohci_t * ohci)
-{
- int timeout = 30;
- int smm_timeout = 50; /* 0,5 sec */
-
- if (readl (&ohci->regs->control) & 0x100) { /* SMM owns the HC */
- writel (0x08, &ohci->regs->cmdstatus); /* request ownership */
- dbg("USB HC TakeOver from SMM");
- while (readl (&ohci->regs->control) & 0x100) {
- wait_ms (10);
- if (--smm_timeout == 0) {
- err("USB HC TakeOver failed!");
- break;
- }
- }
- }
-
- writel ((1 << 31), &ohci->regs->intrdisable); /* Disable HC interrupts */
- dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control));
- /* this seems to be needed for the lucent controller on powerbooks.. */
- writel (0, &ohci->regs->control); /* Move USB to reset state */
-
- writel (1, &ohci->regs->cmdstatus); /* HC Reset */
- while ((readl (&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */
- if (--timeout == 0) {
- err("USB HC reset timed out!");
- return;
- }
- udelay (1);
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Start an OHCI controller, set the BUS operational
- * enable interrupts
- * connect the virtual root hub */
-
-static int hc_start (ohci_t * ohci)
-{
- unsigned int mask;
- unsigned int fminterval;
- struct usb_device * usb_dev;
- struct ohci_device * dev;
-
- /* 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);
-
- writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */
-
- fminterval = 0x2edf;
- writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
- fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
- writel (fminterval, &ohci->regs->fminterval);
- writel (0x628, &ohci->regs->lsthresh);
-
- /* Choose the interrupts we care about now, others later on demand */
- mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO;
-
- writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
- writel (mask, &ohci->regs->intrenable);
- writel (mask, &ohci->regs->intrstatus);
-
-#ifdef OHCI_USE_NPS
- writel ((readl(&ohci->regs->roothub.a) | 0x200) & ~0x100,
- &ohci->regs->roothub.a);
- writel (0x10000, &ohci->regs->roothub.status);
- mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);
-#endif /* OHCI_USE_NPS */
-
- /* connect the virtual root hub */
-
- usb_dev = usb_alloc_dev (NULL, ohci->bus);
- if (!usb_dev) return -1;
-
- dev = usb_to_ohci (usb_dev);
- ohci->bus->root_hub = usb_dev;
- usb_connect (usb_dev);
- if (usb_new_device (usb_dev) != 0) {
- usb_free_dev (usb_dev);
- return -1;
- }
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* an interrupt happens */
-
-static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
-{
- ohci_t * ohci = __ohci;
- struct ohci_regs * regs = ohci->regs;
- int ints;
-
- if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) {
- ints = OHCI_INTR_WDH;
- } else {
- if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0)
- return;
- }
-
- dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
-
- if (ints & OHCI_INTR_WDH) {
- writel (OHCI_INTR_WDH, ®s->intrdisable);
- dl_done_list (ohci, dl_reverse_done_list (ohci));
- writel (OHCI_INTR_WDH, ®s->intrenable);
- }
-
- if (ints & OHCI_INTR_SO) {
- dbg("USB Schedule overrun");
- writel (OHCI_INTR_SO, ®s->intrenable);
- }
-
- if (ints & OHCI_INTR_SF) {
- unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1;
- writel (OHCI_INTR_SF, ®s->intrdisable);
- if (ohci->ed_rm_list[!frame] != NULL) {
- dl_del_list (ohci, !frame);
- }
- if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable);
- }
- writel (ints, ®s->intrstatus);
- writel (OHCI_INTR_MIE, ®s->intrenable);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* allocate OHCI */
-
-static ohci_t * hc_alloc_ohci (void * mem_base)
-{
- int i;
- ohci_t * ohci;
- struct usb_bus * bus;
-
- ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1);
- if (!ohci)
- return NULL;
-
- memset (ohci, 0, sizeof (ohci_t));
-
- ohci->irq = -1;
- ohci->regs = mem_base;
-
- /* for load ballancing of the interrupt branches */
- for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
- for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0;
-
- /* end of control and bulk lists */
- ohci->ed_isotail = NULL;
- ohci->ed_controltail = NULL;
- ohci->ed_bulktail = NULL;
-
- bus = usb_alloc_bus (&sohci_device_operations);
- if (!bus) {
- free_pages ((unsigned long) ohci, 1);
- return NULL;
- }
-
- ohci->bus = bus;
- bus->hcpriv = (void *) ohci;
-
- return ohci;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* De-allocate all resources.. */
-
-static void hc_release_ohci (ohci_t * ohci)
-{
- dbg("USB HC release ohci");
-
- /* disconnect all devices */
- if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub);
-
- hc_reset (ohci);
- writel (OHCI_USB_RESET, &ohci->regs->control);
- wait_ms (10);
-
- if (ohci->irq >= 0) {
- free_irq (ohci->irq, ohci);
- ohci->irq = -1;
- }
-
- usb_deregister_bus (ohci->bus);
- usb_free_bus (ohci->bus);
-
- /* unmap the IO address space */
- iounmap (ohci->regs);
-
- free_pages ((unsigned long) ohci, 1);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Increment the module usage count, start the control thread and
- * return success. */
-
-static int hc_found_ohci (int irq, void * mem_base)
-{
- ohci_t * ohci;
- dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base);
-
- ohci = hc_alloc_ohci (mem_base);
- if (!ohci) {
- return -ENOMEM;
- }
-
- INIT_LIST_HEAD (&ohci->ohci_hcd_list);
- list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
-
- hc_reset (ohci);
- writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
- wait_ms (10);
- usb_register_bus (ohci->bus);
-
- if (request_irq (irq, hc_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) {
- ohci->irq = irq;
- hc_start (ohci);
- return 0;
- }
- err("request interrupt %d failed", irq);
- hc_release_ohci (ohci);
- return -EBUSY;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int hc_start_ohci (struct pci_dev * dev)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- unsigned long mem_base = dev->resource[0].start;
-#else
- unsigned long mem_base = dev->base_address[0];
- if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV;
- mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
-#endif
-
- pci_set_master (dev);
- mem_base = (unsigned long) ioremap_nocache (mem_base, 4096);
-
- if (!mem_base) {
- err("Error mapping OHCI memory");
- return -EFAULT;
- }
- return hc_found_ohci (dev->irq, (void *) mem_base);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_PMAC_PBOOK
-
-/* On Powerbooks, put the controller into suspend mode when going
- * to sleep, and do a resume when waking up. */
-
-static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when)
-{
- struct list_head * ohci_l;
- ohci_t * ohci;
-
- for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
- ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
-
- switch (when) {
- case PBOOK_SLEEP_NOW:
- disable_irq (ohci->irq);
- writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control);
- wait_ms (10);
- break;
- case PBOOK_WAKE:
- writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control);
- wait_ms (20);
- writel (ohci->hc_control = 0xBF, &ohci->regs->control);
- enable_irq (ohci->irq);
- break;
- }
- }
- return PBOOK_SLEEP_OK;
-}
-
-static struct pmu_sleep_notifier ohci_sleep_notifier = {
- ohci_sleep_notify, SLEEP_LEVEL_MISC,
-};
-#endif /* CONFIG_PMAC_PBOOK */
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_APM
-static int handle_apm_event (apm_event_t event)
-{
- static int down = 0;
- ohci_t * ohci;
- struct list_head * ohci_l;
-
- switch (event) {
- case APM_SYS_SUSPEND:
- case APM_USER_SUSPEND:
- if (down) {
- dbg("received extra suspend event");
- break;
- }
- for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
- ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
- dbg("USB-Bus suspend: %p", ohci);
- writel (ohci->hc_control = 0xFF, &ohci->regs->control);
- }
- wait_ms (10);
- down = 1;
- break;
- case APM_NORMAL_RESUME:
- case APM_CRITICAL_RESUME:
- if (!down) {
- dbg("received bogus resume event");
- break;
- }
- for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
- ohci = list_entry(ohci_l, ohci_t, ohci_hcd_list);
- dbg("USB-Bus resume: %p", ohci);
- writel (ohci->hc_control = 0x7F, &ohci->regs->control);
- }
- wait_ms (20);
- for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
- ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
- writel (ohci->hc_control = 0xBF, &ohci->regs->control);
- }
- down = 0;
- break;
- }
- return 0;
-}
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
-
-int ohci_hcd_init (void)
-{
- int ret = -ENODEV;
- struct pci_dev * dev = NULL;
-
- while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) {
- if (hc_start_ohci(dev) >= 0) ret = 0;
- }
-
-#ifdef CONFIG_APM
- apm_register_callback (&handle_apm_event);
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
- pmu_register_sleep_notifier (&ohci_sleep_notifier);
-#endif
- return ret;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef MODULE
-int init_module (void)
-{
- return ohci_hcd_init ();
-}
-
-/*-------------------------------------------------------------------------*/
-
-void cleanup_module (void)
-{
- ohci_t * ohci;
-
-#ifdef CONFIG_APM
- apm_unregister_callback (&handle_apm_event);
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
- pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
-#endif
-
- while (!list_empty (&ohci_hcd_list)) {
- ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list);
- list_del (&ohci->ohci_hcd_list);
- INIT_LIST_HEAD (&ohci->ohci_hcd_list);
- hc_release_ohci (ohci);
- }
-}
-#endif //MODULE
-
+++ /dev/null
- /*
- * URB OHCI HCD (Host Controller Driver) for USB.
- *
- *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- *
- * ohci-hcd.h
- *
- */
-
-
-#define MODSTR "ohci: "
-
-
-static int cc_to_error[16] = {
-
-/* mapping of the OHCI CC status to error codes */
-#ifdef USB_ST_CRC /* status codes */
- /* No Error */ USB_ST_NOERROR,
- /* CRC Error */ USB_ST_CRC,
- /* Bit Stuff */ USB_ST_BITSTUFF,
- /* Data Togg */ USB_ST_CRC,
- /* Stall */ USB_ST_STALL,
- /* DevNotResp */ USB_ST_NORESPONSE,
- /* PIDCheck */ USB_ST_BITSTUFF,
- /* UnExpPID */ USB_ST_BITSTUFF,
- /* DataOver */ USB_ST_DATAOVERRUN,
- /* DataUnder */ USB_ST_DATAUNDERRUN,
- /* reservd */ USB_ST_NORESPONSE,
- /* reservd */ USB_ST_NORESPONSE,
- /* BufferOver */ USB_ST_BUFFEROVERRUN,
- /* BuffUnder */ USB_ST_BUFFERUNDERRUN,
- /* Not Access */ USB_ST_NORESPONSE,
- /* Not Access */ USB_ST_NORESPONSE
-};
-
-#else /* error codes */
- /* No Error */ 0,
- /* CRC Error */ -EILSEQ,
- /* Bit Stuff */ -EPROTO,
- /* Data Togg */ -EILSEQ,
- /* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
- /* PIDCheck */ -EPROTO,
- /* UnExpPID */ -EPROTO,
- /* DataOver */ -EOVERFLOW,
- /* DataUnder */ -EREMOTEIO,
- /* reservd */ -ETIMEDOUT,
- /* reservd */ -ETIMEDOUT,
- /* BufferOver */ -ECOMM,
- /* BuffUnder */ -ECOMM,
- /* Not Access */ -ETIMEDOUT,
- /* Not Access */ -ETIMEDOUT
-};
-#define USB_ST_URB_PENDING -EINPROGRESS
-#endif
-
-
-
-struct ed;
-struct td;
-/* for ED and TD structures */
-
-/* ED States */
-
-#define ED_NEW 0x00
-#define ED_UNLINK 0x01
-#define ED_OPER 0x02
-#define ED_DEL 0x04
-#define ED_URB_DEL 0x08
-
-/* usb_ohci_ed */
-typedef struct ed {
- __u32 hwINFO;
- __u32 hwTailP;
- __u32 hwHeadP;
- __u32 hwNextED;
-
- struct ed * ed_prev;
- __u8 int_period;
- __u8 int_branch;
- __u8 int_load;
- __u8 int_interval;
- __u8 state;
- __u8 type;
- __u16 last_iso;
- struct ed * ed_rm_list;
-
-} ed_t;
-
-
-/* TD info field */
-#define TD_CC 0xf0000000
-#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_EC 0x0C000000
-#define TD_T 0x03000000
-#define TD_T_DATA0 0x02000000
-#define TD_T_DATA1 0x03000000
-#define TD_T_TOGGLE 0x00000000
-#define TD_R 0x00040000
-#define TD_DI 0x00E00000
-#define TD_DI_SET(X) (((X) & 0x07)<< 21)
-#define TD_DP 0x00180000
-#define TD_DP_SETUP 0x00000000
-#define TD_DP_IN 0x00100000
-#define TD_DP_OUT 0x00080000
-
-#define TD_ISO 0x00010000
-#define TD_DEL 0x00020000
-
-/* CC Codes */
-#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
-#define TD_BUFFEROVERRUN 0x0C
-#define TD_BUFFERUNDERRUN 0x0D
-#define TD_NOTACCESSED 0x0F
-
-
-#define MAXPSW 1
-
-typedef struct td {
- __u32 hwINFO;
- __u32 hwCBP; /* Current Buffer Pointer */
- __u32 hwNextTD; /* Next TD Pointer */
- __u32 hwBE; /* Memory Buffer End Pointer */
- __u16 hwPSW[MAXPSW];
-
- __u8 type;
- __u8 index;
- struct ed * ed;
- struct td * next_dl_td;
- urb_t * urb;
-} td_t;
-
-
-/* TD types */
-#define BULK 0x03
-#define INT 0x01
-#define CTRL 0x02
-#define ISO 0x00
-
-#define SEND 0x01
-#define ST_ADDR 0x02
-#define ADD_LEN 0x04
-#define DEL 0x08
-
-
-#define OHCI_ED_SKIP (1 << 14)
-
-/*
- * The HCCA (Host Controller Communications Area) is a 256 byte
- * structure defined in the OHCI spec. that the host controller is
- * told the base address of. It must be 256-byte aligned.
- */
-
-#define NUM_INTS 32 /* part of the OHCI standard */
-struct ohci_hcca {
- __u32 int_table[NUM_INTS]; /* Interrupt ED table */
- __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];
-} __attribute((aligned(256)));
-
-
-/*
- * Maximum number of root hub ports.
- */
-#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
-
-/*
- * This is the structure of the OHCI controller's memory mapped I/O
- * region. This is Memory Mapped I/O. You must use the readl() and
- * writel() macros defined in asm/io.h to access these!!
- */
-struct ohci_regs {
- /* control and status registers */
- __u32 revision;
- __u32 control;
- __u32 cmdstatus;
- __u32 intrstatus;
- __u32 intrenable;
- __u32 intrdisable;
- /* memory pointers */
- __u32 hcca;
- __u32 ed_periodcurrent;
- __u32 ed_controlhead;
- __u32 ed_controlcurrent;
- __u32 ed_bulkhead;
- __u32 ed_bulkcurrent;
- __u32 donehead;
- /* frame counters */
- __u32 fminterval;
- __u32 fmremaining;
- __u32 fmnumber;
- __u32 periodicstart;
- __u32 lsthresh;
- /* Root hub ports */
- struct ohci_roothub_regs {
- __u32 a;
- __u32 b;
- __u32 status;
- __u32 portstatus[MAX_ROOT_PORTS];
- } roothub;
-} __attribute((aligned(32)));
-
-/*
- * cmdstatus register */
-#define OHCI_CLF 0x02
-#define OHCI_BLF 0x04
-
-/*
- * Interrupt register masks
- */
-#define OHCI_INTR_SO (1)
-#define OHCI_INTR_WDH (1 << 1)
-#define OHCI_INTR_SF (1 << 2)
-#define OHCI_INTR_RD (1 << 3)
-#define OHCI_INTR_UE (1 << 4)
-#define OHCI_INTR_FNO (1 << 5)
-#define OHCI_INTR_RHSC (1 << 6)
-#define OHCI_INTR_OC (1 << 30)
-#define OHCI_INTR_MIE (1 << 31)
-
-/*
- * Control register masks
- */
-#define OHCI_USB_RESET 0
-#define OHCI_USB_RESUME (1 << 6)
-#define OHCI_USB_OPER (2 << 6)
-#define OHCI_USB_SUSPEND (3 << 6)
-
-
-/* Virtual Root HUB */
-struct virt_root_hub {
- int devnum; /* Address of Root Hub endpoint */
- void * urb;
- void * int_addr;
- int send;
- int interval;
- struct timer_list rh_int_timer;
-};
-
-/* destination of request */
-#define RH_INTERFACE 0x01
-#define RH_ENDPOINT 0x02
-#define RH_OTHER 0x03
-
-#define RH_CLASS 0x20
-#define RH_VENDOR 0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS 0x0080
-#define RH_CLEAR_FEATURE 0x0100
-#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
-#define RH_SET_DESCRIPTOR 0x0700
-#define RH_GET_CONFIGURATION 0x0880
-#define RH_SET_CONFIGURATION 0x0900
-#define RH_GET_STATE 0x0280
-#define RH_GET_INTERFACE 0x0A80
-#define RH_SET_INTERFACE 0x0B00
-#define RH_SYNC_FRAME 0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP 0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION 0x00
-#define RH_PORT_ENABLE 0x01
-#define RH_PORT_SUSPEND 0x02
-#define RH_PORT_OVER_CURRENT 0x03
-#define RH_PORT_RESET 0x04
-#define RH_PORT_POWER 0x08
-#define RH_PORT_LOW_SPEED 0x09
-#define RH_C_PORT_CONNECTION 0x10
-#define RH_C_PORT_ENABLE 0x11
-#define RH_C_PORT_SUSPEND 0x12
-#define RH_C_PORT_OVER_CURRENT 0x13
-#define RH_C_PORT_RESET 0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER 0x00
-#define RH_C_HUB_OVER_CURRENT 0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP 0x00
-#define RH_ENDPOINT_STALL 0x01
-
-#define RH_ACK 0x01
-#define RH_REQ_ERR -1
-#define RH_NACK 0x00
-
-/* Root-Hub Register info */
-
-#define RH_PS_CCS 0x00000001
-#define RH_PS_PES 0x00000002
-#define RH_PS_PSS 0x00000004
-#define RH_PS_POCI 0x00000008
-#define RH_PS_PRS 0x00000010
-#define RH_PS_PPS 0x00000100
-#define RH_PS_LSDA 0x00000200
-#define RH_PS_CSC 0x00010000
-#define RH_PS_PESC 0x00020000
-#define RH_PS_PSSC 0x00040000
-#define RH_PS_OCIC 0x00080000
-#define RH_PS_PRSC 0x00100000
-
-/* Root hub status bits */
-#define RH_HS_LPS 0x00000001
-#define RH_HS_OCI 0x00000002
-#define RH_HS_DRWE 0x00008000
-#define RH_HS_LPSC 0x00010000
-#define RH_HS_OCIC 0x00020000
-#define RH_HS_CRWE 0x80000000
-
-#define min(a,b) (((a)<(b))?(a):(b))
-
-
-/* urb */
-typedef struct
-{
- ed_t * ed;
- __u16 length; // number of tds associated with this request
- __u16 td_cnt; // number of tds already serviced
- int state;
- void * wait;
- td_t * td[0]; // list pointer to all corresponding TDs associated with this request
-
-} urb_priv_t;
-#define URB_DEL 1
-
-/*
- * This is the full ohci controller description
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs. (Linus)
- */
-
-
-typedef struct ohci {
- struct ohci_hcca hcca; /* hcca */
-
- int irq;
- struct ohci_regs * regs; /* OHCI controller's memory */
- struct list_head ohci_hcd_list; /* list of all ohci_hcd */
-
- struct ohci * next; // chain of uhci device contexts
- struct list_head urb_list; // list of all pending urbs
- spinlock_t urb_list_lock; // lock to keep consistency
-
- int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/
- ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */
- ed_t * ed_bulktail; /* last endpoint of bulk list */
- ed_t * ed_controltail; /* last endpoint of control list */
- ed_t * ed_isotail; /* last endpoint of iso list */
- int intrstatus;
- __u32 hc_control; /* copy of the hc control reg */
- struct usb_bus * bus;
- struct usb_device * dev[128];
- struct virt_root_hub rh;
-} ohci_t;
-
-
-#define NUM_TDS 0 /* num of preallocated transfer descriptors */
-#define NUM_EDS 32 /* num of preallocated endpoint descriptors */
-
-struct ohci_device {
- ed_t ed[NUM_EDS];
- int ed_cnt;
- void * wait;
-};
-
-// #define ohci_to_usb(ohci) ((ohci)->usb)
-#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
-
-/* hcd */
-/* endpoint */
-static int ep_link(ohci_t * ohci, ed_t * ed);
-static int ep_unlink(ohci_t * ohci, ed_t * ed);
-static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load);
-static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
-/* td */
-static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type, int index);
-static void td_submit_urb(urb_t * urb);
-/* root hub */
-static int rh_submit_urb(urb_t * urb);
-static int rh_unlink_urb(urb_t * urb);
-static int rh_init_int_timer(urb_t * urb);
-
-#ifdef DEBUG
-#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
-static int __ohci_free_cnt = 0;
-#else
-#define OHCI_FREE(x) kfree(x)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-#endif
-
+++ /dev/null
-/*
- * $Id: uhci-debug.c,v 1.12 1999/12/13 15:24:42 fliegl Exp $
- */
-
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#define DEBUG
-
-#include "usb.h"
-#include "uhci.h"
-
-void dump_urb (purb_t purb)
-{
- dbg("urb :%p", purb);
- dbg("next :%p", purb->next);
- dbg("dev :%p", purb->dev);
- dbg("pipe :%08X", purb->pipe);
- dbg("status :%d", purb->status);
- dbg("transfer_flags :%08X", purb->transfer_flags);
- dbg("transfer_buffer :%p", purb->transfer_buffer);
- dbg("transfer_buffer_length:%d", purb->transfer_buffer_length);
- dbg("actual_length :%d", purb->actual_length);
- dbg("setup_packet :%p", purb->setup_packet);
- dbg("start_frame :%d", purb->start_frame);
- dbg("number_of_packets :%d", purb->number_of_packets);
- dbg("interval :%d", purb->interval);
- dbg("error_count :%d", purb->error_count);
- dbg("context :%p", purb->context);
- dbg("complete :%p", purb->complete);
-}
-
-void beep (long freq)
-{
- long v;
- char low, high;
-
- if (!freq)
- outb (inb (0x61) & 252, 0x61);
- else {
- outb (inb (0x61) | 0x3, 0x61);
-
- v = 1193180L / freq;
-
- low = (char) (v & 255);
- high = (char) ((v >> 8) & 255);
-
- outb (182, 0x43);
- outb (low, 0x42);
- outb (high, 0x42);
- }
-}
-
-void uhci_show_qh (puhci_desc_t qh)
-{
- if (qh->type != QH_TYPE) {
- dbg("qh has not QH_TYPE");
- return;
- }
- dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
-
- if (qh->hw.qh.head & UHCI_PTR_TERM)
- dbg("Head Terminate");
- else {
- if (qh->hw.qh.head & UHCI_PTR_QH)
- dbg("Head points to QH");
- else
- dbg("Head points to TD");
-
- dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
- }
- if (qh->hw.qh.element & UHCI_PTR_TERM)
- dbg("Element Terminate");
- else {
-
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Element points to QH");
- else
- dbg("Element points to TD");
- dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
- }
-}
-
-void uhci_show_td (puhci_desc_t td)
-{
- char *spid;
-
- switch (td->hw.td.info & 0xff) {
- case USB_PID_SETUP:
- spid = "SETUP";
- break;
- case USB_PID_OUT:
- spid = " OUT ";
- break;
- case USB_PID_IN:
- spid = " IN ";
- break;
- default:
- spid = " ? ";
- break;
- }
-
- dbg("uhci_show_td %p (%08lX) MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
- td,
- virt_to_bus(td),
- td->hw.td.info >> 21,
- ((td->hw.td.info >> 19) & 1),
- (td->hw.td.info >> 15) & 15,
- (td->hw.td.info >> 8) & 127,
- (td->hw.td.info & 0xff),
- spid,
- td->hw.td.buffer);
-
- dbg("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
- td->hw.td.status & 0x7ff,
- ((td->hw.td.status >> 27) & 3),
- (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
- (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
- (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
- (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
- (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
- (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
- (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
- (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
- (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
- (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
- );
-
- if (td->hw.td.link & UHCI_PTR_TERM)
- dbg("Link Terminate");
- else {
- if (td->hw.td.link & UHCI_PTR_QH)
- dbg("%s, link points to QH @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- else
- dbg("%s, link points to TD @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- }
-}
-
-void uhci_show_td_queue (puhci_desc_t td)
-{
- dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
- while (1) {
- uhci_show_td (td);
- if (td->hw.td.link & UHCI_PTR_TERM)
- break;
- //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
- // break;
- if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
- td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
- else {
- dbg("td points to itself!");
- break;
- }
-// schedule();
- }
-}
-
-void uhci_show_queue (puhci_desc_t qh)
-{
- dbg("uhci_show_queue %p:", qh);
- while (1) {
- uhci_show_qh (qh);
-
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Warning: qh->element points to qh!");
- else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
- uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
-
- if (qh->hw.qh.head & UHCI_PTR_TERM)
- break;
-
- if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS))
- qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS);
- else {
- dbg("qh points to itself!");
- break;
- }
- }
-}
-
-static void uhci_show_sc (int port, unsigned short status)
-{
- dbg(" stat%d = %04x %s%s%s%s%s%s%s%s",
- 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 " : "");
-}
-
-void uhci_show_status (puhci_t s)
-{
- unsigned int io_addr = s->io_addr;
- unsigned short usbcmd, usbstat, usbint, usbfrnum;
- unsigned int flbaseadd;
- unsigned char sof;
- unsigned short portsc1, portsc2;
-
- 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);
-
- dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s",
- 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 " : "");
-
- dbg(" usbstat = %04x %s%s%s%s%s%s",
- 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 " : "");
-
- dbg(" usbint = %04x", usbint);
- dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1,
- 0xfff & (4 * (unsigned int) usbfrnum));
- dbg(" flbaseadd = %08x", flbaseadd);
- dbg(" sof = %02x", sof);
- uhci_show_sc (1, portsc1);
- uhci_show_sc (2, portsc2);
-}
-void uhci_show_qh(puhci_desc_t qh);
-void uhci_show_td(puhci_desc_t td);
-void uhci_show_td_queue(puhci_desc_t td);
-void uhci_show_queue(puhci_desc_t qh);
-void uhci_show_status(puhci_t s);
-void beep(long freq);
-void dump_urb (purb_t purb);
\ No newline at end of file
+#ifdef DEBUG
+
+static void uhci_show_qh (puhci_desc_t qh)
+{
+ if (qh->type != QH_TYPE) {
+ printk (KERN_DEBUG MODSTR "qh has not QH_TYPE\n");
+ return;
+ }
+ printk (KERN_DEBUG MODSTR "uhci_show_qh %p (%08lX):\n", qh, virt_to_bus (qh));
+
+ if (qh->hw.qh.head & UHCI_PTR_TERM)
+ printk (KERN_DEBUG MODSTR "Head Terminate\n");
+ else {
+ if (qh->hw.qh.head & UHCI_PTR_QH)
+ printk (KERN_DEBUG MODSTR "Head points to QH\n");
+ else
+ printk (KERN_DEBUG MODSTR "Head points to TD\n");
+
+ printk (KERN_DEBUG MODSTR "head: %08X\n", qh->hw.qh.head & ~UHCI_PTR_BITS);
+ }
+ if (qh->hw.qh.element & UHCI_PTR_TERM)
+ printk (KERN_DEBUG MODSTR "Element Terminate\n");
+ else {
+
+ if (qh->hw.qh.element & UHCI_PTR_QH)
+ printk (KERN_DEBUG MODSTR "Element points to QH\n");
+ else
+ printk (KERN_DEBUG MODSTR "Element points to TD\n");
+ printk (KERN_DEBUG MODSTR "element: %08X\n", qh->hw.qh.element & ~UHCI_PTR_BITS);
+ }
+}
+#endif
+
+static void uhci_show_td (puhci_desc_t td)
+{
+ char *spid;
+ printk (KERN_DEBUG MODSTR "uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
+
+ switch (td->hw.td.info & 0xff) {
+ case USB_PID_SETUP:
+ spid = "SETUP";
+ break;
+ case USB_PID_OUT:
+ spid = " OUT ";
+ break;
+ case USB_PID_IN:
+ spid = " IN ";
+ break;
+ default:
+ spid = " ? ";
+ break;
+ }
+
+ printk ("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)\n",
+ td->hw.td.info >> 21,
+ ((td->hw.td.info >> 19) & 1),
+ (td->hw.td.info >> 15) & 15,
+ (td->hw.td.info >> 8) & 127,
+ (td->hw.td.info & 0xff),
+ spid,
+ td->hw.td.buffer);
+
+ printk (KERN_DEBUG MODSTR "Len=%02x e%d %s%s%s%s%s%s%s%s%s%s\n",
+ td->hw.td.status & 0x7ff,
+ ((td->hw.td.status >> 27) & 3),
+ (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
+ (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
+ (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
+ (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
+ (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
+ (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+ (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
+ (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
+ (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+ (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
+ );
+#if 1
+ if (td->hw.td.link & UHCI_PTR_TERM)
+ printk (KERN_DEBUG MODSTR "Link Terminate\n");
+ else {
+ if (td->hw.td.link & UHCI_PTR_QH)
+ printk (KERN_DEBUG MODSTR "%s, link points to QH @ %08x\n",
+ (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
+ td->hw.td.link & ~UHCI_PTR_BITS);
+ else
+ printk (KERN_DEBUG MODSTR "%s, link points to TD @ %08x \n",
+ (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
+ td->hw.td.link & ~UHCI_PTR_BITS);
+ }
+#endif
+}
+#ifdef DEBUG
+static void uhci_show_td_queue (puhci_desc_t td)
+{
+ printk (KERN_DEBUG MODSTR "uhci_show_td_queue %p (%08lX):\n", td, virt_to_bus (td));
+ while (1) {
+ uhci_show_td (td);
+ if (td->hw.td.link & UHCI_PTR_TERM)
+ break;
+ //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
+ // break;
+ if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
+ td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
+ else {
+ printk (KERN_DEBUG MODSTR "td points to itself!\n");
+ break;
+ }
+// schedule();
+ }
+}
+
+static void uhci_show_queue (puhci_desc_t qh)
+{
+ printk (KERN_DEBUG MODSTR "uhci_show_queue %p:\n", qh);
+ while (1) {
+ uhci_show_qh (qh);
+
+ if (qh->hw.qh.element & UHCI_PTR_QH)
+ printk (KERN_DEBUG MODSTR "Warning: qh->element points to qh!\n");
+ else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+ uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
+
+ if (qh->hw.qh.head & UHCI_PTR_TERM)
+ break;
+
+ if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS))
+ qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS);
+ else {
+ printk (KERN_DEBUG MODSTR "qh points to itself!\n");
+ break;
+ }
+ }
+}
+
+static void uhci_show_sc (int port, unsigned short status)
+{
+ printk (" 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 " : "");
+}
+
+void uhci_show_status (puhci_t s)
+{
+ unsigned int io_addr = s->io_addr;
+ unsigned short usbcmd, usbstat, usbint, usbfrnum;
+ unsigned int flbaseadd;
+ unsigned char sof;
+ unsigned short portsc1, portsc2;
+
+ 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);
+
+ printk (" 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 " : "");
+
+ printk (" 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 " : "");
+
+ printk (" usbint = %04x\n", usbint);
+ printk (" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
+ 0xfff & (4 * (unsigned int) usbfrnum));
+ printk (" flbaseadd = %08x\n", flbaseadd);
+ printk (" sof = %02x\n", sof);
+ uhci_show_sc (1, portsc1);
+ uhci_show_sc (2, portsc2);
+}
+#endif
+++ /dev/null
-/*
- * Universal Host Controller Interface driver for USB (take II).
- *
- * (c) 1999 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
- * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
- * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
- * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
- *
- * HW-initalization based on material of
- *
- * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999 Johannes Erdfelt
- * (C) Copyright 1999 Randy Dunlap
- *
- * $Id: uhci.c,v 1.149 1999/12/26 20:57:14 acher Exp $
- */
-
-#include <linux/config.h>
-#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/malloc.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h> /* for in_interrupt() */
-#include <linux/init.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#undef DEBUG
-
-#include "usb.h"
-#include "uhci.h"
-#include "uhci-debug.h"
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-#define __init
-#define __exit
-#endif
-
-#ifdef __alpha
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-extern long __kernel_thread (unsigned long, int (*)(void *), void *);
-static inline long kernel_thread (int (*fn) (void *), void *arg, unsigned long flags)
-{
- return __kernel_thread (flags | CLONE_VM, fn, arg);
-}
-#undef CONFIG_APM
-#endif
-#endif
-
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-static int handle_apm_event (apm_event_t event);
-#endif
-
-/* We added an UHCI_SLAB slab support just for debugging purposes. In real
- life this compile option is NOT recommended, because slab caches are not
- suitable for modules.
-*/
-
-// #define _UHCI_SLAB
-#ifdef _UHCI_SLAB
-static kmem_cache_t *uhci_desc_kmem;
-static kmem_cache_t *urb_priv_kmem;
-#endif
-
-static int rh_submit_urb (purb_t purb);
-static int rh_unlink_urb (purb_t purb);
-static puhci_t devs = NULL;
-
-/*-------------------------------------------------------------------*/
-static void queue_urb (puhci_t s, struct list_head *p, int do_lock)
-{
- unsigned long flags=0;
-
- if (do_lock)
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
- list_add_tail (p, &s->urb_list);
-
- if (do_lock)
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
-}
-
-/*-------------------------------------------------------------------*/
-static void dequeue_urb (puhci_t s, struct list_head *p, int do_lock)
-{
- unsigned long flags=0;
-
- if (do_lock)
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
- list_del (p);
-
- if (do_lock)
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
-}
-
-/*-------------------------------------------------------------------*/
-static int alloc_td (puhci_desc_t * new, int flags)
-{
-#ifdef _UHCI_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
-#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
-#endif
- if (!*new)
- return -ENOMEM;
-
- memset (*new, 0, sizeof (uhci_desc_t));
- (*new)->hw.td.link = UHCI_PTR_TERM | (flags & UHCI_PTR_BITS); // last by default
-
- (*new)->type = TD_TYPE;
- INIT_LIST_HEAD (&(*new)->vertical);
- INIT_LIST_HEAD (&(*new)->horizontal);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-/* insert td at last position in td-list of qh (vertical) */
-static int insert_td (puhci_t s, puhci_desc_t qh, puhci_desc_t new, int flags)
-{
- uhci_desc_t *prev;
- unsigned long xxx;
-
- spin_lock_irqsave (&s->td_lock, xxx);
-
- list_add_tail (&new->vertical, &qh->vertical);
-
- if (qh->hw.qh.element & UHCI_PTR_TERM) {
- // virgin qh without any tds
- qh->hw.qh.element = virt_to_bus (new); /* QH's cannot have the DEPTH bit set */
- }
- else {
- // already tds inserted
- prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
- // implicitely remove TERM bit of prev
- prev->hw.td.link = virt_to_bus (new) | (flags & UHCI_PTR_DEPTH);
- }
-
- spin_unlock_irqrestore (&s->td_lock, xxx);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-/* insert new_td after td (horizontal) */
-static int insert_td_horizontal (puhci_t s, puhci_desc_t td, puhci_desc_t new, int flags)
-{
- uhci_desc_t *next;
- unsigned long xxx;
-
- spin_lock_irqsave (&s->td_lock, xxx);
-
- next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
- new->hw.td.link = td->hw.td.link;
- list_add (&new->horizontal, &td->horizontal);
- td->hw.td.link = virt_to_bus (new);
-
- spin_unlock_irqrestore (&s->td_lock, xxx);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int unlink_td (puhci_t s, puhci_desc_t element)
-{
- uhci_desc_t *next, *prev;
- int dir = 0;
- unsigned long xxx;
-
- spin_lock_irqsave (&s->td_lock, xxx);
-
- next = list_entry (element->vertical.next, uhci_desc_t, vertical);
-
- if (next == element) {
- dir = 1;
- next = list_entry (element->horizontal.next, uhci_desc_t, horizontal);
- prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
- }
- else {
- prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
- }
-
- if (prev->type == TD_TYPE)
- prev->hw.td.link = element->hw.td.link;
- else
- prev->hw.qh.element = element->hw.td.link;
-
- wmb ();
-
- if (dir == 0)
- list_del (&element->vertical);
- else
- list_del (&element->horizontal);
-
- spin_unlock_irqrestore (&s->td_lock, xxx);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int delete_desc (puhci_desc_t element)
-{
-#ifdef _UHCI_SLAB
- kmem_cache_free(uhci_desc_kmem, element);
-#else
- kfree (element);
-#endif
- return 0;
-}
-/*-------------------------------------------------------------------*/
-// Allocates qh element
-static int alloc_qh (puhci_desc_t * new)
-{
-#ifdef _UHCI_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
-#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
-#endif
- if (!*new)
- return -ENOMEM;
-
- memset (*new, 0, sizeof (uhci_desc_t));
- (*new)->hw.qh.head = UHCI_PTR_TERM;
- (*new)->hw.qh.element = UHCI_PTR_TERM;
- (*new)->type = QH_TYPE;
- INIT_LIST_HEAD (&(*new)->horizontal);
- INIT_LIST_HEAD (&(*new)->vertical);
-
- dbg("Allocated qh @ %p", *new);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-// inserts new qh before/after the qh at pos
-// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
-static int insert_qh (puhci_t s, puhci_desc_t pos, puhci_desc_t new, int flags)
-{
- puhci_desc_t old;
- unsigned long xxx;
-
- spin_lock_irqsave (&s->qh_lock, xxx);
-
- if (!flags) {
- // (OLD) (POS) -> (OLD) (NEW) (POS)
- old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
- list_add_tail (&new->horizontal, &pos->horizontal);
- new->hw.qh.head = MAKE_QH_ADDR (pos) ;
-
- if (!(old->hw.qh.head & UHCI_PTR_TERM))
- old->hw.qh.head = MAKE_QH_ADDR (new) ;
- }
- else {
- // (POS) (OLD) -> (POS) (NEW) (OLD)
- old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
- list_add (&new->horizontal, &pos->horizontal);
- pos->hw.qh.head = MAKE_QH_ADDR (new) ;
- new->hw.qh.head = MAKE_QH_ADDR (old);
- }
-
- wmb ();
-
- spin_unlock_irqrestore (&s->qh_lock, xxx);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int unlink_qh (puhci_t s, puhci_desc_t element)
-{
- puhci_desc_t next, prev;
- unsigned long xxx;
-
- spin_lock_irqsave (&s->qh_lock, xxx);
-
- next = list_entry (element->horizontal.next, uhci_desc_t, horizontal);
- prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
- prev->hw.qh.head = element->hw.qh.head;
- wmb ();
- list_del (&element->horizontal);
-
- spin_unlock_irqrestore (&s->qh_lock, xxx);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int delete_qh (puhci_t s, puhci_desc_t qh)
-{
- puhci_desc_t td;
- struct list_head *p;
-
- list_del (&qh->horizontal);
-
- while ((p = qh->vertical.next) != &qh->vertical) {
- td = list_entry (p, uhci_desc_t, vertical);
- unlink_td (s, td);
- delete_desc (td);
- }
-
- delete_desc (qh);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-void clean_td_chain (puhci_desc_t td)
-{
- struct list_head *p;
- puhci_desc_t td1;
-
- if (!td)
- return;
-
- while ((p = td->horizontal.next) != &td->horizontal) {
- td1 = list_entry (p, uhci_desc_t, horizontal);
- delete_desc (td1);
- }
-
- delete_desc (td);
-}
-/*-------------------------------------------------------------------*/
-// Removes ALL qhs in chain (paranoia!)
-static void cleanup_skel (puhci_t s)
-{
- unsigned int n;
- puhci_desc_t td;
-
- dbg("cleanup_skel");
-
- for (n = 0; n < 8; n++) {
- td = s->int_chain[n];
- clean_td_chain (td);
- }
-
- if (s->iso_td) {
- for (n = 0; n < 1024; n++) {
- td = s->iso_td[n];
- clean_td_chain (td);
- }
- kfree (s->iso_td);
- }
-
- if (s->framelist)
- free_page ((unsigned long) s->framelist);
-
- if (s->control_chain) {
- // completed init_skel?
- struct list_head *p;
- puhci_desc_t qh, qh1;
-
- qh = s->control_chain;
- while ((p = qh->horizontal.next) != &qh->horizontal) {
- qh1 = list_entry (p, uhci_desc_t, horizontal);
- delete_qh (s, qh1);
- }
- delete_qh (s, qh);
- }
- else {
- if (s->control_chain)
- kfree (s->control_chain);
- if (s->bulk_chain)
- kfree (s->bulk_chain);
- if (s->chain_end)
- kfree (s->chain_end);
- }
-}
-/*-------------------------------------------------------------------*/
-// allocates framelist and qh-skeletons
-// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
-static int init_skel (puhci_t s)
-{
- int n, ret;
- puhci_desc_t qh, td;
-
- dbg("init_skel");
-
- s->framelist = (__u32 *) get_free_page (GFP_KERNEL);
-
- if (!s->framelist)
- return -ENOMEM;
-
- memset (s->framelist, 0, 4096);
-
- dbg("allocating iso desc pointer list");
- s->iso_td = (puhci_desc_t *) kmalloc (1024 * sizeof (puhci_desc_t), GFP_KERNEL);
-
- if (!s->iso_td)
- goto init_skel_cleanup;
-
- s->control_chain = NULL;
- s->bulk_chain = NULL;
- s->chain_end = NULL;
-
- dbg("allocating iso descs");
- for (n = 0; n < 1024; n++) {
- // allocate skeleton iso/irq-tds
- ret = alloc_td (&td, 0);
- if (ret)
- goto init_skel_cleanup;
- s->iso_td[n] = td;
- s->framelist[n] = ((__u32) virt_to_bus (td));
- }
-
- dbg("allocating qh: chain_end");
- ret = alloc_qh (&qh);
-
- if (ret)
- goto init_skel_cleanup;
-
- s->chain_end = qh;
-
- dbg("allocating qh: bulk_chain");
- ret = alloc_qh (&qh);
-
- if (ret)
- goto init_skel_cleanup;
-
- insert_qh (s, s->chain_end, qh, 0);
- s->bulk_chain = qh;
- dbg("allocating qh: control_chain");
- ret = alloc_qh (&qh);
-
- if (ret)
- goto init_skel_cleanup;
-
- insert_qh (s, s->bulk_chain, qh, 0);
- s->control_chain = qh;
- for (n = 0; n < 8; n++)
- s->int_chain[n] = 0;
-
- dbg("allocating skeleton INT-TDs");
-
- for (n = 0; n < 8; n++) {
- puhci_desc_t td;
-
- alloc_td (&td, 0);
- if (!td)
- goto init_skel_cleanup;
- s->int_chain[n] = td;
- if (n == 0) {
- s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain);
- }
- else {
- s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
- }
- }
-
- dbg("linking skeleton INT-TDs");
-
- for (n = 0; n < 1024; n++) {
- // link all iso-tds to the interrupt chains
- int m, o;
- //dbg("framelist[%i]=%x",n,s->framelist[n]);
- for (o = 1, m = 2; m <= 128; o++, m += m) {
- // n&(m-1) = n%m
- if ((n & (m - 1)) == ((m - 1) / 2)) {
- ((puhci_desc_t) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
- }
- }
- }
-
- //uhci_show_queue(s->control_chain);
- dbg("init_skel exit");
- return 0; // OK
-
- init_skel_cleanup:
- cleanup_skel (s);
- return -ENOMEM;
-}
-
-/*-------------------------------------------------------------------*/
-static void fill_td (puhci_desc_t td, int status, int info, __u32 buffer)
-{
- td->hw.td.status = status;
- td->hw.td.info = info;
- td->hw.td.buffer = buffer;
-}
-
-/*-------------------------------------------------------------------*/
-// LOW LEVEL STUFF
-// assembles QHs und TDs for control, bulk and iso
-/*-------------------------------------------------------------------*/
-static int uhci_submit_control_urb (purb_t purb)
-{
- puhci_desc_t qh, td;
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- purb_priv_t purb_priv = purb->hcpriv;
- unsigned long destination, status;
- int maxsze = usb_maxpacket (purb->dev, purb->pipe, usb_pipeout (purb->pipe));
- unsigned long len, bytesrequested;
- char *data;
-
- dbg("uhci_submit_control start");
- alloc_qh (&qh); // alloc qh for this request
-
- if (!qh)
- return -ENOMEM;
-
- alloc_td (&td, UHCI_PTR_DEPTH); // get td for setup stage
-
- if (!td) {
- delete_qh (s, qh);
- return -ENOMEM;
- }
-
- /* The "pipe" thing contains the destination in bits 8--18 */
- destination = (purb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
-
- /* 3 errors */
- status = (purb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
- (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
-
- /* Build the TD for the control request, try forever, 8 bytes of data */
- fill_td (td, status, destination | (7 << 21), virt_to_bus (purb->setup_packet));
-
- /* 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_PID_IN); /* SETUP -> IN */
-
- if (usb_pipeout (purb->pipe))
- destination ^= (USB_PID_IN ^ USB_PID_OUT); /* IN -> OUT */
-
- insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
-#if 0
- dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", purb->pipe,
- purb->setup_packet[0], purb->setup_packet[1], purb->setup_packet[2], purb->setup_packet[3],
- purb->setup_packet[4], purb->setup_packet[5], purb->setup_packet[6], purb->setup_packet[7]);
- //uhci_show_td(td);
-#endif
- /* Build the DATA TD's */
- len = purb->transfer_buffer_length;
- bytesrequested = len;
- data = purb->transfer_buffer;
-
- while (len > 0) {
- int pktsze = len;
-
- alloc_td (&td, UHCI_PTR_DEPTH);
- if (!td) {
- delete_qh (s, qh);
- return -ENOMEM;
- }
-
- if (pktsze > maxsze)
- pktsze = maxsze;
-
- destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1
-
- fill_td (td, status, destination | ((pktsze - 1) << 21),
- virt_to_bus (data)); // Status, pktsze bytes of data
-
- insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue 'data stage'-td in qh
-
- data += pktsze;
- len -= pktsze;
- }
-
- /* Build the final TD for control status */
- /* It's only IN if the pipe is out AND we aren't expecting data */
- destination &= ~0xFF;
-
- if (usb_pipeout (purb->pipe) | (bytesrequested == 0))
- destination |= USB_PID_IN;
- else
- destination |= USB_PID_OUT;
-
- destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */
-
- alloc_td (&td, UHCI_PTR_DEPTH);
-
- if (!td) {
- delete_qh (s, qh);
- return -ENOMEM;
- }
-
- /* no limit on errors on final packet , 0 bytes of data */
- fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
- 0);
-
- insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue status td
-
-
- list_add (&qh->desc_list, &purb_priv->desc_list);
-
- /* Start it up... put low speed first */
- if (purb->pipe & TD_CTRL_LS)
- insert_qh (s, s->control_chain, qh, 1); // insert after control chain
- else
- insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
- //uhci_show_queue(s->control_chain);
-
- dbg("uhci_submit_control end");
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int uhci_submit_bulk_urb (purb_t purb)
-{
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- purb_priv_t purb_priv = purb->hcpriv;
- puhci_desc_t qh, td;
- unsigned long destination, status;
- char *data;
- unsigned int pipe = purb->pipe;
- int maxsze = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
- int info, len;
-
- /* shouldn't the clear_halt be done in the USB core or in the client driver? - Thomas */
- if (usb_endpoint_halted (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) &&
- usb_clear_halt (purb->dev, usb_pipeendpoint (pipe) | (pipe & USB_DIR_IN)))
- return -EPIPE;
-
- if (!maxsze)
- return -EMSGSIZE;
- /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
-
- alloc_qh (&qh); // get qh for this request
-
- if (!qh)
- return -ENOMEM;
-
- /* The "pipe" thing contains the destination in bits 8--18. */
- destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
-
- /* 3 errors */
- status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
- ((purb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27);
-
- /* Build the TDs for the bulk request */
- len = purb->transfer_buffer_length;
- data = purb->transfer_buffer;
- dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
-
- while (len > 0) {
- int pktsze = len;
-
- alloc_td (&td, UHCI_PTR_DEPTH);
-
- if (!td) {
- delete_qh (s, qh);
- return -ENOMEM;
- }
-
- if (pktsze > maxsze)
- pktsze = maxsze;
-
- // pktsze bytes of data
- info = destination | ((pktsze - 1) << 21) |
- (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
-
- fill_td (td, status, info, virt_to_bus (data));
-
- data += pktsze;
- len -= pktsze;
-
- if (!len)
- td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
- //dbg("insert td %p, len %i",td,pktsze);
-
- insert_td (s, qh, td, UHCI_PTR_DEPTH);
-
- /* Alternate Data0/1 (start with Data0) */
- usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
- }
-
- list_add (&qh->desc_list, &purb_priv->desc_list);
-
- insert_qh (s, s->chain_end, qh, 0); // insert before end marker
- //uhci_show_queue(s->bulk_chain);
-
- dbg("uhci_submit_bulk_urb: exit");
- return 0;
-}
-/*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-static int uhci_unlink_urb (purb_t purb)
-{
- puhci_t s;
- puhci_desc_t qh;
- puhci_desc_t td;
- purb_priv_t purb_priv;
- unsigned long flags=0;
- struct list_head *p;
-
- if (!purb) // you never know...
- return -1;
-
- s = (puhci_t) purb->dev->bus->hcpriv; // get pointer to uhci struct
-
- if (usb_pipedevice (purb->pipe) == s->rh.devnum)
- return rh_unlink_urb (purb);
-
- if(!in_interrupt()) {
- // is the following really necessary? dequeue_urb has its own spinlock (GA)
- spin_lock_irqsave (&s->unlink_urb_lock, flags); // do not allow interrupts
- }
-
- //dbg("unlink_urb called %p",purb);
- if (purb->status == USB_ST_URB_PENDING) {
- // URB probably still in work
- purb_priv = purb->hcpriv;
- dequeue_urb (s, &purb->urb_list,1);
- purb->status = USB_ST_URB_KILLED; // mark urb as killed
-
- if(!in_interrupt()) {
- spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here
- }
-
- switch (usb_pipetype (purb->pipe)) {
- case PIPE_ISOCHRONOUS:
- case PIPE_INTERRUPT:
- for (p = purb_priv->desc_list.next; p != &purb_priv->desc_list; p = p->next) {
- td = list_entry (p, uhci_desc_t, desc_list);
- unlink_td (s, td);
- }
- // wait at least 1 Frame
- if (in_interrupt ())
- udelay (1000);
- else
- schedule_timeout (1 + 1 * HZ / 1000);
- while ((p = purb_priv->desc_list.next) != &purb_priv->desc_list) {
- td = list_entry (p, uhci_desc_t, desc_list);
- list_del (p);
- delete_desc (td);
- }
- break;
-
- case PIPE_BULK:
- case PIPE_CONTROL:
- qh = list_entry (purb_priv->desc_list.next, uhci_desc_t, desc_list);
-
- unlink_qh (s, qh); // remove this qh from qh-list
- // wait at least 1 Frame
-
- if (in_interrupt ())
- udelay (1000);
- else
- schedule_timeout (1 + 1 * HZ / 1000);
- delete_qh (s, qh); // remove it physically
-
- }
-
-#ifdef _UHCI_SLAB
- kmem_cache_free(urb_priv_kmem, purb->hcpriv);
-#else
- kfree (purb->hcpriv);
-#endif
- if (purb->complete) {
- dbg("unlink_urb: calling completion");
- purb->complete ((struct urb *) purb);
- usb_dec_dev_use (purb->dev);
- }
- return 0;
- }
- else {
- if(!in_interrupt())
- spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here
- }
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-// In case of ASAP iso transfer, search the URB-list for already queued URBs
-// for this EP and calculate the earliest start frame for the new
-// URB (easy seamless URB continuation!)
-static int find_iso_limits (purb_t purb, unsigned int *start, unsigned int *end)
-{
- purb_t u, last_urb = NULL;
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- struct list_head *p = s->urb_list.next;
- int ret=-1;
- unsigned long flags;
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
- for (; p != &s->urb_list; p = p->next) {
- u = list_entry (p, urb_t, urb_list);
- // look for pending URBs with identical pipe handle
- // works only because iso doesn't toggle the data bit!
- if ((purb->pipe == u->pipe) && (purb->dev == u->dev) && (u->status == USB_ST_URB_PENDING)) {
- if (!last_urb)
- *start = u->start_frame;
- last_urb = u;
- }
- }
-
- if (last_urb) {
- *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
- ret=0;
- }
-
- spin_unlock_irqrestore(&s->urb_list_lock, flags);
-
- return ret; // no previous urb found
-
-}
-/*-------------------------------------------------------------------*/
-// adjust start_frame according to scheduling constraints (ASAP etc)
-
-static void jnx_show_desc (puhci_desc_t d)
-{
- switch (d->type) {
- case TD_TYPE:
- dbg("td @ 0x%08lx: link 0x%08x status 0x%08x info 0x%08x buffer 0x%08x",
- (unsigned long) d, d->hw.td.link, d->hw.td.status, d->hw.td.info, d->hw.td.buffer);
- if (!(d->hw.td.link & UHCI_PTR_TERM))
- jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.td.link & ~UHCI_PTR_BITS));
- break;
-
- case QH_TYPE:
- dbg("qh @ 0x%08lx: head 0x%08x element 0x%08x",
- (unsigned long) d, d->hw.qh.head, d->hw.qh.element);
- if (!(d->hw.qh.element & UHCI_PTR_TERM))
- jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.qh.element & ~UHCI_PTR_BITS));
- if (!(d->hw.qh.head & UHCI_PTR_TERM))
- jnx_show_desc ((puhci_desc_t) bus_to_virt (d->hw.qh.head & ~UHCI_PTR_BITS));
- break;
-
- default:
- dbg("desc @ 0x%08lx: invalid type %u", (unsigned long) d, d->type);
- }
-}
-
-/*-------------------------------------------------------------------*/
-static int iso_find_start (purb_t purb)
-{
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- unsigned int now;
- unsigned int start_limit = 0, stop_limit = 0, queued_size;
- int limits;
-
- now = UHCI_GET_CURRENT_FRAME (s) & 1023;
-
- if ((unsigned) purb->number_of_packets > 900)
- return -EFBIG;
-
- limits = find_iso_limits (purb, &start_limit, &stop_limit);
- queued_size = (stop_limit - start_limit) & 1023;
-
- if (purb->transfer_flags & USB_ISO_ASAP) {
- // first iso
- if (limits) {
- // 10ms setup should be enough //FIXME!
- purb->start_frame = (now + 10) & 1023;
- }
- else {
- purb->start_frame = stop_limit; //seamless linkage
-
- if (((now - purb->start_frame) & 1023) <= (unsigned) purb->number_of_packets) {
- dbg("iso_find_start: warning, ASAP gap, should not happen");
- dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
- now, purb->start_frame, purb->number_of_packets, purb->pipe);
- {
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- struct list_head *p;
- purb_t u;
- int a = -1, b = -1;
- unsigned long flags;
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
-
- for (; p != &s->urb_list; p = p->next) {
- u = list_entry (p, urb_t, urb_list);
- if (purb->dev != u->dev)
- continue;
- dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
- u->pipe, u->status, u->start_frame, u->number_of_packets);
- if (!usb_pipeisoc (u->pipe))
- continue;
- if (a == -1)
- a = u->start_frame;
- b = (u->start_frame + u->number_of_packets - 1) & 1023;
- }
- spin_unlock_irqrestore(&s->urb_list_lock, flags);
-#if 0
- if (a != -1 && b != -1) {
- do {
- jnx_show_desc (s->iso_td[a]);
- a = (a + 1) & 1023;
- }
- while (a != b);
- }
-#endif
- }
- purb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME!
- //return -EAGAIN; //FIXME
-
- }
- }
- }
- else {
- purb->start_frame &= 1023;
- if (((now - purb->start_frame) & 1023) < (unsigned) purb->number_of_packets) {
- dbg("iso_find_start: now between start_frame and end");
- return -EAGAIN;
- }
- }
-
- /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
- if (limits)
- return 0;
- if (((purb->start_frame - start_limit) & 1023) < queued_size ||
- ((purb->start_frame + purb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
- dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
- purb->start_frame, purb->number_of_packets, start_limit, stop_limit);
- return -EAGAIN;
- }
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-// submits USB interrupt (ie. polling ;-)
-// ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
-
-static int uhci_submit_int_urb (purb_t purb)
-{
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- purb_priv_t purb_priv = purb->hcpriv;
- int nint, n, ret;
- puhci_desc_t td;
- int status, destination;
- int now;
- int info;
- unsigned int pipe = purb->pipe;
-
- //dbg("SUBMIT INT");
-
- if (purb->interval < 0 || purb->interval >= 256)
- return -EINVAL;
-
- if (purb->interval == 0)
- nint = 0;
- else {
- for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n
- {
- if (purb->interval < n) {
- purb->interval = n / 2;
- break;
- }
- }
- nint--;
- }
- dbg("Rounded interval to %i, chain %i", purb->interval, nint);
-
- now = UHCI_GET_CURRENT_FRAME (s) & 1023;
- purb->start_frame = now; // remember start frame, just in case...
-
- purb->number_of_packets = 1;
-
- // INT allows only one packet
- if (purb->transfer_buffer_length > usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)))
- return -EINVAL;
-
- ret = alloc_td (&td, UHCI_PTR_DEPTH);
-
- if (ret)
- return -ENOMEM;
-
- status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
- (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (1 << 27);
-
- destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe) |
- (((purb->transfer_buffer_length - 1) & 0x7ff) << 21);
-
-
- info = destination | (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
-
- fill_td (td, status, info, virt_to_bus (purb->transfer_buffer));
- list_add_tail (&td->desc_list, &purb_priv->desc_list);
- insert_td_horizontal (s, s->int_chain[nint], td, UHCI_PTR_DEPTH); // store in INT-TDs
-
- usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-
-#if 0
- td = tdm[purb->number_of_packets];
- fill_td (td, TD_CTRL_IOC, 0, 0);
- insert_td_horizontal (s, s->iso_td[(purb->start_frame + (purb->number_of_packets) * purb->interval + 1) & 1023], td, UHCI_PTR_DEPTH);
- list_add_tail (&td->desc_list, &purb_priv->desc_list);
-#endif
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int uhci_submit_iso_urb (purb_t purb)
-{
- puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- purb_priv_t purb_priv = purb->hcpriv;
- int n, ret;
- puhci_desc_t td, *tdm;
- int status, destination;
- unsigned long flags;
- spinlock_t lock;
-
- spin_lock_init (&lock);
- spin_lock_irqsave (&lock, flags); // Disable IRQs to schedule all ISO-TDs in time
-
- ret = iso_find_start (purb); // adjusts purb->start_frame for later use
-
- if (ret)
- goto err;
-
- tdm = (puhci_desc_t *) kmalloc (purb->number_of_packets * sizeof (puhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
-
- if (!tdm) {
- ret = -ENOMEM;
- goto err;
- }
-
- // First try to get all TDs
- for (n = 0; n < purb->number_of_packets; n++) {
- dbg("n:%d purb->iso_frame_desc[n].length:%d", n, purb->iso_frame_desc[n].length);
- if (!purb->iso_frame_desc[n].length) {
- // allows ISO striping by setting length to zero in iso_descriptor
- tdm[n] = 0;
- continue;
- }
- ret = alloc_td (&td, UHCI_PTR_DEPTH);
- if (ret) {
- int i; // Cleanup allocated TDs
-
- for (i = 0; i < n; n++)
- if (tdm[i])
- kfree (tdm[i]);
- kfree (tdm);
- ret = -ENOMEM;
- goto err;
- }
- tdm[n] = td;
- }
-
- status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (purb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD);
-
- destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe);
-
- // Queue all allocated TDs
- for (n = 0; n < purb->number_of_packets; n++) {
- td = tdm[n];
- if (!td)
- continue;
- if (n + 1 >= purb->number_of_packets)
- status |= TD_CTRL_IOC;
-
- fill_td (td, status, destination | (((purb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
- virt_to_bus (purb->transfer_buffer + purb->iso_frame_desc[n].offset));
- list_add_tail (&td->desc_list, &purb_priv->desc_list);
- insert_td_horizontal (s, s->iso_td[(purb->start_frame + n) & 1023], td, UHCI_PTR_DEPTH); // store in iso-tds
- //uhci_show_td(td);
-
- }
-
- kfree (tdm);
- dbg("ISO-INT# %i, start %i, now %i", purb->number_of_packets, purb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);
- ret = 0;
-
- err:
- spin_unlock_irqrestore (&lock, flags);
- return ret;
-
-}
-/*-------------------------------------------------------------------*/
-static int search_dev_ep (puhci_t s, purb_t purb)
-{
- unsigned long flags;
- struct list_head *p = s->urb_list.next;
- purb_t tmp;
-
- dbg("search_dev_ep:");
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
- for (; p != &s->urb_list; p = p->next) {
- tmp = list_entry (p, urb_t, urb_list);
- dbg("urb: %p", tmp);
- // we can accept this urb if it is not queued at this time
- // or if non-iso transfer requests should be scheduled for the same device and pipe
- if ((usb_pipetype (purb->pipe) != PIPE_ISOCHRONOUS &&
- tmp->dev == purb->dev && tmp->pipe == purb->pipe) || (purb == tmp)) {
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
- return 1; // found another urb already queued for processing
- }
- }
-
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int uhci_submit_urb (purb_t purb)
-{
- puhci_t s;
- purb_priv_t purb_priv;
- int ret = 0;
-
- if (!purb->dev || !purb->dev->bus)
- return -ENODEV;
-
- s = (puhci_t) purb->dev->bus->hcpriv;
- //dbg("submit_urb: %p type %d",purb,usb_pipetype(purb->pipe));
-
- if (usb_pipedevice (purb->pipe) == s->rh.devnum)
- return rh_submit_urb (purb); /* virtual root hub */
-
- usb_inc_dev_use (purb->dev);
-
- if (search_dev_ep (s, purb)) {
- usb_dec_dev_use (purb->dev);
- return -ENXIO; // urb already queued
-
- }
-
-#ifdef _UHCI_SLAB
- purb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
-#else
- purb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
-#endif
- if (!purb_priv) {
- usb_dec_dev_use (purb->dev);
- return -ENOMEM;
- }
-
- purb->hcpriv = purb_priv;
- INIT_LIST_HEAD (&purb_priv->desc_list);
- purb_priv->short_control_packet=0;
- dbg("submit_urb: scheduling %p", purb);
-
- switch (usb_pipetype (purb->pipe)) {
- case PIPE_ISOCHRONOUS:
- ret = uhci_submit_iso_urb (purb);
- break;
- case PIPE_INTERRUPT:
- ret = uhci_submit_int_urb (purb);
- break;
- case PIPE_CONTROL:
- //dump_urb (purb);
- ret = uhci_submit_control_urb (purb);
- break;
- case PIPE_BULK:
- ret = uhci_submit_bulk_urb (purb);
- break;
- default:
- ret = -EINVAL;
- }
-
- dbg("submit_urb: scheduled with ret: %d", ret);
-
- if (ret != USB_ST_NOERROR) {
- usb_dec_dev_use (purb->dev);
-#ifdef _UHCI_SLAB
- kmem_cache_free(urb_priv_kmem, purb_priv);
-#else
- kfree (purb_priv);
-#endif
- return ret;
- }
-
- purb->status = USB_ST_URB_PENDING;
- queue_urb (s, &purb->urb_list,1);
- dbg("submit_urb: exit");
-
- return 0;
-}
-/*-------------------------------------------------------------------
- Virtual Root Hub
- -------------------------------------------------------------------*/
-
-static __u8 root_hub_dev_des[] =
-{
- 0x12, /* __u8 bLength; */
- 0x01, /* __u8 bDescriptorType; Device */
- 0x00, /* __u16 bcdUSB; v1.0 */
- 0x01,
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; */
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- 0x00, /* __u16 idVendor; */
- 0x00,
- 0x00, /* __u16 idProduct; */
- 0x00,
- 0x00, /* __u16 bcdDevice; */
- 0x00,
- 0x00, /* __u8 iManufacturer; */
- 0x00, /* __u8 iProduct; */
- 0x00, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, /* __u16 wTotalLength; */
- 0x00,
- 0x01, /* __u8 bNumInterfaces; */
- 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; */
-
- /* 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; */
- 0x00, /* __u8 if_iInterface; */
-
- /* endpoint */
- 0x07, /* __u8 ep_bLength; */
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
- 0x00,
- 0xff /* __u8 ep_bInterval; 255 ms */
-};
-
-
-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 *** */
-};
-
-/*-------------------------------------------------------------------------*/
-/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-static int rh_send_irq (purb_t purb)
-{
-
- int len = 1;
- int i;
- puhci_t uhci = purb->dev->bus->hcpriv;
- unsigned int io_addr = uhci->io_addr;
- __u16 data = 0;
-
- for (i = 0; i < uhci->rh.numports; i++) {
- data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
- len = (i + 1) / 8 + 1;
- }
-
- *(__u16 *) purb->transfer_buffer = cpu_to_le16 (data);
- purb->actual_length = len;
- purb->status = USB_ST_NOERROR;
-
- if ((data > 0) && (uhci->rh.send != 0)) {
- dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
- inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
- purb->complete (purb);
-
- }
- return USB_ST_NOERROR;
-}
-
-/*-------------------------------------------------------------------------*/
-/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-static int rh_init_int_timer (purb_t purb);
-
-static void rh_int_timer_do (unsigned long ptr)
-{
- int len;
-
- purb_t purb = (purb_t) ptr;
- puhci_t uhci = purb->dev->bus->hcpriv;
-
- if (uhci->rh.send) {
- len = rh_send_irq (purb);
- if (len > 0) {
- purb->actual_length = len;
- if (purb->complete)
- purb->complete (purb);
- }
- }
- rh_init_int_timer (purb);
-}
-
-/*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer (purb_t purb)
-{
- puhci_t uhci = purb->dev->bus->hcpriv;
-
- uhci->rh.interval = purb->interval;
- init_timer (&uhci->rh.rh_int_timer);
- uhci->rh.rh_int_timer.function = rh_int_timer_do;
- uhci->rh.rh_int_timer.data = (unsigned long) purb;
- uhci->rh.rh_int_timer.expires = jiffies + (HZ * (purb->interval < 30 ? 30 : purb->interval)) / 1000;
- add_timer (&uhci->rh.rh_int_timer);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-#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))
-
-
-/*-------------------------------------------------------------------------*/
-/****
- ** Root Hub Control Pipe
- *************************/
-
-
-static int rh_submit_urb (purb_t purb)
-{
- struct usb_device *usb_dev = purb->dev;
- puhci_t uhci = usb_dev->bus->hcpriv;
- unsigned int pipe = purb->pipe;
- devrequest *cmd = (devrequest *) purb->setup_packet;
- void *data = purb->transfer_buffer;
- int leni = purb->transfer_buffer_length;
- int len = 0;
- int status = 0;
- int stat = USB_ST_NOERROR;
- int i;
- unsigned int io_addr = uhci->io_addr;
- __u16 cstatus;
-
- __u16 bmRType_bReq;
- __u16 wValue;
- __u16 wIndex;
- __u16 wLength;
-
- if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
- dbg("Root-Hub submit IRQ: every %d ms", purb->interval);
- uhci->rh.urb = purb;
- uhci->rh.send = 1;
- uhci->rh.interval = purb->interval;
- rh_init_int_timer (purb);
-
- return USB_ST_NOERROR;
- }
-
-
- bmRType_bReq = cmd->requesttype | cmd->request << 8;
- wValue = le16_to_cpu (cmd->value);
- wIndex = le16_to_cpu (cmd->index);
- wLength = le16_to_cpu (cmd->length);
-
- for (i = 0; i < 8; i++)
- uhci->rh.c_p_r[i] = 0;
-
- dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x",
- uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
-
- switch (bmRType_bReq) {
- /* 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 RH_GET_STATUS:
- *(__u16 *) data = cpu_to_le16 (1);
- OK (2);
- case RH_GET_STATUS | RH_INTERFACE:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
- case RH_GET_STATUS | RH_ENDPOINT:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
- case RH_GET_STATUS | RH_CLASS:
- *(__u32 *) data = cpu_to_le32 (0);
- OK (4); /* hub power ** */
- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
- cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
- ((status & USBPORTSC_PEC) >> (3 - 1)) |
- (uhci->rh.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 *) data = cpu_to_le16 (status);
- *(__u16 *) (data + 2) = cpu_to_le16 (cstatus);
- OK (4);
-
- case RH_CLEAR_FEATURE | RH_ENDPOINT:
- switch (wValue) {
- case (RH_ENDPOINT_STALL):
- OK (0);
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_CLASS:
- switch (wValue) {
- case (RH_C_HUB_OVER_CURRENT):
- OK (0); /* hub power over current ** */
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_ENABLE):
- CLR_RH_PORTSTAT (USBPORTSC_PE);
- OK (0);
- case (RH_PORT_SUSPEND):
- CLR_RH_PORTSTAT (USBPORTSC_SUSP);
- OK (0);
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_C_PORT_CONNECTION):
- SET_RH_PORTSTAT (USBPORTSC_CSC);
- OK (0);
- case (RH_C_PORT_ENABLE):
- SET_RH_PORTSTAT (USBPORTSC_PEC);
- OK (0);
- case (RH_C_PORT_SUSPEND):
-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
- OK (0);
- case (RH_C_PORT_OVER_CURRENT):
- OK (0); /* port power over current ** */
- case (RH_C_PORT_RESET):
- uhci->rh.c_p_r[wIndex - 1] = 0;
- OK (0);
- }
- break;
-
- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_SUSPEND):
- SET_RH_PORTSTAT (USBPORTSC_SUSP);
- OK (0);
- case (RH_PORT_RESET):
- SET_RH_PORTSTAT (USBPORTSC_PR);
- wait_ms (10);
- uhci->rh.c_p_r[wIndex - 1] = 1;
- CLR_RH_PORTSTAT (USBPORTSC_PR);
- udelay (10);
- SET_RH_PORTSTAT (USBPORTSC_PE);
- wait_ms (10);
- SET_RH_PORTSTAT (0xa);
- OK (0);
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_PORT_ENABLE):
- SET_RH_PORTSTAT (USBPORTSC_PE);
- OK (0);
- }
- break;
-
- case RH_SET_ADDRESS:
- uhci->rh.devnum = wValue;
- OK (0);
-
- case RH_GET_DESCRIPTOR:
- switch ((wValue & 0xff00) >> 8) {
- case (0x01): /* device descriptor */
- len = min (leni, min (sizeof (root_hub_dev_des), wLength));
- memcpy (data, root_hub_dev_des, len);
- OK (len);
- case (0x02): /* configuration descriptor */
- len = min (leni, min (sizeof (root_hub_config_des), wLength));
- memcpy (data, root_hub_config_des, len);
- OK (len);
- case (0x03): /*string descriptors */
- stat = -EPIPE;
- }
- break;
-
- case RH_GET_DESCRIPTOR | RH_CLASS:
- root_hub_hub_des[2] = uhci->rh.numports;
- len = min (leni, min (sizeof (root_hub_hub_des), wLength));
- memcpy (data, root_hub_hub_des, len);
- OK (len);
-
- case RH_GET_CONFIGURATION:
- *(__u8 *) data = 0x01;
- OK (1);
-
- case RH_SET_CONFIGURATION:
- OK (0);
- default:
- stat = -EPIPE;
- }
-
-
- dbg("Root-Hub stat port1: %x port2: %x",
- inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
-
- purb->actual_length = len;
- purb->status = stat;
- if (purb->complete)
- purb->complete (purb);
- return USB_ST_NOERROR;
-}
-/*-------------------------------------------------------------------------*/
-
-static int rh_unlink_urb (purb_t purb)
-{
- puhci_t uhci = purb->dev->bus->hcpriv;
-
- dbg("Root-Hub unlink IRQ");
- uhci->rh.send = 0;
- del_timer (&uhci->rh.rh_int_timer);
- return 0;
-}
-/*-------------------------------------------------------------------*/
-
-#define UHCI_DEBUG
-
-/*
- * 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 USB_ST_NOERROR;
- if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */
- return USB_ST_BITSTUFF;
- if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */
- if (dir_out)
- return USB_ST_NORESPONSE;
- else
- return USB_ST_CRC;
- }
- if (status & TD_CTRL_NAK) /* NAK */
- return USB_ST_TIMEOUT;
- if (status & TD_CTRL_BABBLE) /* Babble */
- return -EPIPE;
- if (status & TD_CTRL_DBUFERR) /* Buffer error */
- return USB_ST_BUFFERUNDERRUN;
- if (status & TD_CTRL_STALLED) /* Stalled */
- return -EPIPE;
- if (status & TD_CTRL_ACTIVE) /* Active */
- return USB_ST_NOERROR;
-
- return USB_ST_INTERNALERROR;
-}
-
-/*
- * Only the USB core should call uhci_alloc_dev and uhci_free_dev
- */
-static int uhci_alloc_dev (struct usb_device *usb_dev)
-{
- return 0;
-}
-
-static int uhci_free_dev (struct usb_device *usb_dev)
-{
- 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 usb_device *usb_dev)
-{
- return UHCI_GET_CURRENT_FRAME ((puhci_t) usb_dev->bus->hcpriv);
-}
-
-struct usb_operations uhci_device_operations =
-{
- uhci_alloc_dev,
- uhci_free_dev,
- uhci_get_current_frame_number,
- uhci_submit_urb,
- uhci_unlink_urb
-};
-
-/*
- * For IN-control transfers, process_transfer gets a bit more complicated,
- * since there are devices that return less data (eg. strings) than they
- * have announced. This leads to a queue abort due to the short packet,
- * the status stage is not executed. If this happens, the status stage
- * is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
- */
-static int process_transfer (puhci_t s, purb_t purb)
-{
- int ret = USB_ST_NOERROR;
- purb_priv_t purb_priv = purb->hcpriv;
- struct list_head *qhl = purb_priv->desc_list.next;
- puhci_desc_t qh = list_entry (qhl, uhci_desc_t, desc_list);
- struct list_head *p = qh->vertical.next;
- puhci_desc_t desc= list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
- puhci_desc_t last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
- int data_toggle = usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); // save initial data_toggle
-
-
- // extracted and remapped info from TD
- int maxlength;
- int actual_length;
- int status = USB_ST_NOERROR;
-
- dbg("process_transfer: urb contains bulk/control request");
-
-
- /* if the status phase has been retriggered and the
- queue is empty or the last status-TD is inactive, the retriggered
- status stage is completed
- */
-#if 1
- if (purb_priv->short_control_packet &&
- ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE))))
- goto transfer_finished;
-#endif
- purb->actual_length=0;
-
- for (; p != &qh->vertical; p = p->next) {
- desc = list_entry (p, uhci_desc_t, vertical);
-
- if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs
- return ret;
-
- // extract transfer parameters from TD
- actual_length = (desc->hw.td.status + 1) & 0x7ff;
- maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
- status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
-
- // see if EP is stalled
- if (status == -EPIPE) {
- // set up stalled condition
- usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
- }
-
- // if any error occured stop processing of further TDs
- if (status != USB_ST_NOERROR) {
- // only set ret if status returned an error
- uhci_show_td (desc);
- ret = status;
- purb->error_count++;
- break;
- }
- else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
- purb->actual_length += actual_length;
-
-#if 0
- if (i++==0)
- uhci_show_td (desc); // show first TD of each transfer
-#endif
-
- // got less data than requested
- if ( (actual_length < maxlength)) {
- if (purb->transfer_flags & USB_DISABLE_SPD) {
- ret = USB_ST_SHORT_PACKET; // treat as real error
- dbg("process_transfer: SPD!!");
- break; // exit after this TD because SP was detected
- }
-
- // short read during control-IN: re-start status stage
- if ((usb_pipetype (purb->pipe) == PIPE_CONTROL)) {
- if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {
- uhci_show_td (last_desc);
- qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
- info("short packet during control transfer, retrigger status stage @ %p",last_desc);
- purb_priv->short_control_packet=1;
- return 0;
- }
- }
- // all other cases: short read is OK
- data_toggle = uhci_toggle (desc->hw.td.info);
- break;
- }
-
- data_toggle = uhci_toggle (desc->hw.td.info);
- //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
-
- }
- usb_settoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe), !data_toggle);
- transfer_finished:
-
- /* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of */
- /* the amount we requested */
- if (desc->hw.td.status & TD_CTRL_IOC &&
- status & TD_CTRL_ACTIVE &&
- status & TD_CTRL_NAK )
- {
- ret=0;
- status=0;
- }
-
- unlink_qh (s, qh);
- delete_qh (s, qh);
-
- purb->status = status;
-
- dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
- purb,purb->transfer_buffer_length,purb->actual_length, purb->status, purb->error_count);
- //dbg("process_transfer: exit");
- return ret;
-}
-
-static int process_interrupt (puhci_t s, purb_t purb)
-{
- int i, ret = USB_ST_URB_PENDING;
- purb_priv_t purb_priv = purb->hcpriv;
- struct list_head *p = purb_priv->desc_list.next;
- puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
- int data_toggle = usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe),
- usb_pipeout (purb->pipe)); // save initial data_toggle
- // extracted and remapped info from TD
-
- int actual_length;
- int status = USB_ST_NOERROR;
-
- //dbg("urb contains interrupt request");
-
- for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-)
- {
- desc = list_entry (p, uhci_desc_t, desc_list);
-
- if (desc->hw.td.status & TD_CTRL_ACTIVE) {
- // do not process active TDs
- //dbg("TD ACT Status @%p %08x",desc,desc->hw.td.status);
- break;
- }
-
- if (!desc->hw.td.status & TD_CTRL_IOC) {
- // do not process one-shot TDs, no recycling
- break;
- }
- // extract transfer parameters from TD
-
- actual_length = (desc->hw.td.status + 1) & 0x7ff;
- status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
-
- // see if EP is stalled
- if (status == -EPIPE) {
- // set up stalled condition
- usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
- }
-
- // if any error occured: ignore this td, and continue
- if (status != USB_ST_NOERROR) {
- purb->error_count++;
- goto recycle;
- }
- else
- purb->actual_length = actual_length;
-
- // FIXME: SPD?
-
- data_toggle = uhci_toggle (desc->hw.td.info);
-
- if (purb->complete && status != USB_ST_TIMEOUT) {
- // for last td, no user completion is needed
- dbg("process_interrupt: calling completion");
- purb->status = status;
- purb->complete ((struct urb *) purb);
- purb->status = USB_ST_URB_PENDING;
- }
- recycle:
- // Recycle INT-TD if interval!=0, else mark TD as one-shot
- if (purb->interval) {
- desc->hw.td.status |= TD_CTRL_ACTIVE;
- desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
- desc->hw.td.info |= (usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe),
- usb_pipeout (purb->pipe)) << TD_TOKEN_TOGGLE);
- usb_dotoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
- }
- else {
- desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD
- }
- }
-
- return ret;
-}
-
-
-static int process_iso (puhci_t s, purb_t purb)
-{
- int i;
- int ret = USB_ST_NOERROR;
- purb_priv_t purb_priv = purb->hcpriv;
- struct list_head *p = purb_priv->desc_list.next;
- puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
-
- dbg("urb contains iso request");
- if (desc->hw.td.status & TD_CTRL_ACTIVE)
- return USB_ST_PARTIAL_ERROR; // last TD not finished
-
- purb->error_count = 0;
- purb->actual_length = 0;
- purb->status = USB_ST_NOERROR;
-
- for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) {
- desc = list_entry (p, uhci_desc_t, desc_list);
-
- //uhci_show_td(desc);
- if (desc->hw.td.status & TD_CTRL_ACTIVE) {
- // means we have completed the last TD, but not the TDs before
- dbg("TD still active (%x)- grrr. paranoia!", desc->hw.td.status);
- ret = USB_ST_PARTIAL_ERROR;
- purb->iso_frame_desc[i].status = ret;
- unlink_td (s, desc);
- goto err;
- }
-
- unlink_td (s, desc);
-
- if (purb->number_of_packets <= i) {
- dbg("purb->number_of_packets (%d)<=(%d)", purb->number_of_packets, i);
- ret = USB_ST_URB_INVALID_ERROR;
- goto err;
- }
-
- if (purb->iso_frame_desc[i].offset + purb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) {
- // Hm, something really weird is going on
- dbg("Pointer Paranoia: %p!=%p", purb->iso_frame_desc[i].offset + purb->transfer_buffer, bus_to_virt (desc->hw.td.buffer));
- ret = USB_ST_URB_INVALID_ERROR;
- purb->iso_frame_desc[i].status = ret;
- goto err;
- }
- purb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff;
- purb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
- purb->actual_length += purb->iso_frame_desc[i].actual_length;
-
- err:
-
- if (purb->iso_frame_desc[i].status != USB_ST_NOERROR) {
- purb->error_count++;
- purb->status = purb->iso_frame_desc[i].status;
- }
- dbg("process_iso: len:%d status:%x",
- purb->iso_frame_desc[i].length, purb->iso_frame_desc[i].status);
-
- delete_desc (desc);
- list_del (p);
- }
- dbg("process_iso: exit %i (%d)", i, ret);
- return ret;
-}
-
-
-static int process_urb (puhci_t s, struct list_head *p)
-{
- int ret = USB_ST_NOERROR;
- purb_t purb;
-
- spin_lock(&s->urb_list_lock);
- purb=list_entry (p, urb_t, urb_list);
- dbg("found queued urb: %p", purb);
-
- switch (usb_pipetype (purb->pipe)) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- ret = process_transfer (s, purb);
- break;
- case PIPE_ISOCHRONOUS:
- ret = process_iso (s, purb);
- break;
- case PIPE_INTERRUPT:
- ret = process_interrupt (s, purb);
- break;
- }
-
- spin_unlock(&s->urb_list_lock);
-
- if (purb->status != USB_ST_URB_PENDING) {
- int proceed = 0;
- dbg("dequeued urb: %p", purb);
- dequeue_urb (s, p, 1);
-
-#ifdef _UHCI_SLAB
- kmem_cache_free(urb_priv_kmem, purb->hcpriv);
-#else
- kfree (purb->hcpriv);
-#endif
-
- if ((usb_pipetype (purb->pipe) != PIPE_INTERRUPT)) {
- purb_t tmp = purb->next; // pointer to first urb
- int is_ring = 0;
-
- if (purb->next) {
- do {
- if (tmp->status != USB_ST_URB_PENDING) {
- proceed = 1;
- break;
- }
- tmp = tmp->next;
- }
- while (tmp != NULL && tmp != purb->next);
- if (tmp == purb->next)
- is_ring = 1;
- }
-
- // In case you need the current URB status for your completion handler
- if (purb->complete && (!proceed || (purb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
- dbg("process_transfer: calling early completion");
- purb->complete ((struct urb *) purb);
- if (!proceed && is_ring && (purb->status != USB_ST_URB_KILLED))
- uhci_submit_urb (purb);
- }
-
- if (proceed && purb->next) {
- // if there are linked urbs - handle submitting of them right now.
- tmp = purb->next; // pointer to first urb
-
- do {
- if ((tmp->status != USB_ST_URB_PENDING) && (tmp->status != USB_ST_URB_KILLED) && uhci_submit_urb (tmp) != USB_ST_NOERROR)
- break;
- tmp = tmp->next;
- }
- while (tmp != NULL && tmp != purb->next); // submit until we reach NULL or our own pointer or submit fails
-
- if (purb->complete && !(purb->transfer_flags & USB_URB_EARLY_COMPLETE)) {
- dbg("process_transfer: calling completion");
- purb->complete ((struct urb *) purb);
- }
- }
- usb_dec_dev_use (purb->dev);
- }
- }
-
- return ret;
-}
-
-static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
-{
- puhci_t s = __uhci;
- unsigned int io_addr = s->io_addr;
- unsigned short status;
- struct list_head *p, *p2;
-
- /*
- * Read the interrupt status, and write it back to clear the
- * interrupt cause
- */
- dbg("interrupt");
- status = inw (io_addr + USBSTS);
-
- if (!status) /* shared interrupt, not mine */
- return;
-
- if (status != 1) {
- dbg("interrupt, status %x", status);
- //uhci_show_status (s);
- }
- //beep(1000);
- /*
- * the following is very subtle and was blatantly wrong before
- * traverse the list in *reverse* direction, because new entries
- * may be added at the end.
- * also, because process_urb may unlink the current urb,
- * we need to advance the list before
- * - Thomas Sailer
- */
-
- spin_lock(&s->unlink_urb_lock);
- spin_lock (&s->urb_list_lock);
- p = s->urb_list.prev;
- spin_unlock (&s->urb_list_lock);
-
- while (p != &s->urb_list) {
- p2 = p;
- p = p->prev;
- process_urb (s, p2);
- }
-
- spin_unlock(&s->unlink_urb_lock);
-
- outw (status, io_addr + USBSTS);
-#ifdef __alpha
- mb (); // ?
-#endif
- dbg("done");
-}
-
-static void reset_hc (puhci_t s)
-{
- unsigned int io_addr = s->io_addr;
-
- s->apm_state = 0;
- /* Global reset for 50ms */
- outw (USBCMD_GRESET, io_addr + USBCMD);
- wait_ms (50);
- outw (0, io_addr + USBCMD);
- wait_ms (10);
-}
-
-static void start_hc (puhci_t s)
-{
- unsigned int io_addr = s->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) {
- err("USBCMD_HCRESET timed out!");
- 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 (virt_to_bus (s->framelist), io_addr + USBFLBASEADD);
-
- /* Run and mark it configured with a 64-byte max packet */
- outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
- s->apm_state = 1;
-}
-
-static void __exit uhci_cleanup_dev(puhci_t s)
-{
- struct usb_device *root_hub = s->bus->root_hub;
- if (root_hub)
- usb_disconnect (&root_hub);
-
- usb_deregister_bus (s->bus);
-
- reset_hc (s);
- release_region (s->io_addr, s->io_size);
- free_irq (s->irq, s);
- usb_free_bus (s->bus);
- cleanup_skel (s);
- kfree (s);
-
-}
-
-static int __init uhci_start_usb (puhci_t s)
-{ /* start it up */
- /* connect the virtual root hub */
- struct usb_device *usb_dev;
-
- usb_dev = usb_alloc_dev (NULL, s->bus);
- if (!usb_dev)
- return -1;
-
- s->bus->root_hub = usb_dev;
- usb_connect (usb_dev);
-
- if (usb_new_device (usb_dev) != 0) {
- usb_free_dev (usb_dev);
- return -1;
- }
-
- return 0;
-}
-
-static int __init alloc_uhci (int irq, unsigned int io_addr, unsigned int io_size)
-{
- puhci_t s;
- struct usb_bus *bus;
-
- s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
- if (!s)
- return -1;
-
- memset (s, 0, sizeof (uhci_t));
- INIT_LIST_HEAD (&s->urb_list);
- spin_lock_init (&s->urb_list_lock);
- spin_lock_init (&s->qh_lock);
- spin_lock_init (&s->td_lock);
- spin_lock_init (&s->unlink_urb_lock);
- s->irq = -1;
- s->io_addr = io_addr;
- s->io_size = io_size;
- s->next = devs; //chain new uhci device into global list
-
- bus = usb_alloc_bus (&uhci_device_operations);
- if (!bus) {
- kfree (s);
- return -1;
- }
-
- s->bus = bus;
- bus->hcpriv = s;
-
- /* 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, so default to 2 */
- /* According to the UHCI spec, Bit 7 is always set to 1. So we try */
- /* to use this to our advantage */
-
- for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) {
- unsigned int portstatus;
-
- portstatus = inw (io_addr + 0x10 + (s->maxports * 2));
- dbg("port %i, adr %x status %x", s->maxports,
- io_addr + 0x10 + (s->maxports * 2), portstatus);
- if (!(portstatus & 0x0080))
- break;
- }
- dbg("Detected %d ports", s->maxports);
-
- /* This is experimental so anything less than 2 or greater than 8 is */
- /* something weird and we'll ignore it */
- if (s->maxports < 2 || s->maxports > 8) {
- dbg("Port count misdetected, forcing to 2 ports");
- s->maxports = 2;
- }
-
- s->rh.numports = s->maxports;
-
- if (init_skel (s)) {
- usb_free_bus (bus);
- kfree(s);
- return -1;
- }
-
- request_region (s->io_addr, io_size, MODNAME);
- reset_hc (s);
- usb_register_bus (s->bus);
-
- start_hc (s);
-
- if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
- err("request_irq %d failed!",irq);
- usb_free_bus (bus);
- reset_hc (s);
- release_region (s->io_addr, s->io_size);
- cleanup_skel(s);
- kfree(s);
- return -1;
- }
-
- s->irq = irq;
-
- if(uhci_start_usb (s) < 0) {
- uhci_cleanup_dev(s);
- return -1;
- }
-
- //chain new uhci device into global list
- devs = s;
-
- return 0;
-}
-
-static int __init start_uhci (struct pci_dev *dev)
-{
- int i;
-
- /* Search for the IO base address.. */
- for (i = 0; i < 6; i++) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
- unsigned int io_addr = dev->resource[i].start;
- unsigned int io_size =
- dev->resource[i].end - dev->resource[i].start + 1;
- if (!(dev->resource[i].flags & 1))
- continue;
-#else
- unsigned int io_addr = dev->base_address[i];
- unsigned int io_size = 0x14;
- if (!(io_addr & 1))
- continue;
- io_addr &= ~1;
-#endif
-
- /* Is it already in use? */
- if (check_region (io_addr, io_size))
- break;
- /* disable legacy emulation */
- pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
-
- return alloc_uhci(dev->irq, io_addr, io_size);
- }
- return -1;
-}
-
-#ifdef CONFIG_APM
-static int handle_apm_event (apm_event_t event)
-{
- static int down = 0;
- puhci_t s = devs;
- dbg("handle_apm_event(%d)", event);
- switch (event) {
- case APM_SYS_SUSPEND:
- case APM_USER_SUSPEND:
- if (down) {
- dbg("received extra suspend event");
- break;
- }
- while (s) {
- reset_hc (s);
- s = s->next;
- }
- down = 1;
- break;
- case APM_NORMAL_RESUME:
- case APM_CRITICAL_RESUME:
- if (!down) {
- dbg("received bogus resume event");
- break;
- }
- down = 0;
- while (s) {
- start_hc (s);
- s = s->next;
- }
- break;
- }
- return 0;
-}
-#endif
-
-int __init uhci_init (void)
-{
- int retval = -ENODEV;
- struct pci_dev *dev = NULL;
- u8 type;
- int i=0;
-
-#ifdef _UHCI_SLAB
- char *slabname=kmalloc(16, GFP_KERNEL);
-
- if(!slabname)
- return -ENOMEM;
-
- strcpy(slabname, "uhci_desc");
- uhci_desc_kmem = kmem_cache_create(slabname, sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-
- if(!uhci_desc_kmem) {
- err("kmem_cache_create for uhci_desc failed (out of memory)");
- return -ENOMEM;
- }
-
- slabname=kmalloc(16, GFP_KERNEL);
-
- if(!slabname)
- return -ENOMEM;
-
- strcpy(slabname, "urb_priv");
- urb_priv_kmem = kmem_cache_create(slabname, sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-
- if(!urb_priv_kmem) {
- err("kmem_cache_create for urb_priv_t failed (out of memory)");
- return -ENOMEM;
- }
-#endif
- info(VERSTR);
-
- for (;;) {
- dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
- if (!dev)
- break;
-
- /* Is it UHCI */
- pci_read_config_byte (dev, PCI_CLASS_PROG, &type);
- if (type != 0)
- continue;
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
- pci_enable_device (dev);
-#endif
- if(!dev->irq)
- {
- err("Found UHCI device with no IRQ assigned. Check BIOS settings!");
- continue;
- }
-
- /* Ok set it up */
- retval = start_uhci (dev);
-
- if (!retval)
- i++;
-
- }
-
-#ifdef CONFIG_APM
- if(i)
- apm_register_callback (&handle_apm_event);
-#endif
- return retval;
-}
-
-void __exit uhci_cleanup (void)
-{
- puhci_t s;
- while ((s = devs)) {
- devs = devs->next;
- uhci_cleanup_dev(s);
- }
-#ifdef _UHCI_SLAB
- kmem_cache_shrink(uhci_desc_kmem);
- kmem_cache_shrink(urb_priv_kmem);
-#endif
-}
-
-#ifdef MODULE
-int init_module (void)
-{
- return uhci_init ();
-}
-
-void cleanup_module (void)
-{
-#ifdef CONFIG_APM
- apm_unregister_callback (&handle_apm_event);
-#endif
- uhci_cleanup ();
-}
-
-#endif //MODULE
+++ /dev/null
-#ifndef __LINUX_UHCI_H
-#define __LINUX_UHCI_H
-
-/*
- $Id: uhci.h,v 1.30 1999/12/15 17:57:25 fliegl Exp $
- */
-#define MODNAME "usb-uhci"
-#define VERSTR "version v0.9 time " __TIME__ " " __DATE__
-
-/* 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_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 0x000F
-#define UHCI_PTR_TERM 0x0001
-#define UHCI_PTR_QH 0x0002
-#define UHCI_PTR_DEPTH 0x0004
-
-#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 */
-
-/*
- * for 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_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_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
-#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
-#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS)
-
-/*
- * for TD <flags>:
- */
-#define UHCI_TD_REMOVE 0x0001 /* Remove when done */
-
-/*
- * for TD <info>: (a.k.a. Token)
- */
-#define TD_TOKEN_TOGGLE 19
-
-#define uhci_maxlen(token) ((token) >> 21)
-#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
-#define uhci_endpoint(token) (((token) >> 15) & 0xf)
-#define uhci_devaddr(token) (((token) >> 8) & 0x7f)
-#define uhci_devep(token) (((token) >> 8) & 0x7ff)
-#define uhci_packetid(token) ((token) & 0xff)
-#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
-#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
-
-/* ------------------------------------------------------------------------------------
- New TD/QH-structures
- ------------------------------------------------------------------------------------ */
-typedef enum {
- TD_TYPE, QH_TYPE
-} uhci_desc_type_t;
-
-typedef struct {
- __u32 link;
- __u32 status;
- __u32 info;
- __u32 buffer;
-} uhci_td_t, *puhci_td_t;
-
-typedef struct {
- __u32 head;
- __u32 element; /* Queue element pointer */
-} uhci_qh_t, *puhci_qh_t;
-
-typedef struct {
- union {
- uhci_td_t td;
- uhci_qh_t qh;
- } hw;
- uhci_desc_type_t type;
- struct list_head horizontal;
- struct list_head vertical;
- struct list_head desc_list;
-} uhci_desc_t, *puhci_desc_t;
-
-typedef struct {
- struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
- int short_control_packet;
-} urb_priv_t, *purb_priv_t;
-
-struct virt_root_hub {
- int devnum; /* Address of Root Hub endpoint */
- void *urb;
- void *int_addr;
- int send;
- int interval;
- int numports;
- int c_p_r[8];
- struct timer_list rh_int_timer;
-};
-
-typedef struct uhci {
- int irq;
- unsigned int io_addr;
- unsigned int io_size;
- unsigned int maxports;
-
- int apm_state;
-
- struct uhci *next; // chain of uhci device contexts
-
- struct list_head urb_list; // list of all pending urbs
-
- spinlock_t urb_list_lock; // lock to keep consistency
-
- struct usb_bus *bus; // our bus
-
- spinlock_t unlink_urb_lock; // lock for unlink_urb
-
- __u32 *framelist;
- uhci_desc_t **iso_td;
- uhci_desc_t *int_chain[8];
- uhci_desc_t *control_chain;
- uhci_desc_t *bulk_chain;
- uhci_desc_t *chain_end;
- spinlock_t qh_lock;
- spinlock_t td_lock;
- struct virt_root_hub rh; //private data of the virtual root hub
-} uhci_t, *puhci_t;
-
-
-#define MAKE_TD_ADDR(a) (virt_to_bus(a)&~UHCI_PTR_QH)
-#define MAKE_QH_ADDR(a) (virt_to_bus(a)|UHCI_PTR_QH)
-#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM))
-
-/* ------------------------------------------------------------------------------------
- Virtual Root HUB
- ------------------------------------------------------------------------------------ */
-/* destination of request */
-#define RH_INTERFACE 0x01
-#define RH_ENDPOINT 0x02
-#define RH_OTHER 0x03
-
-#define RH_CLASS 0x20
-#define RH_VENDOR 0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS 0x0080
-#define RH_CLEAR_FEATURE 0x0100
-#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
-#define RH_SET_DESCRIPTOR 0x0700
-#define RH_GET_CONFIGURATION 0x0880
-#define RH_SET_CONFIGURATION 0x0900
-#define RH_GET_STATE 0x0280
-#define RH_GET_INTERFACE 0x0A80
-#define RH_SET_INTERFACE 0x0B00
-#define RH_SYNC_FRAME 0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP 0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION 0x00
-#define RH_PORT_ENABLE 0x01
-#define RH_PORT_SUSPEND 0x02
-#define RH_PORT_OVER_CURRENT 0x03
-#define RH_PORT_RESET 0x04
-#define RH_PORT_POWER 0x08
-#define RH_PORT_LOW_SPEED 0x09
-#define RH_C_PORT_CONNECTION 0x10
-#define RH_C_PORT_ENABLE 0x11
-#define RH_C_PORT_SUSPEND 0x12
-#define RH_C_PORT_OVER_CURRENT 0x13
-#define RH_C_PORT_RESET 0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER 0x00
-#define RH_C_HUB_OVER_CURRENT 0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP 0x00
-#define RH_ENDPOINT_STALL 0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP 0x00
-
-
-#define RH_ACK 0x01
-#define RH_REQ_ERR -1
-#define RH_NACK 0x00
-
-#define min(a,b) (((a)<(b))?(a):(b))
-
-#endif
int usb_acm_init(void);
int usb_audio_init(void);
int usb_cpia_init(void);
+int usb_ibmcam_init(void);
int usb_ov511_init(void);
int usb_dc2xx_init(void);
int usb_scanner_init(void);
#ifdef CONFIG_USB_CPIA
usb_cpia_init();
#endif
+#ifdef CONFIG_USB_IBMCAM
+ usb_ibmcam_init();
+#endif
#ifdef CONFIG_USB_OV511
usb_ov511_init();
#endif
*/
#include <linux/version.h>
#include <linux/kernel.h>
-#include <linux/slab.h>
-
+#include <linux/mm.h>
+#include <linux/malloc.h>
#define DEBUG
#include "usb.h"
*/
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(" iInterface = %02x\n", desc->iInterface);
}
+void usb_show_hid_descriptor(struct usb_hid_descriptor * desc)
+{
+ int i;
+
+ printk(" HID:\n");
+ printk(" HID version %x.%02x\n", desc->bcdHID >> 8, desc->bcdHID & 0xff);
+ printk(" bLength = %4d\n", desc->bLength);
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bCountryCode = %02x\n", desc->bCountryCode);
+ printk(" bNumDescriptors = %02x\n", desc->bNumDescriptors);
+
+ for (i=0; i<desc->bNumDescriptors; i++) {
+ printk(" %d:\n", i);
+ printk(" bDescriptorType = %02x\n", desc->desc[i].bDescriptorType);
+ printk(" wDescriptorLength = %04x\n", desc->desc[i].wDescriptorLength);
+ }
+}
+
void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc)
{
char *LengthCommentString = (desc->bLength ==
kfree(buf);
}
+void usb_dump_urb (purb_t purb)
+{
+ printk ("urb :%p\n", purb);
+ printk ("next :%p\n", purb->next);
+ printk ("dev :%p\n", purb->dev);
+ printk ("pipe :%08X\n", purb->pipe);
+ printk ("status :%d\n", purb->status);
+ printk ("transfer_flags :%08X\n", purb->transfer_flags);
+ printk ("transfer_buffer :%p\n", purb->transfer_buffer);
+ printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+ printk ("actual_length :%d\n", purb->actual_length);
+ printk ("setup_packet :%p\n", purb->setup_packet);
+ printk ("start_frame :%d\n", purb->start_frame);
+ printk ("number_of_packets :%d\n", purb->number_of_packets);
+ printk ("interval :%d\n", purb->interval);
+ printk ("error_count :%d\n", purb->error_count);
+ printk ("context :%p\n", purb->context);
+ printk ("complete :%p\n", purb->complete);
+}
+
--- /dev/null
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ *
+ * History:
+ *
+ * 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
+ *
+
+ */
+
+#include <linux/config.h>
+#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/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#undef DEBUG
+#define OHCI_USE_NPS
+
+#include "usb.h"
+#include "usb-ohci.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event (apm_event_t event);
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD (op_wakeup);
+static LIST_HEAD (ohci_hcd_list);
+spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+
+/*-------------------------------------------------------------------------*
+ * URB support functions
+ *-------------------------------------------------------------------------*/
+
+/* free the private part of an URB */
+
+static void urb_rm_priv (urb_t * urb)
+{
+ urb_priv_t * urb_priv = urb->hcpriv;
+ int i;
+ void * wait;
+
+ if (!urb_priv) return;
+
+ wait = urb_priv->wait;
+
+ for (i = 0; i < urb_priv->length; i++) {
+ if (urb_priv->td [i]) {
+ OHCI_FREE (urb_priv->td [i]);
+ }
+ }
+ kfree (urb->hcpriv);
+ urb->hcpriv = NULL;
+
+ if (wait) {
+ add_wait_queue (&op_wakeup, wait);
+ wake_up (&op_wakeup);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header */
+
+static void urb_print (urb_t * urb, char * str, int small)
+{
+ unsigned int pipe= urb->pipe;
+ int i, len;
+
+ if (!urb->dev || !urb->dev->bus) {
+ dbg("%s URB: no dev", str);
+ return;
+ }
+
+ dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)",
+ str,
+ sohci_get_current_frame_number (urb->dev),
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? 'O': 'I',
+ usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+ (usb_pipecontrol (pipe)? "CTRL": "BULK"),
+ urb->transfer_flags,
+ urb->actual_length,
+ urb->transfer_buffer_length,
+ urb->status, urb->status);
+ if (!small) {
+ if (usb_pipecontrol (pipe)) {
+ printk (KERN_DEBUG __FILE__ ": cmd(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);
+ }
+ }
+
+}
+/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t * ohci, char * str) {
+ int i, j;
+ __u32 * ed_p;
+ for (i= 0; i < 32; i++) {
+ j = 5;
+ printk (KERN_DEBUG __FILE__ " %s branch int %2d(%2x):", str, i, i);
+ ed_p = &(ohci->hcca.int_table [i]);
+ while (*ed_p != 0 && j--) {
+ ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p));
+ printk (" ed: %4x;", ed->hwINFO);
+ ed_p = &ed->hwNextED;
+ }
+ printk ("\n");
+ }
+}
+
+
+#endif
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* return a request to the completion handler */
+
+static int sohci_return_urb (urb_t * urb)
+{
+ urb_priv_t * urb_priv = urb->hcpriv;
+ urb_t * urbt;
+ unsigned long flags;
+ int i;
+
+ /* just to be sure */
+ if (!urb->complete) {
+ urb_rm_priv (urb);
+ usb_dec_dev_use (urb->dev);
+ return -1;
+ }
+
+ if (!urb_priv) return -1; /* urb already unlinked */
+
+#ifdef DEBUG
+ urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_INTERRUPT:
+ urb->complete (urb); /* call complete and requeue URB */
+ urb->actual_length = 0;
+ urb->status = USB_ST_URB_PENDING;
+ if (urb_priv->state != URB_DEL)
+ td_submit_urb (urb);
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
+ if (urbt) { /* send the reply and requeue URB */
+ urb->complete (urb);
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ urb->actual_length = 0;
+ urb->status = USB_ST_URB_PENDING;
+ urb->start_frame = urb_priv->ed->last_iso + 1;
+ if (urb_priv->state != URB_DEL) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = -EXDEV;
+ }
+ td_submit_urb (urb);
+ }
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ } else { /* unlink URB, call complete */
+ urb_rm_priv (urb);
+ usb_dec_dev_use (urb->dev);
+ urb->complete (urb);
+ }
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL: /* unlink URB, call complete */
+ urb_rm_priv (urb);
+ usb_dec_dev_use (urb->dev);
+ urb->complete (urb);
+ break;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+
+static int sohci_submit_urb (urb_t * urb)
+{
+ ohci_t * ohci;
+ ed_t * ed;
+ urb_priv_t * urb_priv;
+ unsigned int pipe = urb->pipe;
+ int i, size = 0;
+ unsigned long flags;
+
+ if (!urb->dev || !urb->dev->bus) return -EINVAL;
+
+ if (urb->hcpriv) return -EINVAL; /* urb already in use */
+
+ usb_inc_dev_use (urb->dev);
+ ohci = (ohci_t *) urb->dev->bus->hcpriv;
+
+#ifdef DEBUG
+ urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+
+ if (usb_pipedevice (pipe) == ohci->rh.devnum)
+ return rh_submit_urb (urb); /* a request to the virtual root hub */
+
+ /* every endpoint has a ed, locate and fill it */
+ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
+ usb_dec_dev_use (urb->dev);
+ return -ENOMEM;
+ }
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (usb_pipetype (pipe)) {
+ case PIPE_BULK: /* one TD for every 4096 Byte */
+ size = (urb->transfer_buffer_length - 1) / 4096 + 1;
+ break;
+ case PIPE_ISOCHRONOUS: /* number of packets from URB */
+ size = urb->number_of_packets;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = -EXDEV;
+ }
+ break;
+ case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+ size = (urb->transfer_buffer_length == 0)? 2:
+ (urb->transfer_buffer_length - 1) / 4096 + 3;
+ break;
+ case PIPE_INTERRUPT: /* one TD */
+ size = 1;
+
+ break;
+ }
+
+ /* allocate the private part or the URB */
+ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!urb_priv) {
+ usb_dec_dev_use (urb->dev);
+ return -ENOMEM;
+ }
+ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
+
+ /* fill the private part of the URB */
+ urb->hcpriv = urb_priv;
+ urb_priv->length = size;
+ urb_priv->td_cnt = 0;
+ urb_priv->state = 0;
+ urb_priv->ed = ed;
+ urb_priv->wait = NULL;
+
+ /* allocate the TDs */
+ for (i = 0; i < size; i++) {
+ OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
+ if (!urb_priv->td[i]) {
+ usb_dec_dev_use (urb->dev);
+ urb_rm_priv (urb);
+ return -ENOMEM;
+ }
+ }
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+ urb_rm_priv(urb);
+ usb_dec_dev_use (urb->dev);
+ return -EINVAL;
+ }
+
+ /* for ISOC transfers calculate start frame index */
+ if (urb->transfer_flags & USB_ISO_ASAP) {
+ urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1):
+ (le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
+ }
+
+ td_submit_urb (urb); /* fill the TDs and link it to the ed */
+
+ if (ed->state != ED_OPER) /* link the ed into a chain if is not already */
+ ep_link (ohci, ed);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ urb->status = USB_ST_URB_PENDING;
+ // queue_urb(s, &urb->urb_list);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* deactivate all TDs and remove the private part of the URB */
+
+static int sohci_unlink_urb (urb_t * urb)
+{
+ unsigned long flags;
+ ohci_t * ohci;
+ DECLARE_WAITQUEUE (wait, current);
+
+ if (!urb) /* just to be sure */
+ return -EINVAL;
+
+#ifdef DEBUG
+ urb_print (urb, "UNLINK", 1);
+#endif
+
+ ohci = (ohci_t *) urb->dev->bus->hcpriv;
+
+ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
+ return rh_unlink_urb (urb); /* a request to the virtual root hub */
+
+ if (urb->hcpriv) {
+ if (urb->status == USB_ST_URB_PENDING) { /* URB active? */
+ urb_priv_t * urb_priv = urb->hcpriv;
+ urb_priv->state = URB_DEL;
+ /* we want to delete the TDs of an URB from an ed
+ * request the deletion, it will be handled at the next USB-frame */
+ urb_priv->wait = &wait;
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ ep_rm_ed (urb->dev, urb_priv->ed);
+ urb_priv->ed->state |= ED_URB_DEL;
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ if(schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */
+ remove_wait_queue (&op_wakeup, &wait);
+ else
+ err("unlink URB timeout!");
+ } else
+ urb_rm_priv (urb);
+ usb_dec_dev_use (urb->dev);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* allocate private data space for a usb device */
+
+static int sohci_alloc_dev (struct usb_device *usb_dev)
+{
+ struct ohci_device * dev;
+
+ dev = kmalloc (sizeof (*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ usb_dev->hcpriv = dev;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* free private data space of usb device */
+
+static int sohci_free_dev (struct usb_device * usb_dev)
+{
+ unsigned long flags;
+ int i, cnt = 0;
+ ed_t * ed;
+ DECLARE_WAITQUEUE (wait, current);
+ struct ohci_device * dev = usb_to_ohci (usb_dev);
+ ohci_t * ohci = usb_dev->bus->hcpriv;
+
+ if (!dev) return 0;
+
+ if (usb_dev->devnum >= 0) {
+
+ /* delete all TDs of all EDs */
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ for(i = 0; i < NUM_EDS; i++) {
+ ed = &(dev->ed[i]);
+ if (ed->state != ED_NEW) {
+ if (ed->state == ED_OPER) ep_unlink (ohci, ed);
+ ep_rm_ed (usb_dev, ed);
+ ed->state = ED_DEL;
+ cnt++;
+ }
+ }
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ if (cnt > 0) {
+ dev->wait = &wait;
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout (HZ / 10);
+ remove_wait_queue (&op_wakeup, &wait);
+ }
+ }
+ kfree (dev);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev)
+{
+ ohci_t * ohci = usb_dev->bus->hcpriv;
+
+ return le16_to_cpu (ohci->hcca.frame_no);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_operations sohci_device_operations = {
+ sohci_alloc_dev,
+ sohci_free_dev,
+ sohci_get_current_frame_number,
+ sohci_submit_urb,
+ sohci_unlink_urb
+};
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right branch to insert an interrupt ed into the int tree
+ * do some load ballancing;
+ * returns the branch and
+ * sets the interval to interval = 2^integer (ld (interval)) */
+
+static int ep_int_ballance (ohci_t * ohci, int interval, int load)
+{
+ int i, branch = 0;
+
+ /* search for the least loaded interrupt endpoint branch of all 32 branches */
+ for (i = 0; i < 32; i++)
+ if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i;
+
+ branch = branch % interval;
+ for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
+
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 2^int( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{
+ int i;
+ for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
+ return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree
+ * in order to process it sequentially the indexes of the branches have to be mapped
+ * the mapping reverses the bits of a word of num_bits length */
+
+static int ep_rev (int num_bits, int word)
+{
+ int i, wout = 0;
+
+ for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
+ return wout;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t * ohci, ed_t * edi)
+{
+ int int_branch;
+ int i;
+ int inter;
+ int interval;
+ int load;
+ __u32 * ed_p;
+ volatile ed_t * ed = edi;
+
+ ed->state = ED_OPER;
+
+ switch (ed->type) {
+ case CTRL:
+ ed->hwNextED = 0;
+ if (ohci->ed_controltail == NULL) {
+ writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ ohci->ed_controltail = edi;
+ break;
+
+ case BULK:
+ ed->hwNextED = 0;
+ if (ohci->ed_bulktail == NULL) {
+ writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ ohci->ed_bulktail = edi;
+ break;
+
+ case INT:
+ load = ed->int_load;
+ interval = ep_2_n_interval (ed->int_period);
+ ed->int_interval = interval;
+ int_branch = ep_int_ballance (ohci, interval, load);
+ ed->int_branch = int_branch;
+
+ for (i = 0; i < ep_rev (6, interval); i += inter) {
+ inter = 1;
+ for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]);
+ (*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval);
+ ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+ ed->hwNextED = *ed_p;
+ *ed_p = cpu_to_le32 (virt_to_bus (ed));
+ }
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "LINK_INT");
+#endif
+ break;
+
+ case ISO:
+ ed->hwNextED = 0;
+ ed->int_interval = 1;
+ if (ohci->ed_isotail != NULL) {
+ ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+ ed->ed_prev = ohci->ed_isotail;
+ } else {
+ for ( i = 0; i < 32; i += inter) {
+ inter = 1;
+ for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
+ *ed_p != 0;
+ ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+ *ed_p = cpu_to_le32 (virt_to_bus (ed));
+ }
+ ed->ed_prev = NULL;
+ }
+ ohci->ed_isotail = edi;
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "LINK_ISO");
+#endif
+ break;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 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 int ep_unlink (ohci_t * ohci, ed_t * ed)
+{
+ int int_branch;
+ int i;
+ int inter;
+ int interval;
+ __u32 * ed_p;
+
+
+ switch (ed->type) {
+ case CTRL:
+ if (ed->ed_prev == NULL) {
+ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if(ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ } else {
+ ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case BULK:
+ if (ed->ed_prev == NULL) {
+ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ } else {
+ ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case INT:
+ int_branch = ed->int_branch;
+ interval = ed->int_interval;
+
+ for (i = 0; i < ep_rev (6, interval); i += inter) {
+ for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1;
+ (*ed_p != 0) && (*ed_p != ed->hwNextED);
+ ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED),
+ inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) {
+ if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+ *ed_p = ed->hwNextED;
+ break;
+ }
+ }
+ }
+ for (i = int_branch; i < 32; i += interval) ohci->ohci_int_load[i] -= ed->int_load;
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "UNLINK_INT");
+#endif break;
+
+ case ISO:
+ if (ohci->ed_isotail == ed)
+ ohci->ed_isotail = ed->ed_prev;
+ if (ed->hwNextED != 0)
+ ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+
+ if (ed->ed_prev != NULL) {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ } else {
+ for (i = 0; i < 32; i += inter) {
+ inter = 1;
+ for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]);
+ *ed_p != 0;
+ ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) {
+ inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+ if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+ *ed_p = ed->hwNextED;
+ break;
+ }
+ }
+ }
+ }
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "UNLINK_ISO");
+#endif
+ break;
+ }
+ ed->state = ED_UNLINK;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
+ * but the USB stack is a little bit stateless so we do it at every transaction
+ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are setted anyway even though most of them should not change */
+
+static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load)
+{
+ ohci_t * ohci = usb_dev->bus->hcpriv;
+ td_t * td;
+ ed_t * ed_ret;
+ volatile ed_t * ed;
+
+
+ spin_lock (&usb_ed_lock);
+
+ ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
+ (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+
+ if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
+ return NULL; /* pending delete request */
+
+ if (ed->state == ED_NEW) {
+ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
+ OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
+ if(!td) return NULL; /* out of memory */
+ ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+ ed->hwHeadP = ed->hwTailP;
+ ed->state = ED_UNLINK;
+ ed->type = usb_pipetype (pipe);
+ usb_to_ohci (usb_dev)->ed_cnt++;
+ }
+
+ ohci->dev[usb_pipedevice (pipe)] = usb_dev;
+
+ ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
+ | usb_pipeendpoint (pipe) << 7
+ | (usb_pipeisoc (pipe)? 0x8000: 0)
+ | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
+ | usb_pipeslow (pipe) << 13
+ | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
+
+ if (ed->type == INT && ed->state == ED_UNLINK) {
+ ed->int_period = interval;
+ ed->int_load = load;
+ }
+
+ spin_unlock(&usb_ed_lock);
+ return ed_ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request the removal of an endpoint
+ * put the ep on the rm_list and request a stop of the bulk or ctrl list
+ * real removal is done at the next start of frame (SOF) hardware interrupt */
+
+static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
+{
+ unsigned int frame;
+ ohci_t * ohci = usb_dev->bus->hcpriv;
+
+ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return;
+
+ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
+
+ frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
+ ed->ed_rm_list = ohci->ed_rm_list[frame];
+ ohci->ed_rm_list[frame] = ed;
+
+ switch (ed->type) {
+ case CTRL: /* stop CTRL list */
+ writel (ohci->hc_control &= ~(0x01 << 4), &ohci->regs->control);
+ break;
+ case BULK: /* stop BULK list */
+ writel (ohci->hc_control &= ~(0x01 << 5), &ohci->regs->control);
+ break;
+ }
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* prepare a TD */
+
+static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, int index)
+{
+ volatile td_t * td, * td_pt;
+ urb_priv_t * urb_priv = urb->hcpriv;
+
+ if (index >= urb_priv->length) {
+ err("internal OHCI error: TD index > length");
+ return;
+ }
+
+ td_pt = urb_priv->td [index];
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
+ td->ed = urb_priv->ed;
+ td->index = index;
+ td->urb = urb;
+ td->hwINFO = cpu_to_le32 (info);
+ td->type = type;
+ if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
+ td->hwCBP = cpu_to_le32 (((!data || !len)?
+ 0 : virt_to_bus (data)) & 0xFFFFF000);
+ td->ed->last_iso = info & 0xffff;
+ } else {
+ td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)));
+ }
+ td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1));
+ td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
+ td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
+ td_pt->hwNextTD = 0;
+ td->ed->hwTailP = td->hwNextTD;
+
+ td->next_dl_td = NULL; //td_pt;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_urb (urb_t * urb)
+{
+ urb_priv_t * urb_priv = urb->hcpriv;
+ ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
+ void * ctrl = urb->setup_packet;
+ void * data = urb->transfer_buffer;
+ int data_len = urb->transfer_buffer_length;
+ int cnt = 0;
+ __u32 info = 0;
+
+
+ urb_priv->td_cnt = 0;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_BULK:
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE;
+ while(data_len > 4096) {
+ td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ data += 4096; data_len -= 4096; cnt++;
+ }
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
+ td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ cnt++;
+ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ break;
+
+ case PIPE_INTERRUPT:
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
+ td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++);
+ break;
+
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++);
+ if (data_len > 0) {
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+ td_fill (info, data, data_len, urb, ADD_LEN, cnt++);
+ }
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (info, NULL, 0, urb, 0, cnt++);
+ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff),
+ (__u8 *) data + urb->iso_frame_desc[cnt].offset,
+ urb->iso_frame_desc[cnt].length, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ }
+ break;
+ }
+ if (urb_priv->length != cnt)
+ dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+
+static td_t * dl_reverse_done_list (ohci_t * ohci)
+{
+ __u32 td_list_hc;
+ td_t * td_rev = NULL;
+ td_t * td_list = NULL;
+ urb_priv_t * urb_priv = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+
+ td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;
+ ohci->hcca.done_head = 0;
+
+ while (td_list_hc) {
+ td_list = (td_t *) bus_to_virt (td_list_hc);
+
+ if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
+ urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
+ dbg(" USB-error/status: %x : %p",
+ TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
+ if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
+ if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
+ td_list->ed->hwHeadP =
+ (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
+ (td_list->ed->hwHeadP & cpu_to_le32 (0x2));
+ urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
+ } else
+ td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
+ }
+ }
+
+ td_list->next_dl_td = td_rev;
+ td_rev = td_list;
+ td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;
+ }
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+ return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some pending requests to remove
+ * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
+ * - some URBs/TDs if urb_priv->state == URB_DEL */
+
+static void dl_del_list (ohci_t * ohci, unsigned int frame)
+{
+ unsigned long flags;
+ ed_t * ed;
+ __u32 edINFO;
+ td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
+ __u32 * td_p;
+ int ctrl = 0, bulk = 0;
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
+
+ tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+ tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+ edINFO = le32_to_cpup (&ed->hwINFO);
+ td_p = &ed->hwHeadP;
+
+ for (td = tdHeadP; td != tdTailP; td = td_next) {
+ urb_t * urb = td->urb;
+ urb_priv_t * urb_priv = td->urb->hcpriv;
+
+ td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+ if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
+ *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
+ if(++ (urb_priv->td_cnt) == urb_priv->length)
+ urb_rm_priv (urb);
+ } else {
+ td_p = &td->hwNextTD;
+ }
+
+ }
+ if (ed->state & ED_DEL) { /* set by sohci_free_dev */
+ struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
+ OHCI_FREE (tdTailP); /* free dummy td */
+ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
+ ed->state = ED_NEW;
+ /* if all eds are removed wake up sohci_free_dev */
+ if ((! --dev->ed_cnt) && dev->wait) {
+ add_wait_queue (&op_wakeup, dev->wait);
+ wake_up (&op_wakeup);
+ }
+ }
+ else {
+ ed->state &= ~ED_URB_DEL;
+ ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
+ }
+
+ if ((ed->type & 3) == CTRL) ctrl |= 1;
+ if ((ed->type & 3) == BULK) bulk |= 1;
+ }
+
+ if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
+ if (bulk) writel (0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
+ if (!ohci->ed_rm_list[!frame]) /* start CTRL u. BULK list */
+ writel (ohci->hc_control |= (0x03<<4), &ohci->regs->control);
+ ohci->ed_rm_list[frame] = NULL;
+
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+
+static void dl_done_list (ohci_t * ohci, td_t * td_list)
+{
+ td_t * td_list_next = NULL;
+ ed_t * ed;
+ int dlen = 0;
+ int cc = 0;
+ urb_t * urb;
+ urb_priv_t * urb_priv;
+ __u32 tdINFO, tdBE, tdCBP, edHeadP, edTailP;
+ __u16 tdPSW;
+ unsigned long flags;
+
+ while (td_list) {
+ td_list_next = td_list->next_dl_td;
+
+ urb = td_list->urb;
+ urb_priv = urb->hcpriv;
+ tdINFO = le32_to_cpup (&td_list->hwINFO);
+ tdBE = le32_to_cpup (&td_list->hwBE);
+ tdCBP = le32_to_cpup (&td_list->hwCBP);
+
+ ed = td_list->ed;
+
+ if (td_list->type & ST_ADDR)
+ urb->actual_length = 0;
+
+ if (td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */
+ if (tdINFO & TD_ISO) {
+ tdPSW = le16_to_cpu (td_list->hwPSW[0]);
+ cc = (tdPSW >> 12) & 0xF;
+ if (cc < 0xE) {
+ if (usb_pipeout(urb->pipe)) {
+ dlen = urb->iso_frame_desc[td_list->index].length;
+ } else {
+ dlen = tdPSW & 0x3ff;
+ }
+ urb->actual_length += dlen;
+ urb->iso_frame_desc[td_list->index].actual_length = dlen;
+ if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
+ urb->iso_frame_desc[td_list->index].status = cc_to_error[cc];
+ }
+ } else {
+ if (tdBE != 0) {
+ dlen = (bus_to_virt (tdBE) - urb->transfer_buffer + 1);
+ if (td_list->hwCBP == 0)
+ urb->actual_length += dlen;
+ else
+ urb->actual_length += (bus_to_virt(tdCBP) - urb->transfer_buffer);
+ }
+ }
+ }
+ /* error code of transfer */
+ cc = TD_CC_GET (tdINFO);
+ if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+ if (++(urb_priv->td_cnt) == urb_priv->length) {
+ if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) {
+ urb->status = cc_to_error[cc];
+ sohci_return_urb (urb);
+ } else {
+ urb_rm_priv (urb);
+ }
+ }
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ if (ed->state != ED_NEW) {
+ edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+ edTailP = le32_to_cpup (&ed->hwTailP);
+
+ if((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink (ohci, ed); /* unlink eds if they are not busy */
+
+ }
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ td_list = td_list_next;
+ }
+}
+
+
+
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub
+ *-------------------------------------------------------------------------*/
+
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __u16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x00, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 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; */
+
+ /* 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; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+/*
+For OHCI we need just the 2nd Byte, so we
+don't need this constant byte-array
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x00, * __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; *** 8 Ports max *** *
+ 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** *
+};
+*/
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */
+
+static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
+{
+ int num_ports;
+ int i;
+ int ret;
+ int len;
+
+ __u8 data[8];
+
+ num_ports = readl (&ohci->regs->roothub.a) & 0xff;
+ *(__u8 *) data = (readl (&ohci->regs->roothub.status) & 0x00030000) > 0? 1: 0;
+ ret = *(__u8 *) data;
+
+ for ( i = 0; i < num_ports; i++) {
+ *(__u8 *) (data + (i + 1) / 8) |=
+ ((readl (&ohci->regs->roothub.portstatus[i]) & 0x001f0000) > 0? 1: 0) << ((i + 1) % 8);
+ ret += *(__u8 *) (data + (i + 1) / 8);
+ }
+ len = i/8 + 1;
+
+ if (ret > 0) {
+ memcpy (rh_data, data, min (len, min (rh_len, sizeof(data))));
+ return len;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+
+static void rh_int_timer_do (unsigned long ptr)
+{
+ int len;
+
+ urb_t * urb = (urb_t *) ptr;
+ ohci_t * ohci = urb->dev->bus->hcpriv;
+
+ if(ohci->rh.send) {
+ len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
+ if (len > 0) {
+ urb->actual_length = len;
+#ifdef DEBUG
+ urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+#endif
+ if (urb->complete) urb->complete (urb);
+ }
+ }
+ rh_init_int_timer (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Root Hub INTs are polled by this timer */
+
+static int rh_init_int_timer (urb_t * urb)
+{
+ ohci_t * ohci = urb->dev->bus->hcpriv;
+
+ ohci->rh.interval = urb->interval;
+ init_timer (&ohci->rh.rh_int_timer);
+ ohci->rh.rh_int_timer.function = rh_int_timer_do;
+ ohci->rh.rh_int_timer.data = (unsigned long) urb;
+ ohci->rh.rh_int_timer.expires =
+ jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
+ add_timer (&ohci->rh.rh_int_timer);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x) len = (x); break
+#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status)
+#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
+#define RD_RH_STAT readl(&ohci->regs->roothub.status)
+#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1])
+
+/* request to virtual root hub */
+
+static int rh_submit_urb (urb_t * urb)
+{
+ struct usb_device * usb_dev = urb->dev;
+ ohci_t * ohci = usb_dev->bus->hcpriv;
+ unsigned int pipe = urb->pipe;
+ devrequest * cmd = (devrequest *) urb->setup_packet;
+ void * data = urb->transfer_buffer;
+ int leni = urb->transfer_buffer_length;
+ int len = 0;
+ int status = TD_CC_NOERROR;
+
+ __u8 datab[16];
+ __u8 * data_buf = datab;
+
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+ if (usb_pipeint(pipe)) {
+
+ ohci->rh.urb = urb;
+ ohci->rh.send = 1;
+ ohci->rh.interval = urb->interval;
+ rh_init_int_timer(urb);
+ urb->status = cc_to_error [TD_CC_NOERROR];
+
+ return 0;
+ }
+
+ bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+ wValue = le16_to_cpu (cmd->value);
+ wIndex = le16_to_cpu (cmd->index);
+ wLength = le16_to_cpu (cmd->length);
+ switch (bmRType_bReq) {
+ /* 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 RH_GET_STATUS:
+ *(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data_buf = cpu_to_le32 (RD_RH_STAT & 0x7fff7fff); OK (4);
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL): OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case RH_C_HUB_LOCAL_POWER:
+ OK(0);
+ case (RH_C_HUB_OVER_CURRENT):
+ WR_RH_STAT(RH_HS_OCIC); OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+ case (RH_C_PORT_CONNECTION):
+ WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+ case (RH_C_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+ case (RH_C_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+ case (RH_C_PORT_OVER_CURRENT):
+ WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+ case (RH_C_PORT_RESET):
+ WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
+ case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+ if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PRS ); OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
+ case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+ if((RD_RH_PORTSTAT &1) != 0) WR_RH_PORTSTAT (RH_PS_PES ); OK (0);
+ }
+ break;
+
+ case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min (leni, min (sizeof (root_hub_dev_des), wLength));
+ data_buf = root_hub_dev_des; OK(len);
+ case (0x02): /* configuration descriptor */
+ len = min (leni, min (sizeof (root_hub_config_des), wLength));
+ data_buf = root_hub_config_des; OK(len);
+ case (0x03): /* string descriptors */
+ default:
+ status = TD_CC_STALL;
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ *(__u8 *) (data_buf+1) = 0x29;
+ *(__u32 *) (data_buf+2) = cpu_to_le32 (readl (&ohci->regs->roothub.a));
+ *(__u8 *) data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */
+
+ len = min (leni, min(*(__u8 *) data_buf, wLength));
+ *(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */
+ if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */
+ *(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff;
+ *(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16;
+ } else {
+ *(__u32 *) (data_buf+7) = cpu_to_le32 (readl(&ohci->regs->roothub.b));
+ }
+ OK (len);
+
+ case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
+
+ case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
+
+ default:
+ status = TD_CC_STALL;
+ }
+
+ dbg("USB HC roothubstat1: %x", readl ( &(ohci->regs->roothub.portstatus[0]) ));
+ dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
+
+ len = min(len, leni);
+ memcpy (data, data_buf, len);
+ urb->actual_length = len;
+ urb->status = cc_to_error [status];
+
+#ifdef DEBUG
+ urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+#endif
+
+ if (urb->complete) urb->complete (urb);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_unlink_urb (urb_t * urb)
+{
+ ohci_t * ohci = urb->dev->bus->hcpriv;
+
+ ohci->rh.send = 0;
+ del_timer (&ohci->rh.rh_int_timer);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC not the BUS */
+
+static void hc_reset (ohci_t * ohci)
+{
+ int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
+
+ if (readl (&ohci->regs->control) & 0x100) { /* SMM owns the HC */
+ writel (0x08, &ohci->regs->cmdstatus); /* request ownership */
+ dbg("USB HC TakeOver from SMM");
+ while (readl (&ohci->regs->control) & 0x100) {
+ wait_ms (10);
+ if (--smm_timeout == 0) {
+ err("USB HC TakeOver failed!");
+ break;
+ }
+ }
+ }
+
+ writel ((1 << 31), &ohci->regs->intrdisable); /* Disable HC interrupts */
+ dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control));
+ /* this seems to be needed for the lucent controller on powerbooks.. */
+ writel (0, &ohci->regs->control); /* Move USB to reset state */
+
+ writel (1, &ohci->regs->cmdstatus); /* HC Reset */
+ while ((readl (&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */
+ if (--timeout == 0) {
+ err("USB HC reset timed out!");
+ return;
+ }
+ udelay (1);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+ unsigned int mask;
+ unsigned int fminterval;
+ struct usb_device * usb_dev;
+ struct ohci_device * dev;
+
+ /* 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);
+
+ writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */
+
+ fminterval = 0x2edf;
+ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+ writel (fminterval, &ohci->regs->fminterval);
+ writel (0x628, &ohci->regs->lsthresh);
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO;
+
+ writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
+ writel (mask, &ohci->regs->intrenable);
+ writel (mask, &ohci->regs->intrstatus);
+
+#ifdef OHCI_USE_NPS
+ writel ((readl(&ohci->regs->roothub.a) | 0x200) & ~0x100,
+ &ohci->regs->roothub.a);
+ writel (0x10000, &ohci->regs->roothub.status);
+ mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);
+#endif /* OHCI_USE_NPS */
+
+ /* connect the virtual root hub */
+
+ usb_dev = usb_alloc_dev (NULL, ohci->bus);
+ if (!usb_dev) return -1;
+
+ dev = usb_to_ohci (usb_dev);
+ ohci->bus->root_hub = usb_dev;
+ usb_connect (usb_dev);
+ if (usb_new_device (usb_dev) != 0) {
+ usb_free_dev (usb_dev);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
+{
+ ohci_t * ohci = __ohci;
+ struct ohci_regs * regs = ohci->regs;
+ int ints;
+
+ if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) {
+ ints = OHCI_INTR_WDH;
+ } else {
+ if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0)
+ return;
+ }
+
+ dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
+
+ if (ints & OHCI_INTR_WDH) {
+ writel (OHCI_INTR_WDH, ®s->intrdisable);
+ dl_done_list (ohci, dl_reverse_done_list (ohci));
+ writel (OHCI_INTR_WDH, ®s->intrenable);
+ }
+
+ if (ints & OHCI_INTR_SO) {
+ dbg("USB Schedule overrun");
+ writel (OHCI_INTR_SO, ®s->intrenable);
+ }
+
+ if (ints & OHCI_INTR_SF) {
+ unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1;
+ writel (OHCI_INTR_SF, ®s->intrdisable);
+ if (ohci->ed_rm_list[!frame] != NULL) {
+ dl_del_list (ohci, !frame);
+ }
+ if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable);
+ }
+ writel (ints, ®s->intrstatus);
+ writel (OHCI_INTR_MIE, ®s->intrenable);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* allocate OHCI */
+
+static ohci_t * hc_alloc_ohci (void * mem_base)
+{
+ int i;
+ ohci_t * ohci;
+ struct usb_bus * bus;
+
+ ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1);
+ if (!ohci)
+ return NULL;
+
+ memset (ohci, 0, sizeof (ohci_t));
+
+ ohci->irq = -1;
+ ohci->regs = mem_base;
+
+ /* for load ballancing of the interrupt branches */
+ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0;
+
+ /* end of control and bulk lists */
+ ohci->ed_isotail = NULL;
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ bus = usb_alloc_bus (&sohci_device_operations);
+ if (!bus) {
+ free_pages ((unsigned long) ohci, 1);
+ return NULL;
+ }
+
+ ohci->bus = bus;
+ bus->hcpriv = (void *) ohci;
+
+ return ohci;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t * ohci)
+{
+ dbg("USB HC release ohci");
+
+ /* disconnect all devices */
+ if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub);
+
+ hc_reset (ohci);
+ writel (OHCI_USB_RESET, &ohci->regs->control);
+ wait_ms (10);
+
+ if (ohci->irq >= 0) {
+ free_irq (ohci->irq, ohci);
+ ohci->irq = -1;
+ }
+
+ usb_deregister_bus (ohci->bus);
+ usb_free_bus (ohci->bus);
+
+ /* unmap the IO address space */
+ iounmap (ohci->regs);
+
+ free_pages ((unsigned long) ohci, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Increment the module usage count, start the control thread and
+ * return success. */
+
+static int hc_found_ohci (int irq, void * mem_base)
+{
+ ohci_t * ohci;
+ dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base);
+
+ ohci = hc_alloc_ohci (mem_base);
+ if (!ohci) {
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+ list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
+
+ hc_reset (ohci);
+ writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
+ wait_ms (10);
+ usb_register_bus (ohci->bus);
+
+ if (request_irq (irq, hc_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) {
+ ohci->irq = irq;
+ hc_start (ohci);
+ return 0;
+ }
+ err("request interrupt %d failed", irq);
+ hc_release_ohci (ohci);
+ return -EBUSY;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int hc_start_ohci (struct pci_dev * dev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ unsigned long mem_base = dev->resource[0].start;
+#else
+ unsigned long mem_base = dev->base_address[0];
+ if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV;
+ mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
+#endif
+
+ pci_set_master (dev);
+ mem_base = (unsigned long) ioremap_nocache (mem_base, 4096);
+
+ if (!mem_base) {
+ err("Error mapping OHCI memory");
+ return -EFAULT;
+ }
+ return hc_found_ohci (dev->irq, (void *) mem_base);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PMAC_PBOOK
+
+/* On Powerbooks, put the controller into suspend mode when going
+ * to sleep, and do a resume when waking up. */
+
+static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when)
+{
+ struct list_head * ohci_l;
+ ohci_t * ohci;
+
+ for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
+ ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
+
+ switch (when) {
+ case PBOOK_SLEEP_NOW:
+ disable_irq (ohci->irq);
+ writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control);
+ wait_ms (10);
+ break;
+ case PBOOK_WAKE:
+ writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control);
+ wait_ms (20);
+ writel (ohci->hc_control = 0xBF, &ohci->regs->control);
+ enable_irq (ohci->irq);
+ break;
+ }
+ }
+ return PBOOK_SLEEP_OK;
+}
+
+static struct pmu_sleep_notifier ohci_sleep_notifier = {
+ ohci_sleep_notify, SLEEP_LEVEL_MISC,
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_APM
+static int handle_apm_event (apm_event_t event)
+{
+ static int down = 0;
+ ohci_t * ohci;
+ struct list_head * ohci_l;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ dbg("received extra suspend event");
+ break;
+ }
+ for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
+ ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
+ dbg("USB-Bus suspend: %p", ohci);
+ writel (ohci->hc_control = 0xFF, &ohci->regs->control);
+ }
+ wait_ms (10);
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ dbg("received bogus resume event");
+ break;
+ }
+ for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
+ ohci = list_entry(ohci_l, ohci_t, ohci_hcd_list);
+ dbg("USB-Bus resume: %p", ohci);
+ writel (ohci->hc_control = 0x7F, &ohci->regs->control);
+ }
+ wait_ms (20);
+ for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
+ ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
+ writel (ohci->hc_control = 0xBF, &ohci->regs->control);
+ }
+ down = 0;
+ break;
+ }
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
+
+int ohci_hcd_init (void)
+{
+ int ret = -ENODEV;
+ struct pci_dev * dev = NULL;
+
+ while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) {
+ if (hc_start_ohci(dev) >= 0) ret = 0;
+ }
+
+#ifdef CONFIG_APM
+ apm_register_callback (&handle_apm_event);
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+ pmu_register_sleep_notifier (&ohci_sleep_notifier);
+#endif
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef MODULE
+int init_module (void)
+{
+ return ohci_hcd_init ();
+}
+
+/*-------------------------------------------------------------------------*/
+
+void cleanup_module (void)
+{
+ ohci_t * ohci;
+
+#ifdef CONFIG_APM
+ apm_unregister_callback (&handle_apm_event);
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+ pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
+#endif
+
+ while (!list_empty (&ohci_hcd_list)) {
+ ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list);
+ list_del (&ohci->ohci_hcd_list);
+ INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+ hc_release_ohci (ohci);
+ }
+}
+#endif //MODULE
+
--- /dev/null
+ /*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ *
+ * usb-ohci.h
+ *
+ */
+
+
+#define MODSTR "ohci: "
+
+
+static int cc_to_error[16] = {
+
+/* mapping of the OHCI CC status to error codes */
+#ifdef USB_ST_CRC /* status codes */
+ /* No Error */ USB_ST_NOERROR,
+ /* CRC Error */ USB_ST_CRC,
+ /* Bit Stuff */ USB_ST_BITSTUFF,
+ /* Data Togg */ USB_ST_CRC,
+ /* Stall */ USB_ST_STALL,
+ /* DevNotResp */ USB_ST_NORESPONSE,
+ /* PIDCheck */ USB_ST_BITSTUFF,
+ /* UnExpPID */ USB_ST_BITSTUFF,
+ /* DataOver */ USB_ST_DATAOVERRUN,
+ /* DataUnder */ USB_ST_DATAUNDERRUN,
+ /* reservd */ USB_ST_NORESPONSE,
+ /* reservd */ USB_ST_NORESPONSE,
+ /* BufferOver */ USB_ST_BUFFEROVERRUN,
+ /* BuffUnder */ USB_ST_BUFFERUNDERRUN,
+ /* Not Access */ USB_ST_NORESPONSE,
+ /* Not Access */ USB_ST_NORESPONSE
+};
+
+#else /* error codes */
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* reservd */ -ETIMEDOUT,
+ /* reservd */ -ETIMEDOUT,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ECOMM,
+ /* Not Access */ -ETIMEDOUT,
+ /* Not Access */ -ETIMEDOUT
+};
+#define USB_ST_URB_PENDING -EINPROGRESS
+#endif
+
+
+
+struct ed;
+struct td;
+/* for ED and TD structures */
+
+/* ED States */
+
+#define ED_NEW 0x00
+#define ED_UNLINK 0x01
+#define ED_OPER 0x02
+#define ED_DEL 0x04
+#define ED_URB_DEL 0x08
+
+/* usb_ohci_ed */
+typedef struct ed {
+ __u32 hwINFO;
+ __u32 hwTailP;
+ __u32 hwHeadP;
+ __u32 hwNextED;
+
+ struct ed * ed_prev;
+ __u8 int_period;
+ __u8 int_branch;
+ __u8 int_load;
+ __u8 int_interval;
+ __u8 state;
+ __u8 type;
+ __u16 last_iso;
+ struct ed * ed_rm_list;
+
+} ed_t;
+
+
+/* TD info field */
+#define TD_CC 0xf0000000
+#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_EC 0x0C000000
+#define TD_T 0x03000000
+#define TD_T_DATA0 0x02000000
+#define TD_T_DATA1 0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R 0x00040000
+#define TD_DI 0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP 0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN 0x00100000
+#define TD_DP_OUT 0x00080000
+
+#define TD_ISO 0x00010000
+#define TD_DEL 0x00020000
+
+/* CC Codes */
+#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
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+#define TD_NOTACCESSED 0x0F
+
+
+#define MAXPSW 1
+
+typedef struct td {
+ __u32 hwINFO;
+ __u32 hwCBP; /* Current Buffer Pointer */
+ __u32 hwNextTD; /* Next TD Pointer */
+ __u32 hwBE; /* Memory Buffer End Pointer */
+ __u16 hwPSW[MAXPSW];
+
+ __u8 type;
+ __u8 index;
+ struct ed * ed;
+ struct td * next_dl_td;
+ urb_t * urb;
+} td_t;
+
+
+/* TD types */
+#define BULK 0x03
+#define INT 0x01
+#define CTRL 0x02
+#define ISO 0x00
+
+#define SEND 0x01
+#define ST_ADDR 0x02
+#define ADD_LEN 0x04
+#define DEL 0x08
+
+
+#define OHCI_ED_SKIP (1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+ __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];
+} __attribute((aligned(256)));
+
+
+/*
+ * Maximum number of root hub ports.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+/*
+ * cmdstatus register */
+#define OHCI_CLF 0x02
+#define OHCI_BLF 0x04
+
+/*
+ * Interrupt register masks
+ */
+#define OHCI_INTR_SO (1)
+#define OHCI_INTR_WDH (1 << 1)
+#define OHCI_INTR_SF (1 << 2)
+#define OHCI_INTR_RD (1 << 3)
+#define OHCI_INTR_UE (1 << 4)
+#define OHCI_INTR_FNO (1 << 5)
+#define OHCI_INTR_RHSC (1 << 6)
+#define OHCI_INTR_OC (1 << 30)
+#define OHCI_INTR_MIE (1 << 31)
+
+/*
+ * Control register masks
+ */
+#define OHCI_USB_RESET 0
+#define OHCI_USB_RESUME (1 << 6)
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+ int devnum; /* Address of Root Hub endpoint */
+ void * urb;
+ void * int_addr;
+ int send;
+ int interval;
+ struct timer_list rh_int_timer;
+};
+
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+/* Root-Hub Register info */
+
+#define RH_PS_CCS 0x00000001
+#define RH_PS_PES 0x00000002
+#define RH_PS_PSS 0x00000004
+#define RH_PS_POCI 0x00000008
+#define RH_PS_PRS 0x00000010
+#define RH_PS_PPS 0x00000100
+#define RH_PS_LSDA 0x00000200
+#define RH_PS_CSC 0x00010000
+#define RH_PS_PESC 0x00020000
+#define RH_PS_PSSC 0x00040000
+#define RH_PS_OCIC 0x00080000
+#define RH_PS_PRSC 0x00100000
+
+/* Root hub status bits */
+#define RH_HS_LPS 0x00000001
+#define RH_HS_OCI 0x00000002
+#define RH_HS_DRWE 0x00008000
+#define RH_HS_LPSC 0x00010000
+#define RH_HS_OCIC 0x00020000
+#define RH_HS_CRWE 0x80000000
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+
+/* urb */
+typedef struct
+{
+ ed_t * ed;
+ __u16 length; // number of tds associated with this request
+ __u16 td_cnt; // number of tds already serviced
+ int state;
+ void * wait;
+ td_t * td[0]; // list pointer to all corresponding TDs associated with this request
+
+} urb_priv_t;
+#define URB_DEL 1
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+ struct ohci_hcca hcca; /* hcca */
+
+ int irq;
+ struct ohci_regs * regs; /* OHCI controller's memory */
+ struct list_head ohci_hcd_list; /* list of all ohci_hcd */
+
+ struct ohci * next; // chain of uhci device contexts
+ struct list_head urb_list; // list of all pending urbs
+ spinlock_t urb_list_lock; // lock to keep consistency
+
+ int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/
+ ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */
+ ed_t * ed_bulktail; /* last endpoint of bulk list */
+ ed_t * ed_controltail; /* last endpoint of control list */
+ ed_t * ed_isotail; /* last endpoint of iso list */
+ int intrstatus;
+ __u32 hc_control; /* copy of the hc control reg */
+ struct usb_bus * bus;
+ struct usb_device * dev[128];
+ struct virt_root_hub rh;
+} ohci_t;
+
+
+#define NUM_TDS 0 /* num of preallocated transfer descriptors */
+#define NUM_EDS 32 /* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+ ed_t ed[NUM_EDS];
+ int ed_cnt;
+ void * wait;
+};
+
+// #define ohci_to_usb(ohci) ((ohci)->usb)
+#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load);
+static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
+/* td */
+static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type, int index);
+static void td_submit_urb(urb_t * urb);
+/* root hub */
+static int rh_submit_urb(urb_t * urb);
+static int rh_unlink_urb(urb_t * urb);
+static int rh_init_int_timer(urb_t * urb);
+
+#ifdef DEBUG
+#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x)
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
+static int __ohci_free_cnt = 0;
+#else
+#define OHCI_FREE(x) kfree(x)
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+#endif
+
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (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.
dbg("get_serial_by_minor %d", minor);
- for (i = 0; i < SERIAL_TTY_MINORS; ++i)
- if (serial_table[i])
- if (serial_table[i] != SERIAL_PTR_EMPTY)
- if (serial_table[i]->minor == minor)
- return (serial_table[i]);
+ if (serial_table[minor] == NULL)
+ return (NULL);
- return (NULL);
+ if (serial_table[minor] != SERIAL_PTR_EMPTY)
+ return (serial_table[minor]);
+
+ i = minor;
+ while (serial_table[i] == SERIAL_PTR_EMPTY) {
+ if (i == 0)
+ return (NULL);
+ --i;
+ }
+ return (serial_table[i]);
}
dbg("serial_open");
+ /* initialize the pointer incase something fails */
+ tty->driver_data = NULL;
+
/* get the serial object associated with this tty pointer */
serial = get_serial_by_minor (MINOR(tty->device));
{
/* send out two unknown commands that I found by looking at a Win98 trace */
int response;
+ int i;
unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL);
if (!transfer_buffer) {
dbg("visor_setup: Set config to 1");
usb_set_configuration (serial->dev, 1);
- response = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x03, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), 0x03, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
if (response < 0) {
err("visor_startup: error getting first vendor specific message");
} else {
- dbg("visor_startup: First vendor specific message successful");
+ dbg("visor_startup: First vendor specific message successful, data received:");
+ for (i = 0; i < response; ++i)
+ dbg(" 0x%.2x", transfer_buffer[i]);
}
- response = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x01, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), 0x01, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
if (response < 0) {
err("visor_startup: error getting second vendor specific message");
} else {
- dbg("visor_startup: Second vendor specific message successful");
+ dbg("visor_startup: Second vendor specific message successful, data received:");
+ for (i = 0; i < response; ++i)
+ dbg(" 0x%.2x", transfer_buffer[i]);
}
kfree (transfer_buffer);
int num_interrupt_in = 0;
int num_bulk_in = 0;
int num_bulk_out = 0;
+ int num_ports;
/* loop through our list of known serial converters, and see if this device matches */
device_num = 0;
/* found all that we need */
info("%s converter detected", type->name);
- serial = get_free_serial (type->num_ports, &minor);
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ if (type == &generic_device)
+ num_ports = num_bulk_out;
+ else
+#endif
+ num_ports = type->num_ports;
+
+ serial = get_free_serial (num_ports, &minor);
if (serial == NULL) {
err("No more free serial devices");
return NULL;
serial->dev = dev;
serial->type = type;
serial->minor = minor;
- serial->num_ports = type->num_ports;
+ 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;
--- /dev/null
+/*
+ * Universal Host Controller Interface driver for USB (take II).
+ *
+ * (c) 1999 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+ * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
+ * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
+ * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
+ *
+ * HW-initalization based on material of
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * $Id: usb-uhci.c,v 1.169 2000/01/20 19:50:11 acher Exp $
+ */
+
+
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+
+#include <linux/config.h>
+#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/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "usb.h"
+#include "usb-uhci.h"
+
+//#define DEBUG
+#include "uhci-debug.h"
+
+#ifdef dbg
+ #undef dbg
+#endif
+#undef DEBUG
+#ifdef DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+// Please leave the following intact, it makes debugging
+// (esp. after an hard oops crash) a lot easier!
+#define _static //static
+
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event (apm_event_t event);
+#endif
+
+/* We added an UHCI_SLAB slab support just for debugging purposes. In real
+ life this compile option is NOT recommended, because slab caches are not
+ suitable for modules.
+*/
+
+// #define _UHCI_SLAB
+#ifdef _UHCI_SLAB
+static kmem_cache_t *uhci_desc_kmem;
+static kmem_cache_t *urb_priv_kmem;
+#endif
+
+static int rh_submit_urb (purb_t purb);
+static int rh_unlink_urb (purb_t purb);
+static puhci_t devs = NULL;
+
+/* used by userspace UHCI data structure dumper */
+puhci_t *uhci_devices = &devs;
+
+/*-------------------------------------------------------------------*/
+_static void queue_urb (puhci_t s, struct list_head *p, int do_lock)
+{
+ unsigned long flags=0;
+
+ if (do_lock)
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ list_add_tail (p, &s->urb_list);
+
+ if (do_lock)
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+
+/*-------------------------------------------------------------------*/
+_static void dequeue_urb (puhci_t s, struct list_head *p, int do_lock)
+{
+ unsigned long flags=0;
+
+ if (do_lock)
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ list_del (p);
+
+ if (do_lock)
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+
+/*-------------------------------------------------------------------*/
+_static int alloc_td (puhci_desc_t * new, int flags)
+{
+#ifdef _UHCI_SLAB
+ *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+#else
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+#endif
+ if (!*new)
+ return -ENOMEM;
+
+ memset (*new, 0, sizeof (uhci_desc_t));
+ (*new)->hw.td.link = UHCI_PTR_TERM | (flags & UHCI_PTR_BITS); // last by default
+
+ (*new)->type = TD_TYPE;
+ mb();
+ INIT_LIST_HEAD (&(*new)->vertical);
+ INIT_LIST_HEAD (&(*new)->horizontal);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert td at last position in td-list of qh (vertical) */
+_static int insert_td (puhci_t s, puhci_desc_t qh, puhci_desc_t new, int flags)
+{
+ uhci_desc_t *prev;
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ list_add_tail (&new->vertical, &qh->vertical);
+
+ if (qh->hw.qh.element & UHCI_PTR_TERM) {
+ // virgin qh without any tds
+ qh->hw.qh.element = virt_to_bus (new); /* QH's cannot have the DEPTH bit set */
+ }
+ else {
+ // already tds inserted
+ prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
+ // implicitely remove TERM bit of prev
+ prev->hw.td.link = virt_to_bus (new) | (flags & UHCI_PTR_DEPTH);
+ }
+ mb();
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert new_td after td (horizontal) */
+_static int insert_td_horizontal (puhci_t s, puhci_desc_t td, puhci_desc_t new, int flags)
+{
+ uhci_desc_t *next;
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
+ new->hw.td.link = td->hw.td.link;
+ mb();
+ list_add (&new->horizontal, &td->horizontal);
+ td->hw.td.link = virt_to_bus (new);
+ mb();
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int unlink_td (puhci_t s, puhci_desc_t element)
+{
+ uhci_desc_t *next, *prev;
+ int dir = 0;
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ next = list_entry (element->vertical.next, uhci_desc_t, vertical);
+
+ if (next == element) {
+ dir = 1;
+ next = list_entry (element->horizontal.next, uhci_desc_t, horizontal);
+ prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+ }
+ else {
+ prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
+ }
+
+ if (prev->type == TD_TYPE)
+ prev->hw.td.link = element->hw.td.link;
+ else
+ prev->hw.qh.element = element->hw.td.link;
+
+ mb ();
+
+ if (dir == 0)
+ list_del (&element->vertical);
+ else
+ list_del (&element->horizontal);
+
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int delete_desc (puhci_desc_t element)
+{
+#ifdef _UHCI_SLAB
+ kmem_cache_free(uhci_desc_kmem, element);
+#else
+ kfree (element);
+#endif
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+// Allocates qh element
+_static int alloc_qh (puhci_desc_t * new)
+{
+#ifdef _UHCI_SLAB
+ *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+#else
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+#endif
+ if (!*new)
+ return -ENOMEM;
+
+ memset (*new, 0, sizeof (uhci_desc_t));
+ (*new)->hw.qh.head = UHCI_PTR_TERM;
+ (*new)->hw.qh.element = UHCI_PTR_TERM;
+ (*new)->type = QH_TYPE;
+ mb();
+ INIT_LIST_HEAD (&(*new)->horizontal);
+ INIT_LIST_HEAD (&(*new)->vertical);
+
+ dbg (KERN_DEBUG MODSTR "Allocated qh @ %p\n", *new);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+// inserts new qh before/after the qh at pos
+// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
+_static int insert_qh (puhci_t s, puhci_desc_t pos, puhci_desc_t new, int flags)
+{
+ puhci_desc_t old;
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->qh_lock, xxx);
+
+ if (!flags) {
+ // (OLD) (POS) -> (OLD) (NEW) (POS)
+ old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
+ list_add_tail (&new->horizontal, &pos->horizontal);
+ new->hw.qh.head = MAKE_QH_ADDR (pos) ;
+ mb();
+ if (!(old->hw.qh.head & UHCI_PTR_TERM))
+ old->hw.qh.head = MAKE_QH_ADDR (new) ;
+ }
+ else {
+ // (POS) (OLD) -> (POS) (NEW) (OLD)
+ old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
+ list_add (&new->horizontal, &pos->horizontal);
+ new->hw.qh.head = MAKE_QH_ADDR (old);
+ mb();
+ pos->hw.qh.head = MAKE_QH_ADDR (new) ;
+ }
+
+ mb ();
+
+ spin_unlock_irqrestore (&s->qh_lock, xxx);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int unlink_qh (puhci_t s, puhci_desc_t element)
+{
+ puhci_desc_t next, prev;
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->qh_lock, xxx);
+
+ next = list_entry (element->horizontal.next, uhci_desc_t, horizontal);
+ prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+ prev->hw.qh.head = element->hw.qh.head;
+ mb ();
+ list_del (&element->horizontal);
+
+ spin_unlock_irqrestore (&s->qh_lock, xxx);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int delete_qh (puhci_t s, puhci_desc_t qh)
+{
+ puhci_desc_t td;
+ struct list_head *p;
+
+ list_del (&qh->horizontal);
+
+ while ((p = qh->vertical.next) != &qh->vertical) {
+ td = list_entry (p, uhci_desc_t, vertical);
+ unlink_td (s, td);
+ delete_desc (td);
+ }
+
+ delete_desc (qh);
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+void clean_td_chain (puhci_desc_t td)
+{
+ struct list_head *p;
+ puhci_desc_t td1;
+
+ if (!td)
+ return;
+
+ while ((p = td->horizontal.next) != &td->horizontal) {
+ td1 = list_entry (p, uhci_desc_t, horizontal);
+ delete_desc (td1);
+ }
+
+ delete_desc (td);
+}
+/*-------------------------------------------------------------------*/
+// Removes ALL qhs in chain (paranoia!)
+_static void cleanup_skel (puhci_t s)
+{
+ unsigned int n;
+ puhci_desc_t td;
+
+ printk (KERN_DEBUG MODSTR "Cleanup_skel\n");
+
+ for (n = 0; n < 8; n++) {
+ td = s->int_chain[n];
+ clean_td_chain (td);
+ }
+
+ if (s->iso_td) {
+ for (n = 0; n < 1024; n++) {
+ td = s->iso_td[n];
+ clean_td_chain (td);
+ }
+ kfree (s->iso_td);
+ }
+
+ if (s->framelist)
+ free_page ((unsigned long) s->framelist);
+
+ if (s->control_chain) {
+ // completed init_skel?
+ struct list_head *p;
+ puhci_desc_t qh, qh1;
+
+ qh = s->control_chain;
+ while ((p = qh->horizontal.next) != &qh->horizontal) {
+ qh1 = list_entry (p, uhci_desc_t, horizontal);
+ delete_qh (s, qh1);
+ }
+ delete_qh (s, qh);
+ }
+ else {
+ if (s->control_chain)
+ kfree (s->control_chain);
+ if (s->bulk_chain)
+ kfree (s->bulk_chain);
+ if (s->chain_end)
+ kfree (s->chain_end);
+ }
+ printk (KERN_DEBUG MODSTR "Cleanup_skel finished\n");
+}
+/*-------------------------------------------------------------------*/
+// allocates framelist and qh-skeletons
+// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
+_static int init_skel (puhci_t s)
+{
+ int n, ret;
+ puhci_desc_t qh, td;
+
+ dbg (KERN_DEBUG MODSTR "init_skel\n");
+
+ s->framelist = (__u32 *) get_free_page (GFP_KERNEL);
+
+ if (!s->framelist)
+ return -ENOMEM;
+
+ memset (s->framelist, 0, 4096);
+
+ dbg (KERN_DEBUG MODSTR "allocating iso desc pointer list\n");
+ s->iso_td = (puhci_desc_t *) kmalloc (1024 * sizeof (puhci_desc_t), GFP_KERNEL);
+
+ if (!s->iso_td)
+ goto init_skel_cleanup;
+
+ s->control_chain = NULL;
+ s->bulk_chain = NULL;
+ s->chain_end = NULL;
+
+ dbg (KERN_DEBUG MODSTR "allocating iso descs\n");
+ for (n = 0; n < 1024; n++) {
+ // allocate skeleton iso/irq-tds
+ ret = alloc_td (&td, 0);
+ if (ret)
+ goto init_skel_cleanup;
+ s->iso_td[n] = td;
+ s->framelist[n] = ((__u32) virt_to_bus (td));
+ }
+
+ dbg (KERN_DEBUG MODSTR "allocating qh: chain_end\n");
+ ret = alloc_qh (&qh);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ s->chain_end = qh;
+
+ dbg (KERN_DEBUG MODSTR "allocating qh: bulk_chain\n");
+ ret = alloc_qh (&qh);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ insert_qh (s, s->chain_end, qh, 0);
+ s->bulk_chain = qh;
+ dbg (KERN_DEBUG MODSTR "allocating qh: control_chain\n");
+ ret = alloc_qh (&qh);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ insert_qh (s, s->bulk_chain, qh, 0);
+ s->control_chain = qh;
+ for (n = 0; n < 8; n++)
+ s->int_chain[n] = 0;
+
+ dbg (KERN_DEBUG MODSTR "Allocating skeleton INT-TDs\n");
+
+ for (n = 0; n < 8; n++) {
+ puhci_desc_t td;
+
+ alloc_td (&td, 0);
+ if (!td)
+ goto init_skel_cleanup;
+ s->int_chain[n] = td;
+ if (n == 0) {
+ s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+ }
+ else {
+ s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
+ }
+ }
+
+ dbg (KERN_DEBUG MODSTR "Linking skeleton INT-TDs\n");
+
+ for (n = 0; n < 1024; n++) {
+ // link all iso-tds to the interrupt chains
+ int m, o;
+ dbg("framelist[%i]=%x\n",n,s->framelist[n]);
+ if ((n&127)==127)
+ ((puhci_desc_t) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
+ else {
+ for (o = 1, m = 2; m <= 128; o++, m += m) {
+ // n&(m-1) = n%m
+ if ((n & (m - 1)) == ((m - 1) / 2)) {
+ ((puhci_desc_t) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
+ }
+ }
+ }
+ }
+
+ mb();
+ //uhci_show_queue(s->control_chain);
+ dbg (KERN_DEBUG MODSTR "init_skel exit\n");
+ return 0; // OK
+
+ init_skel_cleanup:
+ cleanup_skel (s);
+ return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (puhci_desc_t td, int status, int info, __u32 buffer)
+{
+ td->hw.td.status = status;
+ td->hw.td.info = info;
+ td->hw.td.buffer = buffer;
+}
+
+/*-------------------------------------------------------------------*/
+// LOW LEVEL STUFF
+// assembles QHs und TDs for control, bulk and iso
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_control_urb (purb_t purb)
+{
+ puhci_desc_t qh, td;
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ purb_priv_t purb_priv = purb->hcpriv;
+ unsigned long destination, status;
+ int maxsze = usb_maxpacket (purb->dev, purb->pipe, usb_pipeout (purb->pipe));
+ unsigned long len, bytesrequested;
+ char *data;
+
+ dbg (KERN_DEBUG MODSTR "uhci_submit_control start\n");
+ alloc_qh (&qh); // alloc qh for this request
+
+ if (!qh)
+ return -ENOMEM;
+
+ alloc_td (&td, UHCI_PTR_DEPTH); // get td for setup stage
+
+ if (!td) {
+ delete_qh (s, qh);
+ return -ENOMEM;
+ }
+
+ /* The "pipe" thing contains the destination in bits 8--18 */
+ destination = (purb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+ /* 3 errors */
+ status = (purb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+ (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+ /* Build the TD for the control request, try forever, 8 bytes of data */
+ fill_td (td, status, destination | (7 << 21), virt_to_bus (purb->setup_packet));
+
+ /* 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_PID_IN); /* SETUP -> IN */
+
+ if (usb_pipeout (purb->pipe))
+ destination ^= (USB_PID_IN ^ USB_PID_OUT); /* IN -> OUT */
+
+ insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
+#if 0
+ printk ("SETUP to pipe %x: %x %x %x %x %x %x %x %x\n", purb->pipe,
+ purb->setup_packet[0], purb->setup_packet[1], purb->setup_packet[2], purb->setup_packet[3],
+ purb->setup_packet[4], purb->setup_packet[5], purb->setup_packet[6], purb->setup_packet[7]);
+ //uhci_show_td(td);
+#endif
+
+ /* Build the DATA TD's */
+ len = purb->transfer_buffer_length;
+ bytesrequested = len;
+ data = purb->transfer_buffer;
+
+ while (len > 0) {
+ int pktsze = len;
+
+ alloc_td (&td, UHCI_PTR_DEPTH);
+ if (!td) {
+ delete_qh (s, qh);
+ return -ENOMEM;
+ }
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1
+
+ fill_td (td, status, destination | ((pktsze - 1) << 21),
+ virt_to_bus (data)); // Status, pktsze bytes of data
+
+ insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue 'data stage'-td in qh
+
+ data += pktsze;
+ len -= pktsze;
+ }
+
+ /* Build the final TD for control status */
+ /* It's only IN if the pipe is out AND we aren't expecting data */
+ destination &= ~0xFF;
+
+ if (usb_pipeout (purb->pipe) | (bytesrequested == 0))
+ destination |= USB_PID_IN;
+ else
+ destination |= USB_PID_OUT;
+
+ destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */
+
+ alloc_td (&td, UHCI_PTR_DEPTH);
+
+ if (!td) {
+ delete_qh (s, qh);
+ return -ENOMEM;
+ }
+
+ /* no limit on errors on final packet , 0 bytes of data */
+ fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
+ 0);
+
+ insert_td (s, qh, td, UHCI_PTR_DEPTH); // queue status td
+
+
+ list_add (&qh->desc_list, &purb_priv->desc_list);
+
+ purb->status = USB_ST_URB_PENDING;
+ queue_urb (s, &purb->urb_list,1); // queue before inserting in desc chain
+
+ //uhci_show_queue(qh);
+
+ /* Start it up... put low speed first */
+ if (purb->pipe & TD_CTRL_LS)
+ insert_qh (s, s->control_chain, qh, 1); // insert after control chain
+ else
+ insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
+
+ //uhci_show_queue(qh);
+ dbg (KERN_DEBUG MODSTR "uhci_submit_control end\n");
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_bulk_urb (purb_t purb)
+{
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ purb_priv_t purb_priv = purb->hcpriv;
+ puhci_desc_t qh, td;
+ unsigned long destination, status;
+ char *data;
+ unsigned int pipe = purb->pipe;
+ int maxsze = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+ int info, len;
+
+ /* shouldn't the clear_halt be done in the USB core or in the client driver? - Thomas */
+ if (usb_endpoint_halted (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) &&
+ usb_clear_halt (purb->dev, usb_pipeendpoint (pipe) | (pipe & USB_DIR_IN)))
+ return -EPIPE;
+
+ if (!maxsze)
+ return -EMSGSIZE;
+ /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+
+ alloc_qh (&qh); // get qh for this request
+
+ if (!qh)
+ return -ENOMEM;
+
+ /* The "pipe" thing contains the destination in bits 8--18. */
+ destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
+
+ /* 3 errors */
+ status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+ ((purb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+ /* Build the TDs for the bulk request */
+ len = purb->transfer_buffer_length;
+ data = purb->transfer_buffer;
+ dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: pipe %x, len %d\n", pipe, len);
+
+ while (len > 0) {
+ int pktsze = len;
+
+ alloc_td (&td, UHCI_PTR_DEPTH);
+
+ if (!td) {
+ delete_qh (s, qh);
+ return -ENOMEM;
+ }
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ // pktsze bytes of data
+ info = destination | ((pktsze - 1) << 21) |
+ (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+ fill_td (td, status, info, virt_to_bus (data));
+
+ data += pktsze;
+ len -= pktsze;
+
+ if (!len)
+ td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
+ //dbg("insert td %p, len %i\n",td,pktsze);
+
+ insert_td (s, qh, td, UHCI_PTR_DEPTH);
+
+ /* Alternate Data0/1 (start with Data0) */
+ usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+ }
+
+ list_add (&qh->desc_list, &purb_priv->desc_list);
+
+ purb->status = USB_ST_URB_PENDING;
+ queue_urb (s, &purb->urb_list,1);
+
+ insert_qh (s, s->chain_end, qh, 0); // insert before end marker
+ //uhci_show_queue(s->bulk_chain);
+
+ dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: exit\n");
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+// Problem: unlinking in interrupt requires waiting for one frame (udelay)
+// to allow the whole structures to be safely removed
+_static int uhci_unlink_urb (purb_t purb)
+{
+ puhci_t s;
+ puhci_desc_t qh;
+ puhci_desc_t td;
+ purb_priv_t purb_priv;
+ unsigned long flags=0;
+ struct list_head *p;
+
+ if (!purb) // you never know...
+ return -1;
+
+ s = (puhci_t) purb->dev->bus->hcpriv; // get pointer to uhci struct
+
+ if (usb_pipedevice (purb->pipe) == s->rh.devnum)
+ return rh_unlink_urb (purb);
+
+ if(!in_interrupt()) {
+ spin_lock_irqsave (&s->unlink_urb_lock, flags); // do not allow interrupts
+ }
+
+ //dbg("unlink_urb called %p\n",purb);
+ if (purb->status == USB_ST_URB_PENDING) {
+ // URB probably still in work
+ purb_priv = purb->hcpriv;
+ dequeue_urb (s, &purb->urb_list,1);
+ purb->status = USB_ST_URB_KILLED; // mark urb as killed
+
+ if(!in_interrupt()) {
+ spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here
+ }
+
+ switch (usb_pipetype (purb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ for (p = purb_priv->desc_list.next; p != &purb_priv->desc_list; p = p->next) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ unlink_td (s, td);
+ }
+ // wait at least 1 Frame
+ if (in_interrupt ())
+ udelay (1000);
+ else
+ wait_ms(1);
+ while ((p = purb_priv->desc_list.next) != &purb_priv->desc_list) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ list_del (p);
+ delete_desc (td);
+ }
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ qh = list_entry (purb_priv->desc_list.next, uhci_desc_t, desc_list);
+
+ unlink_qh (s, qh); // remove this qh from qh-list
+ // wait at least 1 Frame
+
+ if (in_interrupt ())
+ udelay (1000);
+ else
+ wait_ms(1);
+ delete_qh (s, qh); // remove it physically
+
+ }
+
+#ifdef _UHCI_SLAB
+ kmem_cache_free(urb_priv_kmem, purb->hcpriv);
+#else
+ kfree (purb->hcpriv);
+#endif
+ if (purb->complete) {
+ dbg (KERN_DEBUG MODSTR "unlink_urb: calling completion\n");
+ purb->complete ((struct urb *) purb);
+ usb_dec_dev_use (purb->dev);
+ }
+ return 0;
+ }
+ else {
+ if(!in_interrupt())
+ spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here
+ }
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+// In case of ASAP iso transfer, search the URB-list for already queued URBs
+// for this EP and calculate the earliest start frame for the new
+// URB (easy seamless URB continuation!)
+_static int find_iso_limits (purb_t purb, unsigned int *start, unsigned int *end)
+{
+ purb_t u, last_urb = NULL;
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ struct list_head *p = s->urb_list.next;
+ int ret=-1;
+ unsigned long flags;
+
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ for (; p != &s->urb_list; p = p->next) {
+ u = list_entry (p, urb_t, urb_list);
+ // look for pending URBs with identical pipe handle
+ // works only because iso doesn't toggle the data bit!
+ if ((purb->pipe == u->pipe) && (purb->dev == u->dev) && (u->status == USB_ST_URB_PENDING)) {
+ if (!last_urb)
+ *start = u->start_frame;
+ last_urb = u;
+ }
+ }
+
+ if (last_urb) {
+ *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+ ret=0;
+ }
+
+ spin_unlock_irqrestore(&s->urb_list_lock, flags);
+
+ return ret; // no previous urb found
+
+}
+/*-------------------------------------------------------------------*/
+// adjust start_frame according to scheduling constraints (ASAP etc)
+
+_static int iso_find_start (purb_t purb)
+{
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ unsigned int now;
+ unsigned int start_limit = 0, stop_limit = 0, queued_size;
+ int limits;
+
+ now = UHCI_GET_CURRENT_FRAME (s) & 1023;
+
+ if ((unsigned) purb->number_of_packets > 900)
+ return -EFBIG;
+
+ limits = find_iso_limits (purb, &start_limit, &stop_limit);
+ queued_size = (stop_limit - start_limit) & 1023;
+
+ if (purb->transfer_flags & USB_ISO_ASAP) {
+ // first iso
+ if (limits) {
+ // 10ms setup should be enough //FIXME!
+ purb->start_frame = (now + 10) & 1023;
+ }
+ else {
+ purb->start_frame = stop_limit; //seamless linkage
+
+ if (((now - purb->start_frame) & 1023) <= (unsigned) purb->number_of_packets) {
+ printk (KERN_DEBUG MODSTR "iso_find_start: warning, ASAP gap, should not happen\n");
+ printk (KERN_DEBUG MODSTR "iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x\n",
+ now, purb->start_frame, purb->number_of_packets, purb->pipe);
+// The following code is only for debugging purposes...
+#if 0
+ {
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ struct list_head *p;
+ purb_t u;
+ int a = -1, b = -1;
+ unsigned long flags;
+
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ p=s->urb_list.next;
+
+ for (; p != &s->urb_list; p = p->next) {
+ u = list_entry (p, urb_t, urb_list);
+ if (purb->dev != u->dev)
+ continue;
+ printk (KERN_DEBUG MODSTR "urb: pipe 0x%08x status %d start_frame %u number_of_packets %u\n",
+ u->pipe, u->status, u->start_frame, u->number_of_packets);
+ if (!usb_pipeisoc (u->pipe))
+ continue;
+ if (a == -1)
+ a = u->start_frame;
+ b = (u->start_frame + u->number_of_packets - 1) & 1023;
+ }
+ spin_unlock_irqrestore(&s->urb_list_lock, flags);
+ }
+#endif
+ purb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME!
+ //return -EAGAIN; //FIXME
+ }
+ }
+ }
+ else {
+ purb->start_frame &= 1023;
+ if (((now - purb->start_frame) & 1023) < (unsigned) purb->number_of_packets) {
+ printk (KERN_DEBUG MODSTR "iso_find_start: now between start_frame and end\n");
+ return -EAGAIN;
+ }
+ }
+
+ /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
+ if (limits)
+ return 0;
+
+ if (((purb->start_frame - start_limit) & 1023) < queued_size ||
+ ((purb->start_frame + purb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
+ printk (KERN_DEBUG MODSTR "iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u\n",
+ purb->start_frame, purb->number_of_packets, start_limit, stop_limit);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+// submits USB interrupt (ie. polling ;-)
+// ASAP-flag set implicitely
+// if period==0, the the transfer is only done once (usb_scsi need this...)
+
+_static int uhci_submit_int_urb (purb_t purb)
+{
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ purb_priv_t purb_priv = purb->hcpriv;
+ int nint, n, ret;
+ puhci_desc_t td;
+ int status, destination;
+ int now;
+ int info;
+ unsigned int pipe = purb->pipe;
+
+ //printk("SUBMIT INT\n");
+
+ if (purb->interval < 0 || purb->interval >= 256)
+ return -EINVAL;
+
+ if (purb->interval == 0)
+ nint = 0;
+ else {
+ for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n
+ {
+ if (purb->interval < n) {
+ purb->interval = n / 2;
+ break;
+ }
+ }
+ nint--;
+ }
+ dbg(KERN_INFO "Rounded interval to %i, chain %i\n", purb->interval, nint);
+
+ now = UHCI_GET_CURRENT_FRAME (s) & 1023;
+ purb->start_frame = now; // remember start frame, just in case...
+
+ purb->number_of_packets = 1;
+
+ // INT allows only one packet
+ if (purb->transfer_buffer_length > usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)))
+ return -EINVAL;
+
+ ret = alloc_td (&td, UHCI_PTR_DEPTH);
+
+ if (ret)
+ return -ENOMEM;
+
+ status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+ (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+ destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe) |
+ (((purb->transfer_buffer_length - 1) & 0x7ff) << 21);
+
+
+ info = destination | (usb_gettoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+ fill_td (td, status, info, virt_to_bus (purb->transfer_buffer));
+ list_add_tail (&td->desc_list, &purb_priv->desc_list);
+
+ purb->status = USB_ST_URB_PENDING;
+ queue_urb (s, &purb->urb_list,1);
+
+ insert_td_horizontal (s, s->int_chain[nint], td, UHCI_PTR_DEPTH); // store in INT-TDs
+
+ usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
+#if 0
+ td = tdm[purb->number_of_packets];
+ fill_td (td, TD_CTRL_IOC, 0, 0);
+ insert_td_horizontal (s, s->iso_td[(purb->start_frame + (purb->number_of_packets) * purb->interval + 1) & 1023], td, UHCI_PTR_DEPTH);
+ list_add_tail (&td->desc_list, &purb_priv->desc_list);
+#endif
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_iso_urb (purb_t purb)
+{
+ puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
+ purb_priv_t purb_priv = purb->hcpriv;
+ int pipe=purb->pipe;
+ int maxsze = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+ int n, ret, last=0;
+ puhci_desc_t td, *tdm;
+ int status, destination;
+ unsigned long flags;
+ spinlock_t lock;
+
+ spin_lock_init (&lock);
+ spin_lock_irqsave (&lock, flags); // Disable IRQs to schedule all ISO-TDs in time
+
+ ret = iso_find_start (purb); // adjusts purb->start_frame for later use
+
+ if (ret)
+ goto err;
+
+ tdm = (puhci_desc_t *) kmalloc (purb->number_of_packets * sizeof (puhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+
+ if (!tdm) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ // First try to get all TDs
+ for (n = 0; n < purb->number_of_packets; n++) {
+ dbg (KERN_DEBUG MODSTR "n:%d purb->iso_frame_desc[n].length:%d\n", n, purb->iso_frame_desc[n].length);
+ if (!purb->iso_frame_desc[n].length) {
+ // allows ISO striping by setting length to zero in iso_descriptor
+ tdm[n] = 0;
+ continue;
+ }
+ if(purb->iso_frame_desc[n].length > maxsze) {
+ printk(KERN_ERR MODSTR"submit_iso: purb->iso_frame_desc[%d].length(%d)>%d",n , purb->iso_frame_desc[n].length, maxsze);
+ tdm[n] = 0;
+ continue;
+ }
+ ret = alloc_td (&td, UHCI_PTR_DEPTH);
+ if (ret) {
+ int i; // Cleanup allocated TDs
+
+ for (i = 0; i < n; n++)
+ if (tdm[i])
+ kfree (tdm[i]);
+ kfree (tdm);
+ ret = -ENOMEM;
+ goto err;
+ }
+ last=n;
+ tdm[n] = td;
+ }
+
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (purb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD);
+
+ destination = (purb->pipe & PIPE_DEVEP_MASK) | usb_packetid (purb->pipe);
+
+
+ // Queue all allocated TDs
+ for (n = 0; n < purb->number_of_packets; n++) {
+ td = tdm[n];
+ if (!td)
+ continue;
+
+ if (n == last)
+ status |= TD_CTRL_IOC;
+
+ fill_td (td, status, destination | (((purb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
+ virt_to_bus (purb->transfer_buffer + purb->iso_frame_desc[n].offset));
+ list_add_tail (&td->desc_list, &purb_priv->desc_list);
+
+ if (n == last) {
+ purb->status = USB_ST_URB_PENDING;
+ queue_urb (s, &purb->urb_list,1);
+ }
+ insert_td_horizontal (s, s->iso_td[(purb->start_frame + n) & 1023], td, UHCI_PTR_DEPTH); // store in iso-tds
+ //uhci_show_td(td);
+
+ }
+
+ kfree (tdm);
+ dbg ("ISO-INT# %i, start %i, now %i\n", purb->number_of_packets, purb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);
+ ret = 0;
+
+ err:
+ spin_unlock_irqrestore (&lock, flags);
+ return ret;
+
+}
+/*-------------------------------------------------------------------*/
+_static int search_dev_ep (puhci_t s, purb_t purb)
+{
+ unsigned long flags;
+ struct list_head *p = s->urb_list.next;
+ purb_t tmp;
+ unsigned int mask = usb_pipecontrol(purb->pipe) ? (~USB_DIR_IN) : (~0);
+
+ dbg (KERN_DEBUG MODSTR "search_dev_ep:\n");
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ for (; p != &s->urb_list; p = p->next) {
+ tmp = list_entry (p, urb_t, urb_list);
+ dbg (KERN_DEBUG MODSTR "urb: %p\n", tmp);
+ // we can accept this urb if it is not queued at this time
+ // or if non-iso transfer requests should be scheduled for the same device and pipe
+ if ((!usb_pipeisoc(purb->pipe) && tmp->dev == purb->dev && !((tmp->pipe ^ purb->pipe) & mask)) ||
+ (purb == tmp)) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ return 1; // found another urb already queued for processing
+ }
+ }
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_urb (purb_t purb)
+{
+ puhci_t s;
+ purb_priv_t purb_priv;
+ int ret = 0;
+
+ if (!purb->dev || !purb->dev->bus)
+ return -ENODEV;
+
+ s = (puhci_t) purb->dev->bus->hcpriv;
+ //printk( MODSTR"submit_urb: %p type %d\n",purb,usb_pipetype(purb->pipe));
+
+ if (usb_pipedevice (purb->pipe) == s->rh.devnum)
+ return rh_submit_urb (purb); /* virtual root hub */
+
+ usb_inc_dev_use (purb->dev);
+
+ if (search_dev_ep (s, purb)) {
+ usb_dec_dev_use (purb->dev);
+ return -ENXIO; // urb already queued
+
+ }
+
+#ifdef _UHCI_SLAB
+ purb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+#else
+ purb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+#endif
+ if (!purb_priv) {
+ usb_dec_dev_use (purb->dev);
+ return -ENOMEM;
+ }
+
+ purb->hcpriv = purb_priv;
+ INIT_LIST_HEAD (&purb_priv->desc_list);
+ purb_priv->short_control_packet=0;
+ dbg (KERN_DEBUG MODSTR "submit_urb: scheduling %p\n", purb);
+
+ switch (usb_pipetype (purb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ret = uhci_submit_iso_urb (purb);
+ break;
+ case PIPE_INTERRUPT:
+ ret = uhci_submit_int_urb (purb);
+ break;
+ case PIPE_CONTROL:
+ //dump_urb (purb);
+ ret = uhci_submit_control_urb (purb);
+ break;
+ case PIPE_BULK:
+ ret = uhci_submit_bulk_urb (purb);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ dbg (KERN_DEBUG MODSTR "submit_urb: scheduled with ret: %d\n", ret);
+
+ if (ret != USB_ST_NOERROR) {
+ usb_dec_dev_use (purb->dev);
+#ifdef _UHCI_SLAB
+ kmem_cache_free(urb_priv_kmem, purb_priv);
+#else
+ kfree (purb_priv);
+#endif
+ return ret;
+ }
+/*
+ purb->status = USB_ST_URB_PENDING;
+ queue_urb (s, &purb->urb_list,1);
+ dbg (KERN_DEBUG MODSTR "submit_urb: exit\n");
+*/
+ return 0;
+}
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+_static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __u16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x00, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+_static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 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; */
+
+ /* 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; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+
+_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 *** */
+};
+
+/*-------------------------------------------------------------------------*/
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+_static int rh_send_irq (purb_t purb)
+{
+
+ int len = 1;
+ int i;
+ puhci_t uhci = purb->dev->bus->hcpriv;
+ unsigned int io_addr = uhci->io_addr;
+ __u16 data = 0;
+
+ for (i = 0; i < uhci->rh.numports; i++) {
+ data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+ len = (i + 1) / 8 + 1;
+ }
+
+ *(__u16 *) purb->transfer_buffer = cpu_to_le16 (data);
+ purb->actual_length = len;
+ purb->status = USB_ST_NOERROR;
+
+ if ((data > 0) && (uhci->rh.send != 0)) {
+ dbg (KERN_DEBUG MODSTR "Root-Hub INT complete: port1: %x port2: %x data: %x\n",
+ inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
+ purb->complete (purb);
+
+ }
+ return USB_ST_NOERROR;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+_static int rh_init_int_timer (purb_t purb);
+
+_static void rh_int_timer_do (unsigned long ptr)
+{
+ int len;
+
+ purb_t purb = (purb_t) ptr;
+ puhci_t uhci = purb->dev->bus->hcpriv;
+
+ if (uhci->rh.send) {
+ len = rh_send_irq (purb);
+ if (len > 0) {
+ purb->actual_length = len;
+ if (purb->complete)
+ purb->complete (purb);
+ }
+ }
+ rh_init_int_timer (purb);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Root Hub INTs are polled by this timer */
+_static int rh_init_int_timer (purb_t purb)
+{
+ puhci_t uhci = purb->dev->bus->hcpriv;
+
+ uhci->rh.interval = purb->interval;
+ init_timer (&uhci->rh.rh_int_timer);
+ uhci->rh.rh_int_timer.function = rh_int_timer_do;
+ uhci->rh.rh_int_timer.data = (unsigned long) purb;
+ uhci->rh.rh_int_timer.expires = jiffies + (HZ * (purb->interval < 30 ? 30 : purb->interval)) / 1000;
+ add_timer (&uhci->rh.rh_int_timer);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#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))
+
+
+/*-------------------------------------------------------------------------*/
+/****
+ ** Root Hub Control Pipe
+ *************************/
+
+
+_static int rh_submit_urb (purb_t purb)
+{
+ struct usb_device *usb_dev = purb->dev;
+ puhci_t uhci = usb_dev->bus->hcpriv;
+ unsigned int pipe = purb->pipe;
+ devrequest *cmd = (devrequest *) purb->setup_packet;
+ void *data = purb->transfer_buffer;
+ int leni = purb->transfer_buffer_length;
+ int len = 0;
+ int status = 0;
+ int stat = USB_ST_NOERROR;
+ int i;
+ unsigned int io_addr = uhci->io_addr;
+ __u16 cstatus;
+
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+ if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+ dbg (KERN_DEBUG MODSTR "Root-Hub submit IRQ: every %d ms\n", purb->interval);
+ uhci->rh.urb = purb;
+ uhci->rh.send = 1;
+ uhci->rh.interval = purb->interval;
+ rh_init_int_timer (purb);
+
+ return USB_ST_NOERROR;
+ }
+
+
+ bmRType_bReq = cmd->requesttype | cmd->request << 8;
+ wValue = le16_to_cpu (cmd->value);
+ wIndex = le16_to_cpu (cmd->index);
+ wLength = le16_to_cpu (cmd->length);
+
+ for (i = 0; i < 8; i++)
+ uhci->rh.c_p_r[i] = 0;
+
+ dbg(KERN_DEBUG MODSTR "Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x\n",
+ uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+ switch (bmRType_bReq) {
+ /* 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 RH_GET_STATUS:
+ *(__u16 *) data = cpu_to_le16 (1);
+ OK (2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data = cpu_to_le16 (0);
+ OK (2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data = cpu_to_le16 (0);
+ OK (2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data = cpu_to_le32 (0);
+ OK (4); /* hub power ** */
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+ cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+ ((status & USBPORTSC_PEC) >> (3 - 1)) |
+ (uhci->rh.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 *) data = cpu_to_le16 (status);
+ *(__u16 *) (data + 2) = cpu_to_le16 (cstatus);
+ OK (4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case (RH_C_HUB_OVER_CURRENT):
+ OK (0); /* hub power over current ** */
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ CLR_RH_PORTSTAT (USBPORTSC_PE);
+ OK (0);
+ case (RH_PORT_SUSPEND):
+ CLR_RH_PORTSTAT (USBPORTSC_SUSP);
+ OK (0);
+ case (RH_PORT_POWER):
+ OK (0); /* port power ** */
+ case (RH_C_PORT_CONNECTION):
+ SET_RH_PORTSTAT (USBPORTSC_CSC);
+ OK (0);
+ case (RH_C_PORT_ENABLE):
+ SET_RH_PORTSTAT (USBPORTSC_PEC);
+ OK (0);
+ case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+ OK (0);
+ case (RH_C_PORT_OVER_CURRENT):
+ OK (0); /* port power over current ** */
+ case (RH_C_PORT_RESET):
+ uhci->rh.c_p_r[wIndex - 1] = 0;
+ OK (0);
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ SET_RH_PORTSTAT (USBPORTSC_SUSP);
+ OK (0);
+ case (RH_PORT_RESET):
+ SET_RH_PORTSTAT (USBPORTSC_PR);
+ wait_ms (10);
+ uhci->rh.c_p_r[wIndex - 1] = 1;
+ CLR_RH_PORTSTAT (USBPORTSC_PR);
+ udelay (10);
+ SET_RH_PORTSTAT (USBPORTSC_PE);
+ wait_ms (10);
+ SET_RH_PORTSTAT (0xa);
+ OK (0);
+ case (RH_PORT_POWER):
+ OK (0); /* port power ** */
+ case (RH_PORT_ENABLE):
+ SET_RH_PORTSTAT (USBPORTSC_PE);
+ OK (0);
+ }
+ break;
+
+ case RH_SET_ADDRESS:
+ uhci->rh.devnum = wValue;
+ OK (0);
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min (leni, min (sizeof (root_hub_dev_des), wLength));
+ memcpy (data, root_hub_dev_des, len);
+ OK (len);
+ case (0x02): /* configuration descriptor */
+ len = min (leni, min (sizeof (root_hub_config_des), wLength));
+ memcpy (data, root_hub_config_des, len);
+ OK (len);
+ case (0x03): /*string descriptors */
+ stat = -EPIPE;
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ root_hub_hub_des[2] = uhci->rh.numports;
+ len = min (leni, min (sizeof (root_hub_hub_des), wLength));
+ memcpy (data, root_hub_hub_des, len);
+ OK (len);
+
+ case RH_GET_CONFIGURATION:
+ *(__u8 *) data = 0x01;
+ OK (1);
+
+ case RH_SET_CONFIGURATION:
+ OK (0);
+ default:
+ stat = -EPIPE;
+ }
+
+
+ printk (KERN_DEBUG MODSTR "Root-Hub stat port1: %x port2: %x \n",
+ inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
+
+ purb->actual_length = len;
+ purb->status = stat;
+ if (purb->complete)
+ purb->complete (purb);
+ return USB_ST_NOERROR;
+}
+/*-------------------------------------------------------------------------*/
+
+_static int rh_unlink_urb (purb_t purb)
+{
+ puhci_t uhci = purb->dev->bus->hcpriv;
+
+ dbg (KERN_DEBUG MODSTR "Root-Hub unlink IRQ\n");
+ uhci->rh.send = 0;
+ del_timer (&uhci->rh.rh_int_timer);
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+
+#define UHCI_DEBUG
+
+/*
+ * 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 USB_ST_NOERROR;
+ 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 -EPIPE;
+ if (status & TD_CTRL_DBUFERR) /* Buffer error */
+ return -ENOSR;
+ if (status & TD_CTRL_STALLED) /* Stalled */
+ return -EPIPE;
+ if (status & TD_CTRL_ACTIVE) /* Active */
+ return USB_ST_NOERROR;
+
+ return -EPROTO;
+}
+
+/*
+ * Only the USB core should call uhci_alloc_dev and uhci_free_dev
+ */
+_static int uhci_alloc_dev (struct usb_device *usb_dev)
+{
+ return 0;
+}
+
+_static int uhci_free_dev (struct usb_device *usb_dev)
+{
+ 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 usb_device *usb_dev)
+{
+ return UHCI_GET_CURRENT_FRAME ((puhci_t) usb_dev->bus->hcpriv);
+}
+
+struct usb_operations uhci_device_operations =
+{
+ uhci_alloc_dev,
+ uhci_free_dev,
+ uhci_get_current_frame_number,
+ uhci_submit_urb,
+ uhci_unlink_urb
+};
+
+/*
+ * For IN-control transfers, process_transfer gets a bit more complicated,
+ * since there are devices that return less data (eg. strings) than they
+ * have announced. This leads to a queue abort due to the short packet,
+ * the status stage is not executed. If this happens, the status stage
+ * is manually re-executed.
+ * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
+ * when the transfered length fits exactly in maxsze-packets. A bit
+ * more intelligence is needed to detect this and finish without error.
+ */
+_static int process_transfer (puhci_t s, purb_t purb)
+{
+ int ret = USB_ST_NOERROR;
+ purb_priv_t purb_priv = purb->hcpriv;
+ struct list_head *qhl = purb_priv->desc_list.next;
+ puhci_desc_t qh = list_entry (qhl, uhci_desc_t, desc_list);
+ struct list_head *p = qh->vertical.next;
+ puhci_desc_t desc= list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
+ puhci_desc_t last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
+ int data_toggle = usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe)); // save initial data_toggle
+
+
+ // extracted and remapped info from TD
+ int maxlength;
+ int actual_length;
+ int status = USB_ST_NOERROR;
+
+ dbg (KERN_DEBUG MODSTR "process_transfer: urb contains bulk/control request\n");
+
+
+ /* if the status phase has been retriggered and the
+ queue is empty or the last status-TD is inactive, the retriggered
+ status stage is completed
+ */
+#if 1
+ if (purb_priv->short_control_packet &&
+ ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE))))
+ goto transfer_finished;
+#endif
+ purb->actual_length=0;
+
+ for (; p != &qh->vertical; p = p->next) {
+ desc = list_entry (p, uhci_desc_t, vertical);
+
+ if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs
+ return ret;
+
+ // extract transfer parameters from TD
+ actual_length = (desc->hw.td.status + 1) & 0x7ff;
+ maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
+ status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
+
+ // see if EP is stalled
+ if (status == -EPIPE) {
+ // set up stalled condition
+ usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
+ }
+
+ // if any error occured stop processing of further TDs
+ if (status != USB_ST_NOERROR) {
+ // only set ret if status returned an error
+ uhci_show_td (desc);
+ ret = status;
+ purb->error_count++;
+ break;
+ }
+ else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
+ purb->actual_length += actual_length;
+
+#if 0
+ // if (i++==0)
+ uhci_show_td (desc); // show first TD of each transfer
+#endif
+
+ // got less data than requested
+ if ( (actual_length < maxlength)) {
+ if (purb->transfer_flags & USB_DISABLE_SPD) {
+ ret = USB_ST_SHORT_PACKET; // treat as real error
+ printk (KERN_DEBUG MODSTR "process_transfer: SPD!!\n");
+ break; // exit after this TD because SP was detected
+ }
+
+ // short read during control-IN: re-start status stage
+ if ((usb_pipetype (purb->pipe) == PIPE_CONTROL)) {
+ if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {
+
+ qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
+ printk(KERN_DEBUG MODSTR "uhci: short packet during control transfer, retrigger status stage @ %p\n",last_desc);
+ uhci_show_td (desc);
+ uhci_show_td (last_desc);
+ purb_priv->short_control_packet=1;
+ return 0;
+ }
+ }
+ // all other cases: short read is OK
+ data_toggle = uhci_toggle (desc->hw.td.info);
+ break;
+ }
+
+ data_toggle = uhci_toggle (desc->hw.td.info);
+ //printk(KERN_DEBUG MODSTR"process_transfer: len:%d status:%x mapped:%x toggle:%d\n", actual_length, desc->hw.td.status,status, data_toggle);
+
+ }
+ usb_settoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe), !data_toggle);
+ transfer_finished:
+
+ /* APC BackUPS Pro kludge */
+ /* It tries to send all of the descriptor instead of */
+ /* the amount we requested */
+ if (desc->hw.td.status & TD_CTRL_IOC &&
+ status & TD_CTRL_ACTIVE &&
+ status & TD_CTRL_NAK )
+ {
+ printk("APS WORKAROUND\n");
+ ret=0;
+ status=0;
+ }
+
+ unlink_qh (s, qh);
+ delete_qh (s, qh);
+
+ purb->status = status;
+
+ dbg(KERN_DEBUG MODSTR"process_transfer: urb %p, wanted len %d, len %d status %x err %d\n",
+ purb,purb->transfer_buffer_length,purb->actual_length, purb->status, purb->error_count);
+ //dbg(KERN_DEBUG MODSTR"process_transfer: exit\n");
+#if 0
+ if (purb->actual_length){
+ char *uu;
+ uu=purb->transfer_buffer;
+ dbg(KERN_DEBUG MODSTR"%x %x %x %x %x %x %x %x\n",
+ *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
+ }
+#endif
+ return ret;
+}
+
+_static int process_interrupt (puhci_t s, purb_t purb)
+{
+ int i, ret = USB_ST_URB_PENDING;
+ purb_priv_t purb_priv = purb->hcpriv;
+ struct list_head *p = purb_priv->desc_list.next;
+ puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+ int actual_length;
+ int status = USB_ST_NOERROR;
+
+ //printk(KERN_DEBUG MODSTR"urb contains interrupt request\n");
+
+ for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-)
+ {
+ desc = list_entry (p, uhci_desc_t, desc_list);
+
+ if (desc->hw.td.status & TD_CTRL_ACTIVE) {
+ // do not process active TDs
+ //printk("TD ACT Status @%p %08x\n",desc,desc->hw.td.status);
+ break;
+ }
+
+ if (!desc->hw.td.status & TD_CTRL_IOC) {
+ // do not process one-shot TDs, no recycling
+ break;
+ }
+ // extract transfer parameters from TD
+
+ actual_length = (desc->hw.td.status + 1) & 0x7ff;
+ status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
+
+ // see if EP is stalled
+ if (status == -EPIPE) {
+ // set up stalled condition
+ usb_endpoint_halt (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
+ }
+
+ // if any error occured: ignore this td, and continue
+ if (status != USB_ST_NOERROR) {
+ uhci_show_td (desc);
+ purb->error_count++;
+ goto recycle;
+ }
+ else
+ purb->actual_length = actual_length;
+
+ // FIXME: SPD?
+
+ recycle:
+ if (purb->complete) {
+ //printk (KERN_DEBUG MODSTR "process_interrupt: calling completion, status %i\n",status);
+ purb->status = status;
+ purb->complete ((struct urb *) purb);
+ purb->status = USB_ST_URB_PENDING;
+ }
+
+ // Recycle INT-TD if interval!=0, else mark TD as one-shot
+ if (purb->interval) {
+ desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
+ if (status==0) {
+ desc->hw.td.info |= (usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe),
+ usb_pipeout (purb->pipe)) << TD_TOKEN_TOGGLE);
+ usb_dotoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
+ } else {
+ desc->hw.td.info |= (!usb_gettoggle (purb->dev, usb_pipeendpoint (purb->pipe),
+ usb_pipeout (purb->pipe)) << TD_TOKEN_TOGGLE);
+ }
+ desc->hw.td.status= (purb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+ (purb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+ wmb();
+ }
+ else {
+ desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD
+ }
+ }
+
+ return ret;
+}
+
+
+_static int process_iso (puhci_t s, purb_t purb)
+{
+ int i;
+ int ret = USB_ST_NOERROR;
+ purb_priv_t purb_priv = purb->hcpriv;
+ struct list_head *p = purb_priv->desc_list.next;
+ puhci_desc_t desc = list_entry (purb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+ dbg ( /*KERN_DEBUG */ MODSTR "urb contains iso request\n");
+ if (desc->hw.td.status & TD_CTRL_ACTIVE)
+ return USB_ST_PARTIAL_ERROR; // last TD not finished
+
+ purb->error_count = 0;
+ purb->actual_length = 0;
+ purb->status = USB_ST_NOERROR;
+
+ for (i = 0; p != &purb_priv->desc_list; p = p->next, i++) {
+ desc = list_entry (p, uhci_desc_t, desc_list);
+
+ //uhci_show_td(desc);
+ if (desc->hw.td.status & TD_CTRL_ACTIVE) {
+ // means we have completed the last TD, but not the TDs before
+ desc->hw.td.status &= ~TD_CTRL_ACTIVE;
+ printk (KERN_DEBUG MODSTR "TD still active (%x)- grrr. paranoia!\n", desc->hw.td.status);
+ ret = USB_ST_PARTIAL_ERROR;
+ purb->iso_frame_desc[i].status = ret;
+ unlink_td (s, desc);
+ // FIXME: immediate deletion may be dangerous
+ goto err;
+ }
+
+ unlink_td (s, desc);
+
+ if (purb->number_of_packets <= i) {
+ dbg (KERN_DEBUG MODSTR "purb->number_of_packets (%d)<=(%d)\n", purb->number_of_packets, i);
+ ret = USB_ST_URB_INVALID_ERROR;
+ goto err;
+ }
+
+ if (purb->iso_frame_desc[i].offset + purb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) {
+ // Hm, something really weird is going on
+ dbg (KERN_DEBUG MODSTR "Pointer Paranoia: %p!=%p\n", purb->iso_frame_desc[i].offset + purb->transfer_buffer, bus_to_virt (desc->hw.td.buffer));
+ ret = USB_ST_URB_INVALID_ERROR;
+ purb->iso_frame_desc[i].status = ret;
+ goto err;
+ }
+ purb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff;
+ purb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (purb->pipe));
+ purb->actual_length += purb->iso_frame_desc[i].actual_length;
+
+ err:
+
+ if (purb->iso_frame_desc[i].status != USB_ST_NOERROR) {
+ purb->error_count++;
+ purb->status = purb->iso_frame_desc[i].status;
+ }
+ dbg (KERN_DEBUG MODSTR "process_iso: len:%d status:%x\n",
+ purb->iso_frame_desc[i].length, purb->iso_frame_desc[i].status);
+
+ delete_desc (desc);
+ list_del (p);
+ }
+ dbg ( /*KERN_DEBUG */ MODSTR "process_iso: exit %i (%d)\n", i, ret);
+ return ret;
+}
+
+
+_static int process_urb (puhci_t s, struct list_head *p)
+{
+ int ret = USB_ST_NOERROR;
+ purb_t purb;
+
+ spin_lock(&s->urb_list_lock);
+ purb=list_entry (p, urb_t, urb_list);
+ dbg ( /*KERN_DEBUG */ MODSTR "found queued urb: %p\n", purb);
+
+ switch (usb_pipetype (purb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ ret = process_transfer (s, purb);
+ break;
+ case PIPE_ISOCHRONOUS:
+ ret = process_iso (s, purb);
+ break;
+ case PIPE_INTERRUPT:
+ ret = process_interrupt (s, purb);
+ break;
+ }
+
+ spin_unlock(&s->urb_list_lock);
+
+ if (purb->status != USB_ST_URB_PENDING) {
+ int proceed = 0;
+ dbg ( /*KERN_DEBUG */ MODSTR "dequeued urb: %p\n", purb);
+ dequeue_urb (s, p, 1);
+
+#ifdef _UHCI_SLAB
+ kmem_cache_free(urb_priv_kmem, purb->hcpriv);
+#else
+ kfree (purb->hcpriv);
+#endif
+
+ if ((usb_pipetype (purb->pipe) != PIPE_INTERRUPT)) {
+ purb_t tmp = purb->next; // pointer to first urb
+ int is_ring = 0;
+
+ if (purb->next) {
+ do {
+ if (tmp->status != USB_ST_URB_PENDING) {
+ proceed = 1;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ while (tmp != NULL && tmp != purb->next);
+ if (tmp == purb->next)
+ is_ring = 1;
+ }
+
+ // In case you need the current URB status for your completion handler
+ if (purb->complete && (!proceed || (purb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
+ dbg (KERN_DEBUG MODSTR "process_transfer: calling early completion\n");
+ purb->complete ((struct urb *) purb);
+ if (!proceed && is_ring && (purb->status != USB_ST_URB_KILLED))
+ uhci_submit_urb (purb);
+ }
+
+ if (proceed && purb->next) {
+ // if there are linked urbs - handle submitting of them right now.
+ tmp = purb->next; // pointer to first urb
+
+ do {
+ if ((tmp->status != USB_ST_URB_PENDING) && (tmp->status != USB_ST_URB_KILLED) && uhci_submit_urb (tmp) != USB_ST_NOERROR)
+ break;
+ tmp = tmp->next;
+ }
+ while (tmp != NULL && tmp != purb->next); // submit until we reach NULL or our own pointer or submit fails
+
+ if (purb->complete && !(purb->transfer_flags & USB_URB_EARLY_COMPLETE)) {
+ dbg ( /*KERN_DEBUG */ MODSTR "process_transfer: calling completion\n");
+ purb->complete ((struct urb *) purb);
+ }
+ }
+ usb_dec_dev_use (purb->dev);
+ }
+ }
+
+ return ret;
+}
+
+_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
+{
+ puhci_t s = __uhci;
+ unsigned int io_addr = s->io_addr;
+ unsigned short status;
+ struct list_head *p, *p2;
+
+ /*
+ * 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;
+
+ dbg ("interrupt\n");
+
+ if (status != 1) {
+ printk (KERN_DEBUG MODSTR "interrupt, status %x\n", status);
+
+ // remove host controller halted state
+ if ((status&0x20) && (s->running)) {
+ // more to be done - check TDs for invalid entries
+ // but TDs are only invalid if somewhere else is a (memory ?) problem
+ outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
+ }
+ //uhci_show_status (s);
+ }
+ //beep(1000);
+ /*
+ * the following is very subtle and was blatantly wrong before
+ * traverse the list in *reverse* direction, because new entries
+ * may be added at the end.
+ * also, because process_urb may unlink the current urb,
+ * we need to advance the list before
+ * - Thomas Sailer
+ */
+
+ spin_lock(&s->unlink_urb_lock);
+ spin_lock (&s->urb_list_lock);
+ p = s->urb_list.prev;
+ spin_unlock (&s->urb_list_lock);
+
+ while (p != &s->urb_list) {
+ p2 = p;
+ p = p->prev;
+ process_urb (s, p2);
+ }
+
+ spin_unlock(&s->unlink_urb_lock);
+
+ outw (status, io_addr + USBSTS);
+#ifdef __alpha
+ mb (); // ?
+#endif
+ dbg ("done\n\n\n");
+}
+
+_static void reset_hc (puhci_t s)
+{
+ unsigned int io_addr = s->io_addr;
+
+ s->apm_state = 0;
+ /* Global reset for 50ms */
+ outw (USBCMD_GRESET, io_addr + USBCMD);
+ wait_ms (50);
+ outw (0, io_addr + USBCMD);
+ wait_ms (10);
+}
+
+_static void start_hc (puhci_t s)
+{
+ unsigned int io_addr = s->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 MODSTR "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 (virt_to_bus (s->framelist), io_addr + USBFLBASEADD);
+
+ /* Run and mark it configured with a 64-byte max packet */
+ outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+ s->apm_state = 1;
+ s->running = 1;
+}
+
+_static void __exit uhci_cleanup_dev(puhci_t s)
+{
+ struct usb_device *root_hub = s->bus->root_hub;
+ if (root_hub)
+ usb_disconnect (&root_hub);
+
+ usb_deregister_bus (s->bus);
+ s->running = 0;
+ reset_hc (s);
+ release_region (s->io_addr, s->io_size);
+ free_irq (s->irq, s);
+ usb_free_bus (s->bus);
+ cleanup_skel (s);
+ kfree (s);
+
+}
+
+_static int __init uhci_start_usb (puhci_t s)
+{ /* start it up */
+ /* connect the virtual root hub */
+ struct usb_device *usb_dev;
+
+ usb_dev = usb_alloc_dev (NULL, s->bus);
+ if (!usb_dev)
+ return -1;
+
+ s->bus->root_hub = usb_dev;
+ usb_connect (usb_dev);
+
+ if (usb_new_device (usb_dev) != 0) {
+ usb_free_dev (usb_dev);
+ return -1;
+ }
+
+ return 0;
+}
+
+_static int __init alloc_uhci (int irq, unsigned int io_addr, unsigned int io_size)
+{
+ puhci_t s;
+ struct usb_bus *bus;
+
+ s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
+ if (!s)
+ return -1;
+
+ memset (s, 0, sizeof (uhci_t));
+ INIT_LIST_HEAD (&s->urb_list);
+ spin_lock_init (&s->urb_list_lock);
+ spin_lock_init (&s->qh_lock);
+ spin_lock_init (&s->td_lock);
+ spin_lock_init (&s->unlink_urb_lock);
+ s->irq = -1;
+ s->io_addr = io_addr;
+ s->io_size = io_size;
+ s->next = devs; //chain new uhci device into global list
+
+ bus = usb_alloc_bus (&uhci_device_operations);
+ if (!bus) {
+ kfree (s);
+ return -1;
+ }
+
+ s->bus = bus;
+ bus->hcpriv = s;
+
+ /* 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, so default to 2 */
+ /* According to the UHCI spec, Bit 7 is always set to 1. So we try */
+ /* to use this to our advantage */
+
+ for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) {
+ unsigned int portstatus;
+
+ portstatus = inw (io_addr + 0x10 + (s->maxports * 2));
+ printk ("port %i, adr %x status %x\n", s->maxports,
+ io_addr + 0x10 + (s->maxports * 2), portstatus);
+ if (!(portstatus & 0x0080))
+ break;
+ }
+ dbg (KERN_DEBUG MODSTR "Detected %d ports\n", s->maxports);
+
+ /* This is experimental so anything less than 2 or greater than 8 is */
+ /* something weird and we'll ignore it */
+ if (s->maxports < 2 || s->maxports > 8) {
+ dbg (KERN_DEBUG "Port count misdetected, forcing to 2 ports\n");
+ s->maxports = 2;
+ }
+
+ s->rh.numports = s->maxports;
+
+ if (init_skel (s)) {
+ usb_free_bus (bus);
+ kfree(s);
+ return -1;
+ }
+
+ request_region (s->io_addr, io_size, MODNAME);
+ reset_hc (s);
+ usb_register_bus (s->bus);
+
+ start_hc (s);
+
+ if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
+ printk(MODSTR KERN_ERR"request_irq %d failed!\n",irq);
+ usb_free_bus (bus);
+ reset_hc (s);
+ release_region (s->io_addr, s->io_size);
+ cleanup_skel(s);
+ kfree(s);
+ return -1;
+ }
+
+ s->irq = irq;
+
+ if(uhci_start_usb (s) < 0) {
+ uhci_cleanup_dev(s);
+ return -1;
+ }
+
+ //chain new uhci device into global list
+ devs = s;
+
+ return 0;
+}
+
+_static int __init start_uhci (struct pci_dev *dev)
+{
+ int i;
+
+ /* Search for the IO base address.. */
+ for (i = 0; i < 6; i++) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
+ unsigned int io_addr = dev->resource[i].start;
+ unsigned int io_size =
+ dev->resource[i].end - dev->resource[i].start + 1;
+ if (!(dev->resource[i].flags & 1))
+ continue;
+#else
+ unsigned int io_addr = dev->base_address[i];
+ unsigned int io_size = 0x14;
+ if (!(io_addr & 1))
+ continue;
+ io_addr &= ~1;
+#endif
+
+ /* Is it already in use? */
+ if (check_region (io_addr, io_size))
+ break;
+ /* disable legacy emulation */
+ pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+ return alloc_uhci(dev->irq, io_addr, io_size);
+ }
+ return -1;
+}
+
+#ifdef CONFIG_APM
+_static int handle_apm_event (apm_event_t event)
+{
+ static int down = 0;
+ puhci_t s = devs;
+ printk ("handle_apm_event(%d)\n", event);
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ dbg (KERN_DEBUG MODSTR "received extra suspend event\n");
+ break;
+ }
+ while (s) {
+ reset_hc (s);
+ s = s->next;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ dbg (KERN_DEBUG MODSTR "received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ while (s) {
+ start_hc (s);
+ s = s->next;
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
+int __init uhci_init (void)
+{
+ int retval = -ENODEV;
+ struct pci_dev *dev = NULL;
+ u8 type;
+ int i=0;
+
+#ifdef _UHCI_SLAB
+ char *slabname=kmalloc(16, GFP_KERNEL);
+
+ if(!slabname)
+ return -ENOMEM;
+
+ strcpy(slabname, "uhci_desc");
+ uhci_desc_kmem = kmem_cache_create(slabname, sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+ if(!uhci_desc_kmem) {
+ printk(KERN_ERR MODSTR"kmem_cache_create for uhci_desc failed (out of memory)\n");
+ return -ENOMEM;
+ }
+
+ slabname=kmalloc(16, GFP_KERNEL);
+
+ if(!slabname)
+ return -ENOMEM;
+
+ strcpy(slabname, "urb_priv");
+ urb_priv_kmem = kmem_cache_create(slabname, sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+ if(!urb_priv_kmem) {
+ printk(KERN_ERR MODSTR"kmem_cache_create for urb_priv_t failed (out of memory)\n");
+ return -ENOMEM;
+ }
+#endif
+ printk (KERN_INFO MODSTR VERSTR "\n");
+
+ for (;;) {
+ dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
+ if (!dev)
+ break;
+
+ /* Is it UHCI */
+ pci_read_config_byte (dev, PCI_CLASS_PROG, &type);
+ if (type != 0)
+ continue;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
+ pci_enable_device (dev);
+#endif
+ if(!dev->irq)
+ {
+ printk(KERN_ERR MODSTR"Found UHCI device with no IRQ assigned. Check BIOS settings!\n");
+ continue;
+ }
+
+ /* Ok set it up */
+ retval = start_uhci (dev);
+
+ if (!retval)
+ i++;
+
+ }
+
+#ifdef CONFIG_APM
+ if(i)
+ apm_register_callback (&handle_apm_event);
+#endif
+ return retval;
+}
+
+void __exit uhci_cleanup (void)
+{
+ puhci_t s;
+ while ((s = devs)) {
+ devs = devs->next;
+ uhci_cleanup_dev(s);
+ }
+#ifdef _UHCI_SLAB
+ kmem_cache_shrink(uhci_desc_kmem);
+ kmem_cache_shrink(urb_priv_kmem);
+#endif
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return uhci_init ();
+}
+
+void cleanup_module (void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback (&handle_apm_event);
+#endif
+ uhci_cleanup ();
+}
+
+#endif //MODULE
--- /dev/null
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+/*
+ $Id: usb-uhci.h,v 1.31 2000/01/15 22:02:30 acher Exp $
+ */
+#define MODNAME "usb-uhci"
+#define MODSTR MODNAME": "
+#define VERSTR "version v0.9 time " __TIME__ " " __DATE__
+
+/* 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_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 0x000F
+#define UHCI_PTR_TERM 0x0001
+#define UHCI_PTR_QH 0x0002
+#define UHCI_PTR_DEPTH 0x0004
+
+#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 */
+
+/*
+ * for 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_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_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS)
+
+/*
+ * for TD <flags>:
+ */
+#define UHCI_TD_REMOVE 0x0001 /* Remove when done */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE 19
+
+#define uhci_maxlen(token) ((token) >> 21)
+#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token) (((token) >> 15) & 0xf)
+#define uhci_devaddr(token) (((token) >> 8) & 0x7f)
+#define uhci_devep(token) (((token) >> 8) & 0x7ff)
+#define uhci_packetid(token) ((token) & 0xff)
+#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
+
+/* ------------------------------------------------------------------------------------
+ New TD/QH-structures
+ ------------------------------------------------------------------------------------ */
+typedef enum {
+ TD_TYPE, QH_TYPE
+} uhci_desc_type_t;
+
+typedef struct {
+ __u32 link;
+ __u32 status;
+ __u32 info;
+ __u32 buffer;
+} uhci_td_t, *puhci_td_t;
+
+typedef struct {
+ __u32 head;
+ __u32 element; /* Queue element pointer */
+} uhci_qh_t, *puhci_qh_t;
+
+typedef struct {
+ union {
+ uhci_td_t td;
+ uhci_qh_t qh;
+ } hw;
+ uhci_desc_type_t type;
+ struct list_head horizontal;
+ struct list_head vertical;
+ struct list_head desc_list;
+} uhci_desc_t, *puhci_desc_t;
+
+typedef struct {
+ struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
+ int short_control_packet;
+} urb_priv_t, *purb_priv_t;
+
+struct virt_root_hub {
+ int devnum; /* Address of Root Hub endpoint */
+ void *urb;
+ void *int_addr;
+ int send;
+ int interval;
+ int numports;
+ int c_p_r[8];
+ struct timer_list rh_int_timer;
+};
+
+typedef struct uhci {
+ int irq;
+ unsigned int io_addr;
+ unsigned int io_size;
+ unsigned int maxports;
+ int running;
+
+ int apm_state;
+
+ struct uhci *next; // chain of uhci device contexts
+
+ struct list_head urb_list; // list of all pending urbs
+
+ spinlock_t urb_list_lock; // lock to keep consistency
+
+ struct usb_bus *bus; // our bus
+
+ spinlock_t unlink_urb_lock; // lock for unlink_urb
+
+ __u32 *framelist;
+ uhci_desc_t **iso_td;
+ uhci_desc_t *int_chain[8];
+ uhci_desc_t *control_chain;
+ uhci_desc_t *bulk_chain;
+ uhci_desc_t *chain_end;
+ spinlock_t qh_lock;
+ spinlock_t td_lock;
+ struct virt_root_hub rh; //private data of the virtual root hub
+} uhci_t, *puhci_t;
+
+
+#define MAKE_TD_ADDR(a) (virt_to_bus(a)&~UHCI_PTR_QH)
+#define MAKE_QH_ADDR(a) (virt_to_bus(a)|UHCI_PTR_QH)
+#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM))
+
+/* ------------------------------------------------------------------------------------
+ Virtual Root HUB
+ ------------------------------------------------------------------------------------ */
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP 0x00
+
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+#endif
* It should be considered a slave, with no callbacks. Callbacks
* are evil.
*
- * $Id: usb.c,v 1.39 1999/12/27 15:17:47 acher Exp $
+ * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $
*/
#include <linux/config.h>
/*-------------------------------------------------------------------*/
void usb_free_urb(urb_t* urb)
{
- kfree(urb);
+ if(urb)
+ kfree(urb);
}
/*-------------------------------------------------------------------*/
int usb_submit_urb(urb_t *urb)
static void usb_api_blocking_completion(urb_t *urb)
{
api_wrapper_data *awd = (api_wrapper_data *)urb->context;
-
+
if (waitqueue_active(awd->wakeup))
wake_up(awd->wakeup);
#if 0
*-------------------------------------------------------------------*/
// Starts urb and waits for completion or timeout
-static int usb_start_wait_urb(urb_t *urb, int timeout, unsigned long* rval)
+static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
{
DECLARE_WAITQUEUE(wait, current);
DECLARE_WAIT_QUEUE_HEAD(wqh);
awd.wakeup=&wqh;
awd.handler=0;
init_waitqueue_head(&wqh);
- current->state = TASK_UNINTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
add_wait_queue(&wqh, &wait);
urb->context=&awd;
status=usb_submit_urb(urb);
if (!status) {
// timeout
- dbg("usb_control/bulk_msg: timeout");
+ printk("usb_control/bulk_msg: timeout\n");
usb_unlink_urb(urb); // remove urb safely
status=-ETIMEDOUT;
}
else
status=urb->status;
- if (rval)
- *rval=urb->actual_length;
+ if (actual_length)
+ *actual_length=urb->actual_length;
usb_free_urb(urb);
return status;
{
urb_t *urb;
int retv;
- unsigned long length;
+ int length;
urb=usb_alloc_urb(0);
if (!urb)
FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, /* build urb */
(usb_complete_t)usb_api_blocking_completion,0);
-
+
retv=usb_start_wait_urb(urb,timeout, &length);
if (retv < 0)
return retv;
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)
{
- devrequest dr;
-
- dr.requesttype = requesttype;
- dr.request = request;
- dr.value = cpu_to_le16p(&value);
- dr.index = cpu_to_le16p(&index);
- dr.length = cpu_to_le16p(&size);
+ devrequest *dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+ int ret;
+
+ if(!dr)
+ return -ENOMEM;
+
+ dr->requesttype = requesttype;
+ dr->request = request;
+ dr->value = cpu_to_le16p(&value);
+ dr->index = cpu_to_le16p(&index);
+ dr->length = cpu_to_le16p(&size);
+
//dbg("usb_control_msg");
- return usb_internal_control_msg(dev, pipe, &dr, data, size, timeout);
+
+ ret=usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+ kfree(dr);
+
+ return ret;
}
/*-------------------------------------------------------------------*/
/* synchronous behavior */
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
- void *data, int len, unsigned long *rval, int timeout)
+ void *data, int len, int *actual_length, int timeout)
{
urb_t *urb;
FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len, /* build urb */
(usb_complete_t)usb_api_blocking_completion,0);
- return usb_start_wait_urb(urb,timeout,rval);
+ return usb_start_wait_urb(urb,timeout,actual_length);
}
/*-------------------------------------------------------------------*/
void *usb_request_bulk(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id)
{
urb_t *urb;
+ DECLARE_WAITQUEUE(wait, current);
+ DECLARE_WAIT_QUEUE_HEAD(wqh);
api_wrapper_data *awd;
if (!(urb=usb_alloc_urb(0)))
for (i = 0; i < config->bNumInterfaces; i++) {
header = (struct usb_descriptor_header *)buffer;
- if (header->bLength > size) {
+ if ((header->bLength > size) || (header->bLength <= 2)) {
err("ran out of descriptors parsing");
return -1;
}
/* Free up all the children.. */
for (i = 0; i < USB_MAXCHILDREN; i++) {
struct usb_device **child = dev->children + i;
- usb_disconnect(child);
+ if (*child)
+ usb_disconnect(child);
}
/* remove /proc/bus/usb entry */
{
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 simulatenously 8-)
+ */
dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */
devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
if (devnum < 128) {
{
int i = 5;
int result;
+
+ memset(buf,0,size); // Make sure we parse really received data
while (i--) {
if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
* endp: endpoint number in bits 0-3;
* direction flag in bit 7 (1 = IN, 0 = OUT)
*/
-int usb_clear_halt(struct usb_device *dev, int endp)
+int usb_clear_halt(struct usb_device *dev, int pipe)
{
int result;
__u16 status;
+ int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
/*
if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp)))
if (status & 1)
return -EPIPE; /* still halted */
- usb_endpoint_running(dev, endp & 0x0f, usb_endpoint_out(endp));
+ usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
/* toggle is reset on clear */
- usb_settoggle(dev, endp & 0x0f, usb_endpoint_out(endp), 0);
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
return 0;
}
unsigned int cfgno;
unsigned char buffer[8];
unsigned char *bigbuffer;
+ unsigned int tmp;
struct usb_config_descriptor *desc =
(struct usb_config_descriptor *)buffer;
/* 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 < 0) {
- err("unable to get descriptor");
+ if (result < 8) {
+ if (result < 0)
+ err("unable to get descriptor");
+ else
+ err("config descriptor too short (expected %i, got %i)",8,result);
goto err;
}
result=-ENOMEM;
goto err;
}
-
+ tmp=desc->wTotalLength;
/* Now that we know the length, get the whole thing */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, desc->wTotalLength);
if (result < 0) {
err("couldn't get all of config descriptors");
kfree(bigbuffer);
goto err;
+ }
+
+ if (result < tmp) {
+ err("config descriptor too short (expected %i, got %i)",tmp,result);
+ kfree(bigbuffer);
+ goto err;
}
result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
kfree(bigbuffer);
{
unsigned char *buf;
int addr, err;
+ int tmp;
info("USB new device connect, assigned device number %d", dev->devnum);
dev->devnum = 0;
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
- if (err < 0) {
- err("USB device not responding, giving up (error=%d)", err);
+ if (err < 8) {
+ if (err < 0)
+ err("USB device not responding, giving up (error=%d)", err);
+ else
+ err("USB device descriptor short read (expected %i, got %i)",8,err);
clear_bit(addr, &dev->bus->devmap.devicemap);
dev->devnum = -1;
return 1;
}
-
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
switch (dev->descriptor.bMaxPacketSize0) {
wait_ms(10); /* Let the SET_ADDRESS settle */
+ tmp = sizeof(dev->descriptor);
+
err = usb_get_device_descriptor(dev);
- if (err < 0) {
- err("unable to get device descriptor (error=%d)",err);
+ if (err < tmp) {
+ if (err < 0)
+ err("unable to get device descriptor (error=%d)",err);
+ else
+ err("USB device descriptor short read (expected %i, got %i)",tmp,err);
+
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
dev->devnum = -1;
return 1;
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
/* USB constants */
static __inline__ void wait_ms(unsigned int ms)
{
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1 + ms * HZ / 1000);
+ if(!in_interrupt()) {
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms * HZ / 1000);
+ }
+ else
+ mdelay(ms);
}
typedef struct {
int extralen;
} __attribute__ ((packed));
+/* HID descriptor */
+struct usb_hid_class_descriptor {
+ __u8 bDescriptorType;
+ __u16 wDescriptorLength;
+} __attribute__ ((packed));
+
+
+struct usb_hid_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdHID;
+ __u8 bCountryCode;
+ __u8 bNumDescriptors;
+
+ struct usb_hid_class_descriptor desc[1];
+} __attribute__ ((packed));
+
/* Interface descriptor */
struct usb_interface_descriptor {
__u8 bLength;
int usb_submit_urb(purb_t purb);
int usb_unlink_urb(purb_t purb);
int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout);
-int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout);
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
/*-------------------------------------------------------------------*
* COMPATIBILITY STUFF *
int usb_set_report(struct usb_device *dev, unsigned char type,
unsigned char id, unsigned char index, void *buf, int size);
int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
-int usb_clear_halt(struct usb_device *dev, int endp);
+int usb_clear_halt(struct usb_device *dev, int pipe);
#define usb_get_extra_descriptor(ifpoint,type,ptr)\
__usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,type,(void**)ptr)
/* if we stall, we need to clear it before we go on */
if (result == USB_ST_STALL) {
US_DEBUGP("clearing endpoint halt for pipe %x\n", pipe);
- usb_clear_halt(us->pusb_dev,
- usb_pipeendpoint(pipe) | (pipe & USB_DIR_IN));
+ usb_clear_halt(us->pusb_dev,pipe);
}
/* we want to retry if the device reported NAK */
schedule_timeout(HZ*6);
US_DEBUGP("pop_CB_reset: clearing endpoint halt\n");
- usb_clear_halt(us->pusb_dev, us->ep_in | USB_DIR_IN);
- usb_clear_halt(us->pusb_dev, us->ep_out | USB_DIR_OUT);
+ usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("pop_CB_reset done\n");
return 0;
NULL, 0, HZ*5);
if (result)
US_DEBUGP("Bulk hard reset failed %d\n", result);
- usb_clear_halt(us->pusb_dev, us->ep_in | USB_DIR_IN);
- usb_clear_halt(us->pusb_dev, us->ep_out | USB_DIR_OUT);
+ usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
/* long wait for reset */
/*
* wmforce.c Version 0.1
*
- * Copyright (c) 1999 Vojtech Pavlik
+ * Copyright (c) 2000 Vojtech Pavlik
*
- * USB Logitech WingMan Force tablet support
+ * USB Logitech WingMan Force joystick support
*
* Sponsored by SuSE
*/
/* kill() */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
- uid_t _uid32; /* sender's uid */
+ uid_t _uid; /* sender's uid */
} _kill;
/* POSIX.1b timers */
/* POSIX.1b signals */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
sigval_t _sigval;
- uid_t _uid32; /* sender's uid */
} _rt;
/* SIGCHLD */
struct {
pid_t _pid; /* which child */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
- uid_t _uid32; /* sender's uid */
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
} _sifields;
} siginfo_t;
-#define UID16_SIGINFO_COMPAT_NEEDED
-
/*
* How these fields are to be accessed.
*/
#define si_pid _sifields._kill._pid
-#ifdef __KERNEL__
-#define si_uid _sifields._kill._uid32
-#define si_uid16 _sifields._kill._uid
-#else
#define si_uid _sifields._kill._uid
-#endif /* __KERNEL__ */
#define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime
/* kill() */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
- uid_t _uid32; /* sender's uid */
+ uid_t _uid; /* sender's uid */
} _kill;
/* POSIX.1b timers */
/* POSIX.1b signals */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
sigval_t _sigval;
- uid_t _uid32; /* sender's uid */
} _rt;
/* SIGCHLD */
struct {
pid_t _pid; /* which child */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
- uid_t _uid32; /* sender's uid */
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
} _sifields;
} siginfo_t;
-#define UID16_SIGINFO_COMPAT_NEEDED
-
/*
* How these fields are to be accessed.
*/
#define si_pid _sifields._kill._pid
-#ifdef __KERNEL__
-#define si_uid _sifields._kill._uid32
-#define si_uid16 _sifields._kill._uid
-#else
#define si_uid _sifields._kill._uid
-#endif /* __KERNEL__ */
#define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime
/* kill() */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
- uid_t _uid32; /* sender's uid */
+ uid_t _uid; /* sender's uid */
} _kill;
/* POSIX.1b timers */
/* POSIX.1b signals */
struct {
pid_t _pid; /* sender's pid */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
sigval_t _sigval;
- uid_t _uid32; /* sender's uid */
} _rt;
/* SIGCHLD */
struct {
pid_t _pid; /* which child */
- old_uid_t _uid; /* backwards compatibility */
+ uid_t _uid; /* sender's uid */
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
- uid_t _uid32; /* sender's uid */
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
} _sifields;
} siginfo_t;
-#define UID16_SIGINFO_COMPAT_NEEDED
-
/*
* How these fields are to be accessed.
*/
#define si_pid _sifields._kill._pid
-#ifdef __KERNEL__
-#define si_uid _sifields._kill._uid32
-#define si_uid16 _sifields._kill._uid
-#else
#define si_uid _sifields._kill._uid
-#endif /* __KERNEL__ */
#define si_status _sifields._sigchld._status
#define si_utime _sifields._sigchld._utime
#define si_stime _sifields._sigchld._stime
#define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid)
#define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid)
-/* specific to kernel/signal.c */
-#ifdef UID16_SIGINFO_COMPAT_NEEDED
-#define SET_SIGINFO_UID16(var, uid) var = high2lowuid(uid)
-#else
-#define SET_SIGINFO_UID16(var, uid) do { ; } while (0)
-#endif
-
#else
#define SET_UID16(var, uid) do { ; } while (0)
#define SET_STAT_UID(stat, uid) (stat).st_uid = uid
#define SET_STAT_GID(stat, gid) (stat).st_gid = gid
-#define SET_SIGINFO_UID16(var, uid) do { ; } while (0)
-
#endif /* CONFIG_UID16 */
#define BTN_MODE 0x13c
#define BTN_DIGI 0x140
-#define BTN_PEN 0x140
-#define BTN_RUBBER 0x141
-#define BTN_PEN_SIDE 0x142
-#define BTN_PEN_SIDE2 0x143
-#define BTN_NEAR 0x144
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOUCH 0x147
+#define BTN_STYLUS 0x148
+#define BTN_STYLUS2 0x149
#define KEY_MAX 0x1ff
* GFP bitmasks..
*/
#define __GFP_WAIT 0x01
-#define __GFP_LOW 0x02
-#define __GFP_MED 0x04
-#define __GFP_HIGH 0x08
-#define __GFP_IO 0x10
-#define __GFP_SWAP 0x20
+#define __GFP_HIGH 0x02
+#define __GFP_IO 0x04
+#define __GFP_SWAP 0x08
#ifdef CONFIG_HIGHMEM
-#define __GFP_HIGHMEM 0x40
+#define __GFP_HIGHMEM 0x10
#else
#define __GFP_HIGHMEM 0x0 /* noop */
#endif
-#define __GFP_DMA 0x80
+#define __GFP_DMA 0x20
-#define GFP_BUFFER (__GFP_LOW | __GFP_WAIT)
+#define GFP_BUFFER (__GFP_WAIT)
#define GFP_ATOMIC (__GFP_HIGH)
-#define GFP_USER (__GFP_LOW | __GFP_WAIT | __GFP_IO)
+#define GFP_USER (__GFP_WAIT | __GFP_IO)
#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM)
-#define GFP_KERNEL (__GFP_MED | __GFP_WAIT | __GFP_IO)
+#define GFP_KERNEL (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
#define GFP_NFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
#define GFP_KSWAPD (__GFP_IO | __GFP_SWAP)
#define SLAB_NFS GFP_NFS
#define SLAB_DMA GFP_DMA
-#define SLAB_LEVEL_MASK 0x0000007fUL
+#define SLAB_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_SWAP| \
+ __GFP_HIGHMEM)
#define SLAB_NO_GROW 0x00001000UL /* don't grow a cache */
/* flags to pass to kmem_cache_create().
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/sched.h>
-#include <linux/highuid.h>
#include <asm/uaccess.h>
info->si_code = 0;
info->si_pid = 0;
info->si_uid = 0;
- SET_SIGINFO_UID16(info->si_uid16, 0);
}
if (reset)
q->info.si_code = SI_USER;
q->info.si_pid = current->pid;
q->info.si_uid = current->uid;
- SET_SIGINFO_UID16(q->info.si_uid16, current->uid);
break;
case 1:
q->info.si_signo = sig;
q->info.si_code = SI_KERNEL;
q->info.si_pid = 0;
q->info.si_uid = 0;
- SET_SIGINFO_UID16(q->info.si_uid16, 0);
break;
default:
q->info = *info;
info.si_code = SI_USER;
info.si_pid = current->pid;
info.si_uid = current->uid;
- SET_SIGINFO_UID16(info.si_uid16, current->uid);
return kill_something_info(sig, &info, pid);
}
freed = try_to_free_pages(gfp_mask, zone);
current->flags &= ~PF_MEMALLOC;
- if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
+ if (!freed && !(gfp_mask & __GFP_HIGH))
return 0;
return 1;
}
freed = try_to_free_pages(gfp_mask, zone);
current->flags &= ~PF_MEMALLOC;
- if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
+ if (!freed && !(gfp_mask & __GFP_HIGH))
return 0;
return 1;
}