- Oops. Moved back stallion.c to drivers/char. It's not a TV driver.
Never has been, and I don't see it ever really becoming one ;)
- mca.c: outp_b() should be outb_p(). Obviously nobody actually
_uses_ the MCA bus any more ;)
- umsdos should be ok again after the page_address() type-changes.
- re-enable asynchronous read-ahead code.
- Sun ESP driver update
- netfilter debug fixes
- IPv6 needs to register before proto_init()
- socket() error code fix (EAFNOSUPPORT instead of EINVAL)
- potential TCP socket leak fix
- don't self-deadlock on the kbd_controller_lock when probing for the mouse
- CONFIG_SMB_NLS_REMOTE didn't work. Silly typo.
- scheduler wakeup race condition could cause delayed scheduling on SMP..
- net/packet/af_packet.c: use the standard macros for marking page resevredness
- ncpfs buffer-overflow fix
- thread groups, take 1.
- USB storage driver update
@@ -2329,7+2329,7 @@ CONFIG_AGP_VIA AMD Irongate support
CONFIG_AGP_AMD
This option gives you AGP support for the GLX component of the
- XFree86 4.x on Intel AMD Irongate chipset.
+ XFree86 4.x on AMD Irongate chipset.
For the moment, you should probably say N, unless you want to test
the GLX component for XFree86 3.3.6, which can be downloaded from
--- /dev/null
+ Cache and TLB Flushing
+ Under Linux
+
+ David S. Miller <davem@redhat.com>
+
+This document describes the cache/tlb flushing interfaces called
+by the Linux VM subsystem. It enumerates over each interface,
+describes it's intended purpose, and what side effect is expected
+after the interface is invoked.
+
+The side effects described below are stated for a uniprocessor
+implementation, and what is to happen on that single processor. The
+SMP cases are a simple extension, in that you just extend the
+definition such that the side effect for a particular interface occurs
+on all processors in the system. Don't let this scare you into
+thinking SMP cache/tlb flushing must be so inefficient, this is in
+fact an area where many optimizations are possible. For example,
+if it can be proven that a user address space has never executed
+on a cpu (see vma->cpu_vm_mask), one need not perform a flush
+for this address space on that cpu.
+
+First, the TLB flushing interfaces, since they are the simplest. The
+"TLB" is abstracted under Linux as something the cpu uses to cache
+virtual-->physical address translations obtained from the software
+page tables. Meaning that if the software page tables change, it is
+possible for stale translations to exist in this "TLB" cache.
+Therefore when software page table changes occur, the kernel will
+invoke one of the following flush methods _after_ the page table
+changes occur:
+
+1) void flush_tlb_all(void)
+
+ The most severe flush of all. After this interface runs,
+ any previous page table modification whatsoever will be
+ visible to the cpu.
+
+ This is usually invoked when the kernel page tables are
+ changed, since such translations are "global" in nature.
+
+2) void flush_tlb_mm(struct mm_struct *mm)
+
+ This interface flushes an entire user address space from
+ the TLB. After running, this interface must make sure that
+ any previous page table modifications for the address space
+ 'mm' will be visible to the cpu. That is, after running,
+ there will be no entries in the TLB for 'mm'.
+
+ This interface is used to handle whole address space
+ page table operations such as what happens during
+ fork, exit, and exec.
+
+3) void flush_tlb_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+
+ Here we are flushing a specific range of (user) virtual
+ address translations from the TLB. After running, this
+ interface must make sure that any previous page table
+ modifications for the address space 'mm' in the range 'start'
+ to 'end' will be visible to the cpu. That is, after running,
+ there will be no entries in the TLB for 'mm' for virtual
+ addresses in the range 'start' to 'end'.
+
+ Primarily, this is used for munmap() type operations.
+
+ The interface is provided in hopes that the port can find
+ a suitably efficient method for removing multiple page
+ sized translations from the TLB, instead of having the kernel
+ call flush_tlb_page (see below) for each entry which may be
+ modified.
+
+4) void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+
+ This time we need to remove the PAGE_SIZE sized translation
+ from the TLB. The 'vma' is the backing structure used by
+ Linux to keep track of mmap'd regions for a process, the
+ address space is available via vma->vm_mm. Also, one may
+ test (vma->vm_flags & VM_EXEC) to see if this region is
+ executable (and thus could be in the 'instruction TLB' in
+ split-tlb type setups).
+
+ After running, this interface must make sure that any previous
+ page table modification for address space 'vma->vm_mm' for
+ user virtual address 'page' will be visible to the cpu. That
+ is, after running, there will be no entries in the TLB for
+ 'vma->vm_mm' for virtual address 'page'.
+
+ This is used primarily during fault processing.
+
+5) void flush_tlb_pgtables(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+
+ The software page tables for address space 'mm' for virtual
+ addresses in the range 'start' to 'end' are being torn down.
+
+ Some platforms cache the lowest level of the software page tables
+ in a linear virtually mapped array, to make TLB miss processing
+ more efficient. On such platforms, since the TLB is caching the
+ software page table structure, it needs to be flushed when parts
+ of the software page table tree are unlinked/freed.
+
+ Sparc64 is one example of a platform which does this.
+
+ Usually, when munmap()'ing an area of user virtual address
+ space, the kernel leaves the page table parts around and just
+ marks the individual pte's as invalid. However, if very large
+ portions of the address space are unmapped, the kernel frees up
+ those portions of the software page tables to prevent potential
+ excessive kernel memory usage caused by erratic mmap/mmunmap
+ sequences. It is at these times that flush_tlb_pgtables will
+ be invoked.
+
+6) void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long address, pte_t pte)
+
+ At the end of every page fault, this routine is invoked to
+ tell the architecture specific code that a translation
+ described by "pte" now exists at virtual address "address"
+ for address space "vma->vm_mm", in the software page tables.
+
+ A port may use this information in any way it so chooses.
+ For example, it could use this event to pre-load TLB
+ translations for software managed TLB configurations.
+ The sparc64 port currently does this.
+
+Next, we have the cache flushing interfaces. In general, when Linux
+is changing an existing virtual-->physical mapping to a new value,
+the sequence will be in one of the following forms:
+
+ 1) flush_cache_mm(mm);
+ change_all_page_tables_of(mm);
+ flush_tlb_mm(mm);
+
+ 2) flush_cache_range(mm, start, end);
+ change_range_of_page_tables(mm, start, end);
+ flush_tlb_range(mm, start, end);
+
+ 3) flush_cache_page(vma, page);
+ set_pte(pte_pointer, new_pte_val);
+ flush_tlb_page(vma, page);
+
+The cache level flush will always be first, because this allows
+us to properly handle systems whose caches are strict and require
+a virtual-->physical translation to exist for a virtual address
+when that virtual address is flushed from the cache. The HyperSparc
+cpu is one such cpu with this attribute.
+
+The cache flushing routines below need only deal with cache flushing
+to the extent that it is necessary for a particular cpu. Mostly,
+these routines must be implemented for cpus which have virtually
+indexed caches which must be flushed when virtual-->physical
+translations are changed or removed. So, for example, the physically
+indexed physically tagged caches of IA32 processors have no need to
+implement these interfaces since the caches are fully synchronized
+and have no dependency on translation information.
+
+Here are the routines, one by one:
+
+1) void flush_cache_all(void)
+
+ The most severe flush of all. After this interface runs,
+ the entire cpu cache is flushed.
+
+ This is usually invoked when the kernel page tables are
+ changed, since such translations are "global" in nature.
+
+2) void flush_cache_mm(struct mm_struct *mm)
+
+ This interface flushes an entire user address space from
+ the caches. That is, after running, there will be no cache
+ lines assosciated with 'mm'.
+
+ This interface is used to handle whole address space
+ page table operations such as what happens during
+ fork, exit, and exec.
+
+3) void flush_cache_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+
+ Here we are flushing a specific range of (user) virtual
+ addresses from the cache. After running, there will be no
+ entries in the cache for 'mm' for virtual addresses in the
+ range 'start' to 'end'.
+
+ Primarily, this is used for munmap() type operations.
+
+ The interface is provided in hopes that the port can find
+ a suitably efficient method for removing multiple page
+ sized regions from the cache, instead of having the kernel
+ call flush_cache_page (see below) for each entry which may be
+ modified.
+
+4) void flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+
+ This time we need to remove a PAGE_SIZE sized range
+ from the cache. The 'vma' is the backing structure used by
+ Linux to keep track of mmap'd regions for a process, the
+ address space is available via vma->vm_mm. Also, one may
+ test (vma->vm_flags & VM_EXEC) to see if this region is
+ executable (and thus could be in the 'instruction cache' in
+ "Harvard" type cache layouts).
+
+ After running, there will be no entries in the cache for
+ 'vma->vm_mm' for virtual address 'page'.
+
+ This is used primarily during fault processing.
+
+There exists another whole class of cpu cache issues which currently
+require a whole different set of interfaces to handle properly.
+The biggest problem is that of virtual aliasing in the data cache
+of a processor.
+
+Is your port subsceptible to virtual aliasing in it's D-cache?
+Well, if your D-cache is virtually indexed, is larger in size than
+PAGE_SIZE, and does not prevent multiple cache lines for the same
+physical address from existing at once, you have this problem.
+
+If your D-cache has this problem, first define asm/shmparam.h SHMLBA
+properly, it should essentially be the size of your virtually
+addressed D-cache (or if the size is variable, the largest possible
+size). This setting will force the SYSv IPC layer to only allow user
+processes to mmap shared memory at address which are a multiple of
+this value.
+
+Next, you have two methods to solve the D-cache aliasing issue for all
+other cases. Please keep in mind that fact that, for a given page
+mapped into some user address space, there is always at least one more
+mapping, that of the kernel in it's linear mapping starting at
+PAGE_OFFSET. So immediately, once the first user maps a given
+physical page into it's address space, by implication the D-cache
+aliasing problem has the potential to exist since the kernel already
+maps this page at it's virtual address.
+
+First, I describe the old method to deal with this problem. I am
+describing it for documentation purposes, but it is deprecated and the
+latter method I describe next should be used by all new ports and all
+existing ports should move over to the new mechanism as well.
+
+ flush_page_to_ram(struct page *page)
+
+ The physical page 'page' is about to be place into the
+ user address space of a process. If it is possible for
+ stores done recently by the kernel into this physical
+ page, to not be visible to an arbitray mapping in userspace,
+ you must flush this page from the D-cache.
+
+ If the D-cache is writeback in nature, the dirty data (if
+ any) for this physical page must be written back to main
+ memory before the cache lines are invalidated.
+
+Admittedly, the author did not think very much when designing this
+interface. It does not give the architecture enough information about
+what exactly is going on, and there is not context with which to base
+any judgment about whether an alias is possible at all. The new
+interfaces to deal with D-cache aliasing are meant to address this by
+telling the architecture specific code exactly which is going on at
+the proper points in time.
+
+Here is the new interface:
+
+ void copy_user_page(void *from, void *to, unsigned long address)
+ void clear_user_page(void *to, unsigned long address)
+
+ These two routines store data in user anonymous or COW
+ pages. It allows a port to efficiently avoid D-cache alias
+ issues between userspace and the kernel.
+
+ For example, a port may temporarily map 'from' and 'to' to
+ kernel virtual addresses during the copy. The virtual address
+ for these two pages is choosen in such a way that the kernel
+ load/store instructions happen to virtual addresses which are
+ of the same "color" as the user mapping of the page. Sparc64
+ for example, uses this technique.
+
+ The "address" parameter tells the virtual address where the
+ user has this page mapped.
+
+ If D-cache aliasing is not an issue, these two routines may
+ simply call memcpy/memset directly and do nothing more.
+
+ void flush_dcache_page(struct page *page)
+
+ Any time the kernel writes to a page cache page, _OR_
+ the kernel is about to read from a page cache page and
+ user space shared/writable mappings of this page potentially
+ exist, this routine is called.
+
+ The phrase "kernel writes to a page cache page" means,
+ specifically, that the kernel executes store instructions
+ that dirty data in that page at the page->virtual mapping
+ of that page. It is important to flush here to handle
+ D-cache aliasing, to make sure these kernel stores are
+ visible to user space mappings of that page.
+
+ The corollary case is just as important, if there are users
+ which have shared+writable mappings of this file, we must make
+ sure that kernel reads of these pages will see the most recent
+ stores done by the user.
+
+ If D-cache aliasing is not an issue, this routine may
+ simply be defined as a nop on that architecture.
+
+ TODO: If we set aside a few bits in page->flags as
+ "architecture private", these interfaces could
+ be implemented much more efficiently. This would
+ allow one to "defer" (perhaps indefinitely) the
+ actual flush if there are currently no user processes
+ mapping this page.
+
+ The idea is, first at flush_dcache_page() time, if
+ page->mapping->i_mmap is an empty list, just mark
+ one of the architecture private page flag bits.
+ Later, in update_mmu_cache(), a check could be made
+ of this flag bit, and if set the flush is done
+ and the flag bit is cleared.
+
+XXX Not documented: flush_icache_page(), need to talk to Paul
+ Mackerras, David Mosberger-Tang, et al.
+ to see what the expected semantics of this
+ interface are. -DaveM
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 0
-EXTRAVERSION = -test7
+EXTRAVERSION = -test8
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
@@ -671,14+671,7 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs) }
#ifdef CONFIG_SMP
- /*
- * update_process_times() expects us to have done irq_enter().
- * Besides, if we don't timer interrupts ignore the global
- * interrupt lock, which is the WrongThing (tm) to do.
- */
- irq_enter(cpu, 0);
update_process_times(user);
- irq_exit(cpu, 0);
#endif
}
@@ -706,17+699,26 @@ unsigned int apic_timer_irqs [NR_CPUS] = { 0, };
void smp_apic_timer_interrupt(struct pt_regs * regs)
{
+ int cpu = smp_processor_id();
+
/*
* the NMI deadlock-detector uses this.
*/
- apic_timer_irqs[smp_processor_id()]++;
+ apic_timer_irqs[cpu]++;
/*
* NOTE! We'd better ACK the irq immediately,
* because timer handling can be slow.
*/
ack_APIC_irq();
+ /*
+ * update_process_times() expects us to have done irq_enter().
+ * Besides, if we don't timer interrupts ignore the global
+ * interrupt lock, which is the WrongThing (tm) to do.
+ */
+ irq_enter(cpu, 0);
smp_local_timer_interrupt(regs);
+ irq_exit(cpu, 0);
}
/*
@@ -546,7+546,7 @@ unsigned char mca_read_pos(int slot, int reg) outb_p(0x7f, MCA_MOTHERBOARD_SETUP_REG);
byte = inb_p(MCA_POS_REG(reg));
- outp_b(0xff, MCA_MOTHERBOARD_SETUP_REG);
+ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
} else if(slot < MCA_MAX_SLOT_NR) {
/* Make sure motherboard setup is off */
@@ -158,11+158,9 @@ CONFIG_INET=y # CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
-# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
-# CONFIG_INET_ECN is not set
+CONFIG_INET_ECN=y
# CONFIG_SYN_COOKIES is not set
CONFIG_IPV6=m
# CONFIG_IPV6_EUI64 is not set
@@ -345,6+343,7 @@ CONFIG_IEEE1394_RAWIO=m CONFIG_NETDEVICES=y
CONFIG_DUMMY=m
CONFIG_BONDING=m
+CONFIG_TUN=m
CONFIG_PPP=m
# CONFIG_PPP_ASYNC is not set
# CONFIG_PPP_SYNC_TTY is not set
@@ -453,8+452,7 @@ CONFIG_NFSD_V3=y CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
-CONFIG_SMB_FS=m
-CONFIG_SMB_NLS_REMOTE=""
+# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
@@ -246,7+246,8 @@ static ssize_t nvram_read(struct file * file,
spin_unlock_irq (&rtc_lock);
- copy_to_user_ret (buf, contents, tmp - contents, -EFAULT);
+ if (copy_to_user (buf, contents, tmp - contents))
+ return -EFAULT;
*ppos = i;
@@ -264,10+265,9 @@ static ssize_t nvram_write(struct file * file, unsigned i = *ppos;
char * tmp;
- /* could comebody please help me indent this better? */
- copy_from_user_ret (contents, buf, (NVRAM_BYTES - i) < count ?
- (NVRAM_BYTES - i) : count,
- -EFAULT);
+ if (copy_from_user (contents, buf, (NVRAM_BYTES - i) < count ?
+ (NVRAM_BYTES - i) : count))
+ return -EFAULT;
spin_lock_irq (&rtc_lock);
@@ -62,6+62,7 @@ static void kbd_write_command_w(int data); static void kbd_write_output_w(int data);
#ifdef CONFIG_PSMOUSE
static void aux_write_ack(int val);
+static void __aux_write_ack(int val);
#endif
spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
@@ -404,7+405,7 @@ static inline void handle_mouse_event(unsigned char scancode) }
else if(scancode == AUX_RECONNECT){
queue->head = queue->tail = 0; /* Flush input queue */
- aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */
+ __aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */
return;
}
@@ -822,11+823,8 @@ static void aux_write_dev(int val) /*
* Send a byte to the mouse & handle returned ack
*/
-static void aux_write_ack(int val)
+static void __aux_write_ack(int val)
{
- unsigned long flags;
-
- spin_lock_irqsave(&kbd_controller_lock, flags);
kb_wait();
kbd_write_command(KBD_CCMD_WRITE_MOUSE);
kb_wait();
@@ -834,6+832,14 @@ static void aux_write_ack(int val) /* we expect an ACK in response. */
mouse_reply_expected++;
kb_wait();
+}
+
+static void aux_write_ack(int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_controller_lock, flags);
+ __aux_write_ack(val);
spin_unlock_irqrestore(&kbd_controller_lock, flags);
}
-/* $Id: esp.c,v 1.94 2000/03/30 02:09:10 davem Exp $
+/* $Id: esp.c,v 1.95 2000/08/23 22:32:37 davem Exp $
* esp.c: EnhancedScsiProcessor Sun SCSI driver code.
*
* Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
#include <asm/sbus.h>
#include <asm/dma.h>
#include <asm/system.h>
-#include <asm/machines.h>
#include <asm/ptrace.h>
#include <asm/pgtable.h>
#include <asm/oplib.h>
#include <asm/io.h>
#include <asm/irq.h>
+
+#ifndef __sparc_v9__
+#include <asm/machines.h>
#include <asm/idprom.h>
+#endif
#include <linux/module.h>
@@ -1634,6+1637,7 @@ do_sync_known: */
int cdrom_hwbug_wkaround = 0;
+#ifndef __sparc_v9__
/* Never allow disconnects or synchronous transfers on
* SparcStation1 and SparcStation1+. Allowing those
* to be enabled seems to lockup the machine completely.
@@ -1654,6+1658,7 @@ do_sync_known: esp->snip = 0;
goto do_sync_known;
}
+#endif /* !(__sparc_v9__) */
/* We've talked to this guy before,
* but never negotiated. Let's try,
#include <asm/sbus.h>
#include <asm/dma.h>
#include <asm/system.h>
-#include <asm/machines.h>
#include <asm/ptrace.h>
#include <asm/pgtable.h>
#include <asm/oplib.h>
@@ -456,7+456,11 @@ static int soundcore_open(struct inode *, struct file *);
static struct file_operations soundcore_fops=
{
- owner: THIS_MODULE,
+/* owner: THIS_MODULE, * this is a bug: if we have an owner, the kernel
+ generates a MOD_INC_USE_COUNT - thus
+ the module cannot be unloaded since the device
+ is never released here ! - solution: owner
+ has to be NULL. Patch by Peter Wahl <Peter.Wahl@epost.de> */
open: soundcore_open,
};
ifeq ($(CONFIG_USB_STORAGE),y)
SUB_DIRS += storage
- obj-y += storage/usb-storage.o
+ obj-y += storage/storage.o
else
ifeq ($(CONFIG_USB_STORAGE),m)
MOD_IN_SUB_DIRS += storage
-#
-# Makefile for the USB Mass Storage device drivers.
-#
-
-O_TARGET := usb-storage.o
-M_OBJS := usb-storage.o
-O_OBJS := scsiglue.o protocol.o transport.o usb.o
-MOD_LIST_NAME := USB_STORAGE_MODULES
-
-CFLAGS_scsiglue.o:= -I../../scsi/
-CFLAGS_protocol.o:= -I../../scsi/
-CFLAGS_transport.o:= -I../../scsi/
-CFLAGS_debug.o:= -I../../scsi/
-CFLAGS_usb.o:= -I../../scsi/
-CFLAGS_shuttle_usbat.o:= -I../../scsi/
-CFLAGS_sddr09.o:= -I../../scsi/
-CFLAGS_dpcm.o:= -I../../scsi/
-
-ifeq ($(CONFIG_USB_STORAGE_DEBUG),y)
- O_OBJS += debug.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_HP8200e),y)
- O_OBJS += shuttle_usbat.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_SDDR09),y)
- O_OBJS += sddr09.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_FREECOM),y)
- O_OBJS += freecom.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA),y)
- O_OBJS += shuttle_sm.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH),y)
- O_OBJS += shuttle_cf.o
-endif
-
-ifeq ($(CONFIG_USB_STORAGE_DPCM),y)
- O_OBJS += dpcm.o
-endif
-
-include $(TOPDIR)/Rules.make
+#
+# Makefile for the USB Mass Storage device drivers.
+#
+# 15 Aug 2000, Christoph Hellwig <hch@caldera.de>
+# Rewritten to use lists instead of if-statements.
+#
+
+O_TARGET := storage.o
+EXTRA_CFLAGS := -I../../scsi/
+
+list-multi := usb-storage.o
+
+obj-$(CONFIG_USB_STORAGE) += usb-storage.o
+
+usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA) += shuttle_sm.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH) += shuttle_cf.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
+
+usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
+ $(usb-storage-obj-y)
+
+# 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)))
+
+# Take multi-part drivers out of obj-y and put components in.
+obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
+
+# Translate to Rules.make lists.
+O_OBJS := $(obj-y)
+M_OBJS := $(obj-m)
+MI_OBJS := $(int-m)
+
+include $(TOPDIR)/Rules.make
+
+usb-storage.o: $(usb-storage-objs)
+ $(LD) -r -o $@ $(usb-storage-objs)
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Source Code File
*
- * $Id: debug.c,v 1.2 2000/07/19 17:21:39 groovyjava Exp $
+ * $Id: debug.c,v 1.3 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Initial work by:
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Header File
*
- * $Id: debug.h,v 1.3 2000/07/19 19:34:48 groovyjava Exp $
+ * $Id: debug.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Initial work by:
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
*
- * $Id: dpcm.c,v 1.1 2000/08/08 01:26:12 webbb Exp $
+ * $Id: dpcm.c,v 1.3 2000/08/25 00:13:51 mdharm Exp $
*
* DPCM driver v0.1:
*
* First release
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 2000 Brian Webb (webbb@earthlink.net)
*
* This device contains both a CompactFlash card reader, which
#include "dpcm.h"
#include "sddr09.h"
-
/*
* Transport for the Microtech DPCM-USB
*
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
*
- * $Id: dpcm.h,v 1.1 2000/08/08 01:26:12 webbb Exp $
+ * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $
*
* DPCM driver v0.1:
*
* First release
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 2000 Brian Webb (webbb@earthlink.net)
*
* See dpcm.c for more explanation
--- /dev/null
+/* Driver for Freecom USB/IDE adaptor
+ *
+ * $Id: freecom.c,v 1.7 2000/08/25 00:13:51 mdharm Exp $
+ *
+ * Freecom v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (C) 2000 David Brown <usb-storage@davidb.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver was developed with information provided in FREECOM's USB
+ * Programmers Reference Guide. For further information contact Freecom
+ * (http://www.freecom.de/)
+ */
+
+#include <linux/config.h>
+#include "transport.h"
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+#include "freecom.h"
+
+static void pdump (void *, int);
+
+struct freecom_udata {
+ __u8 buffer[64]; /* Common command block. */
+};
+typedef struct freecom_udata *freecom_udata_t;
+
+/* All of the outgoing packets are 64 bytes long. */
+struct freecom_cb_wrap {
+ __u8 Type; /* Command type. */
+ __u8 Timeout; /* Timeout in seconds. */
+ __u8 Atapi[12]; /* An ATAPI packet. */
+ __u8 Filler[50]; /* Padding Data. */
+};
+
+struct freecom_xfer_wrap {
+ __u8 Type; /* Command type. */
+ __u8 Timeout; /* Timeout in seconds. */
+ __u32 Count; /* Number of bytes to transfer. */
+ __u8 Pad[58];
+};
+
+struct freecom_status {
+ __u8 Status;
+ __u8 Reason;
+ __u16 Count;
+ __u8 Pad[60];
+};
+
+/* These are the packet types. The low bit indicates that this command
+ * should wait for an interrupt. */
+#define FCM_PACKET_ATAPI 0x21
+
+/* Receive data from the IDE interface. The ATAPI packet has already
+ * waited, so the data should be immediately available. */
+#define FCM_PACKET_INPUT 0x90
+
+/* All packets (except for status) are 64 bytes long. */
+#define FCM_PACKET_LENGTH 64
+
+static int
+freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
+ int ipipe, int opipe, int count)
+{
+ freecom_udata_t extra = (freecom_udata_t) us->extra;
+ struct freecom_xfer_wrap *fxfr =
+ (struct freecom_xfer_wrap *) extra->buffer;
+ int result, partial;
+ int offset;
+ __u8 *buffer = extra->buffer;
+
+ fxfr->Type = FCM_PACKET_INPUT | 0x00;
+ fxfr->Timeout = 0; /* Short timeout for debugging. */
+ fxfr->Count = cpu_to_le32 (count);
+ memset (fxfr->Pad, 0, sizeof (fxfr->Pad));
+
+ printk (KERN_DEBUG "Read data Freecom! (c=%d)\n", count);
+
+ /* Issue the transfer command. */
+ result = usb_stor_bulk_msg (us, fxfr, opipe,
+ FCM_PACKET_LENGTH, &partial);
+ if (result != 0) {
+ US_DEBUGP ("Freecom readdata xpot failure: r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ printk (KERN_DEBUG "Done issuing read request: %d %d\n",
+ result, partial);
+
+ /* Now transfer all of our blocks. */
+ if (srb->use_sg) {
+ US_DEBUGP ("Need to implement scatter-gather\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ } else {
+ offset = 0;
+
+ while (offset < count) {
+ printk (KERN_DEBUG "Start of read\n");
+ /* Use the given buffer directly, but only if there
+ * is space for an entire packet. */
+
+ if (offset + 64 <= srb->request_bufflen) {
+ result = usb_stor_bulk_msg (
+ us, srb->request_buffer+offset,
+ ipipe, 64, &partial);
+ printk (KERN_DEBUG "Read111 = %d, %d\n",
+ result, partial);
+ pdump (srb->request_buffer+offset,
+ partial);
+ } else {
+ result = usb_stor_bulk_msg (
+ us, buffer,
+ ipipe, 64, &partial);
+ printk (KERN_DEBUG "Read112 = %d, %d\n",
+ result, partial);
+ memcpy (srb->request_buffer+offset,
+ buffer,
+ srb->request_bufflen - offset);
+ pdump (srb->request_buffer+offset,
+ srb->request_bufflen - offset);
+ }
+
+ if (result != 0) {
+ US_DEBUGP ("Freecom readblock r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ offset += 64;
+ }
+ }
+
+ printk (KERN_DEBUG "freecom_readdata done!\n");
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Transport for the Freecom USB/IDE adaptor.
+ *
+ */
+int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ struct freecom_cb_wrap *fcb;
+ struct freecom_status *fst;
+ int ipipe, opipe; /* We need both pipes. */
+ int result;
+ int partial;
+ int length;
+ freecom_udata_t extra;
+
+ /* Allocate a buffer for us. The upper usb transport code will
+ * free this for us when cleaning up. */
+ if (us->extra == NULL) {
+ us->extra = kmalloc (sizeof (struct freecom_udata),
+ GFP_KERNEL);
+ if (us->extra == NULL) {
+ printk (KERN_WARNING USB_STORAGE "Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
+
+ extra = (freecom_udata_t) us->extra;
+
+ fcb = (struct freecom_cb_wrap *) extra->buffer;
+ fst = (struct freecom_status *) extra->buffer;
+
+ printk (KERN_DEBUG "Freecom TRANSPORT STARTED\n");
+
+ /* Get handles for both transports. */
+ opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
+ ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in);
+
+#if 0
+ /* Yuck, let's see if this helps us. Artificially increase the
+ * length on this. */
+ if (srb->cmnd[0] == 0x03 && srb->cmnd[4] == 0x12)
+ srb->cmnd[4] = 0x0E;
+#endif
+
+ /* The ATAPI Command always goes out first. */
+ fcb->Type = FCM_PACKET_ATAPI;
+ fcb->Timeout = 0;
+ memcpy (fcb->Atapi, srb->cmnd, 12);
+ memset (fcb->Filler, 0, sizeof (fcb->Filler));
+
+ pdump (srb->cmnd, 12);
+
+ /* Send it out. */
+ result = usb_stor_bulk_msg (us, fcb, opipe,
+ FCM_PACKET_LENGTH, &partial);
+
+ /* The Freecom device will only fail if there is something wrong in
+ * USB land. It returns the status in its own registers, which
+ * come back in the bulk pipe. */
+ if (result != 0) {
+ US_DEBUGP ("freecom xport failure: r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* There are times we can optimize out this status read, but it
+ * doesn't hurt us to always do it now. */
+ result = usb_stor_bulk_msg (us, fst, ipipe,
+ FCM_PACKET_LENGTH, &partial);
+ printk (KERN_DEBUG "foo Status result %d %d\n", result, partial);
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ pdump ((void *) fst, partial);
+ if (partial != 4 || result != 0) {
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ if ((fst->Reason & 1) != 0) {
+ printk (KERN_DEBUG "operation failed\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* The device might not have as much data available as we
+ * requested. If you ask for more than the device has, this reads
+ * and such will hang. */
+ printk (KERN_DEBUG "Device indicates that it has %d bytes available\n",
+ le16_to_cpu (fst->Count));
+
+ /* Find the length we desire to read. It is the lesser of the SCSI
+ * layer's requested length, and the length the device claims to
+ * have available. */
+ length = us_transfer_length (srb);
+ printk (KERN_DEBUG "SCSI requested %d\n", length);
+ if (length > le16_to_cpu (fst->Count))
+ length = le16_to_cpu (fst->Count);
+
+ /* What we do now depends on what direction the data is supposed to
+ * move in. */
+
+ switch (us->srb->sc_data_direction) {
+ case SCSI_DATA_READ:
+ result = freecom_readdata (srb, us, ipipe, opipe, length);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+ break;
+
+ default:
+ US_DEBUGP ("freecom unimplemented direction: %d\n",
+ us->srb->sc_data_direction);
+ // Return fail, SCSI seems to handle this better.
+ return USB_STOR_TRANSPORT_FAILED;
+ break;
+ }
+
+#if 0
+ /* After the transfer, we can read our status register. */
+ printk (KERN_DEBUG "Going to read status register\n");
+ result = usb_stor_bulk_msg (us, &fst, ipipe,
+ FCM_PACKET_LENGTH, &partial);
+ printk (KERN_DEBUG "Result from read %d %d\n", result, partial);
+ if (result != 0) {
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ if ((fst.Reason & 1) != 0) {
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+#endif
+
+ return USB_STOR_TRANSPORT_GOOD;
+
+ printk (KERN_DEBUG "Freecom: transfer_length = %d\n",
+ us_transfer_length (srb));
+
+ printk (KERN_DEBUG "Freecom: direction = %d\n",
+ srb->sc_data_direction);
+
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+int usb_stor_freecom_reset(struct us_data *us)
+{
+ printk (KERN_DEBUG "freecom reset called\n");
+
+ /* We don't really have this feature. */
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+static void pdump (void *ibuffer, int length)
+{
+ static char line[80];
+ int offset = 0;
+ unsigned char *buffer = (unsigned char *) ibuffer;
+ int i, j;
+ int from, base;
+
+ offset = 0;
+ for (i = 0; i < length; i++) {
+ if ((i & 15) == 0) {
+ if (i > 0) {
+ offset += sprintf (line+offset, " - ");
+ for (j = i - 16; j < i; j++) {
+ if (buffer[j] >= 32 && buffer[j] <= 126)
+ line[offset++] = buffer[j];
+ else
+ line[offset++] = '.';
+ }
+ line[offset] = 0;
+ printk (KERN_DEBUG "%s\n", line);
+ offset = 0;
+ }
+ offset += sprintf (line+offset, "%08x:", i);
+ }
+ else if ((i & 7) == 0) {
+ offset += sprintf (line+offset, " -");
+ }
+ offset += sprintf (line+offset, " %02x", buffer[i] & 0xff);
+ }
+
+ /* Add the last "chunk" of data. */
+ from = (length - 1) % 16;
+ base = ((length - 1) / 16) * 16;
+
+ for (i = from + 1; i < 16; i++)
+ offset += sprintf (line+offset, " ");
+ if (from < 8)
+ offset += sprintf (line+offset, " ");
+ offset += sprintf (line+offset, " - ");
+
+ for (i = 0; i <= from; i++) {
+ if (buffer[base+i] >= 32 && buffer[base+i] <= 126)
+ line[offset++] = buffer[base+i];
+ else
+ line[offset++] = '.';
+ }
+ line[offset] = 0;
+ printk (KERN_DEBUG "%s\n", line);
+ offset = 0;
+}
+
-/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
+/* Driver for Freecom USB/IDE adaptor
*
- * $Id: dpcm.h,v 1.1 2000/08/08 01:26:12 webbb Exp $
+ * $Id: freecom.h,v 1.3 2000/08/25 00:13:51 mdharm Exp $
*
- * DPCM driver v0.1:
+ * Freecom v0.1:
*
* First release
*
- * Current development and maintainance by:
- * (c) 2000 Brian Webb (webbb@earthlink.net)
+ * Current development and maintenance by:
+ * (c) 2000 David Brown <usb-storage@davidb.org>
*
- * See dpcm.c for more explanation
+ * See freecom.c for more explanation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef _MICROTECH_DPCM_USB_H
-#define _MICROTECH_DPCM_USB_H
+#ifndef _FREECOM_USB_H
+#define _FREECOM_USB_H
-extern int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us);
+extern int freecom_transport(Scsi_Cmnd *srb, struct us_data *us);
+extern int usb_stor_freecom_reset(struct us_data *us);
#endif
/* Driver for USB Mass Storage compliant devices
*
- * $Id: protocol.c,v 1.4 2000/08/01 22:01:19 mdharm Exp $
+ * $Id: protocol.c,v 1.5 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Developed with the assistance of:
/* Driver for USB Mass Storage compliant devices
* Protocol Functions Header File
*
- * $Id: protocol.h,v 1.2 2000/08/01 22:01:19 mdharm Exp $
+ * $Id: protocol.h,v 1.3 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* This driver is based on the 'USB Mass Storage Class' document. This
/* Driver for USB Mass Storage compliant devices
* SCSI layer glue code
*
- * $Id: scsiglue.c,v 1.8 2000/08/11 23:15:05 mdharm Exp $
+ * $Id: scsiglue.c,v 1.9 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Developed with the assistance of:
/* Driver for USB Mass Storage compliant devices
* SCSI Connecting Glue Header File
*
- * $Id: scsiglue.h,v 1.3 2000/07/25 23:04:47 mdharm Exp $
+ * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* This driver is based on the 'USB Mass Storage Class' document. This
/* Driver for SanDisk SDDR-09 SmartMedia reader
*
+ * $Id: sddr09.c,v 1.10 2000/08/25 00:13:51 mdharm Exp $
+ *
* SDDR09 driver v0.1:
*
* First release
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
*
* The SanDisk SDDR-09 SmartMedia reader uses the Shuttle EUSB-01 chip.
@@ -316,9+318,9 @@ int sddr09_read_data(struct us_data *us,
// Figure out the initial LBA and page
- pba = (address/info->pagesize)>>4;
+ pba = address >> (info->pageshift + info->blockshift);
lba = info->pba_to_lba[pba];
- page = (address/info->pagesize)&0x0F;
+ page = (address >> info->pageshift) & info->blockmask;
// This could be made much more efficient by checking for
// contiguous LBA's. Another exercise left to the student.
@@ -329,15+331,16 @@ int sddr09_read_data(struct us_data *us,
// Read as many sectors as possible in this block
- pages = 0x10-page;
+ pages = info->blocksize - page;
if (pages > sectors)
pages = sectors;
- US_DEBUGP("Read %01X pages, from PBA %04X"
- " (LBA %04X) page %01X\n",
+ US_DEBUGP("Read %02X pages, from PBA %04X"
+ " (LBA %04X) page %02X\n",
pages, pba, lba, page);
- address = ((pba<<4)+page)*info->pagesize;
+ address = ( (pba << info->blockshift) + page ) <<
+ info->pageshift;
// Unlike in the documentation, the address is in
// words of 2 bytes.
@@ -370,7+373,7 @@ int sddr09_read_data(struct us_data *us,
result = sddr09_bulk_transport(us,
SCSI_DATA_READ, ptr,
- pages*info->pagesize, 0);
+ pages<<info->pageshift, 0);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
@@ -381,7+384,7 @@ int sddr09_read_data(struct us_data *us, page = 0;
lba++;
sectors -= pages;
- ptr += pages*info->pagesize;
+ ptr += (pages << info->pageshift);
}
if (use_sg) {
@@ -435,7+438,7 @@ int sddr09_read_control(struct us_data *us,
result = sddr09_bulk_transport(us,
SCSI_DATA_READ, content,
- blocks*0x40, use_sg);
+ blocks<<6, use_sg); // 0x40 bytes per block
US_DEBUGP("Result for bulk read in read_control %d\n",
result);
@@ -525,7+528,7 @@ int sddr09_reset(struct us_data *us) { }
unsigned long sddr09_get_capacity(struct us_data *us,
- unsigned int *pagesize) {
+ unsigned int *pagesize, unsigned int *blocksize) {
unsigned char manufacturerID;
unsigned char deviceID;
@@ -547,6+550,7 @@ unsigned long sddr09_get_capacity(struct us_data *us, US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
*pagesize = 512;
+ *blocksize = 16;
switch (deviceID) {
@@ -556,8+560,8 @@ unsigned long sddr09_get_capacity(struct us_data *us, *pagesize = 256;
return 0x00100000;
- case 0x5d: // 2MB
- case 0xea:
+ case 0xea: // 2MB
+ case 0x5d: // 5d is a ROM card with pagesize 512.
case 0x64:
if (deviceID!=0x5D)
*pagesize = 256;
@@ -574,9+578,11 @@ unsigned long sddr09_get_capacity(struct us_data *us, return 0x00800000;
case 0x73: // 16MB
+ *blocksize = 32;
return 0x01000000;
case 0x75: // 32MB
+ *blocksize = 32;
return 0x02000000;
default: // unknown
@@ -587,7+593,7 @@ unsigned long sddr09_get_capacity(struct us_data *us,
int sddr09_read_map(struct us_data *us) {
- unsigned char *control;
+ struct scatterlist *sg;
struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
int numblocks;
int i;
@@ -599,25+605,60 @@ int sddr09_read_map(struct us_data *us) { 1, 0, 0, 1, 0, 1, 1, 0
};
int result;
+ int alloc_len;
+ int alloc_blocks;
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);
+ // read 64 (1<<6) bytes for every block
+ // ( 1 << ( blockshift + pageshift ) bytes)
+ // of capacity:
+ // (1<<6)*capacity/(1<<(b+p)) =
+ // ((1<<6)*capacity)>>(b+p) =
+ // capacity>>(b+p-6)
+
+ alloc_len = info->capacity >>
+ (info->blockshift + info->pageshift - 6);
+
+ // Allocate a number of scatterlist structures according to
+ // the number of 128k blocks in the alloc_len. Adding 128k-1
+ // and then dividing by 128k gives the correct number of blocks.
+ // 128k = 1<<17
+
+ alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
+ sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (sg == NULL)
+ return 0;
+ for (i=0; i<alloc_blocks; i++) {
+ if (i<alloc_blocks-1) {
+ sg[i].address = kmalloc( (1<<17), GFP_KERNEL );
+ sg[i].length = (1<<17);
+ } else {
+ sg[i].address = kmalloc(alloc_len, GFP_KERNEL);
+ sg[i].length = alloc_len;
+ }
+ alloc_len -= sg[i].length;
+ }
+ for (i=0; i<alloc_blocks; i++)
+ if (sg[i].address == NULL) {
+ for (i=0; i<alloc_blocks; i++)
+ if (sg[i].address != NULL)
+ kfree(sg[i].address);
+ kfree(sg);
+ return 0;
+ }
- numblocks = info->capacity>>13;
+ numblocks = info->capacity >> (info->blockshift + info->pageshift);
if ( (result = sddr09_read_control(us, 0, numblocks,
- control, 0)) !=
+ (unsigned char *)sg, alloc_blocks)) !=
USB_STOR_TRANSPORT_GOOD) {
- kfree(control);
+ for (i=0; i<alloc_blocks; i++)
+ kfree(sg[i].address);
+ kfree(sg);
return -1;
}
@@ -629,11+670,28 @@ int sddr09_read_map(struct us_data *us) { 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);
+
+ if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
+ if (info->lba_to_pba != NULL)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba != NULL)
+ kfree(info->pba_to_lba);
+ info->lba_to_pba = NULL;
+ info->pba_to_lba = NULL;
+ for (i=0; i<alloc_blocks; i++)
+ kfree(sg[i].address);
+ kfree(sg);
+ return 0;
+ }
+
memset(info->lba_to_pba, 0, numblocks*sizeof(int));
memset(info->pba_to_lba, 0, numblocks*sizeof(int));
+ // Each block is 64 bytes of control data, so block i is located in
+ // scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
+
for (i=0; i<numblocks; i++) {
- ptr = control+64*i;
+ ptr = sg[i>>11].address+(i<<6);
if (ptr[0]!=0xFF || ptr[1]!=0xFF || ptr[2]!=0xFF ||
ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF)
continue;
@@ -655,20+713,30 @@ int sddr09_read_map(struct us_data *us) {
lba = (lba&0x07FF)>>1;
+ /* Every 1024 physical blocks, the LBA numbers
+ * go back to zero, but are within a higher
+ * block of LBA's. In other words, in blocks
+ * 1024-2047 you will find LBA 0-1023 which are
+ * really LBA 1024-2047.
+ */
+
+ lba += (i&~0x3FF);
+
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);
+ if (lba<0x10)
+ US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
info->pba_to_lba[i] = lba;
info->lba_to_pba[lba] = i;
}
- kfree(control);
+ for (i=0; i<alloc_blocks; i++)
+ kfree(sg[i].address);
+ kfree(sg);
return 0;
}
@@ -687,6+755,9 @@ static int init_sddr09(struct us_data *us) { 0x03, 0x20, 0, 0, 0x0e, 0, 0, 0, 0, 0, 0, 0
};
+ // What the hey is all this for? Doesn't seem to
+ // affect the device, so we won't do device inits.
+
if ( (result = sddr09_send_control(us, command, data, 2)) !=
USB_STOR_TRANSPORT_GOOD)
return result;
@@ -741,11+812,7 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us) int i;
char string[64];
unsigned char inquiry_response[36] = {
- 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00,
- 'S', 'a', 'n', 'D', 'i', 's', 'k', ' ',
- 'I', 'm', 'a', 'g', 'e', 'M', 'a', 't',
- 'e', ' ', 'S', 'D', 'D', 'R', '0', '9',
- ' ', ' ', ' ', ' '
+ 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
unsigned char mode_page_01[12] = {
0x01, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -758,17+825,11 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us) unsigned short pages;
struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
-/*
- if (us->flags & US_FL_NEED_INIT) {
- US_DEBUGP("SDDR-09: initializing\n");
- init_sddr09(us);
- us->flags &= ~US_FL_NEED_INIT;
- }
-*/
-
if (!us->extra) {
us->extra = kmalloc(
sizeof(struct sddr09_card_info), GFP_KERNEL);
+ if (!us->extra)
+ return USB_STOR_TRANSPORT_ERROR;
memset(us->extra, 0, sizeof(struct sddr09_card_info));
us->extra_destructor = sddr09_card_info_destructor;
}
@@ -779,14+840,29 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us) respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
- memcpy(ptr, inquiry_response, 36);
+ memset(inquiry_response+8, 0, 28);
+ fill_inquiry_response(us, inquiry_response, 36);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == READ_CAPACITY) {
- capacity = sddr09_get_capacity(us, &info->pagesize);
+ capacity = sddr09_get_capacity(us, &info->pagesize,
+ &info->blocksize);
+
+ if (!capacity)
+ return USB_STOR_TRANSPORT_FAILED;
+
info->capacity = capacity;
+ for (info->pageshift=1;
+ (info->pagesize>>info->pageshift);
+ info->pageshift++);
+ info->pageshift--;
+ for (info->blockshift=1;
+ (info->blocksize>>info->blockshift);
+ info->blockshift++);
+ info->blockshift--;
+ info->blockmask = (1<<info->blockshift)-1;
// Last page in the card
@@ -836,12+912,14 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
// convert page to block and page-within-block
- lba = page>>4;
- page = page&0x0F;
+ lba = page >> info->blockshift;
+ page = page & info->blockmask;
// locate physical block corresponding to logical block
- if (lba>=(info->capacity>>13)) {
+ if (lba >=
+ (info->capacity >>
+ (info->pageshift + info->blockshift) ) ) {
// FIXME: sense buffer?
@@ -866,8+944,8 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us) pba, lba, page, pages);
return sddr09_read_data(us,
- ((pba<<4)+page)*info->pagesize, pages,
- ptr, srb->use_sg);
+ ( (pba << info->blockshift) + page) << info->pageshift,
+ pages, ptr, srb->use_sg);
}
// Pass TEST_UNIT_READY and REQUEST_SENSE through
/* Driver for SanDisk SDDR-09 SmartMedia reader
* Header File
*
- * Current development and maintainance by:
+ * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $
+ *
+ * Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
*
* See sddr09.c for more explanation
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 */
+ unsigned long capacity; /* Size of card in bytes */
+ int pagesize; /* Size of page in bytes */
+ int pageshift; /* log2 of pagesize */
+ int blocksize; /* Size of block in pages */
+ int blockshift; /* log2 of blocksize */
+ int blockmask; /* 2^blockshift - 1 */
+ int *lba_to_pba; /* logical to physical map */
+ int *pba_to_lba; /* physical to logical map */
};
#endif
/* Driver for SCM Microsystems USB-ATAPI cable
*
- * $Id: shuttle_usbat.c,v 1.2 2000/08/03 00:03:39 groovyjava Exp $
+ * $Id: shuttle_usbat.c,v 1.4 2000/08/25 00:13:51 mdharm Exp $
*
* SCM driver v0.2:
*
*
* First release - hp8200e.
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
*
* Many originally ATAPI devices were slightly modified to meet the USB
@@ -734,7+734,7 @@ static int hp_8200e_select_and_test_registers(struct us_data *us) { return result;
}
-static int init_8200e(struct us_data *us) {
+int init_8200e(struct us_data *us) {
int result;
unsigned char status;
@@ -895,11+895,11 @@ int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us) "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* C0-DF */
"XDXXXXXXXXXXXXXX" "XXW00HXXXXXXXXXX"; /* E0-FF */
- if (us->flags & US_FL_NEED_INIT) {
+/* if (us->flags & US_FL_NEED_INIT) {
US_DEBUGP("8200e: initializing\n");
init_8200e(us);
us->flags &= ~US_FL_NEED_INIT;
- }
+ } */
len = srb->request_bufflen;
/* Driver for SCM Microsystems USB-ATAPI cable
* Header File
*
- * $Id: shuttle_usbat.h,v 1.2 2000/08/03 00:03:39 groovyjava Exp $
+ * $Id: shuttle_usbat.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
*
* See scm.c for more explanation
@@ -74,5+74,6 @@ extern int usbat_write_user_io(struct us_data *us, /* HP 8200e stuff */
extern int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us);
+extern int init_8200e(struct us_data *us);
#endif
/* Driver for USB Mass Storage compliant devices
*
- * $Id: transport.c,v 1.12 2000/08/08 15:22:38 gowdy Exp $
+ * $Id: transport.c,v 1.18 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Developed with the assistance of:
/* Calculate the length of the data transfer (not the command) for any
* given SCSI command
*/
-static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us)
-{
- int i;
- unsigned int total = 0;
- struct scatterlist *sg;
-
- /* support those devices which need the length calculated
- * differently
- */
- if (srb->cmnd[0] == INQUIRY) {
- srb->cmnd[4] = 36;
- }
-
- if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE))
- return srb->cmnd[4];
-
- if (srb->cmnd[0] == TEST_UNIT_READY)
- return 0;
-
- /* Are we going to scatter gather? */
- if (srb->use_sg) {
- /* Add up the sizes of all the scatter-gather segments */
- sg = (struct scatterlist *) srb->request_buffer;
- for (i = 0; i < srb->use_sg; i++)
- total += sg[i].length;
-
- return total;
- }
- else
- /* Just return the length of the buffer */
- return srb->request_bufflen;
-}
-
-/* Calculate the length of the data transfer (not the command) for any
- * given SCSI command
- */
-static unsigned int us_transfer_length_new(Scsi_Cmnd *srb, struct us_data *us)
+static unsigned int us_transfer_length(Scsi_Cmnd *srb)
{
int i;
int doDefault = 0;
@@ -245,10+209,6 @@ static unsigned int us_transfer_length_new(Scsi_Cmnd *srb, struct us_data *us) WRITE_SAME 41
*/
- /* Not sure this is right as an INQUIRY can contain nonstandard info */
- if (srb->cmnd[0] == INQUIRY)
- srb->cmnd[4] = 36;
-
if (srb->sc_data_direction == SCSI_DATA_WRITE) {
doDefault = 1;
}
@@ -611,6+571,16 @@ static void us_transfer(Scsi_Cmnd *srb, struct us_data* us) int i;
int result = -1;
struct scatterlist *sg;
+ unsigned int total_transferred = 0;
+ unsigned int transfer_amount;
+
+ /* calculate how much we want to transfer */
+ transfer_amount = us_transfer_length(srb);
+
+ /* was someone foolish enough to request more data than available
+ * buffer space? */
+ if (transfer_amount > srb->request_bufflen)
+ transfer_amount = srb->request_bufflen;
/* are we scatter-gathering? */
if (srb->use_sg) {
@@ -620,8+590,19 @@ static void us_transfer(Scsi_Cmnd *srb, struct us_data* us) */
sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
- result = us_transfer_partial(us, sg[i].address,
- sg[i].length);
+
+ /* transfer the lesser of the next buffer or the
+ * remaining data */
+ if (transfer_amount - total_transferred >=
+ sg[i].length) {
+ result = us_transfer_partial(us, sg[i].address,
+ sg[i].length);
+ total_transferred += sg[i].length;
+ } else
+ result = us_transfer_partial(us, sg[i].address,
+ transfer_amount - total_transferred);
+
+ /* if we get an error, end the loop here */
if (result)
break;
}
@@ -629,7+610,7 @@ static void us_transfer(Scsi_Cmnd *srb, struct us_data* us) else
/* no scatter-gather, just make the request */
result = us_transfer_partial(us, srb->request_buffer,
- srb->request_bufflen);
+ transfer_amount);
/* return the result in the data structure itself */
srb->result = result;
@@ -874,7+855,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
- if (us_transfer_length(srb, us)) {
+ if (us_transfer_length(srb)) {
us_transfer(srb, us);
US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
@@ -975,7+956,7 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
- if (us_transfer_length(srb, us)) {
+ if (us_transfer_length(srb)) {
us_transfer(srb, us);
US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
@@ -1039,10+1020,12 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
/* set up the command wrapper */
bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
- bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb, us));
+ bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb));
bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
bcb.Tag = srb->serial_number;
bcb.Lun = srb->cmnd[1] >> 5;
+ if (us->flags & US_FL_SCM_MULT_TARG)
+ bcb.Lun |= srb->target << 4;
bcb.Length = srb->cmd_len;
/* construct the pipe handle */
@@ -1053,8+1036,9 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) memcpy(bcb.CDB, srb->cmnd, bcb.Length);
/* send it to out endpoint */
- US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n",
- le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun,
+ US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n",
+ le32_to_cpu(bcb.Signature), bcb.Tag,
+ (bcb.Lun >> 4), (bcb.Lun & 0xFF),
bcb.DataTransferLength, bcb.Flags, bcb.Length);
result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN,
&partial);
/* Driver for USB Mass Storage compliant devices
* Transport Functions Header File
*
- * $Id: transport.h,v 1.8 2000/08/08 01:23:55 webbb Exp $
+ * $Id: transport.h,v 1.11 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* This driver is based on the 'USB Mass Storage Class' document. This
#ifndef _TRANSPORT_H_
#define _TRANSPORT_H_
-#include <linux/config.h>
#include <linux/blk.h>
+#include <linux/config.h>
#include "usb.h"
#include "scsi.h"
#endif
#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */
+#ifdef CONFIG_USB_STORAGE_FREECOM
+#define US_PR_FREECOM 0xf1 /* Freecom */
+#endif
+
/*
* Bulk only data structures
*/
/* Driver for USB Mass Storage compliant devices
*
- * $Id: usb.c,v 1.23 2000/08/08 20:46:45 mdharm Exp $
+ * $Id: usb.c,v 1.33 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Developed with the assistance of:
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/config.h>
#include "usb.h"
#include "scsiglue.h"
#include "transport.h"
#ifdef CONFIG_USB_STORAGE_DPCM
#include "dpcm.h"
#endif
+#ifdef CONFIG_USB_STORAGE_FREECOM
+#include "freecom.h"
+#endif
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/malloc.h>
+#include <linux/config.h>
/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -213,8+216,20 @@ static int usb_stor_control_thread(void * __us) /* reject if target != 0 or if LUN is higher than
* the maximum known LUN
*/
- if (us->srb->target || (us->srb->lun > us->max_lun)) {
- US_DEBUGP("Bad device number (%d/%d)\n",
+ if (us->srb->target &&
+ !(us->flags & US_FL_SCM_MULT_TARG)) {
+ US_DEBUGP("Bad target number (%d/%d)\n",
+ us->srb->target, us->srb->lun);
+ us->srb->result = DID_BAD_TARGET << 16;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+ }
+
+ if (us->srb->lun > us->max_lun) {
+ US_DEBUGP("Bad LUN (%d/%d)\n",
us->srb->target, us->srb->lun);
us->srb->result = DID_BAD_TARGET << 16;
@@ -314,6+329,19 @@ static int usb_stor_control_thread(void * __us) * are free to use as many characters as you like.
*/
+int euscsi_init(struct us_data *us)
+{
+ unsigned char bar = 0x1;
+ int result;
+
+ US_DEBUGP("Attempting to init eUSCSI bridge...\n");
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0),
+ 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
+ 0x01, 0x0, &bar, 0x1, 5*HZ);
+ US_DEBUGP("-- result is %d\n", result);
+ US_DEBUGP("-- bar afterwards is %d\n", bar);
+}
+
static struct us_unusual_dev us_unusual_dev_list[] = {
{ 0x03f0, 0x0107, 0x0200, 0x0200,
@@ -339,15+367,61 @@ static struct us_unusual_dev us_unusual_dev_list[] = { { 0x04e6, 0x0002, 0x0100, 0x0100,
"Shuttle",
"eUSCSI Bridge",
- US_SC_SCSI, US_PR_BULK, NULL,
- 0 },
+ US_SC_SCSI, US_PR_BULK, euscsi_init,
+ US_FL_SCM_MULT_TARG },
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+ { 0x04e6, 0x0003, 0x0000, 0x9999,
+ "Sandisk",
+ "ImageMate SDDR09",
+ US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
+ US_FL_SINGLE_LUN | US_FL_START_STOP },
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DPCM
+ { 0x0436, 0x0005, 0x0100, 0x0100,
+ "Microtech",
+ "CameraMate (DPCM_USB)",
+ US_SC_SCSI, US_PR_DPCM_USB, NULL,
+ US_FL_START_STOP },
+#endif
- { 0x04e6, 0x0006, 0x0100, 0x0100,
+ { 0x04e6, 0x0006, 0x0100, 0x0200,
"Shuttle",
"eUSB MMC Adapter",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN},
+ { 0x04e6, 0x0009, 0x0200, 0x0200,
+ "Shuttle",
+ "ATA/ATAPI Bridge",
+ US_SC_8020, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN},
+
+ { 0x04e6, 0x000A, 0x0200, 0x0200,
+ "Shuttle",
+ "Compact Flash Reader",
+ US_SC_8020, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN},
+
+ { 0x04e6, 0x000B, 0x0100, 0x0100,
+ "Shuttle",
+ "eUSCSI Bridge",
+ US_SC_SCSI, US_PR_BULK, euscsi_init,
+ US_FL_SCM_MULT_TARG },
+
+ { 0x04e6, 0x000C, 0x0100, 0x0100,
+ "Shuttle",
+ "eUSCSI Bridge",
+ US_SC_SCSI, US_PR_BULK, euscsi_init,
+ US_FL_SCM_MULT_TARG },
+
+ { 0x04e6, 0x0101, 0x0200, 0x0200,
+ "Shuttle",
+ "CD-RW Device",
+ US_SC_8020, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN},
+
{ 0x054c, 0x0010, 0x0210, 0x0210,
"Sony",
"DSC-S30/S70",
@@ -372,6+446,24 @@ static struct us_unusual_dev us_unusual_dev_list[] = { US_SC_UFI, US_PR_CBI, NULL,
US_FL_SINGLE_LUN},
+ { 0x059f, 0xa601, 0x0200, 0x0200,
+ "LaCie",
+ "USB Hard Disk",
+ US_SC_RBC, US_PR_CB, NULL,
+ 0 },
+
+ { 0x05ab, 0x0031, 0x0100, 0x0100,
+ "In-System",
+ "USB/IDE Bridge",
+ US_SC_8070, US_PR_BULK, NULL,
+ 0 },
+
+ { 0x0693, 0x0005, 0x0100, 0x0100,
+ "Hagiwara",
+ "Flashgate",
+ US_SC_SCSI, US_PR_BULK, NULL,
+ 0 },
+
{ 0x0693, 0x0002, 0x0100, 0x0100,
"Hagiwara",
"FlashGate SmartMedia",
@@ -401,14+493,21 @@ static struct us_unusual_dev us_unusual_dev_list[] = { { 0x07af, 0x0004, 0x0100, 0x0100,
"Microtech",
"USB-SCSI-DB25",
- US_SC_SCSI, US_PR_BULK, NULL,
- 0 },
+ US_SC_SCSI, US_PR_BULK, euscsi_init,
+ US_FL_SCM_MULT_TARG },
+
+#ifdef CONFIG_USB_STORAGE_FREECOM
+ { 0x07ab, 0xfc01, 0x0921, 0x0921,
+ "Freecom",
+ "USB-IDE",
+ US_SC_8070, US_PR_FREECOM, NULL, US_FL_SINGLE_LUN },
+#endif
- { 0x059f, 0xa601, 0x0200, 0x0200,
- "LaCie",
- "USB Hard Disk",
- US_SC_RBC, US_PR_CB, NULL,
- 0 },
+ { 0x07af, 0x0005, 0x0100, 0x0100,
+ "Microtech",
+ "USB-SCSI-HD50",
+ US_SC_SCSI, US_PR_BULK, euscsi_init,
+ US_FL_SCM_MULT_TARG },
#ifdef CONFIG_USB_STORAGE_DPCM
{ 0x07af, 0x0006, 0x0100, 0x0100,
@@ -417,25+516,6 @@ static struct us_unusual_dev us_unusual_dev_list[] = { US_SC_SCSI, US_PR_DPCM_USB, NULL,
US_FL_START_STOP },
#endif
-
- { 0x07af, 0x0005, 0x0100, 0x0100,
- "Microtech",
- "USB-SCSI-HD50",
- US_SC_SCSI, US_PR_BULK, NULL,
- 0 },
-
- { 0x05ab, 0x0031, 0x0100, 0x0100,
- "In-System",
- "USB/IDE Bridge",
- US_SC_8070, US_PR_BULK, NULL,
- 0 },
-
- { 0x0693, 0x0005, 0x0100, 0x0100,
- "Hagiwara",
- "Flashgate",
- US_SC_SCSI, US_PR_BULK, NULL,
- 0 },
-
{ 0 }
};
@@ -615,15+695,10 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* set the interface -- STALL is an acceptable response here */
#ifdef CONFIG_USB_STORAGE_SDDR09
- if (protocol != US_PR_EUSB_SDDR09)
- result = usb_set_interface(dev,
- altsetting->bInterfaceNumber, 0);
- else
+ if (protocol == US_PR_EUSB_SDDR09)
result = usb_set_configuration(dev, 1);
-#else
- result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
-#endif
- US_DEBUGP("Result from usb_set_interface is %d\n", result);
+
+ US_DEBUGP("Result from usb_set_configuration is %d\n", result);
if (result == -EPIPE) {
US_DEBUGP("-- clearing stall on control interface\n");
usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
@@ -632,6+707,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) US_DEBUGP("-- Unknown error. Rejecting device\n");
return NULL;
}
+#endif
/* Do some basic sanity checks, and bail if we find a problem */
if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
@@ -702,7+778,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* Re-Initialize the device if it needs it */
if (unusual_dev && unusual_dev->initFunction)
- (*unusual_dev->initFunction)(ss);
+ (unusual_dev->initFunction)(ss);
} else {
/* New device -- allocate memory and initialize */
@@ -828,6+904,15 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->max_lun = 1;
break;
#endif
+
+#ifdef CONFIG_USB_STORAGE_FREECOM
+ case US_PR_FREECOM:
+ ss->transport_name = "Freecom";
+ ss->transport = freecom_transport;
+ ss->transport_reset = usb_stor_freecom_reset;
+ ss->max_lun = 0;
+ break;
+#endif
default:
ss->transport_name = "Unknown";
@@ -909,7+994,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* Just before we start our control thread, initialize
* the device if it needs initialization */
if (unusual_dev && unusual_dev->initFunction)
- (*unusual_dev->initFunction)(ss);
+ (unusual_dev->initFunction)(ss);
/* start up our control thread */
ss->pid = kernel_thread(usb_stor_control_thread, ss,
/* Driver for USB Mass Storage compliant devices
* Main Header File
*
- * $Id: usb.h,v 1.7 2000/08/15 00:06:38 mdharm Exp $
+ * $Id: usb.h,v 1.8 2000/08/25 00:13:51 mdharm Exp $
*
- * Current development and maintainance by:
+ * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Initial work by:
@@ -496,6+496,27 @@ static inline void flush_old_files(struct files_struct * files) write_unlock(&files->file_lock);
}
+/*
+ * An execve() will automatically "de-thread" the process.
+ * Note: we don't have to hold the tasklist_lock to test
+ * whether we migth need to do this. If we're not part of
+ * a thread group, there is no way we can become one
+ * dynamically. And if we are, we only need to protect the
+ * unlink - even if we race with the last other thread exit,
+ * at worst the list_del_init() might end up being a no-op.
+ */
+static inline void de_thread(struct task_struct *tsk)
+{
+ if (!list_empty(&tsk->thread_group)) {
+ write_lock_irq(&tasklist_lock);
+ list_del_init(&tsk->thread_group);
+ write_unlock_irq(&tasklist_lock);
+ }
+
+ /* Minor oddity: this might stay the same. */
+ tsk->tgid = tsk->pid;
+}
+
int flush_old_exec(struct linux_binprm * bprm)
{
char * name;
@@ -534,6+555,8 @@ int flush_old_exec(struct linux_binprm * bprm)
flush_thread();
+ de_thread(current);
+
if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
permission(bprm->file->f_dentry->d_inode,MAY_READ))
current->dumpable = 0;
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/highuid.h>
+#include <linux/vmalloc.h>
#include <linux/ncp_fs.h>
#define NCP_OBJECT_NAME_MAX_LEN 4096
/* maximum limit for ncp_privatedata_ioctl */
#define NCP_PRIVATE_DATA_MAX_LEN 8192
+/* maximum negotiable packet size */
+#define NCP_PACKET_SIZE_INTERNAL 65536
int ncp_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
@@ -50,11+53,11 @@ int ncp_ioctl(struct inode *inode, struct file *filp, NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
return -EINVAL;
}
- bouncebuffer = kmalloc(NCP_PACKET_SIZE, GFP_NFS);
+ bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
if (!bouncebuffer)
return -ENOMEM;
if (copy_from_user(bouncebuffer, request.data, request.size)) {
- kfree(bouncebuffer);
+ vfree(bouncebuffer);
return -EFAULT;
}
ncp_lock_server(server);
@@ -67,7+70,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, memcpy(server->packet, bouncebuffer, request.size);
result = ncp_request2(server, request.function,
- bouncebuffer, NCP_PACKET_SIZE);
+ bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
if (result < 0)
result = -EIO;
else
@@ -78,7+81,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, if (result >= 0)
if (copy_to_user(request.data, bouncebuffer, result))
result = -EFAULT;
- kfree(bouncebuffer);
+ vfree(bouncebuffer);
return result;
case NCP_IOC_CONN_LOGGED_IN:
@@ -445,7+445,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) memset(mnt, 0, sizeof(struct smb_mount_data_kernel));
strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
SMB_NLS_MAXNAMELEN);
- strncpy(mnt->codepage.local_name, CONFIG_SMB_NLS_REMOTE,
+ strncpy(mnt->codepage.remote_name, CONFIG_SMB_NLS_REMOTE,
SMB_NLS_MAXNAMELEN);
if (ver == SMB_MOUNT_OLDVERSION) {
@@ -142,7+142,7 @@ int umsdos_emd_dir_readentry (struct dentry *demd, loff_t *pos, struct umsdos_di recsize = umsdos_evalrecsize(p->name_len);
if (offs + recsize > PAGE_CACHE_SIZE) {
struct page *page2;
- int part = (page_address(page) + PAGE_CACHE_SIZE) - p->spare;
+ int part = (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare;
page2 = read_cache_page(mapping, 1+(*pos>>PAGE_CACHE_SHIFT),
(filler_t*)mapping->a_ops->readpage, NULL);
if (IS_ERR(page2)) {
@@ -261,7+261,7 @@ int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, p->rdev = cpu_to_le16(entry->rdev);
p->mode = cpu_to_le16(entry->mode);
memcpy(p->name,entry->name,
- (page_address(page) + PAGE_CACHE_SIZE) - p->spare);
+ (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare);
memcpy(page_address(page2),
entry->spare+PAGE_CACHE_SIZE-offs,
offs+info->recsize-PAGE_CACHE_SIZE);
@@ -62,7+62,6 @@ extern inline unsigned long _page_hashfn(struct address_space * mapping, unsigne #define s(x) ((x)+((x)>>PAGE_HASH_BITS))
return s(i+index) & (PAGE_HASH_SIZE-1);
#undef i
-#undef o
#undef s
}
@@ -38,6+38,7 @@ extern unsigned long event; #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
+#define CLONE_THREAD 0x00010000 /* set if we want to clone the "thread group" */
/*
* These are the constant used to fake the fixed-point load-average
@@ -311,6+312,7 @@ struct task_struct { pid_t pgrp;
pid_t tty_old_pgrp;
pid_t session;
+ pid_t tgid;
/* boolean value for session group leader */
int leader;
/*
@@ -319,6+321,7 @@ struct task_struct { * p->p_pptr->pid)
*/
struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
+ struct list_head thread_group;
/* PID hash table linkage. */
struct task_struct *pidhash_next;
@@ -435,6+438,7 @@ struct task_struct { prev_task: &tsk, \
p_opptr: &tsk, \
p_pptr: &tsk, \
+ thread_group: LIST_HEAD_INIT(tsk.thread_group), \
wait_chldexit: __WAIT_QUEUE_HEAD_INITIALIZER(tsk.wait_chldexit),\
real_timer: { \
function: it_real_fn \
@@ -556,8+560,8 @@ extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int kill_pg_info(int, struct siginfo *, pid_t);
extern int kill_sl_info(int, struct siginfo *, pid_t);
extern int kill_proc_info(int, struct siginfo *, pid_t);
-extern int kill_something_info(int, struct siginfo *, int);
extern void notify_parent(struct task_struct *, int);
+extern void do_notify_parent(struct task_struct *, int);
extern void force_sig(int, struct task_struct *);
extern int send_sig(int, struct task_struct *, int);
extern int kill_pg(pid_t, int, int);
@@ -798,6+802,8 @@ do { \ #define for_each_task(p) \
for (p = &init_task ; (p = p->next_task) != &init_task ; )
+#define next_thread(p) \
+ list_entry((p)->thread_group.next, struct task_struct, thread_group)
static inline void del_from_runqueue(struct task_struct * p)
{
@@ -818,6+824,7 @@ static inline void unhash_process(struct task_struct *p) nr_threads--;
unhash_pid(p);
REMOVE_LINKS(p);
+ list_del(&p->thread_group);
write_unlock_irq(&tasklist_lock);
}
#define WNOHANG 0x00000001
#define WUNTRACED 0x00000002
-#define __WALL 0x40000000
-#define __WCLONE 0x80000000
+#define __WNOTHREAD 0x20000000 /* Don't wait on children of other threads in this group */
+#define __WALL 0x40000000 /* Wait on all children, regardless of type */
+#define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */
#ifdef __KERNEL__
@@ -184,7+185,7 @@ extern inline void __add_wait_queue_tail(wait_queue_head_t *head, if (!head->task_list.next || !head->task_list.prev)
WQ_BUG();
#endif
- list_add(&new->task_list, head->task_list.prev);
+ list_add_tail(&new->task_list, &head->task_list);
}
extern inline void __remove_wait_queue(wait_queue_head_t *head,
@@ -144,17+144,29 @@ static inline int has_stopped_jobs(int pgrp) return retval;
}
+/*
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our process
+ * group, and if no such member exists, give it to
+ * the global child reaper process (ie "init")
+ */
static inline void forget_original_parent(struct task_struct * father)
{
- struct task_struct * p;
+ struct task_struct * p, *reaper;
read_lock(&tasklist_lock);
+
+ /* Next in our thread group */
+ reaper = next_thread(father);
+ if (reaper == father)
+ reaper = child_reaper;
+
for_each_task(p) {
if (p->p_opptr == father) {
/* We dont want people slaying init */
p->exit_signal = SIGCHLD;
p->self_exec_id++;
- p->p_opptr = child_reaper; /* init */
+ p->p_opptr = reaper;
if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0);
}
}
@@ -378,7+390,6 @@ static void exit_notify(void) && !capable(CAP_KILL))
current->exit_signal = SIGCHLD;
- notify_parent(current, current->exit_signal);
/*
* This loop does two things:
@@ -390,6+401,7 @@ static void exit_notify(void) */
write_lock_irq(&tasklist_lock);
+ do_notify_parent(current, current->exit_signal);
while (current->p_cptr != NULL) {
p = current->p_cptr;
current->p_cptr = p->p_osptr;
@@ -402,7+414,7 @@ static void exit_notify(void) p->p_osptr->p_ysptr = p;
p->p_pptr->p_cptr = p;
if (p->state == TASK_ZOMBIE)
- notify_parent(p, p->exit_signal);
+ do_notify_parent(p, p->exit_signal);
/*
* process group orphan check
* Case ii: Our child is in a different pgrp
@@ -483,9+495,9 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc {
int flag, retval;
DECLARE_WAITQUEUE(wait, current);
- struct task_struct *p;
+ struct task_struct *tsk;
- if (options & ~(WNOHANG|WUNTRACED|__WCLONE|__WALL))
+ if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
return -EINVAL;
add_wait_queue(¤t->wait_chldexit,&wait);
@@ -493,27+505,30 @@ repeat: flag = 0;
current->state = TASK_INTERRUPTIBLE;
read_lock(&tasklist_lock);
- for (p = current->p_cptr ; p ; p = p->p_osptr) {
- if (pid>0) {
- if (p->pid != pid)
- continue;
- } else if (!pid) {
- if (p->pgrp != current->pgrp)
- continue;
- } else if (pid != -1) {
- if (p->pgrp != -pid)
+ tsk = current;
+ do {
+ struct task_struct *p;
+ for (p = tsk->p_cptr ; p ; p = p->p_osptr) {
+ if (pid>0) {
+ if (p->pid != pid)
+ continue;
+ } else if (!pid) {
+ if (p->pgrp != current->pgrp)
+ continue;
+ } else if (pid != -1) {
+ if (p->pgrp != -pid)
+ continue;
+ }
+ /* Wait for all children (clone and not) if __WALL is set;
+ * otherwise, wait for clone children *only* if __WCLONE is
+ * set; otherwise, wait for non-clone children *only*. (Note:
+ * A "clone" child here is one that reports to its parent
+ * using a signal other than SIGCHLD.) */
+ if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
+ && !(options & __WALL))
continue;
- }
- /* Wait for all children (clone and not) if __WALL is set;
- * otherwise, wait for clone children *only* if __WCLONE is
- * set; otherwise, wait for non-clone children *only*. (Note:
- * A "clone" child here is one that reports to its parent
- * using a signal other than SIGCHLD.) */
- if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
- && !(options & __WALL))
- continue;
- flag = 1;
- switch (p->state) {
+ flag = 1;
+ switch (p->state) {
case TASK_STOPPED:
if (!p->exit_code)
continue;
@@ -543,15+558,19 @@ repeat: REMOVE_LINKS(p);
p->p_pptr = p->p_opptr;
SET_LINKS(p);
+ do_notify_parent(p, SIGCHLD);
write_unlock_irq(&tasklist_lock);
- notify_parent(p, SIGCHLD);
} else
release(p);
goto end_wait4;
default:
continue;
+ }
}
- }
+ if (options & __WNOTHREAD)
+ break;
+ tsk = next_thread(tsk);
+ } while (tsk != current);
read_unlock(&tasklist_lock);
if (flag) {
retval = 0;
@@ -661,7+661,13 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) * Let it rip!
*/
retval = p->pid;
+ p->tgid = retval;
+ INIT_LIST_HEAD(&p->thread_group);
write_lock_irq(&tasklist_lock);
+ if (clone_flags & CLONE_THREAD) {
+ p->tgid = current->tgid;
+ list_add(&p->thread_group, ¤t->thread_group);
+ }
SET_LINKS(p);
hash_pid(p);
nr_threads++;
@@ -455,17+455,35 @@ signed long schedule_timeout(signed long timeout) static inline void __schedule_tail(struct task_struct *prev)
{
#ifdef CONFIG_SMP
- if ((prev->state == TASK_RUNNING) &&
- (prev != idle_task(smp_processor_id()))) {
- unsigned long flags;
-
- spin_lock_irqsave(&runqueue_lock, flags);
- prev->has_cpu = 0;
- reschedule_idle(prev, flags); // spin_unlocks runqueue
- } else {
- wmb();
- prev->has_cpu = 0;
- }
+ unsigned long flags;
+
+ /*
+ * fast path falls through. We have to take the runqueue lock
+ * unconditionally to make sure that the test of prev->state
+ * and setting has_cpu is atomic wrt. interrupts. It's not
+ * a big problem in the common case because we recently took
+ * the runqueue lock so it's likely to be in this processor's
+ * cache.
+ */
+ spin_lock_irqsave(&runqueue_lock, flags);
+ prev->has_cpu = 0;
+ if (prev->state == TASK_RUNNING)
+ goto running_again;
+out_unlock:
+ spin_unlock_irqrestore(&runqueue_lock, flags);
+ return;
+
+ /*
+ * Slow path - we 'push' the previous process and
+ * reschedule_idle() will attempt to find a new
+ * processor for it. (but it might preempt the
+ * current process as well.)
+ */
+running_again:
+ if (prev == idle_task(smp_processor_id()))
+ goto out_unlock;
+ reschedule_idle(prev, flags); // spin_unlocks runqueue
+ return;
#endif /* CONFIG_SMP */
}
@@ -179,7+179,7 @@ printk("SIG dequeue (%s:%d): %d ", current->comm, current->pid, if (!(current->notifier)(current->notifier_data)) {
current->sigpending = 0;
return 0;
- }
+ }
}
}
@@ -287,6+287,18 @@ static int rm_sig_from_queue(int sig, struct task_struct *t) }
/*
+ * Bad permissions for sending the signal
+ */
+int bad_signal(int sig, struct siginfo *info, struct task_struct *t)
+{
+ return (!info || ((unsigned long)info != 1 && SI_FROMUSER(info)))
+ && ((sig != SIGCONT) || (current->session != t->session))
+ && (current->euid ^ t->suid) && (current->euid ^ t->uid)
+ && (current->uid ^ t->suid) && (current->uid ^ t->uid)
+ && !capable(CAP_KILL);
+}
+
+/*
* Determine whether a signal should be posted or not.
*
* Signals with SIG_IGN can be ignored, except for the
@@ -327,37+339,13 @@ static int ignored_signal(int sig, struct task_struct *t) return 1;
}
-int
-send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
+/*
+ * Handle TASK_STOPPED.
+ * Also, return true for the unblockable signals that we
+ * should deliver to all threads..
+ */
+static void handle_stop_signal(int sig, struct task_struct *t)
{
- unsigned long flags;
- int ret;
- struct signal_queue *q = 0;
-
-
-#if DEBUG_SIG
-printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
-#endif
-
- ret = -EINVAL;
- if (sig < 0 || sig > _NSIG)
- goto out_nolock;
- /* The somewhat baroque permissions check... */
- ret = -EPERM;
- if ((!info || ((unsigned long)info != 1 && SI_FROMUSER(info)))
- && ((sig != SIGCONT) || (current->session != t->session))
- && (current->euid ^ t->suid) && (current->euid ^ t->uid)
- && (current->uid ^ t->suid) && (current->uid ^ t->uid)
- && !capable(CAP_KILL))
- goto out_nolock;
-
- /* The null signal is a permissions and process existance probe.
- No signal is actually delivered. Same goes for zombies. */
- ret = 0;
- if (!sig || !t->sig)
- goto out_nolock;
-
- spin_lock_irqsave(&t->sigmask_lock, flags);
switch (sig) {
case SIGKILL: case SIGCONT:
/* Wake up the process if stopped. */
@@ -376,19+364,12 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig); recalc_sigpending(t);
break;
}
+ return 0;
+}
- /* Optimize away the signal, if it's a signal that can be
- handled immediately (ie non-blocked and untraced) and
- that is ignored (either explicitly or by default). */
-
- if (ignored_signal(sig, t))
- goto out;
-
- /* Support queueing exactly one non-rt signal, so that we
- can get more detailed information about the cause of
- the signal. */
- if (sig < SIGRTMIN && sigismember(&t->signal, sig))
- goto out;
+static int deliver_signal(int sig, struct siginfo *info, struct task_struct *t)
+{
+ struct signal_queue * q = NULL;
/* Real-time signals must be queued if sent by sigqueue, or
some other real-time mechanism. It is implementation
@@ -399,8+380,7 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig); pass on the info struct. */
if (atomic_read(&nr_queued_signals) < max_queued_signals) {
- q = (struct signal_queue *)
- kmem_cache_alloc(signal_queue_cachep, GFP_ATOMIC);
+ q = kmem_cache_alloc(signal_queue_cachep, GFP_ATOMIC);
}
if (q) {
@@ -433,8+413,7 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig); * Queue overflow, abort. We may abort if the signal was rt
* and sent by user using something other than kill().
*/
- ret = -EAGAIN;
- goto out;
+ return -EAGAIN;
}
sigaddset(&t->signal, sig);
@@ -459,12+438,118 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig); spin_unlock(&runqueue_lock);
#endif /* CONFIG_SMP */
}
+ return 0;
+}
+
+
+/*
+ * Send a thread-group-wide signal.
+ *
+ * Rule: SIGSTOP and SIGKILL get delivered to _everybody_.
+ *
+ * Others get delivered to the thread that doesn't have them
+ * blocked (just one such thread).
+ *
+ * If all threads have it blocked, it gets delievered to the
+ * thread group leader.
+ */
+static int send_tg_sig_info(int sig, struct siginfo *info, struct task_struct *p)
+{
+ int retval = 0;
+ struct task_struct *tsk;
+
+ if (sig < 0 || sig > _NSIG)
+ return -EINVAL;
+
+ if (bad_signal(sig, info, p))
+ return -EPERM;
+
+ if (!sig)
+ return 0;
+
+ tsk = p;
+ do {
+ unsigned long flags;
+ tsk = next_thread(tsk);
+
+ /* Zombie? Ignore */
+ if (!tsk->sig)
+ continue;
+
+ spin_lock_irqsave(&tsk->sigmask_lock, flags);
+ handle_stop_signal(sig, tsk);
+
+ /* Is the signal ignored by this thread? */
+ if (ignored_signal(sig, tsk))
+ goto next;
+
+ /* Have we already delivered this non-queued signal? */
+ if (sig < SIGRTMIN && sigismember(&tsk->signal, sig))
+ goto next;
+
+ /* Not blocked? Go, girl, go! */
+ if (tsk == p || !sigismember(&tsk->blocked, sig)) {
+ retval = deliver_signal(sig, info, tsk);
+
+ /* Signals other than SIGKILL and SIGSTOP have "once" semantics */
+ if (sig != SIGKILL && sig != SIGSTOP)
+ tsk = p;
+ }
+next:
+ spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
+ if ((tsk->state & TASK_INTERRUPTIBLE) && signal_pending(tsk))
+ wake_up_process(tsk);
+ } while (tsk != p);
+ return retval;
+}
+
+
+int
+send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
+{
+ unsigned long flags;
+ int ret;
+
+
+#if DEBUG_SIG
+printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
+#endif
+
+ ret = -EINVAL;
+ if (sig < 0 || sig > _NSIG)
+ goto out_nolock;
+ /* The somewhat baroque permissions check... */
+ ret = -EPERM;
+ if (bad_signal(sig, info, t))
+ goto out_nolock;
+
+ /* The null signal is a permissions and process existance probe.
+ No signal is actually delivered. Same goes for zombies. */
+ ret = 0;
+ if (!sig || !t->sig)
+ goto out_nolock;
+
+ spin_lock_irqsave(&t->sigmask_lock, flags);
+ handle_stop_signal(sig, t);
+
+ /* Optimize away the signal, if it's a signal that can be
+ handled immediately (ie non-blocked and untraced) and
+ that is ignored (either explicitly or by default). */
+
+ if (ignored_signal(sig, t))
+ goto out;
+
+ /* Support queueing exactly one non-rt signal, so that we
+ can get more detailed information about the cause of
+ the signal. */
+ if (sig < SIGRTMIN && sigismember(&t->signal, sig))
+ goto out;
+ ret = deliver_signal(sig, info, t);
out:
spin_unlock_irqrestore(&t->sigmask_lock, flags);
- if ((t->state & TASK_INTERRUPTIBLE) && signal_pending(t))
- wake_up_process(t);
-
+ if ((t->state & TASK_INTERRUPTIBLE) && signal_pending(t))
+ wake_up_process(t);
out_nolock:
#if DEBUG_SIG
printk(" %d -> %d\n", signal_pending(t), ret);
@@ -509,22+594,17 @@ kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) int retval = -EINVAL;
if (pgrp > 0) {
struct task_struct *p;
- int found = 0;
retval = -ESRCH;
read_lock(&tasklist_lock);
for_each_task(p) {
if (p->pgrp == pgrp) {
int err = send_sig_info(sig, info, p);
- if (err != 0)
+ if (retval)
retval = err;
- else
- found++;
}
}
read_unlock(&tasklist_lock);
- if (found)
- retval = 0;
}
return retval;
}
@@ -541,22+621,17 @@ kill_sl_info(int sig, struct siginfo *info, pid_t sess) int retval = -EINVAL;
if (sess > 0) {
struct task_struct *p;
- int found = 0;
retval = -ESRCH;
read_lock(&tasklist_lock);
for_each_task(p) {
if (p->leader && p->session == sess) {
int err = send_sig_info(sig, info, p);
- if (err)
+ if (retval)
retval = err;
- else
- found++;
}
}
read_unlock(&tasklist_lock);
- if (found)
- retval = 0;
}
return retval;
}
@@ -576,6+651,33 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid) return error;
}
+
+/*
+ * Send a signal to a thread group..
+ *
+ * If the pid is the thread group ID, we consider this
+ * a "thread group" signal. Otherwise it degenerates into
+ * a thread-specific signal.
+ */
+static int kill_tg_info(int sig, struct siginfo *info, pid_t pid)
+{
+ int error;
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ p = find_task_by_pid(pid);
+ error = -ESRCH;
+ if (p) {
+ /* Is it the leader? Otherwise it degenerates into a per-thread thing */
+ if (p->tgid == pid)
+ error = send_tg_sig_info(sig, info, p);
+ else
+ error = send_sig_info(sig, info, p);
+ }
+ read_unlock(&tasklist_lock);
+ return error;
+}
+
/*
* kill_something_info() interprets pid in interesting ways just like kill(2).
*
@@ -583,8+685,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid) * is probably wrong. Should make it like BSD or SYSV.
*/
-int
-kill_something_info(int sig, struct siginfo *info, int pid)
+static int kill_something_info(int sig, struct siginfo *info, int pid)
{
if (!pid) {
return kill_pg_info(sig, info, current->pgrp);
@@ -606,7+707,7 @@ kill_something_info(int sig, struct siginfo *info, int pid) } else if (pid < 0) {
return kill_pg_info(sig, info, -pid);
} else {
- return kill_proc_info(sig, info, pid);
+ return kill_tg_info(sig, info, pid);
}
}
@@ -645,11+746,24 @@ kill_proc(pid_t pid, int sig, int priv) }
/*
+ * Joy. Or not. Pthread wants us to wake up every thread
+ * in our parent group.
+ */
+static void wake_up_parent(struct task_struct *parent)
+{
+ struct task_struct *tsk = parent;
+
+ do {
+ wake_up_interruptible(&tsk->wait_chldexit);
+ tsk = next_thread(tsk);
+ } while (tsk != parent);
+}
+
+/*
* Let a parent know about a status change of a child.
*/
-void
-notify_parent(struct task_struct *tsk, int sig)
+void do_notify_parent(struct task_struct *tsk, int sig)
{
struct siginfo info;
int why, status;
@@ -693,7+807,23 @@ notify_parent(struct task_struct *tsk, int sig) info.si_status = status;
send_sig_info(sig, &info, tsk->p_pptr);
- wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
+ wake_up_parent(tsk->p_pptr);
+}
+
+
+/*
+ * We need the tasklist lock because it's the only
+ * thing that protects out "parent" pointer.
+ *
+ * exit.c calls "do_notify_parent()" directly, because
+ * it already has the tasklist lock.
+ */
+void
+notify_parent(struct task_struct *tsk, int sig)
+{
+ read_lock(&tasklist_lock);
+ do_notify_parent(tsk, sig);
+ read_unlock(&tasklist_lock);
}
EXPORT_SYMBOL(dequeue_signal);
@@ -724,7+724,7 @@ asmlinkage unsigned long sys_alarm(unsigned int seconds) asmlinkage long sys_getpid(void)
{
/* This is SMP safe - current->pid doesn't change */
- return current->pid;
+ return current->tgid;
}
/*
@@ -205,7+205,7 @@ found: bdata->last_pos = start+areasize-1;
bdata->last_offset = remaining_size;
}
- bdata->last_offset &= ~PAGE_MASK;
+ bdata->last_offset &= ~PAGE_MASK;
} else {
bdata->last_pos = start + areasize - 1;
bdata->last_offset = size & ~PAGE_MASK;
@@ -895,7+895,7 @@ static void generic_file_readahead(int reada_ok, * page only.
*/
if (PageLocked(page)) {
- if (!filp->f_ralen || index >= raend || index + filp->f_ralen < raend) {
+ if (!filp->f_ralen || index >= raend || index + filp->f_rawin < raend) {
raend = index;
if (raend < end_index)
max_ahead = filp->f_ramax;
@@ -1072,6+1072,7 @@ found_page:
if (!Page_Uptodate(page))
goto page_not_up_to_date;
+ generic_file_readahead(reada_ok, filp, inode, page);
page_ok:
/*
* Ok, we have the page, and it's up-to-date, so
@@ -632,6+632,9 @@ int ipgre_rcv(struct sk_buff *skb, unsigned short len) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
ipgre_ecn_decapsulate(iph, skb);
netif_rx(skb);
@@ -858,6+861,9 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
IPTUNNEL_XMIT();
*
* The Internet Protocol (IP) output module.
*
- * Version: $Id: ip_output.c,v 1.83 2000/03/25 01:52:08 davem Exp $
+ * Version: $Id: ip_output.c,v 1.84 2000/08/25 02:15:47 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -327,6+327,7 @@ static inline int ip_queue_xmit2(struct sk_buff *skb) kfree_skb(skb);
return -EHOSTUNREACH;
}
+ rt = (struct rtable *)skb->dst;
}
#endif
/*
* Linux NET3: IP/IP protocol decoder.
*
- * Version: $Id: ipip.c,v 1.38 2000/08/02 06:03:59 davem Exp $
+ * Version: $Id: ipip.c,v 1.39 2000/08/25 02:15:47 davem Exp $
*
* Authors:
* Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
@@ -496,6+496,9 @@ int ipip_rcv(struct sk_buff *skb, unsigned short len) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
ipip_ecn_decapsulate(iph, skb);
netif_rx(skb);
@@ -639,6+642,9 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
IPTUNNEL_XMIT();
* Pedro Roque <roque@di.fc.ul.pt>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
- * $Id: sit.c,v 1.42 2000/08/02 06:03:59 davem Exp $
+ * $Id: sit.c,v 1.43 2000/08/25 02:15:47 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -401,6+401,9 @@ int ipip6_rcv(struct sk_buff *skb, unsigned short len) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
ipip6_ecn_decapsulate(iph, skb);
netif_rx(skb);
@@ -567,6+570,9 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
#endif
IPTUNNEL_XMIT();
@@ -1585,7+1585,7 @@ static void free_pg_vec(unsigned long *pg_vec, unsigned order, unsigned len)
pend = virt_to_page(pg_vec[i] + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(pg_vec[i]); page <= pend; page++)
- clear_bit(PG_reserved, &page->flags);
+ ClearPageReserved(page);
free_pages(pg_vec[i], order);
}
}
@@ -1640,7+1640,7 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
pend = virt_to_page(pg_vec[i] + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(pg_vec[i]); page <= pend; page++)
- set_bit(PG_reserved, &page->flags);
+ SetPageReserved(page);
}
/* Page vector is allocated */
@@ -855,7+855,7 @@ int sock_create(int family, int type, int protocol, struct socket **res)
net_family_read_lock();
if (net_families[family] == NULL) {
- i = -EINVAL;
+ i = -EAFNOSUPPORT;
goto out;
}
@@ -1710,10+1710,9 @@ void __init sock_init(void) * Initialize the protocols module.
*/
- proto_init();
-
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
+ proto_init();
/*
* The netlink device handler may be needed early.