P: Matija Nalis
M: Matija Nalis <mnalis-umsdos@voyager.hr>
L: linux-kernel@vger.rutgers.edu
-W: http://www.voyager.hr/~mnalis/umsdos/
+W: http://linux.voyager.hr/umsdos/
S: Maintained
UNIFORM CDROM DRIVER
# use '-fno-strict-aliasing', but only if the compiler can take it
CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi)
-# likewise for -fno-delete-null-pointer-checks
-CFLAGS += $(shell if $(CC) -fno-delete-null-pointer-checks -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-delete-null-pointer-checks"; fi)
export CPPFLAGS CFLAGS AFLAGS
comment 'Kernel hacking'
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_VT
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
endmenu
# Loadable module support
#
CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
-# CONFIG_KMOD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
#
# Processor type and features
desc->depth--;
break;
case 0:
- printk("enable_irq() unbalanced from %p\n",
+ printk("enable_irq(%u) unbalanced from %p\n", irq,
__builtin_return_address(0));
}
spin_unlock_irqrestore(&desc->lock, flags);
* disabled.
*/
+static DECLARE_MUTEX(probe_sem);
+
/**
* probe_irq_on - begin an interrupt autodetect
*
unsigned long val;
unsigned long delay;
+ down(&probe_sem);
/*
* something may have generated an irq long ago and we want to
* flush such a longstanding irq before considering it as spurious.
* Scan the ISA bus interrupt lines and return a bitmap of
* active interrupts. The interrupt probe logic state is then
* returned to its previous value.
+ *
+ * Note: we need to scan all the irq's even though we will
+ * only return ISA irq numbers - just so that we reset them
+ * all to a known state.
*/
-
unsigned int probe_irq_mask(unsigned long val)
{
int i;
unsigned int mask;
mask = 0;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
unsigned int status;
status = desc->status;
if (status & IRQ_AUTODETECT) {
- if (!(status & IRQ_WAITING))
+ if (i < 16 && !(status & IRQ_WAITING))
mask |= 1 << i;
desc->status = status & ~IRQ_AUTODETECT;
}
spin_unlock_irq(&desc->lock);
}
+ up(&probe_sem);
return mask & val;
}
}
spin_unlock_irq(&desc->lock);
}
+ up(&probe_sem);
if (nr_irqs > 1)
irq_found = -irq_found;
if (!shared) {
desc->depth = 0;
- desc->status &= ~IRQ_DISABLED;
+ desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
desc->handler->startup(irq);
}
spin_unlock_irqrestore(&desc->lock,flags);
#
tristate 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
-if [ "$CONFIG_DRM" = "y" ]; then
+if [ "$CONFIG_DRM" != "n" ]; then
tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA
tristate ' ATI Rage 128' CONFIG_DRM_R128
# the Direct Rendering Infrastructure (DRI) in XFree86 4.x.
#
-O_TARGET = drm.o
-
-export-objs = drm_syms.o
-list-multi = drm.o gamma.o tdfx.o r128.o ffb.o mga.o i810.o
-drm-objs = init.o memory.o proc.o auth.o context.o \
- drawable.o bufs.o lists.o lock.o ioctl.o \
- fops.o vm.o dma.o ctxbitmap.o drm_syms.o
-gamma-objs = gamma_drv.o gamma_dma.o
-tdfx-objs = tdfx_drv.o tdfx_context.o
-r128-objs = r128_drv.o r128_context.o r128_dma.o r128_bufs.o
-ffb-objs = ffb_drv.o ffb_context.o
-mga-objs = mga_drv.o mga_context.o mga_dma.o mga_bufs.o mga_state.o
-i810-objs = i810_drv.o i810_context.o i810_dma.o i810_bufs.o
-
-ifeq ($(CONFIG_AGP), y)
- drm-objs += agpsupport.o
+# drm.o is a fake target -- it is never built
+# The real targets are in the module-list
+O_TARGET := drm.o
+module-list := gamma.o tdfx.o r128.o ffb.o mga.o i810.o
+export-objs := $(patsubst %.o,%_drv.o,$(module-list))
+
+# libs-objs are included in every module so that radical changes to the
+# architecture of the DRM support library can be made at a later time.
+#
+# The downside is that each module is larger, and a system that uses
+# more than one module (i.e., a dual-head system) will use more memory
+# (but a system that uses exactly one module will use the same amount of
+# memory).
+#
+# The upside is that if the DRM support library ever becomes insufficient
+# for new families of cards, a new library can be implemented for those new
+# cards without impacting the drivers for the old cards. This is significant,
+# because testing architectural changes to old cards may be impossible, and
+# may delay the implementation of a better architecture. We've traded slight
+# memory waste (in the dual-head case) for greatly improved long-term
+# maintainability.
+#
+lib-objs := init.o memory.o proc.o auth.o context.o drawable.o bufs.o
+lib-objs += lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o
+
+ifeq ($(CONFIG_AGP),y)
+ lib-objs += agpsupport.o
else
- ifeq ($(CONFIG_AGP), m)
- drm-objs += agpsupport.o
+ ifeq ($(CONFIG_AGP),m)
+ lib-objs += agpsupport.o
endif
endif
-obj-$(CONFIG_DRM) += drm.o
-obj-$(CONFIG_DRM_GAMMA) += gamma.o
-obj-$(CONFIG_DRM_TDFX) += tdfx.o
-obj-$(CONFIG_DRM_R128) += r128.o
-obj-$(CONFIG_DRM_FFB) += ffb.o
-obj-$(CONFIG_DRM_MGA) += mga.o
-obj-$(CONFIG_DRM_I810) += i810.o
-
-
-# Extract lists of the multi-part drivers.
-# The 'int-*' lists are the intermediate files used to build the multi's.
-multi-y := $(filter $(list-multi), $(obj-y))
-multi-m := $(filter $(list-multi), $(obj-m))
-int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
-int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+gamma-objs := $(lib-objs) gamma_drv.o gamma_dma.o
+tdfx-objs := $(lib-objs) tdfx_drv.o tdfx_context.o
+r128-objs := $(lib-objs) r128_drv.o r128_dma.o r128_context.o r128_bufs.o
+ffb-objs := $(lib-objs) ffb_drv.o ffb_context.o
+mga-objs := $(lib-objs) mga_drv.o mga_dma.o mga_context.o mga_bufs.o \
+ mga_state.o
+i810-objs := $(lib-objs) i810_drv.o i810_dma.o i810_context.o i810_bufs.o
+
+obj-$(CONFIG_DRM_GAMMA) += gamma.o $(gamma-objs)
+obj-$(CONFIG_DRM_TDFX) += tdfx.o $(tdfx-objs)
+obj-$(CONFIG_DRM_R128) += r128.o $(r128-objs)
+obj-$(CONFIG_DRM_FFB) += ffb.o $(ffb-objs)
+
+ifneq ($CONFIG_AGP),)
+obj-$(CONFIG_DRM_MGA) += mga.o $(mga-objs)
+obj-$(CONFIG_DRM_I810) += i810.o $(i810-objs)
+endif
-# Files that are both resident and modular: remove from modular.
-obj-m := $(filter-out $(obj-y), $(obj-m))
-int-m := $(filter-out $(int-y), $(int-m))
+# Take module names out of obj-y and int-m
-# Take multi-part drivers out of obj-y and put components in.
-obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
+obj-y := $(filter-out $(module-list), $(obj-y))
+int-m := $(filter-out $(module-list), $(obj-m))
# Translate to Rules.make lists.
-O_OBJS := $(filter-out $(export-objs), $(obj-y))
-OX_OBJS := $(filter $(export-objs), $(obj-y))
-M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
-MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
-MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
-MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
+
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter $(module-list), $(obj-m)))
+MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
include $(TOPDIR)/Rules.make
#include <linux/pci.h>
#include <linux/wrapper.h>
#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h> /* For (un)lock_kernel */
#include <asm/io.h>
#include <asm/mman.h>
#include <asm/uaccess.h>
/* Context support */
int irq; /* Interrupt used by board */
__volatile__ long context_flag; /* Context swapping flag */
- __volatile__ long interrupt_flag; /* Interruption handler flag */
- __volatile__ long dma_flag; /* DMA dispatch flag */
+ __volatile__ long interrupt_flag; /* Interruption handler flag */
+ __volatile__ long dma_flag; /* DMA dispatch flag */
struct timer_list timer; /* Timer for delaying ctx switch */
wait_queue_head_t context_wait; /* Processes waiting on ctx switch */
int last_checked; /* Last context checked for DMA */
*/
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/pci.h>
-#include <linux/smp_lock.h> /* For (un)lock_kernel */
#include "drmP.h"
#include "gamma_drv.h"
/* gamma_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
-int gamma_init(void)
+static int gamma_init(void)
{
int retcode;
drm_device_t *dev = &gamma_device;
/* gamma_cleanup is called via cleanup_module at module unload time. */
-void gamma_cleanup(void)
+static void gamma_cleanup(void)
{
drm_device_t *dev = &gamma_device;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
return -EBUSY;
}
spin_unlock(&dev->count_lock);
- retcode = gamma_takedown(dev);
unlock_kernel();
- return retcode;
+ return gamma_takedown(dev);
}
spin_unlock(&dev->count_lock);
}
*
*/
-#include <linux/sched.h>
-
#define __NO_VERSION__
#include "drmP.h"
#include "i810_drv.h"
#define __NO_VERSION__
#include "drmP.h"
#include "i810_drv.h"
-
#include <linux/interrupt.h> /* For task queue support */
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
/* in case we don't have a 2.3.99-pre6 kernel or later: */
#ifndef VM_DONTCOPY
int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
{
- drm_file_t *priv = filp->private_data;
- drm_device_t *dev;
- drm_i810_private_t *dev_priv;
- drm_buf_t *buf;
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev;
+ drm_i810_private_t *dev_priv;
+ drm_buf_t *buf;
drm_i810_buf_priv_t *buf_priv;
lock_kernel();
- dev = priv->dev;
+ dev = priv->dev;
dev_priv = dev->dev_private;
- buf = dev_priv->mmap_buffer;
+ buf = dev_priv->mmap_buffer;
buf_priv = buf->dev_private;
vma->vm_flags |= (VM_IO | VM_DONTCOPY);
#include <linux/config.h>
#include "drmP.h"
#include "i810_drv.h"
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
#define I810_NAME "i810"
#define I810_DESC "Intel I810"
/* i810_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
-int i810_init(void)
+static int i810_init(void)
{
int retcode;
drm_device_t *dev = &i810_device;
/* i810_cleanup is called via cleanup_module at module unload time. */
-void i810_cleanup(void)
+static void i810_cleanup(void)
{
drm_device_t *dev = &i810_device;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
up(&dev->struct_sem);
drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
return -EBUSY;
}
spin_unlock(&dev->count_lock);
- retcode = i810_takedown(dev);
- } else
- spin_unlock(&dev->count_lock);
+ unlock_kernel();
+ return i810_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
unlock_kernel();
return retcode;
}
*
*/
-#include <linux/sched.h>
-
#define __NO_VERSION__
#include "drmP.h"
#include "mga_drv.h"
#include <linux/config.h>
#include "drmP.h"
#include "mga_drv.h"
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
#define MGA_NAME "mga"
#define MGA_DESC "Matrox g200/g400"
/* mga_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
-int mga_init(void)
+static int mga_init(void)
{
int retcode;
drm_device_t *dev = &mga_device;
/* mga_cleanup is called via cleanup_module at module unload time. */
-void mga_cleanup(void)
+static void mga_cleanup(void)
{
drm_device_t *dev = &mga_device;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
up(&dev->struct_sem);
drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
return -EBUSY;
}
spin_unlock(&dev->count_lock);
- retcode = mga_takedown(dev);
- } else
- spin_unlock(&dev->count_lock);
+ unlock_kernel();
+ return mga_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
unlock_kernel();
return retcode;
}
*
*/
-#include <linux/sched.h>
-
#define __NO_VERSION__
#include "drmP.h"
#include "r128_drv.h"
static int r128_alloc_queue(drm_device_t *dev)
{
-#if 0
- static int context = 0;
-#endif
-
return drm_ctxbitmap_next(dev);
}
#include <linux/config.h>
#include "drmP.h"
#include "r128_drv.h"
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
#define R128_NAME "r128"
#define R128_DESC "ATI Rage 128"
/* r128_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
-int r128_init(void)
+static int r128_init(void)
{
int retcode;
drm_device_t *dev = &r128_device;
/* r128_cleanup is called via cleanup_module at module unload time. */
-void r128_cleanup(void)
+static void r128_cleanup(void)
{
drm_device_t *dev = &r128_device;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
int retcode = 0;
lock_kernel();
- dev = priv->dev;
+ dev = priv->dev;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_release(inode, filp))) {
- MOD_DEC_USE_COUNT;
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
return -EBUSY;
}
spin_unlock(&dev->count_lock);
- retcode = r128_takedown(dev);
- } else
- spin_unlock(&dev->count_lock);
+ unlock_kernel();
+ return r128_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
}
+
unlock_kernel();
return retcode;
}
*
*/
-#include <linux/sched.h>
-
#define __NO_VERSION__
#include "drmP.h"
#include "tdfx_drv.h"
*/
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include "drmP.h"
#include "tdfx_drv.h"
/* tdfx_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
-int tdfx_init(void)
+static int tdfx_init(void)
{
int retcode;
drm_device_t *dev = &tdfx_device;
/* tdfx_cleanup is called via cleanup_module at module unload time. */
-void tdfx_cleanup(void)
+static void tdfx_cleanup(void)
{
drm_device_t *dev = &tdfx_device;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
#define __NO_VERSION__
#include "drmP.h"
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
struct vm_operations_struct drm_vm_ops = {
nopage: drm_vm_nopage,
DRM_DEBUG("0x%08lx,0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start);
atomic_inc(&dev->vma_count);
+#if LINUX_VERSION_CODE < 0x020333
+ /* The map can exist after the fd is closed. */
+ MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+
#if DRM_DEBUG_CODE
vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
DRM_DEBUG("0x%08lx,0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start);
+#if LINUX_VERSION_CODE < 0x020333
+ MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
atomic_dec(&dev->vma_count);
#if DRM_DEBUG_CODE
#ifdef CONFIG_MAGIC_SYSRQ
static int sysrq_pressed;
-int sysrq_enabled = 1;
#endif
static struct pm_dev *pm_kbd = NULL;
sysrq_pressed = !up_flag;
return;
} else if (sysrq_pressed) {
- if (!up_flag && sysrq_enabled) {
+ if (!up_flag) {
handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);
return;
}
extern int console_loglevel;
extern struct list_head super_blocks;
+/* Whether we react on sysrq keys or just ignore them */
+int sysrq_enabled = 1;
+
/* Machine specific power off function */
void (*sysrq_power_off)(void) = NULL;
{
int orig_log_level = console_loglevel;
+ if (!sysrq_enabled)
+ return;
+
if (!key)
return;
* change (copied from ide-probe.c)
*/
struct hd_driveid *id;
- unsigned long timeout, irqs, flags;
+ unsigned long timeout, flags;
- probe_irq_off(probe_irq_on());
- irqs = probe_irq_on();
SELECT_MASK(HWIF(drive), drive, 1);
if (IDE_CONTROL_REG)
OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
timeout = jiffies + WAIT_WORSTCASE;
do {
if (0 < (signed long)(jiffies - timeout)) {
- if (irqs)
- (void) probe_irq_off(irqs);
SELECT_MASK(HWIF(drive), drive, 0);
return 0; /* drive timed-out */
}
* 1 device timed-out (no response to identify request)
* 2 device aborted the command (refused to identify itself)
*/
-static int try_to_identify (ide_drive_t *drive, byte cmd)
+static int actual_try_to_identify (ide_drive_t *drive, byte cmd)
{
int rc;
ide_ioreg_t hd_status;
unsigned long timeout;
- unsigned long irqs = 0;
byte s, a;
if (IDE_CONTROL_REG) {
- if (!HWIF(drive)->irq) { /* already got an IRQ? */
- probe_irq_off(probe_irq_on()); /* clear dangling irqs */
- irqs = probe_irq_on(); /* start monitoring irqs */
- OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
- }
-
- ide_delay_50ms(); /* take a deep breath */
+ /* take a deep breath */
+ ide_delay_50ms();
a = IN_BYTE(IDE_ALTSTATUS_REG);
s = IN_BYTE(IDE_STATUS_REG);
if ((a ^ s) & ~INDEX_STAT) {
/* DC4030 hosted drives need their own identify... */
extern int pdc4030_identify(ide_drive_t *);
if (pdc4030_identify(drive)) {
- if (irqs)
- (void) probe_irq_off(irqs);
return 1;
}
} else
timeout += jiffies;
do {
if (0 < (signed long)(jiffies - timeout)) {
- if (irqs)
- (void) probe_irq_off(irqs);
return 1; /* drive timed-out */
}
ide_delay_50ms(); /* give drive a breather */
__restore_flags(flags); /* local CPU only */
} else
rc = 2; /* drive refused ID */
+ return rc;
+}
+
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+ int retval;
+ int autoprobe = 0;
+ unsigned long cookie = 0;
+
if (IDE_CONTROL_REG && !HWIF(drive)->irq) {
- irqs = probe_irq_off(irqs); /* get our irq number */
- if (irqs > 0) {
- HWIF(drive)->irq = irqs; /* save it for later */
- irqs = probe_irq_on();
- OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
- udelay(5);
- (void) probe_irq_off(irqs);
- (void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
- (void) GET_STAT(); /* clear drive IRQ */
-
- } else { /* Mmmm.. multiple IRQs.. don't know which was ours */
- printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
+ autoprobe = 1;
+ cookie = probe_irq_on();
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
+ }
+
+ retval = actual_try_to_identify(drive, cmd);
+
+ if (autoprobe) {
+ int irq;
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
+ (void) GET_STAT(); /* clear drive IRQ */
+ udelay(5);
+ irq = probe_irq_off(cookie);
+ if (!HWIF(drive)->irq) {
+ if (irq > 0) {
+ HWIF(drive)->irq = irq;
+ } else { /* Mmmm.. multiple IRQs.. don't know which was ours */
+ printk("%s: IRQ probe failed (0x%lx)\n", drive->name, cookie);
#ifdef CONFIG_BLK_DEV_CMD640
#ifdef CMD640_DUMP_REGS
- if (HWIF(drive)->chipset == ide_cmd640) {
- printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
- CMD640_DUMP_REGS;
- }
+ if (HWIF(drive)->chipset == ide_cmd640) {
+ printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
+ CMD640_DUMP_REGS;
+ }
#endif /* CMD640_DUMP_REGS */
#endif /* CONFIG_BLK_DEV_CMD640 */
+ }
}
}
- return rc;
+ return retval;
}
+
/*
* do_probe() has the difficult job of finding a drive if it exists,
* without getting hung up if it doesn't exist, without trampling on
} /* End build_auth_frame */
/*===========================================================================*/
-
+#ifdef CONFIG_PROC_FS
static void raycs_write(const char *name, write_proc_t *w, void *data)
{
struct proc_dir_entry * entry = create_proc_entry(name, S_IFREG | S_IWUSR, NULL);
*(int *)data = nr;
return count;
}
+#endif
static int __init init_ray_cs(void)
{
rc = register_pcmcia_driver(&dev_info, &ray_attach, &ray_detach);
DEBUG(1, "raylink init_module register_pcmcia_driver returns 0x%x\n",rc);
+#ifdef CONFIG_PROC_FS
proc_mkdir("driver/ray_cs", 0);
- create_proc_info_entry("driver/ray_cs/ray_cs", 0, NULL, ray_cs_proc_read);
+ create_proc_info_entry("driver/ray_cs/ray_cs", 0, NULL, &ray_cs_proc_read);
raycs_write("driver/ray_cs/essid", write_essid, NULL);
raycs_write("driver/ray_cs/net_type", write_int, &net_type);
raycs_write("driver/ray_cs/translate", write_int, &translate);
+#endif
if (translate != 0) translate = 1;
return 0;
} /* init_ray_cs */
DEBUG(0, "ray_cs: cleanup_module\n");
+#ifdef CONFIG_PROC_FS
remove_proc_entry("ray_cs", proc_root_driver);
+#endif
+
unregister_pcmcia_driver(&dev_info);
while (dev_list != NULL) {
if (dev_list->state & DEV_CONFIG) ray_release((u_long)dev_list);
ray_detach(dev_list);
}
+#ifdef CONFIG_PROC_FS
remove_proc_entry("driver/ray_cs/ray_cs", NULL);
remove_proc_entry("driver/ray_cs/essid", NULL);
remove_proc_entry("driver/ray_cs/net_type", NULL);
remove_proc_entry("driver/ray_cs/translate", NULL);
remove_proc_entry("driver/ray_cs", NULL);
+#endif
} /* exit_ray_cs */
module_init(init_ray_cs);
1105 Sigma Designs, Inc.
8300 REALmagic Hollywood Plus DVD Decoder
1106 VIA Technologies, Inc.
+ 0305 VT8363 [KT133]
0391 VT8371 [KX133]
0501 VT8501
0505 VT82C505
5030 VT82C596 ACPI [Apollo PRO]
6100 VT85C100A [Rhine II]
8231 VT8231 [PCI-to-ISA Bridge]
+ 8305 VT8363 [PCI-PCI Bridge]
8391 VT8371 [PCI-PCI Bridge]
8501 VT8501
8596 VT82C596 [Apollo PRO AGP]
M_OBJS += tcic.o
endif
ifeq ($(CONFIG_CARDBUS),y)
+ M_OBJS += yenta_socket.o
CORE_OBJS += cardbus.o
- MX_OBJS += cb_enabler.o yenta.o pci_socket.o
+ CARDBUS_OBJS := pci_socket.o yenta.o
+ OX_OBJS += pci_socket.o yenta.o
+ MX_OBJS += cb_enabler.o
endif
endif
endif
pcmcia_core.o: $(CORE_OBJS)
$(LD) $(LD_RFLAG) -r -o $@ $(CORE_OBJS)
+
+yenta_socket.o: $(CARDBUS_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ yenta.o pci_socket.o
*/
cb_writel(socket, CB_SOCKET_EVENT, -1);
cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+ exca_writeb(socket, I365_CSCINT, 0);
val = probe_irq_on() & isa_irq_mask;
for (i = 1; i < 16; i++) {
if (!((val >> i) & 1))
pci_socket_t * socket = (pci_socket_t *) data;
DECLARE_WAITQUEUE(wait, current);
+ MOD_INC_USE_COUNT;
daemonize();
strcpy(current->comm, "CardBus Watcher");
schedule_timeout(HZ);
remove_wait_queue(&socket->wait, &wait);
} while (!signal_pending(current));
+ MOD_DEC_USE_COUNT;
return 0;
}
min = PCIBIOS_MIN_MEM; max = ~0U;
if (type & IORESOURCE_IO) {
align = 1024;
- size = 1024;
+ size = 256;
min = PCIBIOS_MIN_IO;
max = 0xffff;
}
yenta_allocate_res(socket, 0, IORESOURCE_MEM|IORESOURCE_PREFETCH);
yenta_allocate_res(socket, 1, IORESOURCE_MEM);
yenta_allocate_res(socket, 2, IORESOURCE_IO);
+ yenta_allocate_res(socket, 3, IORESOURCE_IO); /* PCI isn't clever enough to use this one yet */
}
/*
static struct pt_regs * pt_regs;
#ifdef CONFIG_MAGIC_SYSRQ
-#ifndef CONFIG_PCI
-int sysrq_enabled = 1;
-#endif
unsigned char sun_sysrq_xlate[128] =
"\0\0\0\0\0\201\202\212\203\213\204\214\205\0\206\0" /* 0x00 - 0x0f */
"\207\210\211\0\0\0\0\0\0\0\0\0\0\03312" /* 0x10 - 0x1f */
#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq hack */
if (l1a_state.l1_down) {
- if (!up_flag && sysrq_enabled)
+ if (!up_flag)
handle_sysrq(sun_sysrq_xlate[keycode], pt_regs, kbd, tty);
goto out;
}
/*
- * bluetooth.c Version 0.2
+ * bluetooth.c Version 0.3
*
* Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
*
* USB Bluetooth driver, based on the Bluetooth Spec version 1.0B
*
*
+ * (08/03/2000) Version 0.3 gkh mdc
+ * Merged in Mark's changes to make the driver play nice with the Axis
+ * stack.
+ * Made the write bulk use an urb pool to enable larger transfers with
+ * fewer calls to the driver.
+ * Fixed off by one bug in acl pkt receive
+ * Made packet counters specific to each bluetooth device
+ * Added checks for zero length callbacks
+ * Added buffers for int and bulk packets. Had to do this otherwise
+ * packet types could intermingle.
+ * Made a control urb pool for the control messages.
+ *
* (07/11/2000) Version 0.2 gkh
* Fixed a small bug found by Nils Faerber in the usb_bluetooth_probe
* function.
#include <linux/tty_flip.h>
#include <linux/tty.h>
#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/smp_lock.h>
#define DEBUG
#include <linux/usb.h>
/* Module information */
-MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");
+MODULE_AUTHOR("Greg Kroah-Hartman, Mark Douglas Corner");
MODULE_DESCRIPTION("USB Bluetooth driver");
+/* define this if you have hardware that is not good */
+/*#define BTBUGGYHARDWARE */
/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
#define WIRELESS_CLASS_CODE 0xe0
#define USB_BLUETOOTH_MAGIC 0x6d02 /* magic number for bluetooth struct */
+#define BLUETOOTH_CONTROL_REQUEST_TYPE 0x20
+
+/* Bluetooth packet types */
+#define CMD_PKT 0x01
+#define ACL_PKT 0x02
+#define SCO_PKT 0x03
+#define EVENT_PKT 0x04
+#define ERROR_PKT 0x05
+#define NEG_PKT 0x06
+
+/* Message sizes */
+#define MAX_EVENT_SIZE 0xFF
+#define EVENT_HDR_SIZE 3 /* 2 for the header + 1 for the type indicator */
+#define EVENT_BUFFER_SIZE (MAX_EVENT_SIZE + EVENT_HDR_SIZE)
+
+#define MAX_ACL_SIZE 0xFFFF
+#define ACL_HDR_SIZE 5 /* 4 for the header + 1 for the type indicator */
+#define ACL_BUFFER_SIZE (MAX_ACL_SIZE + ACL_HDR_SIZE)
+
/* parity check flag */
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define NUM_BULK_URBS 24
+#define NUM_CONTROL_URBS 16
struct usb_bluetooth {
int magic;
struct usb_device * dev;
struct tty_driver * tty_driver; /* the tty_driver for this device */
struct tty_struct * tty; /* the coresponding tty for this port */
-
+
unsigned char minor; /* the starting minor number for this device */
char active; /* someone has this device open */
+ int throttle; /* throttled by tty layer */
+
+ __u8 control_out_bInterfaceNum;
+ struct urb * control_urb_pool[NUM_CONTROL_URBS];
+ devrequest dr[NUM_CONTROL_URBS];
unsigned char * interrupt_in_buffer;
struct urb * interrupt_in_urb;
unsigned char * bulk_in_buffer;
struct urb * read_urb;
- unsigned char * bulk_out_buffer;
int bulk_out_size;
- struct urb * write_urb;
+ struct urb * write_urb_pool[NUM_BULK_URBS];
+ __u8 bulk_out_endpointAddress;
wait_queue_head_t write_wait;
struct tq_struct tqueue; /* task queue for line discipline waking up */
+
+ unsigned int int_packet_pos;
+ unsigned char int_buffer[EVENT_BUFFER_SIZE];
+ unsigned int bulk_packet_pos;
+ unsigned char bulk_buffer[ACL_BUFFER_SIZE]; /* 64k preallocated, fix? */
};
-
/* local function prototypes */
static int bluetooth_open (struct tty_struct *tty, struct file *filp);
static void bluetooth_close (struct tty_struct *tty, struct file *filp);
static int bluetooth_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
static void bluetooth_set_termios (struct tty_struct *tty, struct termios *old);
+static void bluetooth_int_callback (struct urb *urb);
+static void bluetooth_ctrl_callback (struct urb *urb);
+static void bluetooth_read_bulk_callback (struct urb *urb);
+static void bluetooth_write_bulk_callback (struct urb *urb);
+
static void * usb_bluetooth_probe (struct usb_device *dev, unsigned int ifnum);
static void usb_bluetooth_disconnect (struct usb_device *dev, void *ptr);
+
static struct usb_driver usb_bluetooth_driver = {
name: "bluetooth",
probe: usb_bluetooth_probe,
static struct usb_bluetooth *bluetooth_table[BLUETOOTH_TTY_MINORS] = {NULL, };
+
static inline int bluetooth_paranoia_check (struct usb_bluetooth *bluetooth, const char *function)
{
if (!bluetooth) {
dbg("%s - bad magic number for bluetooth", function);
return -1;
}
-
+
return 0;
}
-static inline struct usb_bluetooth* get_usb_bluetooth (struct usb_bluetooth *bluetooth, const char *function)
-{
+static inline struct usb_bluetooth* get_usb_bluetooth (struct usb_bluetooth *bluetooth, const char *function)
+{
if (!bluetooth ||
- bluetooth_paranoia_check (bluetooth, function)) {
+ bluetooth_paranoia_check (bluetooth, function)) {
/* then say that we dont have a valid usb_bluetooth thing, which will
- * end up genrating -ENODEV return values */
+ * end up generating -ENODEV return values */
return NULL;
}
}
+static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int value, void *buf, int len)
+{
+ struct urb *urb = NULL;
+ devrequest *dr = NULL;
+ int i;
+ int status;
+
+ dbg (__FUNCTION__);
+
+ /* try to find a free urb in our list */
+ for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+ if (bluetooth->control_urb_pool[i]->status != -EINPROGRESS) {
+ urb = bluetooth->control_urb_pool[i];
+ dr = &bluetooth->dr[i];
+ break;
+ }
+ }
+ if (urb == NULL) {
+ dbg (__FUNCTION__ " - no free urbs");
+ return -ENOMEM;
+ }
+
+ /* free up the last buffer that this urb used */
+ if (urb->transfer_buffer != NULL) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+
+ dr->requesttype = BLUETOOTH_CONTROL_REQUEST_TYPE;
+ dr->request = request;
+ dr->value = cpu_to_le16p(&value);
+ dr->index = cpu_to_le16p(&bluetooth->control_out_bInterfaceNum);
+ dr->length = cpu_to_le16p(&len);
+
+ FILL_CONTROL_URB (urb, bluetooth->dev, usb_sndctrlpipe(bluetooth->dev, 0),
+ (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, 0);
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb);
+ if (status)
+ dbg(__FUNCTION__ " - usb_submit_urb(control) failed with status = %d", status);
+
+ return 0;
+}
+
+
+
+
/*****************************************************************************
* Driver tty interface functions
static int bluetooth_open (struct tty_struct *tty, struct file * filp)
{
struct usb_bluetooth *bluetooth;
-
+ int result;
+
dbg(__FUNCTION__);
/* initialize the pointer incase something fails */
dbg (__FUNCTION__ " - device already open");
return -EINVAL;
}
-
+
/* set up our structure making the tty driver remember our object, and us it */
tty->driver_data = bluetooth;
bluetooth->tty = tty;
-
+
bluetooth->active = 1;
-
- /*Start reading from the device*/
- if (usb_submit_urb(bluetooth->read_urb))
- dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+
+ /* Reset the packet position counters */
+ bluetooth->int_packet_pos = 0;
+ bluetooth->bulk_packet_pos = 0;
+
+#ifndef BTBUGGYHARDWARE
+ /* Start reading from the device */
+ result = usb_submit_urb(bluetooth->read_urb);
+ if (result)
+ dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed with status %d", result);
+#endif
+ result = usb_submit_urb(bluetooth->interrupt_in_urb);
+ if (result)
+ dbg(__FUNCTION__ " - usb_submit_urb(interrupt in) failed with status %d", result);
return 0;
}
static void bluetooth_close (struct tty_struct *tty, struct file * filp)
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+ int i;
if (!bluetooth) {
return;
}
dbg(__FUNCTION__);
-
+
if (!bluetooth->active) {
dbg (__FUNCTION__ " - device not opened");
return;
}
- /* shutdown any bulk reads that might be going on */
- usb_unlink_urb (bluetooth->write_urb);
+ /* shutdown any bulk reads and writes that might be going on */
+ for (i = 0; i < NUM_BULK_URBS; ++i)
+ usb_unlink_urb (bluetooth->write_urb_pool[i]);
usb_unlink_urb (bluetooth->read_urb);
bluetooth->active = 0;
static int bluetooth_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
-
+ struct urb *urb = NULL;
+ unsigned char *new_buffer;
+ const unsigned char *current_position;
+ int status;
+ int bytes_sent;
+ int buffer_size;
+ int i;
+
if (!bluetooth) {
return -ENODEV;
}
-
+
dbg(__FUNCTION__ " - %d byte(s)", count);
if (!bluetooth->active) {
dbg (__FUNCTION__ " - device not opened");
return -EINVAL;
}
-
+
if (count == 0) {
dbg(__FUNCTION__ " - write request of 0 bytes");
- return (0);
+ return 0;
}
-
- if (bluetooth->write_urb->status == -EINPROGRESS) {
- dbg (__FUNCTION__ " - already writing");
- return (0);
+ if (count == 1) {
+ dbg(__FUNCTION__ " - write request only included type %d", buf[0]);
+ return 1;
}
- count = (count > bluetooth->bulk_out_size) ? bluetooth->bulk_out_size : count;
-
#ifdef DEBUG
- {
- int i;
- printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count);
- for (i = 0; i < count; ++i) {
- printk ("%.2x ", buf[i]);
- }
- printk ("\n");
+ printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count);
+ for (i = 0; i < count; ++i) {
+ printk ("%.2x ", buf[i]);
}
+ printk ("\n");
#endif
- if (from_user) {
- copy_from_user(bluetooth->write_urb->transfer_buffer, buf, count);
- }
- else {
- memcpy (bluetooth->write_urb->transfer_buffer, buf, count);
- }
-
- /* send the data out the bulk bluetooth */
- bluetooth->write_urb->transfer_buffer_length = count;
+ switch (*buf) {
+ /* First byte indicates the type of packet */
+ case CMD_PKT:
+ /* dbg(__FUNCTION__ "- Send cmd_pkt len:%d", count);*/
+
+ if (in_interrupt()){
+ printk("cmd_pkt from interrupt!\n");
+ return count;
+ }
+
+ new_buffer = kmalloc (count-1, GFP_KERNEL);
+
+ if (!new_buffer) {
+ err (__FUNCTION__ "- out of memory.");
+ return -ENOMEM;
+ }
+
+ if (from_user)
+ copy_from_user (new_buffer, buf+1, count-1);
+ else
+ memcpy (new_buffer, buf+1, count-1);
+
+ bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1);
+
+ /* need to free new_buffer somehow... FIXME */
+ return count;
+
+ case ACL_PKT:
+ current_position = buf;
+ ++current_position;
+ --count;
+ bytes_sent = 0;
+
+ while (count > 0) {
+ urb = NULL;
+
+ /* try to find a free urb in our list */
+ for (i = 0; i < NUM_BULK_URBS; ++i) {
+ if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+ urb = bluetooth->write_urb_pool[i];
+ break;
+ }
+ }
+ if (urb == NULL) {
+ dbg (__FUNCTION__ " - no free urbs");
+ return bytes_sent;
+ }
+
+ /* free up the last buffer that this urb used */
+ if (urb->transfer_buffer != NULL) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+
+ buffer_size = MIN (count, bluetooth->bulk_out_size);
+
+ new_buffer = kmalloc (buffer_size, GFP_KERNEL);
+ if (new_buffer == NULL) {
+ err(__FUNCTION__" no more kernel memory...");
+ return bytes_sent;
+ }
+
+ if (from_user)
+ copy_from_user(new_buffer, current_position, buffer_size);
+ else
+ memcpy (new_buffer, current_position, buffer_size);
+
+ /* build up our urb */
+ FILL_BULK_URB (urb, bluetooth->dev, usb_sndbulkpipe(bluetooth->dev, bluetooth->bulk_out_endpointAddress),
+ new_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth);
+ urb->transfer_flags |= USB_QUEUE_BULK;
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb);
+ if (status)
+ dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status);
+#ifdef BTBUGGYHARDWARE
+ /* A workaround for the stalled data bug */
+ /* May or may not be needed...*/
+ if (count != 0) {
+ udelay(500);
+ }
+#endif
+ current_position += buffer_size;
+ bytes_sent += buffer_size;
+ count -= buffer_size;
+ }
- if (usb_submit_urb(bluetooth->write_urb))
- dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed");
+ return bytes_sent + 1;
+
+ default :
+ dbg(__FUNCTION__" - unsupported (at this time) write type");
+ }
- return count;
+ return 0;
}
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
int room = 0;
+ int i;
if (!bluetooth) {
return -ENODEV;
}
dbg(__FUNCTION__);
-
+
if (!bluetooth->active) {
dbg (__FUNCTION__ " - device not open");
return -EINVAL;
}
- if (bluetooth->write_urb->status != -EINPROGRESS)
- room = bluetooth->bulk_out_size;
-
+ for (i = 0; i < NUM_BULK_URBS; ++i) {
+ if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+ room += bluetooth->bulk_out_size;
+ }
+ }
+
dbg(__FUNCTION__ " - returns %d", room);
return room;
}
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
int chars = 0;
+ int i;
if (!bluetooth) {
return -ENODEV;
return -EINVAL;
}
-
- if (bluetooth->write_urb->status == -EINPROGRESS)
- chars = bluetooth->write_urb->transfer_buffer_length;
+ for (i = 0; i < NUM_BULK_URBS; ++i) {
+ if (bluetooth->write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += bluetooth->write_urb_pool[i]->transfer_buffer_length;
+ }
+ }
dbg (__FUNCTION__ " - returns %d", chars);
return chars;
dbg (__FUNCTION__ " - device not open");
return;
}
-
- /* FIXME!!! */
+ dbg(__FUNCTION__ " unsupported (at this time)");
+
return;
}
return;
}
- /* FIXME!!! */
-
- return;
+ dbg(__FUNCTION__ " unsupported (at this time)");
}
}
/* FIXME!!! */
-
return -ENOIOCTLCMD;
}
}
/* FIXME!!! */
-
+
return;
}
+#ifdef BTBUGGYHARDWARE
+void btusb_enable_bulk_read(struct tty_struct *tty){
+ struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+ if (!bluetooth) {
+ return;
+ }
+
+ dbg(__FUNCTION__);
+
+ if (!bluetooth->active) {
+ dbg (__FUNCTION__ " - device not open");
+ return;
+ }
+
+ if (bluetooth->read_urb)
+ if (usb_submit_urb(bluetooth->read_urb))
+ dbg (__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+}
+
+void btusb_disable_bulk_read(struct tty_struct *tty){
+ struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+ if (!bluetooth) {
+ return;
+ }
+
+ dbg(__FUNCTION__);
+
+ if (!bluetooth->active) {
+ dbg (__FUNCTION__ " - device not open");
+ return;
+ }
+
+ if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length))
+ usb_unlink_urb(bluetooth->read_urb);
+}
+#endif
+
+
/*****************************************************************************
* urb callback functions
*****************************************************************************/
+
static void bluetooth_int_callback (struct urb *urb)
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
unsigned char *data = urb->transfer_buffer;
- int i;
+ unsigned int i;
+ unsigned int count = urb->actual_length;
+ unsigned int packet_size;
dbg(__FUNCTION__);
-
+
if (!bluetooth) {
dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
return;
}
if (urb->status) {
- dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+ dbg(__FUNCTION__ " - nonzero int status received: %d", urb->status);
return;
}
+ if (!count) {
+ dbg(__FUNCTION__ " - zero length int");
+ return;
+ }
+
+
#ifdef DEBUG
- if (urb->actual_length) {
- printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length);
- for (i = 0; i < urb->actual_length; ++i) {
+ if (count) {
+ printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+ for (i = 0; i < count; ++i) {
printk ("%.2x ", data[i]);
}
printk ("\n");
}
#endif
- /* Don't really know what else to do with this data yet. */
- /* FIXME!!! */
+#ifdef BTBUGGYHARDWARE
+ if ((count >= 2) && (data[0] == 0xFF) && (data[1] == 0x00)) {
+ data += 2;
+ count -= 2;
+ }
+ if (count == 0) {
+ urb->actual_length = 0;
+ return;
+ }
+#endif
+ /* We add a packet type identifier to the beginning of each
+ HCI frame. This makes the data in the tty look like a
+ serial USB devices. Each HCI frame can be broken across
+ multiple URBs so we buffer them until we have a full hci
+ packet */
+
+ if (!bluetooth->int_packet_pos) {
+ bluetooth->int_buffer[0] = EVENT_PKT;
+ bluetooth->int_packet_pos++;
+ }
- return;
+ if (bluetooth->int_packet_pos + count > EVENT_BUFFER_SIZE) {
+ err(__FUNCTION__ " - exceeded EVENT_BUFFER_SIZE");
+ bluetooth->int_packet_pos = 0;
+ return;
+ }
+
+ memcpy (&bluetooth->int_buffer[bluetooth->int_packet_pos],
+ urb->transfer_buffer, count);
+ bluetooth->int_packet_pos += count;
+ urb->actual_length = 0;
+
+ if (bluetooth->int_packet_pos >= EVENT_HDR_SIZE)
+ packet_size = bluetooth->int_buffer[2];
+ else
+ return;
+
+ if (packet_size + EVENT_HDR_SIZE < bluetooth->int_packet_pos) {
+ err(__FUNCTION__ " - packet was too long");
+ bluetooth->int_packet_pos = 0;
+ return;
+ }
+
+ if (packet_size + EVENT_HDR_SIZE == bluetooth->int_packet_pos){
+ for (i = 0; i < bluetooth->int_packet_pos; ++i)
+ tty_insert_flip_char(bluetooth->tty, bluetooth->int_buffer[i], 0);
+ tty_flip_buffer_push(bluetooth->tty);
+
+ bluetooth->int_packet_pos = 0;
+ }
+}
+
+
+static void bluetooth_ctrl_callback (struct urb *urb)
+{
+ struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+
+ dbg(__FUNCTION__);
+
+ if (!bluetooth) {
+ dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+ return;
+ }
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+ return;
+ }
}
static void bluetooth_read_bulk_callback (struct urb *urb)
{
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
- struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
- int i;
+ unsigned int count = urb->actual_length;
+ unsigned int i;
+ uint packet_size;
+
+#ifdef BTBUGGYHARDWARE
+ if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00)
+ && (data[2] == 0x00) && (data[3] == 0x00)) {
+ urb->actual_length = 0;
+ if (usb_submit_urb(urb))
+ dbg(__FUNCTION__ " - failed resubmitting read urb");
+
+ return;
+ }
+#endif
dbg(__FUNCTION__);
-
+
if (!bluetooth) {
dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
return;
return;
}
+ if (!count) {
+ dbg(__FUNCTION__ " - zero length read bulk");
+ return;
+ }
+
#ifdef DEBUG
- if (urb->actual_length) {
- printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length);
- for (i = 0; i < urb->actual_length; ++i) {
+ if (count) {
+ printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+ for (i = 0; i < count; ++i) {
printk ("%.2x ", data[i]);
}
printk ("\n");
}
#endif
+ /* We add a packet type identifier to the beginning of each
+ HCI frame. This makes the data in the tty look like a
+ serial USB devices. Each HCI frame can be broken across
+ multiple URBs so we buffer them until we have a full hci
+ packet */
+
+ if (!bluetooth->bulk_packet_pos) {
+ bluetooth->bulk_buffer[0] = ACL_PKT;
+ bluetooth->bulk_packet_pos++;
+ }
- tty = bluetooth->tty;
- if (urb->actual_length) {
- for (i = 0; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
+ if (bluetooth->bulk_packet_pos + count > ACL_BUFFER_SIZE) {
+ err(__FUNCTION__ " - exceeded ACL_BUFFER_SIZE");
+ bluetooth->bulk_packet_pos = 0;
+ if (usb_submit_urb(urb))
+ dbg(__FUNCTION__ " - failed resubmitting read urb");
+ return;
+ }
+
+ memcpy (&bluetooth->bulk_buffer[bluetooth->bulk_packet_pos],
+ urb->transfer_buffer, count);
+ bluetooth->bulk_packet_pos += count;
+ urb->actual_length = 0;
+
+ if (bluetooth->bulk_packet_pos >= ACL_HDR_SIZE) {
+ packet_size = CHAR2INT16(bluetooth->bulk_buffer[4],bluetooth->bulk_buffer[3]);
+ } else {
+ if (usb_submit_urb(urb))
+ dbg(__FUNCTION__ " - failed resubmitting read urb");
+ return;
}
- /* Continue trying to always read */
+ if (packet_size + ACL_HDR_SIZE < bluetooth->bulk_packet_pos) {
+ err(__FUNCTION__ " - packet was too long");
+ bluetooth->bulk_packet_pos = 0;
+ if (usb_submit_urb(urb))
+ dbg(__FUNCTION__ " - failed resubmitting read urb");
+ return;
+ }
+
+ if (packet_size + ACL_HDR_SIZE == bluetooth->bulk_packet_pos) {
+ for (i = 0; i < bluetooth->bulk_packet_pos; ++i)
+ tty_insert_flip_char(bluetooth->tty, bluetooth->bulk_buffer[i], 0);
+ tty_flip_buffer_push(bluetooth->tty);
+ bluetooth->bulk_packet_pos = 0;
+ }
+
if (usb_submit_urb(urb))
dbg(__FUNCTION__ " - failed resubmitting read urb");
struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
dbg(__FUNCTION__);
-
+
if (!bluetooth) {
dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
return;
return;
}
+ /* wake up our little function to let the tty layer know that something happened */
queue_task(&bluetooth->tqueue, &tq_immediate);
mark_bh(IMMEDIATE_BH);
-
return;
}
struct tty_struct *tty;
dbg(__FUNCTION__);
-
+
if (!bluetooth) {
return;
}
-
+
tty = bluetooth->tty;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
dbg(__FUNCTION__ " - write wakeup call.");
struct usb_endpoint_descriptor *interrupt_in_endpoint[8];
struct usb_endpoint_descriptor *bulk_in_endpoint[8];
struct usb_endpoint_descriptor *bulk_out_endpoint[8];
+ int control_out_endpoint;
+
int minor;
int buffer_size;
int i;
int num_interrupt_in = 0;
int num_bulk_in = 0;
int num_bulk_out = 0;
-
+
/* see if this device has the proper class signature */
if ((dev->descriptor.bDeviceClass != WIRELESS_CLASS_CODE) ||
(dev->descriptor.bDeviceSubClass != RF_SUBCLASS_CODE) ||
(dev->descriptor.bDeviceProtocol != BLUETOOTH_PROGRAMMING_PROTOCOL_CODE)) {
dbg (__FUNCTION__ " - class signature %d, %d, %d did not match",
- dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass,
- dev->descriptor.bDeviceProtocol);
+ dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass,
+ dev->descriptor.bDeviceProtocol);
return NULL;
}
- /* find the endpoints that we need */
interface = &dev->actconfig->interface[ifnum].altsetting[0];
+ control_out_endpoint = interface->bInterfaceNumber;
+
+ /* find the endpoints that we need */
for (i = 0; i < interface->bNumEndpoints; ++i) {
endpoint = &interface->endpoint[i];
-
+
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk in endpoint */
bulk_out_endpoint[num_bulk_out] = endpoint;
++num_bulk_out;
}
-
+
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found a interrupt in endpoint */
++num_interrupt_in;
}
}
-
+
/* according to the spec, we can only have 1 bulk_in, 1 bulk_out, and 1 interrupt_in endpoints */
if ((num_bulk_in != 1) ||
(num_bulk_out != 1) ||
dbg (__FUNCTION__ " - improper number of endpoints. Bluetooth driver not bound.");
return NULL;
}
-
+
MOD_INC_USE_COUNT;
info("USB Bluetooth converter detected");
MOD_DEC_USE_COUNT;
return NULL;
}
-
+
memset(bluetooth, 0, sizeof(struct usb_bluetooth));
-
+
bluetooth->magic = USB_BLUETOOTH_MAGIC;
bluetooth->dev = dev;
bluetooth->minor = minor;
bluetooth->tqueue.routine = bluetooth_softint;
bluetooth->tqueue.data = bluetooth;
+ /* record the interface number for the control out */
+ bluetooth->control_out_bInterfaceNum = control_out_endpoint;
+
+ /* create our control out urb pool */
+ for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+ struct urb *urb = usb_alloc_urb(0);
+ if (urb == NULL) {
+ err("No free urbs available");
+ goto probe_error;
+ }
+ urb->transfer_buffer = NULL;
+ bluetooth->control_urb_pool[i] = urb;
+ }
+
/* set up the endpoint information */
endpoint = bulk_in_endpoint[0];
bluetooth->read_urb = usb_alloc_urb (0);
bluetooth->bulk_in_buffer, buffer_size, bluetooth_read_bulk_callback, bluetooth);
endpoint = bulk_out_endpoint[0];
- bluetooth->write_urb = usb_alloc_urb(0);
- if (!bluetooth->write_urb) {
- err("No free urbs available");
- goto probe_error;
- }
- buffer_size = endpoint->wMaxPacketSize;
- bluetooth->bulk_out_size = buffer_size;
- bluetooth->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
- if (!bluetooth->bulk_out_buffer) {
- err("Couldn't allocate bulk_out_buffer");
- goto probe_error;
+ bluetooth->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+
+ /* create our write urb pool */
+ for (i = 0; i < NUM_BULK_URBS; ++i) {
+ struct urb *urb = usb_alloc_urb(0);
+ if (urb == NULL) {
+ err("No free urbs available");
+ goto probe_error;
+ }
+ urb->transfer_buffer = NULL;
+ bluetooth->write_urb_pool[i] = urb;
}
- FILL_BULK_URB(bluetooth->write_urb, dev, usb_sndbulkpipe(dev, endpoint->bEndpointAddress),
- bluetooth->bulk_out_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth);
+
+ bluetooth->bulk_out_size = endpoint->wMaxPacketSize * 2;
endpoint = interrupt_in_endpoint[0];
bluetooth->interrupt_in_urb = usb_alloc_urb(0);
/* initialize the devfs nodes for this device and let the user know what bluetooths we are bound to */
tty_register_devfs (&bluetooth_tty_driver, 0, minor);
info("Bluetooth converter now attached to ttyBLUE%d (or usb/ttblue/%d for devfs)", minor, minor);
-
+
bluetooth_table[minor] = bluetooth;
-
+
return bluetooth; /* success */
probe_error:
usb_free_urb (bluetooth->read_urb);
if (bluetooth->bulk_in_buffer)
kfree (bluetooth->bulk_in_buffer);
- if (bluetooth->write_urb)
- usb_free_urb (bluetooth->write_urb);
- if (bluetooth->bulk_out_buffer)
- kfree (bluetooth->bulk_out_buffer);
if (bluetooth->interrupt_in_urb)
usb_free_urb (bluetooth->interrupt_in_urb);
if (bluetooth->interrupt_in_buffer)
kfree (bluetooth->interrupt_in_buffer);
-
+ for (i = 0; i < NUM_BULK_URBS; ++i)
+ if (bluetooth->write_urb_pool[i])
+ usb_free_urb (bluetooth->write_urb_pool[i]);
+ for (i = 0; i < NUM_CONTROL_URBS; ++i)
+ if (bluetooth->control_urb_pool[i])
+ usb_free_urb (bluetooth->control_urb_pool[i]);
+
bluetooth_table[minor] = NULL;
/* free up any memory that we allocated */
static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr)
{
struct usb_bluetooth *bluetooth = (struct usb_bluetooth *) ptr;
+ int i;
if (bluetooth) {
bluetooth->active = 0;
}
if (bluetooth->bulk_in_buffer)
kfree (bluetooth->bulk_in_buffer);
-
- if (bluetooth->write_urb) {
- usb_unlink_urb (bluetooth->write_urb);
- usb_free_urb (bluetooth->write_urb);
- }
- if (bluetooth->bulk_out_buffer)
- kfree (bluetooth->bulk_out_buffer);
-
+
if (bluetooth->interrupt_in_urb) {
usb_unlink_urb (bluetooth->interrupt_in_urb);
usb_free_urb (bluetooth->interrupt_in_urb);
kfree (bluetooth->interrupt_in_buffer);
tty_unregister_devfs (&bluetooth_tty_driver, bluetooth->minor);
+
+ if (bluetooth->tty)
+ tty_hangup(bluetooth->tty);
+
+ for (i = 0; i < NUM_BULK_URBS; ++i) {
+ if (bluetooth->write_urb_pool[i]) {
+ usb_unlink_urb (bluetooth->write_urb_pool[i]);
+ if (bluetooth->write_urb_pool[i]->transfer_buffer)
+ kfree (bluetooth->write_urb_pool[i]->transfer_buffer);
+ usb_free_urb (bluetooth->write_urb_pool[i]);
+ }
+ }
+ for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+ if (bluetooth->control_urb_pool[i]) {
+ usb_unlink_urb (bluetooth->control_urb_pool[i]);
+ if (bluetooth->control_urb_pool[i]->transfer_buffer)
+ kfree (bluetooth->control_urb_pool[i]->transfer_buffer);
+ usb_free_urb (bluetooth->control_urb_pool[i]);
+ }
+ }
info("Bluetooth converter now disconnected from ttyBLUE%d", bluetooth->minor);
} else {
info("device disconnected");
}
-
+
MOD_DEC_USE_COUNT;
}
type: TTY_DRIVER_TYPE_SERIAL,
subtype: SERIAL_TYPE_NORMAL,
flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
-
+
refcount: &bluetooth_refcount,
table: bluetooth_tty,
termios: bluetooth_termios,
termios_locked: bluetooth_termios_locked,
-
+
open: bluetooth_open,
close: bluetooth_close,
write: bluetooth_write,
err("usb_register failed for the USB bluetooth driver. Error number %d", result);
return -1;
}
-
+
return 0;
}
/* Driver for USB Mass Storage compliant devices
*
- * $Id: protocol.c,v 1.2 2000/07/19 17:21:39 groovyjava Exp $
+ * $Id: protocol.c,v 1.4 2000/08/01 22:01:19 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
#include "scsiglue.h"
#include "transport.h"
+/***********************************************************************
+ * Helper routines
+ ***********************************************************************/
+
+/* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+ */
+void fix_inquiry_data(Scsi_Cmnd *srb)
+{
+ unsigned char *data_ptr;
+
+ /* verify that it's an INQUIRY command */
+ if (srb->cmnd[0] != INQUIRY)
+ return;
+
+ US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2\n");
+
+ /* find the location of the data */
+ if (srb->use_sg) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *) srb->request_buffer;
+ data_ptr = (unsigned char *) sg[0].address;
+ } else
+ data_ptr = (unsigned char *)srb->request_buffer;
+
+ /* Change the SCSI revision number */
+ data_ptr[2] |= 0x2;
+}
+
/***********************************************************************
* Protocol routines
***********************************************************************/
+void usb_stor_qic157_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ /* Pad the ATAPI command with zeros
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+ for (; srb->cmd_len<12; srb->cmd_len++)
+ srb->cmnd[srb->cmd_len] = 0;
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* send the command to the transport layer */
+ usb_stor_invoke_transport(srb, us);
+
+ /* fix the INQUIRY data if necessary */
+ fix_inquiry_data(srb);
+}
+
void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
{
int old_cmnd = 0;
*/
/* Pad the ATAPI command with zeros */
-
for (; srb->cmd_len<12; srb->cmd_len++)
srb->cmnd[srb->cmd_len] = 0;
if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD))
usb_stor_scsiSense10to6(srb);
- /* Fix-up the return data from an INQUIRY command to show
- * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
- */
- if (srb->cmnd[0] == INQUIRY) {
- ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
- }
+ /* fix the INQUIRY data if necessary */
+ fix_inquiry_data(srb);
}
if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD))
usb_stor_scsiSense10to6(srb);
- /* Fix-up the return data from an INQUIRY command to show
- * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
- */
- if (srb->cmnd[0] == INQUIRY) {
- ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
- }
+ /* Fix the data for an INQUIRY, if necessary */
+ fix_inquiry_data(srb);
}
void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
- /* fix the results of an INQUIRY */
- if (srb->cmnd[0] == INQUIRY) {
- US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
- ((unsigned char*)us->srb->request_buffer)[2] |= 2;
- }
+ /* fix the INQUIRY data if necessary */
+ fix_inquiry_data(srb);
}
/* Driver for USB Mass Storage compliant devices
* Protocol Functions Header File
*
- * $Id: protocol.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: protocol.h,v 1.2 2000/08/01 22:01:19 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
#define US_SC_MAX US_SC_SCSI
extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*);
+extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*);
/* Driver for USB Mass Storage compliant devices
* SCSI layer glue code
*
- * $Id: scsiglue.c,v 1.6 2000/07/25 23:04:47 mdharm Exp $
+ * $Id: scsiglue.c,v 1.7 2000/07/28 20:33:18 gowdy Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Developed with the assistance of:
* (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ * (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov)
*
* Initial work by:
* (c) 1999 Michael Gee (michael@linuxspecific.com)
extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
unsigned int len, unsigned int *act_len);
-#define short_pack(b1,b2) ( ((u16)(b1)) | ( ((u16)(b2))<<8 ) )
+#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
#define LSB_of(s) ((s)&0xFF)
#define MSB_of(s) ((s)>>8)
static int sddr09_raw_bulk(struct us_data *us,
int direction,
unsigned char *data,
- unsigned short len) {
+ unsigned int len) {
int result;
int act_len;
*/
static int sddr09_bulk_transport(struct us_data *us,
- unsigned char *command,
- unsigned short command_len,
int direction,
unsigned char *data,
- unsigned short len,
+ unsigned int len,
int use_sg) {
int result = USB_STOR_TRANSPORT_GOOD;
int transferred = 0;
- unsigned char execute[8] = {
- 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
int i;
struct scatterlist *sg;
char string[64];
-/*
- if (command_len != 0) {
-
- // Fix up the command's data length
- command[6] = len&0xFF;
- command[7] = (len>>8)&0xFF;
-
- result = sddr09_send_control(us,
- execute,
- command,
- command_len);
-
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
- }
-*/
if (len==0)
return USB_STOR_TRANSPORT_GOOD;
- /* transfer the data payload for the command, if there is any */
-
-
- if (command_len != 0)
- direction = (command[0]&0x80) ? SCSI_DATA_READ :
- SCSI_DATA_WRITE;
+ /* transfer the data */
if (direction == SCSI_DATA_WRITE) {
/* Debug-print the first 48 bytes of the write transfer */
if (!use_sg) {
- string[0] = 0;
+ strcpy(string, "wr: ");
for (i=0; i<len && i<48; i++) {
sprintf(string+strlen(string), "%02X ",
data[i]);
if ((i%16)==15) {
US_DEBUGP("%s\n", string);
- string[0] = 0;
+ strcpy(string, "wr: ");
}
}
- if (string[0]!=0)
+ if ((i%16)!=0)
US_DEBUGP("%s\n", string);
}
}
}
}
+ if (direction == SCSI_DATA_READ) {
+
+ /* Debug-print the first 48 bytes of the read transfer */
+
+ if (!use_sg) {
+ strcpy(string, "rd: ");
+ for (i=0; i<len && i<48; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ data[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ strcpy(string, "rd: ");
+ }
+ }
+ if ((i%16)!=0)
+ US_DEBUGP("%s\n", string);
+ }
+ }
+
return result;
}
int result;
unsigned char command[12] = {
- 0xe8, 0x20, MSB_of(address>>16),
- LSB_of(address>>16), MSB_of(address&0xFFFF),
- LSB_of(address&0xFFFF), 0, 0, 0, 0,
- MSB_of(sectors), LSB_of(sectors)
+ 0xe8, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
+ struct sddr09_card_info *info = (struct sddr09_card_info *)us->extra;
+ unsigned int lba;
+ unsigned int pba;
+ unsigned short page;
+ unsigned short pages;
+ unsigned char *buffer = NULL;
+ unsigned char *ptr;
+ struct scatterlist *sg = NULL;
+ int i;
+ int len;
+ int transferred;
- result = sddr09_send_control(us,
- usb_sndctrlpipe(us->pusb_dev,0),
- 0,
- 0x41,
- 0,
- 0,
- command,
- 12);
+ // If we're using scatter-gather, we have to create a new
+ // buffer to read all of the data in first, since a
+ // scatter-gather buffer could in theory start in the middle
+ // of a page, which would be bad. A developer who wants a
+ // challenge might want to write a limited-buffer
+ // version of this code.
+
+ len = sectors*info->pagesize;
+
+ if (use_sg) {
+ sg = (struct scatterlist *)content;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (buffer == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
+ ptr = buffer;
+ } else
+ ptr = content;
+
+ // Figure out the initial LBA and page
+
+ pba = (address/info->pagesize)>>4;
+ lba = info->pba_to_lba[pba];
+ page = (address/info->pagesize)&0x0F;
+
+ // This could be made much more efficient by checking for
+ // contiguous LBA's. Another exercise left to the student.
+
+ while (sectors>0) {
+
+ pba = info->lba_to_pba[lba];
+
+ // Read as many sectors as possible in this block
+
+ pages = 0x10-page;
+ if (pages > sectors)
+ pages = sectors;
+
+ US_DEBUGP("Read %01X pages, from PBA %04X"
+ " (LBA %04X) page %01X\n",
+ pages, pba, lba, page);
+
+ address = ((pba<<4)+page)*info->pagesize;
+
+ // Unlike in the documentation, the address is in
+ // words of 2 bytes.
+
+ command[2] = MSB_of(address>>17);
+ command[3] = LSB_of(address>>17);
+ command[4] = MSB_of((address>>1)&0xFFFF);
+ command[5] = LSB_of((address>>1)&0xFFFF);
+
+ command[10] = MSB_of(pages);
+ command[11] = LSB_of(pages);
+
+ result = sddr09_send_control(us,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ 0,
+ 0x41,
+ 0,
+ 0,
+ command,
+ 12);
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
+ US_DEBUGP("Result for send_control in read_data %d\n",
+ result);
+
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (use_sg)
+ kfree(buffer);
+ return result;
+ }
- result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
- sectors*512, use_sg);
+ result = sddr09_bulk_transport(us,
+ SCSI_DATA_READ, ptr,
+ pages*info->pagesize, 0);
- return result;
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (use_sg)
+ kfree(buffer);
+ return result;
+ }
+
+ page = 0;
+ lba++;
+ sectors -= pages;
+ ptr += pages*info->pagesize;
+ }
+
+ if (use_sg) {
+ transferred = 0;
+ for (i=0; i<use_sg && transferred<len; i++) {
+ memcpy(sg[i].address, buffer+transferred,
+ len-transferred > sg[i].length ?
+ sg[i].length : len-transferred);
+ transferred += sg[i].length;
+ }
+ kfree(buffer);
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
}
int sddr09_read_control(struct us_data *us,
unsigned long address,
- unsigned short sectors,
+ unsigned short blocks,
unsigned char *content,
int use_sg) {
+ // Unlike in the documentation, the last two bytes are the
+ // number of blocks, not sectors.
+
int result;
unsigned char command[12] = {
0xe8, 0x21, MSB_of(address>>16),
LSB_of(address>>16), MSB_of(address&0xFFFF),
LSB_of(address&0xFFFF), 0, 0, 0, 0,
- MSB_of(sectors), LSB_of(sectors)
+ MSB_of(blocks), LSB_of(blocks)
};
+ US_DEBUGP("Read control address %08X blocks %04X\n",
+ address, blocks);
+
result = sddr09_send_control(us,
usb_sndctrlpipe(us->pusb_dev,0),
0,
0,
command,
12);
+
+ US_DEBUGP("Result for send_control in read_control %d\n",
+ result);
if (result != USB_STOR_TRANSPORT_GOOD)
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
- sectors*64, use_sg);
+ SCSI_DATA_READ, content,
+ blocks*0x40, use_sg);
+
+ US_DEBUGP("Result for bulk read in read_control %d\n",
+ result);
return result;
}
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
+ SCSI_DATA_READ, content,
64, 0);
*manufacturerID = content[0];
unsigned char command[12] = {
0xec, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- unsigned char content[2];
result = sddr09_send_control(us,
usb_sndctrlpipe(us->pusb_dev,0),
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, status,
+ SCSI_DATA_READ, status,
1, 0);
return result;
return result;
}
+unsigned long sddr09_get_capacity(struct us_data *us,
+ unsigned int *pagesize) {
+
+ unsigned char manufacturerID;
+ unsigned char deviceID;
+ int result;
+
+ US_DEBUGP("Reading capacity...\n");
+
+ result = sddr09_read_deviceID(us,
+ &manufacturerID,
+ &deviceID);
+
+ US_DEBUGP("Result of read_deviceID is %d\n",
+ result);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return 0;
+
+ US_DEBUGP("Device ID = %02X\n", deviceID);
+ US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
+
+ *pagesize = 512;
+
+ switch (deviceID) {
+
+ case 0x6e: // 1MB
+ case 0xe8:
+ case 0xec:
+ *pagesize = 256;
+ return 0x00100000;
+
+ case 0x5d: // 2MB
+ case 0xea:
+ case 0x64:
+ if (deviceID!=0x5D)
+ *pagesize = 256;
+ return 0x00200000;
+
+ case 0xe3: // 4MB
+ case 0xe5:
+ case 0x6b:
+ case 0xd5:
+ return 0x00400000;
+
+ case 0xe6: // 8MB
+ case 0xd6:
+ return 0x00800000;
+
+ case 0x73: // 16MB
+ return 0x01000000;
+
+ case 0x75: // 32MB
+ return 0x02000000;
+
+ default: // unknown
+ return 0;
+
+ }
+}
+
+int sddr09_read_map(struct us_data *us) {
+
+ unsigned char *control;
+ struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
+ int numblocks;
+ int i;
+ unsigned char *ptr;
+ unsigned short lba;
+ unsigned char parity;
+ unsigned char fast_parity[16] = {
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0
+ };
+ int result;
+
+ if (!info->capacity)
+ return -1;
+
+ /* read 64 (2^6) bytes for every block (8192 (2^13) bytes)
+ of capacity:
+ 64*(capacity/8192) = capacity*(2^6)*(2^-13) =
+ capacity*2^(6-13) = capacity*(2^-7)
+ */
+
+ control = kmalloc(info->capacity>>7, GFP_KERNEL);
+
+
+ numblocks = info->capacity>>13;
+
+ if ( (result = sddr09_read_control(us, 0, numblocks,
+ control, 0)) !=
+ USB_STOR_TRANSPORT_GOOD) {
+ kfree(control);
+ return -1;
+ }
+
+
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ kfree(info->pba_to_lba);
+ info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+ info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+ memset(info->lba_to_pba, 0, numblocks*sizeof(int));
+ memset(info->pba_to_lba, 0, numblocks*sizeof(int));
+
+ for (i=0; i<numblocks; i++) {
+ ptr = control+64*i;
+ if (ptr[0]!=0xFF || ptr[1]!=0xFF || ptr[2]!=0xFF ||
+ ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF)
+ continue;
+ if ((ptr[6]>>4)!=0x01)
+ continue;
+
+ /* ensure even parity */
+
+ lba = short_pack(ptr[7], ptr[6]);
+ parity = 1; // the parity of 0x1000
+ parity ^= fast_parity[lba & 0x000F];
+ parity ^= fast_parity[(lba>>4) & 0x000F];
+ parity ^= fast_parity[(lba>>8) & 0x000F];
+
+ if (parity) { /* bad parity bit */
+ US_DEBUGP("Bad parity in LBA for block %04X\n", i);
+ continue;
+ }
+
+ lba = (lba&0x07FF)>>1;
+
+ if (lba>=numblocks) {
+ US_DEBUGP("Bad LBA %04X for block %04X\n", lba, i);
+ continue;
+ }
+
+ if (i<0x10)
+ US_DEBUGP("LBA %04X <-> PBA %04X\n",
+ lba, i);
+
+ info->pba_to_lba[i] = lba;
+ info->lba_to_pba[lba] = i;
+ }
+
+ kfree(control);
+ return 0;
+}
+
/*
static int init_sddr09(struct us_data *us) {
}
*/
+void sddr09_card_info_destructor(void *extra) {
+ struct sddr09_card_info *info = (struct sddr09_card_info *)extra;
+
+ if (!extra)
+ return;
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ kfree(info->pba_to_lba);
+}
+
/*
* Transport for the Sandisk SDDR-09
*/
int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
- unsigned char send_scsi_command[8] = {
- 0x41, 0, 0, 0, 0, 0, 0, 0
- };
int i;
char string[64];
unsigned char inquiry_response[36] = {
'e', ' ', 'S', 'D', 'D', 'R', '0', '9',
' ', ' ', ' ', ' '
};
- unsigned char deviceID;
- unsigned char manufacturerID;
+ unsigned char mode_page_01[12] = {
+ 0x01, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
unsigned char *ptr;
+ unsigned long capacity;
+ unsigned int lba;
+ unsigned int pba;
+ unsigned int page;
+ unsigned short pages;
+ struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
/*
if (us->flags & US_FL_NEED_INIT) {
}
*/
+ if (!us->extra) {
+ us->extra = kmalloc(
+ sizeof(struct sddr09_card_info), GFP_KERNEL);
+ memset(us->extra, 0, sizeof(struct sddr09_card_info));
+ us->extra_destructor = sddr09_card_info_destructor;
+ }
+
ptr = (unsigned char *)srb->request_buffer;
/* Dummy up a response for INQUIRY since SDDR09 doesn't
respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
- memcpy(srb->request_buffer, inquiry_response, 36);
+ memcpy(ptr, inquiry_response, 36);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == READ_CAPACITY) {
- US_DEBUGP("Reading capacity...\n");
+ capacity = sddr09_get_capacity(us, &info->pagesize);
+ info->capacity = capacity;
- result = sddr09_read_deviceID(us,
- &manufacturerID,
- &deviceID);
+ // Last page in the card
- US_DEBUGP("Result of read_deviceID is %d\n",
- result);
+ capacity /= info->pagesize;
+ capacity--;
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
+ ptr[0] = MSB_of(capacity>>16);
+ ptr[1] = LSB_of(capacity>>16);
+ ptr[2] = MSB_of(capacity&0xFFFF);
+ ptr[3] = LSB_of(capacity&0xFFFF);
- US_DEBUGP("Device ID = %02X\n", deviceID);
- US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
-
- ptr[0] = 0;
- ptr[1] = 0;
- ptr[2] = 0;
- ptr[3] = 0;
-
- switch (deviceID) {
-
- case 0x6e: // 1MB
- case 0xe8:
- case 0xec:
- ptr[4] = 0;
- ptr[5] = 0x10;
- break;
-
- case 0xea: // 2MB
- case 0x64:
- case 0x5d:
- ptr[4] = 0;
- ptr[5] = 0x20;
- break;
-
- case 0xe3: // 4MB
- case 0xe5:
- case 0x6b:
- case 0xd5:
- ptr[4] = 0;
- ptr[5] = 0x40;
- break;
-
- case 0xe6: // 8MB
- case 0xd6:
- ptr[4] = 0;
- ptr[5] = 0x80;
- break;
-
- case 0x75: // 32MB
- ptr[4] = 0x02;
- ptr[5] = 0;
- break;
-
- default: // unknown
- ptr[4] = 0;
- ptr[5] = 0;
+ // The page size
- }
+ ptr[4] = MSB_of(info->pagesize>>16);
+ ptr[5] = LSB_of(info->pagesize>>16);
+ ptr[6] = MSB_of(info->pagesize&0xFFFF);
+ ptr[7] = LSB_of(info->pagesize&0xFFFF);
- ptr[6] = 0;
- ptr[7] = 0;
+ sddr09_read_map(us);
return USB_STOR_TRANSPORT_GOOD;
}
+ if (srb->cmnd[0] == MODE_SENSE) {
+
+ // Read-write error recovery page: there needs to
+ // be a check for write-protect here
+
+ if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
+ if (ptr==NULL || srb->request_bufflen<12)
+ return USB_STOR_TRANSPORT_ERROR;
+ memcpy(ptr, mode_page_01, 12);
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (srb->cmnd[0] == READ_10) {
+
+ page = short_pack(srb->cmnd[3], srb->cmnd[2]);
+ page <<= 16;
+ page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
+ pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
+
+ // convert page to block and page-within-block
+
+ lba = page>>4;
+ page = page&0x0F;
+
+ // locate physical block corresponding to logical block
+
+ if (lba>=(info->capacity>>13)) {
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ pba = info->lba_to_pba[lba];
+
+ // if pba is 0, either it's really 0, in which case
+ // the pba-to-lba map for pba 0 will be the lba,
+ // or that lba doesn't exist.
+
+ if (pba==0 && info->pba_to_lba[0] != lba) {
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X"
+ " pages %d\n",
+ pba, lba, page, pages);
+
+ return sddr09_read_data(us,
+ ((pba<<4)+page)*info->pagesize, pages,
+ ptr, srb->use_sg);
+ }
+
+ // Pass TEST_UNIT_READY and REQUEST_SENSE through
+
+ if (srb->cmnd[0] != TEST_UNIT_READY &&
+ srb->cmnd[0] != REQUEST_SENSE)
+ return USB_STOR_TRANSPORT_ERROR; // FIXME: sense buffer?
+
for (; srb->cmd_len<12; srb->cmd_len++)
srb->cmnd[srb->cmd_len] = 0;
srb->request_bufflen);
result = sddr09_bulk_transport(us,
- NULL, 0, srb->sc_data_direction,
+ srb->sc_data_direction,
srb->request_buffer,
srb->request_bufflen, srb->use_sg);
extern int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us);
+struct sddr09_card_info {
+ unsigned long capacity; /* Size of card in bytes */
+ int pagesize; /* Size of page in bytes */
+ int *lba_to_pba; /* logical to physical map */
+ int *pba_to_lba; /* physical to logical map */
+};
+
#endif
/* Driver for USB Mass Storage compliant devices
*
- * $Id: transport.c,v 1.4 2000/07/25 23:04:47 mdharm Exp $
+ * $Id: transport.c,v 1.5 2000/07/28 22:40:20 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
return 0;
}
+int usb_stor_Bulk_reset(struct us_data *us);
+
int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
{
struct bulk_cb_wrap bcb;
}
/* check bulk status */
- US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
+ US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n",
le32_to_cpu(bcs.Signature), bcs.Tag,
bcs.Residue, bcs.Status);
if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) ||
case US_BULK_STAT_PHASE:
/* phase error */
+ usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_ERROR;
}
return 0;
}
-/* FIXME: Does this work? */
+/* This issues a Bulk-only Reset to the device in question, including
+ * clearing the subsequent endpoint halts that may occur.
+ */
int usb_stor_Bulk_reset(struct us_data *us)
{
int result;
+ US_DEBUGP("Bulk reset requested\n");
+
result = usb_control_msg(us->pusb_dev,
usb_sndctrlpipe(us->pusb_dev,0),
US_BULK_RESET_REQUEST,
/* Driver for USB Mass Storage compliant devices
*
- * $Id: usb.c,v 1.14 2000/07/27 14:42:43 groovyjava Exp $
+ * $Id: usb.c,v 1.16 2000/08/01 22:01:19 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
case US_SC_QIC:
ss->protocol_name = "QIC-157";
- US_DEBUGP("Sorry, device not supported. Please\n");
- US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n");
- US_DEBUGP("if you see this message.\n");
- up(&us_list_semaphore);
- kfree(ss->current_urb);
- kfree(ss);
- return NULL;
+ ss->proto_handler = usb_stor_qic157_command;
break;
case US_SC_8070:
/* Now that scsi_unregister_module is done with the host
* template, we can free the us_data structure (the host
* template is inline in this structure). */
+
+ /* If there's extra data in the us_data structure then
+ * free that first */
+
+ if (us_list->extra) {
+ if (us_list->extra_destructor)
+ (*us_list->extra_destructor)(
+ us_list->extra);
+ kfree(us_list->extra);
+ }
kfree (us_list);
/* advance the list pointer */
/* Driver for USB Mass Storage compliant devices
* Main Header File
*
- * $Id: usb.h,v 1.3 2000/07/20 01:14:56 mdharm Exp $
+ * $Id: usb.h,v 1.4 2000/07/28 20:14:49 groovyjava Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
/* mutual exclusion structures */
struct semaphore notify; /* thread begin/end */
struct semaphore queue_exclusion; /* to protect data structs */
+ void *extra; /* Any extra data */
+ void (*extra_destructor)(void *); /* extra data destructor */
};
/* The list of structures and the protective lock for them */
/*
* 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)
+ * (c) 1999-2000 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 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.232 2000/06/11 13:18:30 acher Exp $
+ * $Id: usb-uhci.c,v 1.236 2000/08/02 20:28:28 acher Exp $
*/
#include <linux/config.h>
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
-#define VERSTR "$Revision: 1.232 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.236 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
#include "usb-uhci.h"
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
- int info, len;
+ int info, len, last;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
urb_priv_t *upriv, *bpriv;
data += pktsze;
len -= pktsze;
- if (!len)
+ last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || (urb->transfer_flags & USB_DISABLE_SPD)));
+
+ if (last)
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
- } while (len > 0);
+ } while (!last);
list_add (&qh->desc_list, &urb_priv->desc_list);
spin_lock_irqsave (&s->urb_list_lock, flags);
+ if (!in_interrupt()) // shouldn't be called from interrupt at all...
+ spin_lock(&urb->lock);
+
if (urb->status == -EINPROGRESS) {
// URB probably still in work
dequeue_urb (s, urb);
uhci_switch_timer_int(s);
s->unlink_urb_done=1;
+
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
+
spin_unlock_irqrestore (&s->urb_list_lock, flags);
urb->status = -ENOENT; // mark urb as killed
urb->complete ((struct urb *) urb);
}
usb_dec_dev_use (urb->dev);
- return 0;
}
- else
+ else {
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ }
+
return 0;
}
/*-------------------------------------------------------------------*/
break;
}
((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ return -EINPROGRESS; // completion will follow
}
- return -EINPROGRESS;
+ return 0; // URB already dead
}
/*-------------------------------------------------------------------*/
_static int uhci_unlink_urb (urb_t *urb)
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
int ret;
- spin_lock_irqsave (&s->urb_list_lock, flags);
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ // The URB needs to be locked if called outside completion context
+
+ if (!in_interrupt())
+ spin_lock(&urb->lock);
+
ret = uhci_unlink_urb_async(s, urb);
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
+
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
return ret;
}
else
#ifdef ISO_SANITY_CHECK
if(urb->iso_frame_desc[n].length > maxsze) {
+
err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
tdm[n] = 0;
- ret=-EINVAL;
- goto inval;
+ ret=-EINVAL;
}
else
#endif
usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
}
- if (status != 0) { // if any error occured stop processing of further TDs
+ if (status && (status != -EPIPE)) { // if any error occurred stop processing of further TDs
// only set ret if status returned an error
- if (status != -EPIPE)
- uhci_show_td (desc);
+ is_error:
ret = status;
urb->error_count++;
break;
data_toggle = uhci_toggle (desc->hw.td.info);
break;
}
+ else if (status)
+ goto is_error;
data_toggle = uhci_toggle (desc->hw.td.info);
queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
is_ring = 1;
}
+ spin_lock(&urb->lock);
spin_unlock(&s->urb_list_lock);
- // In case you need the current URB status for your completion handler
- if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
+ // In case you need the current URB status for your completion handler (before resubmit)
+ if (urb->complete && (!proceed )) {
dbg("process_transfer: calling early completion");
urb->complete ((struct urb *) urb);
if (!proceed && is_ring && (urb->status != -ENOENT))
}
while (tmp != NULL && tmp != urb->next); // submit until we reach NULL or our own pointer or submit fails
- if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) {
+ if (urb->complete) {
dbg("process_transfer: calling completion");
urb->complete ((struct urb *) urb);
}
}
- spin_lock(&s->urb_list_lock);
-
usb_dec_dev_use (urb->dev);
+ spin_unlock(&urb->lock);
+ spin_lock(&s->urb_list_lock);
}
}
break;
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
- if(dev->vendor==0x8086) {
- info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
- }
return alloc_uhci(dev, dev->irq, io_addr, io_size);
}
void usb_free_dev(struct usb_device *dev)
{
if (atomic_dec_and_test(&dev->refcnt)) {
- usb_destroy_configuration(dev);
dev->bus->op->deallocate(dev);
+ usb_destroy_configuration(dev);
kfree(dev);
}
}
if (urb && urb->dev)
return urb->dev->bus->op->submit_urb(urb);
else
- return -1;
+ return -ENODEV;
}
/*-------------------------------------------------------------------*/
if (urb && urb->dev)
return urb->dev->bus->op->unlink_urb(urb);
else
- return -1;
+ return -ENODEV;
}
/*-------------------------------------------------------------------*
* COMPLETION HANDLERS *
}
kfree(dev->config);
}
-
-void usb_init_root_hub(struct usb_device *dev)
-{
- dev->devnum = -1;
- dev->slow = 0;
- dev->actconfig = NULL;
-}
/* for returning string descriptors in UTF-16LE */
static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
usb_disconnect(child);
}
- /* remove /proc/bus/usb entry */
- usbdevfs_remove_device(dev);
-
- /* Free up the device itself, including its device number */
- if (dev->devnum > 0)
+ /* Free the device number and remove the /proc/bus/usb entry */
+ if (dev->devnum > 0) {
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ usbdevfs_remove_device(dev);
+ }
+ /* Free up the device itself */
usb_free_dev(dev);
}
if (result < 0)
return result;
- if (status & 1)
+ if (le16_to_cpu(status) & 1)
return -EPIPE; /* still halted */
usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
}
if (!cp) {
warn("selecting invalid configuration %d", configuration);
- return -1;
+ return -EINVAL;
}
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
warn("too many configurations");
- return -1;
+ return -EINVAL;
}
if (dev->descriptor.bNumConfigurations < 1) {
warn("not enough configurations");
- return -1;
+ return -EINVAL;
}
dev->config = (struct usb_config_descriptor *)
sizeof(struct usb_config_descriptor), GFP_KERNEL);
if (!dev->config) {
err("out of memory");
- return -1;
+ return -ENOMEM;
}
memset(dev->config, 0, dev->descriptor.bNumConfigurations *
sizeof(struct usb_config_descriptor));
dev->descriptor.bNumConfigurations, GFP_KERNEL);
if (!dev->rawdescriptors) {
err("out of memory");
- return -1;
+ return -ENOMEM;
}
for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
if (result < 8) {
if (result < 0)
err("unable to get descriptor");
- else
+ else {
err("config descriptor too short (expected %i, got %i)", 8, result);
+ result = -EINVAL;
+ }
goto err;
}
if (result < length) {
err("config descriptor too short (expected %i, got %i)", length, result);
+ result = -EINVAL;
kfree(bigbuffer);
goto err;
}
if (result > 0)
dbg("descriptor data left");
else if (result < 0) {
- result = -1;
+ result = -EINVAL;
goto err;
}
}
return 1;
}
- dev->actconfig = dev->config;
- usb_set_maxpacket(dev);
-
/* we set the default configuration here */
err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
if (err) {
err("failed to set default configuration (error=%d)", err);
- return -1;
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
}
dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
EXPORT_SYMBOL(usb_interface_claimed);
EXPORT_SYMBOL(usb_driver_release_interface);
-EXPORT_SYMBOL(usb_init_root_hub);
EXPORT_SYMBOL(usb_root_hub_string);
EXPORT_SYMBOL(usb_new_device);
EXPORT_SYMBOL(usb_reset_device);
spin_unlock(&dcache_lock);
return -ENOTEMPTY;
}
- list_del(&dentry->d_hash);
+ list_del_init(&dentry->d_hash);
spin_unlock(&dcache_lock);
dput(ino->dentry);
__remove_from_lru_list(bh, bh->b_list);
}
-static void insert_into_queues(struct buffer_head *bh)
+static void __insert_into_queues(struct buffer_head *bh)
{
struct buffer_head **head = &hash(bh->b_dev, bh->b_blocknr);
- spin_lock(&lru_list_lock);
- write_lock(&hash_table_lock);
__hash_link(bh, head);
__insert_into_lru_list(bh, bh->b_list);
- write_unlock(&hash_table_lock);
- spin_unlock(&lru_list_lock);
}
/* This function must only run if there are no other
* will force it bad). This shouldn't really happen currently, but
* the code is ready.
*/
-struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
+static inline struct buffer_head * __get_hash_table(kdev_t dev, int block, int size)
{
- struct buffer_head **head = &hash(dev, block);
- struct buffer_head *bh;
+ struct buffer_head *bh = hash(dev, block);
- read_lock(&hash_table_lock);
- for(bh = *head; bh; bh = bh->b_next)
+ for (; bh; bh = bh->b_next)
if (bh->b_blocknr == block &&
bh->b_size == size &&
bh->b_dev == dev)
break;
if (bh)
atomic_inc(&bh->b_count);
+
+ return bh;
+}
+
+struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
+{
+ struct buffer_head *bh;
+
+ read_lock(&hash_table_lock);
+ bh = __get_hash_table(dev, block, size);
read_unlock(&hash_table_lock);
return bh;
int isize;
repeat:
- bh = get_hash_table(dev, block, size);
+ spin_lock(&lru_list_lock);
+ write_lock(&hash_table_lock);
+ bh = __get_hash_table(dev, block, size);
if (bh)
goto out;
bh->b_state = 1 << BH_Mapped;
/* Insert the buffer into the regular lists */
- insert_into_queues(bh);
+ __insert_into_queues(bh);
out:
- touch_buffer(bh);
+ write_unlock(&hash_table_lock);
+ spin_unlock(&lru_list_lock);
return bh;
}
* If we block while refilling the free list, somebody may
* create the buffer first ... search the hashes again.
*/
+ write_unlock(&hash_table_lock);
+ spin_unlock(&lru_list_lock);
refill_freelist(size);
goto repeat;
}
*
* This all is required so that we can free up memory
* later.
+ *
+ * Wait:
+ * 0 - no wait (this does not get called - see try_to_free_buffers below)
+ * 1 - start IO for dirty buffers
+ * 2 - wait for completion of locked buffers
*/
static void sync_page_buffers(struct buffer_head *bh, int wait)
{
struct buffer_head *p = tmp;
tmp = tmp->b_this_page;
if (buffer_locked(p)) {
- if (wait)
+ if (wait > 1)
__wait_on_buffer(p);
} else if (buffer_dirty(p))
ll_rw_block(WRITE, 1, &p);
/* Uhhuh, start writeback so that we don't end up with all dirty pages */
spin_unlock(&free_list[index].lock);
write_unlock(&hash_table_lock);
- spin_unlock(&lru_list_lock);
- sync_page_buffers(bh, wait);
+ spin_unlock(&lru_list_lock);
+ if (wait)
+ sync_page_buffers(bh, wait);
return 0;
}
struct inode *inode = dentry->d_inode;
if (inode) {
dentry->d_inode = NULL;
- list_del(&dentry->d_alias);
- INIT_LIST_HEAD(&dentry->d_alias);
+ list_del_init(&dentry->d_alias);
spin_unlock(&dcache_lock);
if (dentry->d_op && dentry->d_op->d_iput)
dentry->d_op->d_iput(dentry, inode);
return;
unhash_it:
- list_del(&dentry->d_hash);
+ list_del_init(&dentry->d_hash);
kill_it: {
struct dentry *parent;
}
}
- list_del(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_hash);
+ list_del_init(&dentry->d_hash);
spin_unlock(&dcache_lock);
return 0;
}
{
struct dentry * parent;
- list_del(&dentry->d_hash);
+ list_del_init(&dentry->d_hash);
list_del(&dentry->d_child);
dentry_iput(dentry);
parent = dentry->d_parent;
if (tmp == &dentry_unused)
break;
dentry_stat.nr_unused--;
- list_del(tmp);
- INIT_LIST_HEAD(tmp);
+ list_del_init(tmp);
dentry = list_entry(tmp, struct dentry, d_lru);
/* Unused dentry with a count? */
}
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
- if (bprm->page[i]) {
+ struct page *page = bprm->page[i];
+ if (page) {
+ bprm->page[i] = NULL;
current->mm->rss++;
- put_dirty_page(current,bprm->page[i],stack_base);
+ put_dirty_page(current,page,stack_base);
}
stack_base += PAGE_SIZE;
}
if (bprm.file)
fput(bprm.file);
- /* Assumes that free_page() can take a NULL argument. */
- /* I hope this is ok for all architectures */
- for (i = 0 ; i < MAX_ARG_PAGES ; i++)
- if (bprm.page[i])
- __free_page(bprm.page[i]);
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ struct page * page = bprm.page[i];
+ if (page)
+ __free_page(page);
+ }
return retval;
}
inode->u.ext2_i.i_dtime = 0;
inode->u.ext2_i.i_block_group = i;
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
- inode->i_flags |= MS_SYNCHRONOUS;
+ inode->i_flags |= S_SYNC;
insert_inode_hash(inode);
inode->i_generation = event++;
mark_inode_dirty(inode);
* David S. Miller (davem@caip.rutgers.edu), 1995
* 64-bit file support on 64-bit platforms by Jakub Jelinek
* (jj@sunsite.ms.mff.cuni.cz)
+ *
+ * Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
*/
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/highuid.h>
-
-
static int ext2_update_inode(struct inode * inode, int do_sync);
/*
clear_inode(inode); /* We must guarantee clearing of inode... */
}
-/*
- * ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
- * superblock in the same manner as are ext2_free_blocks and
- * ext2_new_block. We just wait on the super rather than locking it
- * here, since ext2_new_block will do the necessary locking and we
- * can't block until then.
- */
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
- unsigned short total;
-
lock_kernel();
+ /* Writer: ->i_prealloc* */
if (inode->u.ext2_i.i_prealloc_count) {
- total = inode->u.ext2_i.i_prealloc_count;
+ unsigned short total = inode->u.ext2_i.i_prealloc_count;
+ unsigned long block = inode->u.ext2_i.i_prealloc_block;
inode->u.ext2_i.i_prealloc_count = 0;
- ext2_free_blocks (inode, inode->u.ext2_i.i_prealloc_block, total);
+ inode->u.ext2_i.i_prealloc_block = 0;
+ /* Writer: end */
+ ext2_free_blocks (inode, block, total);
}
unlock_kernel();
#endif
#endif
unsigned long result;
- wait_on_super (inode->i_sb);
#ifdef EXT2_PREALLOCATE
+ /* Writer: ->i_prealloc* */
if (inode->u.ext2_i.i_prealloc_count &&
(goal == inode->u.ext2_i.i_prealloc_block ||
goal + 1 == inode->u.ext2_i.i_prealloc_block))
{
result = inode->u.ext2_i.i_prealloc_block++;
inode->u.ext2_i.i_prealloc_count--;
+ /* Writer: end */
+#ifdef EXT2FS_DEBUG
ext2_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
-
+#endif
} else {
ext2_discard_prealloc (inode);
+#ifdef EXT2FS_DEBUG
ext2_debug ("preallocation miss (%lu/%lu).\n",
alloc_hits, ++alloc_attempts);
+#endif
if (S_ISREG(inode->i_mode))
result = ext2_new_block (inode, goal,
&inode->u.ext2_i.i_prealloc_count,
return p;
}
-static struct buffer_head * inode_getblk (struct inode * inode, int nr,
- int new_block, int * err, int metadata, long *phys, int *new)
+/**
+ * ext2_find_near - find a place for allocation with sufficient locality
+ * @inode: owner
+ * @ind: descriptor of indirect block.
+ *
+ * This function returns the prefered place for block allocation.
+ * It is used when heuristic for sequential allocation fails.
+ * Rules are:
+ * + if there is a block to the left of our position - allocate near it.
+ * + if pointer will live in indirect block - allocate near that block.
+ * + if pointer will live in inode - allocate in the same cylinder group.
+ * Caller must make sure that @ind is valid and will stay that way.
+ */
+
+static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
{
- u32 * p;
- int tmp, goal = 0;
- struct buffer_head * result;
- int blocksize = inode->i_sb->s_blocksize;
+ u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext2_i.i_data;
+ u32 *p;
- p = inode->u.ext2_i.i_data + nr;
-repeat:
- tmp = le32_to_cpu(*p);
- if (tmp) {
- if (metadata) {
- result = getblk (inode->i_dev, tmp, blocksize);
- if (tmp == le32_to_cpu(*p))
- return result;
- brelse (result);
- goto repeat;
- } else {
- *phys = tmp;
- return NULL;
- }
- }
+ /* Try to find previous block */
+ for (p = ind->p - 1; p >= start; p--)
+ if (*p)
+ return le32_to_cpu(*p);
- if (inode->u.ext2_i.i_next_alloc_block == new_block)
- goal = inode->u.ext2_i.i_next_alloc_goal;
+ /* No such thing, so let's try location of indirect block */
+ if (ind->bh)
+ return ind->bh->b_blocknr;
- ext2_debug ("hint = %d,", goal);
+ /*
+ * It is going to be refered from inode itself? OK, just put it into
+ * the same cylinder group then.
+ */
+ return (inode->u.ext2_i.i_block_group *
+ EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+ le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
+}
- if (!goal) {
- for (tmp = nr - 1; tmp >= 0; tmp--) {
- if (inode->u.ext2_i.i_data[tmp]) {
- goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]);
- break;
- }
- }
- if (!goal)
- goal = (inode->u.ext2_i.i_block_group *
- EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
- le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
- }
+/**
+ * ext2_find_goal - find a prefered place for allocation.
+ * @inode: owner
+ * @block: block we want
+ * @chain: chain of indirect blocks
+ * @partial: pointer to the last triple within a chain.
+ *
+ * This function returns the prefered place for block allocation.
+ */
- ext2_debug ("goal = %d.\n", goal);
-
- tmp = ext2_alloc_block (inode, goal, err);
- if (!tmp)
- return NULL;
-
- if (metadata) {
- result = getblk (inode->i_dev, tmp, blocksize);
- if (!buffer_uptodate(result))
- wait_on_buffer(result);
- memset(result->b_data, 0, blocksize);
- mark_buffer_uptodate(result, 1);
- mark_buffer_dirty(result, 1);
- if (*p) {
- ext2_free_blocks (inode, tmp, 1);
- bforget (result);
- goto repeat;
- }
- } else {
- if (*p) {
- /*
- * Nobody is allowed to change block allocation
- * state from under us:
- */
- ext2_error (inode->i_sb, "block_getblk",
- "data block filled under us");
- BUG();
- ext2_free_blocks (inode, tmp, 1);
- goto repeat;
- }
- *phys = tmp;
- result = NULL;
- *err = 0;
- *new = 1;
- }
- *p = cpu_to_le32(tmp);
+static inline unsigned long ext2_find_goal(struct inode *inode,
+ long block,
+ Indirect chain[4],
+ Indirect *partial)
+{
+ unsigned long goal = 0;
- inode->u.ext2_i.i_next_alloc_block = new_block;
- inode->u.ext2_i.i_next_alloc_goal = tmp;
- inode->i_ctime = CURRENT_TIME;
- inode->i_blocks += blocksize/512;
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
- ext2_sync_inode (inode);
- else
- mark_inode_dirty(inode);
- return result;
+ /* Writer: ->i_next_alloc* */
+ if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
+ inode->u.ext2_i.i_next_alloc_block++;
+ inode->u.ext2_i.i_next_alloc_goal++;
+ }
+ /* Writer: end */
+ /* Reader: pointers, ->i_next_alloc* */
+ if (verify_chain(chain, partial)) {
+ /*
+ * try the heuristic for sequential allocation,
+ * failing that at least try to get decent locality.
+ */
+ if (block == inode->u.ext2_i.i_next_alloc_block)
+ goal = inode->u.ext2_i.i_next_alloc_goal;
+ if (!goal)
+ goal = ext2_find_near(inode, partial);
+ }
+ /* Reader: end */
+ return goal;
}
-/*
- * metadata / data
- * possibly create / access
- * can fail due to: - not present
- * - out of space
+/**
+ * ext2_alloc_branch - allocate and set up a chain of blocks.
+ * @inode: owner
+ * @num: depth of the chain (number of blocks to allocate)
+ * @offsets: offsets (in the blocks) to store the pointers to next.
+ * @branch: place to store the chain in.
*
- * NULL return in the data case is mandatory.
+ * This function allocates @num blocks, zeroes out all but the last one,
+ * links them into chain and (if we are synchronous) writes them to disk.
+ * In other words, it prepares a branch that can be spliced onto the
+ * inode. It stores the information about that chain in the branch[], in
+ * the same format as ext2_get_branch() would do. We are calling it after
+ * we had read the existing part of chain and partial points to the last
+ * triple of that (one with zero ->key). Upon the exit we have the same
+ * picture as after the successful ext2_get_block(), excpet that in one
+ * place chain is disconnected - *branch->p is still zero (we did not
+ * set the last link), but branch->key contains the number that should
+ * be placed into *branch->p to fill that gap.
+ *
+ * If allocation fails we free all blocks we've allocated (and forget
+ * ther buffer_heads) and return the error value the from failed
+ * ext2_alloc_block() (normally -ENOSPC). Otherwise we set the chain
+ * as described above and return 0.
*/
-static struct buffer_head * block_getblk (struct inode * inode,
- struct buffer_head * bh, int nr,
- int new_block, int * err, int metadata, long *phys, int *new)
+
+static int ext2_alloc_branch(struct inode *inode,
+ int num,
+ unsigned long goal,
+ int *offsets,
+ Indirect *branch)
{
- int tmp, goal = 0;
- u32 * p;
- struct buffer_head * result;
int blocksize = inode->i_sb->s_blocksize;
+ int n = 0;
+ int err;
+ int i;
+ int parent = ext2_alloc_block(inode, goal, &err);
- result = NULL;
- if (!bh)
- goto out;
- if (!buffer_uptodate(bh)) {
- ll_rw_block (READ, 1, &bh);
- wait_on_buffer (bh);
+ branch[0].key = cpu_to_le32(parent);
+ if (parent) for (n = 1; n < num; n++) {
+ struct buffer_head *bh;
+ /* Allocate the next block */
+ int nr = ext2_alloc_block(inode, parent, &err);
+ if (!nr)
+ break;
+ branch[n].key = cpu_to_le32(nr);
+ /*
+ * Get buffer_head for parent block, zero it out and set
+ * the pointer to new one, then send parent to disk.
+ */
+ bh = getblk(inode->i_dev, parent, blocksize);
if (!buffer_uptodate(bh))
- goto out;
- }
- p = (u32 *) bh->b_data + nr;
-repeat:
- tmp = le32_to_cpu(*p);
- if (tmp) {
- if (metadata) {
- result = getblk (bh->b_dev, tmp, blocksize);
- if (tmp == le32_to_cpu(*p))
- goto out;
- brelse (result);
- goto repeat;
- } else {
- *phys = tmp;
- /* result == NULL */
- goto out;
+ wait_on_buffer(bh);
+ memset(bh->b_data, 0, blocksize);
+ branch[n].bh = bh;
+ branch[n].p = (u32*) bh->b_data + offsets[n];
+ *branch[n].p = branch[n].key;
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
}
+ parent = nr;
}
+ if (n == num)
+ return 0;
- if (inode->u.ext2_i.i_next_alloc_block == new_block)
- goal = inode->u.ext2_i.i_next_alloc_goal;
- if (!goal) {
- for (tmp = nr - 1; tmp >= 0; tmp--) {
- if (le32_to_cpu(((u32 *) bh->b_data)[tmp])) {
- goal = le32_to_cpu(((u32 *)bh->b_data)[tmp]);
- break;
- }
- }
- if (!goal)
- goal = bh->b_blocknr;
- }
- tmp = ext2_alloc_block (inode, goal, err);
- if (!tmp)
- goto out;
- if (metadata) {
- result = getblk (bh->b_dev, tmp, blocksize);
- if (!buffer_uptodate(result))
- wait_on_buffer(result);
- memset(result->b_data, 0, inode->i_sb->s_blocksize);
- mark_buffer_uptodate(result, 1);
- mark_buffer_dirty(result, 1);
- if (*p) {
- ext2_free_blocks (inode, tmp, 1);
- bforget (result);
- goto repeat;
- }
- } else {
- if (*p) {
- /*
- * Nobody is allowed to change block allocation
- * state from under us:
- */
- ext2_error (inode->i_sb, "block_getblk",
- "data block filled under us");
- BUG();
- ext2_free_blocks (inode, tmp, 1);
- goto repeat;
+ /* Allocation failed, free what we already allocated */
+ for (i = 1; i < n; i++)
+ bforget(branch[i].bh);
+ for (i = 0; i < n; i++)
+ ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1);
+ return err;
+}
+
+/**
+ * ext2_splice_branch - splice the allocated branch onto inode.
+ * @inode: owner
+ * @block: (logical) number of block we are adding
+ * @chain: chain of indirect blocks (with a missing link - see
+ * ext2_alloc_branch)
+ * @where: location of missing link
+ * @num: number of blocks we are adding
+ *
+ * This function verifies that chain (up to the missing link) had not
+ * changed, fills the missing link and does all housekeeping needed in
+ * inode (->i_blocks, etc.). In case of success we end up with the full
+ * chain to new block and return 0. Otherwise (== chain had been changed)
+ * we free the new blocks (forgetting their buffer_heads, indeed) and
+ * return -EAGAIN.
+ */
+
+static inline int ext2_splice_branch(struct inode *inode,
+ long block,
+ Indirect chain[4],
+ Indirect *where,
+ int num)
+{
+ int i;
+
+ /* Verify that place we are splicing to is still there and vacant */
+
+ /* Writer: pointers, ->i_next_alloc*, ->i_blocks */
+ if (!verify_chain(chain, where-1) || *where->p)
+ /* Writer: end */
+ goto changed;
+
+ /* That's it */
+
+ *where->p = where->key;
+ inode->u.ext2_i.i_next_alloc_block = block;
+ inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+ inode->i_blocks += num * inode->i_sb->s_blocksize/512;
+
+ /* Writer: end */
+
+ /* We are done with atomic stuff, now do the rest of housekeeping */
+
+ inode->i_ctime = CURRENT_TIME;
+
+ /* had we spliced it onto indirect block? */
+ if (where->bh) {
+ mark_buffer_dirty(where->bh, 1);
+ if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+ ll_rw_block (WRITE, 1, &where->bh);
+ wait_on_buffer(where->bh);
}
- *phys = tmp;
- *new = 1;
- }
- *p = le32_to_cpu(tmp);
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
}
- inode->i_ctime = CURRENT_TIME;
- inode->i_blocks += blocksize/512;
- mark_inode_dirty(inode);
- inode->u.ext2_i.i_next_alloc_block = new_block;
- inode->u.ext2_i.i_next_alloc_goal = tmp;
- *err = 0;
-out:
- brelse (bh);
- return result;
+
+ if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
+ ext2_sync_inode (inode);
+ else
+ mark_inode_dirty(inode);
+ return 0;
+
+changed:
+ for (i = 1; i < num; i++)
+ bforget(where[i].bh);
+ for (i = 0; i < num; i++)
+ ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1);
+ return -EAGAIN;
}
+/*
+ * Allocation strategy is simple: if we have to allocate something, we will
+ * have to go the whole way to leaf. So let's do it before attaching anything
+ * to tree, set linkage between the newborn blocks, write them if sync is
+ * required, recheck the path, free and repeat if check fails, otherwise
+ * set the last missing link (that will protect us from any truncate-generated
+ * removals - all blocks on the path are immune now) and possibly force the
+ * write on the parent block.
+ * That has a nice additional property: no special recovery from the failed
+ * allocations is needed - we simply release blocks and do not touch anything
+ * reachable from inode.
+ */
+
static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
{
- int ret, err, new;
- struct buffer_head *bh;
- unsigned long phys;
+ int err = -EIO;
int offsets[4];
- int *p;
Indirect chain[4];
Indirect *partial;
- int depth;
+ unsigned long goal;
+ int left;
+ int depth = ext2_block_to_path(inode, iblock, offsets);
- depth = ext2_block_to_path(inode, iblock, offsets);
if (depth == 0)
- goto abort;
+ goto out;
lock_kernel();
+reread:
partial = ext2_get_branch(inode, depth, offsets, chain, &err);
+ /* Simplest case - block found, no allocation needed */
if (!partial) {
- unlock_kernel();
- for (partial = chain + depth - 1; partial > chain; partial--)
- brelse(partial->bh);
+got_it:
bh_result->b_dev = inode->i_dev;
bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key);
bh_result->b_state |= (1UL << BH_Mapped);
- return 0;
+ /* Clean up and exit */
+ partial = chain+depth-1; /* the whole chain */
+ goto cleanup;
}
- while (partial > chain) {
- brelse(partial->bh);
- partial--;
- }
-
- if (!create) {
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+cleanup:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
unlock_kernel();
- return 0;
+out:
+ return err;
}
- err = -EIO;
- new = 0;
- ret = 0;
- bh = NULL;
-
/*
- * If this is a sequential block allocation, set the next_alloc_block
- * to this block now so that all the indblock and data block
- * allocations use the same goal zone
+ * Indirect block might be removed by truncate while we were
+ * reading it. Handling of that case (forget what we've got and
+ * reread) is taken out of the main path.
*/
+ if (err == -EAGAIN)
+ goto changed;
- ext2_debug ("block %lu, next %lu, goal %lu.\n", iblock,
- inode->u.ext2_i.i_next_alloc_block,
- inode->u.ext2_i.i_next_alloc_goal);
-
- if (iblock == inode->u.ext2_i.i_next_alloc_block + 1) {
- inode->u.ext2_i.i_next_alloc_block++;
- inode->u.ext2_i.i_next_alloc_goal++;
- }
+ goal = ext2_find_goal(inode, iblock, chain, partial);
+ if (!goal)
+ goto changed;
- err = 0;
+ left = (chain + depth) - partial;
+ err = ext2_alloc_branch(inode, left, goal,
+ offsets+(partial-chain), partial);
+ if (err)
+ goto cleanup;
- /*
- * ok, these macros clean the logic up a bit and make
- * it much more readable:
- */
-#define GET_INODE_DATABLOCK(x) \
- inode_getblk(inode, x, iblock, &err, 0, &phys, &new)
-#define GET_INODE_PTR(x) \
- inode_getblk(inode, x, iblock, &err, 1, NULL, NULL)
-#define GET_INDIRECT_DATABLOCK(x) \
- block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new);
-#define GET_INDIRECT_PTR(x) \
- block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL);
-
- p = offsets;
- if (depth == 1) {
- bh = GET_INODE_DATABLOCK(*p);
- goto out;
- }
- bh = GET_INODE_PTR(*p);
- switch (depth) {
- default: /* case 4: */
- bh = GET_INDIRECT_PTR(*++p);
- case 3:
- bh = GET_INDIRECT_PTR(*++p);
- case 2:
- bh = GET_INDIRECT_DATABLOCK(*++p);
- }
+ if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)
+ goto changed;
-#undef GET_INODE_DATABLOCK
-#undef GET_INODE_PTR
-#undef GET_INDIRECT_DATABLOCK
-#undef GET_INDIRECT_PTR
+ bh_result->b_state |= (1UL << BH_New);
+ goto got_it;
-out:
- if (bh)
- BUG(); // temporary debugging check
- if (err)
- goto abort;
- if (!phys)
- BUG(); // must not happen either
-
- bh_result->b_dev = inode->i_dev;
- bh_result->b_blocknr = phys;
- bh_result->b_state |= (1UL << BH_Mapped); /* safe */
- if (new)
- bh_result->b_state |= (1UL << BH_New);
- unlock_kernel();
-abort:
- return err;
+changed:
+ while (partial > chain) {
+ bforget(partial->bh);
+ partial--;
+ }
+ goto reread;
}
struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err)
inode->i_attr_flags = 0;
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
- inode->i_flags |= MS_SYNCHRONOUS;
+ inode->i_flags |= S_SYNC;
}
if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
inode->i_attr_flags |= ATTR_FLAG_APPEND;
}
if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
inode->i_attr_flags |= ATTR_FLAG_NOATIME;
- inode->i_flags |= MS_NOATIME;
+ inode->i_flags |= S_NOATIME;
}
return;
flags = iattr->ia_attr_flags;
if (flags & ATTR_FLAG_SYNCRONOUS) {
- inode->i_flags |= MS_SYNCHRONOUS;
+ inode->i_flags |= S_SYNC;
inode->u.ext2_i.i_flags |= EXT2_SYNC_FL;
} else {
- inode->i_flags &= ~MS_SYNCHRONOUS;
+ inode->i_flags &= ~S_SYNC;
inode->u.ext2_i.i_flags &= ~EXT2_SYNC_FL;
}
if (flags & ATTR_FLAG_NOATIME) {
- inode->i_flags |= MS_NOATIME;
+ inode->i_flags |= S_NOATIME;
inode->u.ext2_i.i_flags |= EXT2_NOATIME_FL;
} else {
- inode->i_flags &= ~MS_NOATIME;
+ inode->i_flags &= ~S_NOATIME;
inode->u.ext2_i.i_flags &= ~EXT2_NOATIME_FL;
}
if (flags & ATTR_FLAG_APPEND) {
inode->u.ext2_i.i_flags = flags;
if (flags & EXT2_SYNC_FL)
- inode->i_flags |= MS_SYNCHRONOUS;
+ inode->i_flags |= S_SYNC;
else
- inode->i_flags &= ~MS_SYNCHRONOUS;
+ inode->i_flags &= ~S_SYNC;
if (flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
else
else
inode->i_flags &= ~S_IMMUTABLE;
if (flags & EXT2_NOATIME_FL)
- inode->i_flags |= MS_NOATIME;
+ inode->i_flags |= S_NOATIME;
else
- inode->i_flags &= ~MS_NOATIME;
+ inode->i_flags &= ~S_NOATIME;
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
return 0;
if (error)
return error;
/* The following d_move() should become unconditional */
- if (!(old_dir->i_sb->s_flags & MS_ODD_RENAME)) {
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) {
d_move(old_dentry, new_dentry);
}
return 0;
if (srvaddr.sin_addr.s_addr == INADDR_ANY)
goto out_no_remote;
- sb->s_flags |= MS_ODD_RENAME; /* This should go away */
-
sb->s_magic = NFS_SUPER_MAGIC;
sb->s_op = &nfs_sops;
sb->s_blocksize_bits = 0;
/*
* File system information
*/
-static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, 0);
+static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, FS_ODD_RENAME);
extern int nfs_init_fhcache(void);
extern void nfs_destroy_fhcache(void);
memset(&tmp, 0, sizeof(tmp));
tmp.st_dev = kdev_t_to_nr(inode->i_dev);
tmp.st_ino = inode->i_ino;
+#ifdef STAT64_HAS_BROKEN_ST_INO
+ tmp.__st_ino = inode->i_ino;
+#endif
tmp.st_mode = inode->i_mode;
tmp.st_nlink = inode->i_nlink;
tmp.st_uid = inode->i_uid;
static void copy_entry(struct umsdos_dirent *p, struct umsdos_dirent *q)
{
p->name_len = q->name_len;
+ p->name[p->name_len]='\0';
p->flags = q->flags;
p->nlink = le16_to_cpu (q->nlink);
/* FIXME -- 32bit UID/GID issues */
struct umsdos_dirent *p;
int offs = *pos & ~PAGE_CACHE_MASK;
int recsize;
+ int ret = 0;
page = read_cache_page(mapping, *pos>>PAGE_CACHE_SHIFT,
(filler_t*)mapping->a_ops->readpage, NULL);
if (!Page_Uptodate(page))
goto async_fail;
p = (struct umsdos_dirent*)((char*)kmap(page)+offs);
+
+ /* if this is an invalid entry (invalid name length), ignore it */
+ if( p->name_len > UMSDOS_MAXNAME )
+ {
+ printk (KERN_WARNING "Ignoring invalid EMD entry with size %d\n", entry->name_len);
+ p->name_len = 0;
+ ret = -ENAMETOOLONG; /* notify umssync(8) code that something is wrong */
+ }
+
recsize = umsdos_evalrecsize(p->name_len);
if (offs + recsize > PAGE_CACHE_SIZE) {
struct page *page2;
kunmap(page);
page_cache_release(page);
*pos += recsize;
- return 0;
+ return ret;
async_fail:
page_cache_release(page);
page = ERR_PTR(-EIO);
*
* Note: the caller must hold a lock on the parent directory.
*/
-static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
+int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
int free_entry)
{
struct inode *dir = parent->d_inode;
goto out_unlock;
} else {
ret = mapping->a_ops->prepare_write(NULL,page,offs,
- info->recsize);
+ offs + info->recsize);
if (ret)
goto out_unlock;
p->name_len = entry->name_len;
p->mode = cpu_to_le16(entry->mode);
memcpy(p->spare,entry->spare,((char*)p+info->recsize)-p->spare);
ret = mapping->a_ops->commit_write(NULL,page,offs,
- info->recsize);
+ offs + info->recsize);
if (ret)
goto out_unlock;
}
if (page) {
kunmap(page);
page_cache_release(page);
+ page = NULL;
}
if (pos >= emd_dir->i_size) {
info->f_pos = empty.posok;
,atomic_read(&inode->i_count)));
if (inode == pseudo_root) {
- printk (KERN_ERR "Umsdos: Oops releasing pseudo_root."
- " Notify jacques@solucorp.qc.ca\n");
+ printk (KERN_ERR "Umsdos: debug: releasing pseudo_root - ino=%lu count=%d\n", inode->i_ino, atomic_read(&inode->i_count));
}
if (atomic_read(&inode->i_count) == 1)
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
- ret = -EPERM;
- if (!demd->d_inode) {
- printk(KERN_WARNING
+ ret = 0;
+ /* don't do anything if directory is not promoted to umsdos yet */
+ if (!demd->d_inode) {
+ Printk((KERN_DEBUG
"UMSDOS_notify_change: no EMD file %s/%s\n",
- demd->d_parent->d_name.name, demd->d_name.name);
+ demd->d_parent->d_name.name, demd->d_name.name));
goto out_dput;
}
- ret = 0;
/* don't do anything if this is the EMD itself */
if (inode == demd->d_inode)
goto out_dput;
put_inode: UMSDOS_put_inode,
delete_inode: fat_delete_inode,
put_super: UMSDOS_put_super,
- statfs: fat_statfs,
+ statfs: UMSDOS_statfs,
clear_inode: fat_clear_inode,
};
+int UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+{
+ int ret;
+ ret = fat_statfs (sb, buf);
+ if (!ret)
+ buf->f_namelen = UMSDOS_MAXNAME;
+ return ret;
+}
+
/*
* Read the super block of an Extended MS-DOS FS.
*/
if (!res)
goto out_fail;
- printk (KERN_INFO "UMSDOS 0.86 "
+ printk (KERN_INFO "UMSDOS 0.86i "
"(compatibility level %d.%d, fast msdos)\n",
UMSDOS_VERSION, UMSDOS_RELEASE);
struct umsdos_info info;
ret = umsdos_emd_dir_readentry (demd, &pos, &entry);
+
+ if (ret == -ENAMETOOLONG) {
+ printk (KERN_INFO "Fixing EMD entry with invalid size -- zeroing out\n");
+ memset (&info, 0, sizeof (info));
+ info.f_pos = f_pos;
+ info.recsize = UMSDOS_REC_SIZE;
+ ret = umsdos_writeentry (dentry, &info, 1);
+ continue;
+ }
+
if (ret)
break;
if (entry.name_len <= 0)
new_info->entry.nlink = old_info->entry.nlink;
}
-#ifdef OBSOLETE
-#define chkstk() \
-if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
- printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
- , current->comm,STACK_MAGIC \
- ,*(unsigned long *)current->kernel_stack_page \
- ,__LINE__); \
-}
-
-#undef chkstk
-#define chkstk() do { } while (0);
-#endif
-
/*
* Rename a file (move) in the file system.
*/
unsigned long tmp;
#if defined(__alpha_cix__) || defined(__alpha_fix__)
- __asm__ ("ftoit $f0,%0\n\t"
+ __asm__ __volatile__ ("ftoit $f0,%0\n\t"
"itoft %1,$f0\n\t"
"mt_fpcr $f0\n\t"
"itoft %0,$f0"
unsigned short st_dev;
unsigned char __pad0[10];
- unsigned long st_ino;
+#define STAT64_HAS_BROKEN_ST_INO 1
+ unsigned long __st_ino;
unsigned int st_mode;
unsigned int st_nlink;
unsigned long st_ctime;
unsigned long __pad7; /* will be high 32 bits of ctime someday */
- unsigned long __unused1;
- unsigned long __unused2;
+ unsigned long long st_ino;
};
#endif
unsigned short st_dev;
unsigned char __pad0[10];
- unsigned long st_ino;
+#define STAT64_HAS_BROKEN_ST_INO 1
+ unsigned long __st_ino;
+
unsigned int st_mode;
unsigned int st_nlink;
unsigned long st_ctime;
unsigned long __pad7; /* will be high 32 bits of ctime someday */
- unsigned long __unused1;
- unsigned long __unused2;
+ unsigned long long st_ino;
};
#endif
#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
#define rmb() mb()
#define wmb() __asm__ __volatile__ ("": : :"memory")
-#define set_rmb(var, value) do { xchg(&var, value); } while (0)
-#define set_mb(var, value) set_rmb(var, value)
+#define set_mb(var, value) do { xchg(&var, value); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
/* interrupt control.. */
* Linus just yet. Grrr...
*/
#define set_mb(var, value) do { (var) = (value); mb(); } while (0)
-#define set_rmb(var, value) do { (var) = (value); mb(); } while (0)
#define set_wmb(var, value) do { (var) = (value); mb(); } while (0)
/*
* insane amounts of padding around dev_t's.
*/
struct stat64 {
+ unsigned char __pad0[6];
unsigned short st_dev;
- unsigned char __pad0[10];
- unsigned long st_ino;
+ unsigned long long st_ino;
+
unsigned int st_mode;
unsigned int st_nlink;
#define mb() asm volatile ("" : : :"memory")
#define rmb() asm volatile ("" : : :"memory")
#define wmb() asm volatile ("" : : :"memory")
-#define set_rmb(var, value) do { xchg(&var, value); } while (0)
-#define set_mb(var, value) set_rmb(var, value)
+#define set_mb(var, value) do { xchg(&var, value); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
#define set_mb(var, value) \
do { var = value; mb(); } while (0)
-#define set_rmb(var, value) \
-do { var = value; rmb(); } while (0)
-
#define set_wmb(var, value) \
do { var = value; wmb(); } while (0)
#define set_mb(var, value) \
do { var = value; mb(); } while (0)
-#define set_rmb(var, value) \
-do { var = value; rmb(); } while (0)
-
#define set_wmb(var, value) \
do { var = value; wmb(); } while (0)
*/
struct stat64 {
unsigned long long st_dev; /* Device. */
- unsigned short int __pad1;
- unsigned long st_ino; /* File serial number. */
+ unsigned long long st_ino; /* File serial number. */
unsigned int st_mode; /* File mode. */
unsigned int st_nlink; /* Link count. */
unsigned int st_uid; /* User ID of the file's owner. */
#define wmb() __asm__ __volatile__ ("eieio" : : : "memory")
#define set_mb(var, value) do { var = value; mb(); } while (0)
-#define set_rmb(var, value) do { var = value; rmb(); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
extern void xmon_irq(int, void *, struct pt_regs *);
*/
struct stat64 {
unsigned short st_dev;
- unsigned char __pad0[10];
+ unsigned char __pad0[6];
- unsigned long st_ino;
+ unsigned long long st_ino;
unsigned int st_mode;
unsigned int st_nlink;
#define wmb() eieio()
#define set_mb(var, value) do { var = value; mb(); } while (0)
-#define set_rmb(var, value) do { var = value; rmb(); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
/* interrupt control.. */
#define mb() __asm__ __volatile__ ("": : :"memory")
#define rmb() mb()
#define wmb() __asm__ __volatile__ ("": : :"memory")
-#define set_rmb(var, value) do { xchg(&var, value); } while (0)
-#define set_mb(var, value) set_rmb(var, value)
+#define set_mb(var, value) do { xchg(&var, value); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
/* Interrupt Control */
struct stat64 {
unsigned char __pad0[6];
unsigned short st_dev;
- unsigned char __pad1[4];
- unsigned int st_ino;
+ unsigned long long st_ino;
+
unsigned int st_mode;
unsigned int st_nlink;
#define rmb() mb()
#define wmb() mb()
#define set_mb(__var, __value) do { __var = __value; mb(); } while(0)
-#define set_rmb(__var, __value) set_mb(__var, __value)
#define set_wmb(__var, __value) set_mb(__var, __value)
#define nop() __asm__ __volatile__ ("nop");
struct stat64 {
unsigned char __pad0[6];
unsigned short st_dev;
- unsigned char __pad1[4];
- unsigned int st_ino;
+ unsigned long long st_ino;
+
unsigned int st_mode;
unsigned int st_nlink;
#define wmb() membar("#StoreLoad | #StoreStore")
#define set_mb(__var, __value) \
do { __var = __value; membar("#StoreLoad | #StoreStore"); } while(0)
-#define set_rmb(__var, __value) \
- do { __var = __value; membar("#StoreLoad"); } while(0)
#define set_wmb(__var, __value) \
do { __var = __value; membar("#StoreStore"); } while(0)
*/
#define FS_NOMOUNT 16 /* Never mount from userland */
#define FS_LITTER 32 /* Keeps the tree in dcache */
+#define FS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon
+ * as nfs_rename() will be cleaned up
+ */
/*
* These are the fs-independent mount-flags: up to 16 flags are supported
*/
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
-#define S_QUOTA 128 /* Quota initialized for file/directory/symlink */
-#define S_APPEND 256 /* Append-only file */
-#define S_IMMUTABLE 512 /* Immutable file */
#define MS_NOATIME 1024 /* Do not update access times. */
#define MS_NODIRATIME 2048 /* Do not update directory access times */
-#define MS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon
- * as nfs_rename() will be cleaned up
- */
-#define S_DEAD (1<<16) /* removed, but still open directory */
-
/*
* Flags that can be altered by MS_REMOUNT
*/
#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+/* Inode flags - they have nothing to superblock flags now */
+
+#define S_SYNC 1 /* Writes are synced at once */
+#define S_NOATIME 2 /* Do not update access times */
+#define S_QUOTA 4 /* Quota initialized for file */
+#define S_APPEND 8 /* Append-only file */
+#define S_IMMUTABLE 16 /* Immutable file */
+#define S_DEAD 32 /* removed, but still open directory */
+
/*
* Note that nosuid etc flags are inode-specific: setting some file-system
* flags just means all the inodes inherit those flags by default. It might be
* i_flags updated. Hence, i_flags no longer inherit the superblock mount
* flags, so these have to be checked separately. -- rmk@arm.uk.linux.org
*/
-#define __IS_FLG(inode,flg) (((inode)->i_sb && (inode)->i_sb->s_flags & (flg)) \
- || (inode)->i_flags & (flg))
+#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
-#define IS_RDONLY(inode) (((inode)->i_sb) && ((inode)->i_sb->s_flags & MS_RDONLY))
+#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
#define IS_NOSUID(inode) __IS_FLG(inode, MS_NOSUID)
#define IS_NODEV(inode) __IS_FLG(inode, MS_NODEV)
#define IS_NOEXEC(inode) __IS_FLG(inode, MS_NOEXEC)
-#define IS_SYNC(inode) __IS_FLG(inode, MS_SYNCHRONOUS)
+#define IS_SYNC(inode) (__IS_FLG(inode, MS_SYNCHRONOUS) || ((inode)->i_flags & S_SYNC))
#define IS_MANDLOCK(inode) __IS_FLG(inode, MS_MANDLOCK)
#define IS_QUOTAINIT(inode) ((inode)->i_flags & S_QUOTA)
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
-#define IS_NOATIME(inode) __IS_FLG(inode, MS_NOATIME)
+#define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME))
#define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME)
#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
*/
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.n
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
#else
#define CHECK_EMERGENCY_SYNC
#endif
-
-extern int sysrq_enabled;
int umsdos_delentry (struct dentry *, struct umsdos_info *, int);
int umsdos_findentry (struct dentry *, struct umsdos_info *, int);
int umsdos_isempty (struct dentry *);
+int umsdos_writeentry (struct dentry *, struct umsdos_info *, int);
/* file.c 25/01/95 02.25.38 */
int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
int umsdos_notify_change_locked(struct dentry *, struct iattr *attr);
void UMSDOS_put_inode (struct inode *);
-int UMSDOS_statfs (struct super_block *, struct statfs *, int);
+int UMSDOS_statfs (struct super_block *, struct statfs *);
struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
void UMSDOS_put_super (struct super_block *);
extern 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);
-extern void usb_init_root_hub(struct usb_device *dev);
extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len);
extern void usb_connect(struct usb_device *dev);
extern void usb_disconnect(struct usb_device **);
}
/* Drop the "current user" thing */
- free_uid(current);
+ {
+ struct user_struct *user = current->user;
+ current->user = INIT_USER;
+ atomic_inc(¤t->user->__count);
+ free_uid(user);
+ }
/* Give kmod all effective privileges.. */
current->uid = current->euid = current->fsuid = 0;
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
-
extern void timer_bh(void);
extern void tqueue_bh(void);
extern void immediate_bh(void);
void tasklet_kill(struct tasklet_struct *t)
{
+ if (in_interrupt())
+ printk("Attempt to kill tasklet from interrupt\n");
+
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
- if (in_interrupt())
- panic("Attempt to kill tasklet from interrupt\n");
- schedule();
+ current->state = TASK_RUNNING;
+ do {
+ current->policy |= SCHED_YIELD;
+ schedule();
+ } while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
extern int sysctl_overcommit_memory;
extern int max_threads;
extern int nr_queued_signals, max_queued_signals;
+extern int sysrq_enabled;
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
static int maxolduid = 65535;
* of zone - it's old.
*/
if (page->buffers) {
- int wait = ((gfp_mask & __GFP_IO) && (nr_dirty-- < 0));
+ int wait;
+ /*
+ * 0 - free it if can do so without IO
+ * 1 - start write-out of dirty buffers
+ * 2 - wait for locked buffers
+ */
+ wait = (gfp_mask & __GFP_IO) ? (nr_dirty-- < 0) ? 2 : 1 : 0;
if (!try_to_free_buffers(page, wait))
goto unlock_continue;
/* page was locked, inode can't go away under us */
copy_page((void *)vaddr, (void *)page_address(page));
kunmap(highpage);
- /* Preserve the caching of the swap_entry. */
- highpage->index = page->index;
- highpage->mapping = page->mapping;
+ if (page->mapping)
+ BUG();
/*
* We can just forget the old page since
zone_t *zone;
int i;
- sum = nr_lru_pages;
+ sum = nr_lru_pages / 3;
for (i = 0; i < NUMNODES; i++)
for (zone = NODE_DATA(i)->node_zones; zone <= NODE_DATA(i)->node_zones+ZONE_NORMAL; zone++)
sum += zone->free_pages;
*/
if (!mm)
return;
+ vmlist_access_lock(mm);
for (vma = mm->mmap; vma; vma = vma->vm_next) {
pgd_t * pgd = pgd_offset(mm, vma->vm_start);
unuse_vma(vma, pgd, entry, page);
}
+ vmlist_access_unlock(mm);
return;
}