diff --git a/Documentation/Changes b/Documentation/Changes index fce16f7..7331868 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -91,7 +91,12 @@ The Red Hat gcc 2.96 compiler subtree can also be used to build this tree. You should ensure you use gcc-2.96-74 or later. gcc-2.96-54 will not build the kernel correctly. -gcc 4 is not supported. +gcc 3.3 and 3.4 are both known to work well. gcc 4.0 and 4.1 also work on +a several architectures (i386, x86_64, ppc, sparc, sparc64, alpha). Other +archs will not work. Versions 4.2 and onwards are not supported anymore. +Supporting them would require massive in-depth changes which will add a +lot of bugs and might break older compilers. If you don't have any gcc +below 4.2, check Documentation/using-newer-gcc.txt for instructions. In addition, please pay attention to compiler optimization. Anything greater than -O2 may not be wise. Similarly, if you choose to use gcc-2.95.x diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 6fc2a2d..ed2b560 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -3209,6 +3209,17 @@ CONFIG_IP_NF_TARGET_LOG If you want to compile it as a module, say M here and read . If unsure, say `N'. +CLASSIFY target support +CONFIG_IP_NF_TARGET_CLASSIFY + This option adds a `CLASSIFY' target, which enables the user to set + the priority of a packet. Some qdiscs can use this value for classification, + among these are: + + atm, cbq, dsmark, pfifo_fast, htb, prio + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + ipchains (2.2-style) support CONFIG_IP_NF_COMPAT_IPCHAINS This option places ipchains (with masquerading and redirection diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d1fa3d0..afbd0fd 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -561,6 +561,9 @@ running once the system is up. root= [KNL] root filesystem. + rootdelay= [KNL] Delay (in seconds) to pause before attempting to + mount the root filesystem + rootflags= [KNL] set root filesystem mount option string rootfstype= [KNL] set root filesystem type diff --git a/Documentation/using-newer-gcc.txt b/Documentation/using-newer-gcc.txt new file mode 100644 index 0000000..7834427 --- /dev/null +++ b/Documentation/using-newer-gcc.txt @@ -0,0 +1,187 @@ +Building older versions of GCC compatible with Linux Kernel 2.4 +=============================================================== + + +This document explains how to build an older supported version of the GCC +compiler from a distribution which only offers incompatible recent versions. + + +Context +======= + +When Linux 2.4.0 was released in early 2001, GCC 2.95.2 was mainstream and +GCC 2.91.66 was still widely used. The GCC development model was evolving +and still confused. Since then, GCC has evolved a lot and stabilized. New +versions are regularly released, and some old features from the early code +get deprecated then removed. + +The kernel heavily relies on GCC's capabilities and behaviour. Some of the +code in Linux looks strange but is in fact intended to workaround early GCC +bugs. For these reasons, almost every new major GCC release breaks the kernel +build process. GCC 3.4 was a real pain to introduce as it required a lot of +rewriting in sensible areas, and GCC 4 required a lot of work, though this +work was less complicated thanks to the cleanup efforts invested in GCC 3.4. + +Starting with GCC 4.2, the output code randomly fails depending on section +ordering, which itself depends on the declaration order of functions, module +parameters and many other things. The nasty part is that the code builds but +randomly fails at runtime, so it is almost impossible to fix it and ensure +that everything works, especially in the drivers area where most of the +problems lie. + +As of 2008, GCC 4.3.2 is advertised as the current release and 4.2 the previous +release. Most distributions have been shipping with 4.2 and 4.3 for some time, +so building Linux 2.4 on a recent distribution has become a real problem for +users who still have to support kernel 2.4 on servers, firewalls or any other +system. + + +Solution : the two-minutes process +================================== + +If it is not possible to adapt the kernel to GCC, let's adapt GCC to the +kernel. We're lucky, building GCC to build just a kernel is not hard and +is rather fast. I call that a two-minutes process because building an +older GCC takes about 1 minute, and the kernel with that GCC also takes +one minute. + +First, you have to select which version of GCC you want to build your kernel +with. Here are some comments on possible versions : + + - 2.95.3 : very well tested for the kernel, builds kernels very fast, + requires a lot of patches and is rather hard to build.. + + - 3.0 : very buggy, avoid it. + + - 3.1 & 3.2 : apparently less buggy but rarely used so bugs might have + remained unnoticed. + + - 3.3 : used and tested for a long time. A bit slow but easy to build. + + - 3.4 : was recently introduced, received less testing, though seems + OK. Builds kernels faster than 3.3, and is easy to build too. + + - 4.0 & 4.1 : received little testing, particularly slow but may produce + smaller kernels when compiled with -Os. + +Always take the last maintenance version of a compiler (eg: 3.4.6 for 3.4). + +For best reliability and less hassle, I tend to recommend GCC 3.3.6. For +improved build times (about 30% lower) and improved kernel performance, I'd +recommend 3.4.6. It tends to produce more efficient code on i386, but has +had a long history of causing annoyances with inline declarations. It seems +OK though, and I build all my kernels with it. We'll assume 3.4 is used for +the rest of this document, though what is described will work with 3.3 to +4.1 unless stated otherwise. + + +Instructions +============ + +1) Download gcc sources from the nearest mirror +----------------------------------------------- + +Find a mirror address here : [ http://gcc.gnu.org/mirrors.html ] or download +from this directory : + + ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.4.6/ + +Get gcc-core-3.4.6.tar.bz2. It only contains the C compiler, which is what you +want. + +2) Prepare your build environment +--------------------------------- + +Create a temporary directory where you'll extract the sources. Don't build on +NFS, it may be slow. Use /tmp if you want. You'll need about 150 MB of free +space. You'll have to extract the sources in that new directory, and create a +temporary build directory aside it : + + $ mkdir /tmp/gcc-build + $ cd /tmp/gcc-build + $ tar jxf /tmp/gcc-core-3.4.6.tar.bz2 + $ mkdir build + +3) Configure gcc +---------------- + +You don't want your new gcc to conflict with the one already in place. I +recommend simply prefixing it with "kernel-", and not installing it in +/usr/bin, but rather /opt/kgcc/bin or anywhere else (/usr/local/bin will be +used by default). I recommend choosing a place you already have in your PATH +(such as the default /usr/local/bin), so that you don't have to pass the full +path to the binary when building. + + $ cd /tmp/gcc-build/build + $ ../gcc-3.4.6/configure --disable-locale --disable-shared --disable-nls \ + --enable-languages=c \ + --prefix=/opt/kgcc --program-prefix=kernel- + +If you're using GCC 3.3, you may see strange messages indicating that some +programs were not found (eg: kernel-objdump). Simply ignore them. + +Note that you can set a lot of options, even use it as a cross-compiler. While +very frequent, such a build will not be covered by this document. + +4) Build GCC +------------ + +Both GCC 3.3 and 3.4 support parallel building, which reduces build time on SMP +systems : + + $ make -j 4 + +If the build fails here because of some options you added above, you'll have to +remove the build dir and recreate it. + +5) Install your new GCC +----------------------- + +The binaries may be a bit big, but you can strip them. Both GCC 3.3 and 3.4 +support a trick on the command line during the installation process, which +consists in passing the "-s" flag to "install" : + + $ sudo make install INSTALL_PROGRAM='${INSTALL} -s' + +It will be installed under the directory referred to by the "prefix" option +above, or /usr/local/bin if none was specified : + + $ ls -l /opt/kgcc/bin/kernel-gcc + -rwxr-xr-x 3 root root 73124 Sep 6 22:45 /opt/kgcc/bin/kernel-gcc + + $ /opt/kgcc/bin/kernel-gcc -v + Reading specs from /tmp/gcc-3.4.6-build/tmp-inst/opt/kgcc/bin/... + Configured with: ../gcc-3.4.6/configure --disable-shared --disable-... + Thread model: posix + gcc version 3.4.6 + +6) Using your new compiler +-------------------------- + +The compiler just has to be passed to "make" via the "CC" variable for all +commands : + + $ make CC=/opt/kgcc/bin/kernel-gcc -j 4 dep bzImage modules modules_install + + or more simply, when you have it in your path : + + $ make CC=kernel-gcc -j 4 dep bzImage modules modules_install + + +Conclusion +========== + +Building an older GCC on to build an older kernel on a newer machine is not +really hard. It becomes harder when you have to cross-build (eg: you're +building on a 64-bit machine for a 32-bit one). But for this, I would recommend +that you check the excellent "crosstool" utility from Dan Kegel. It supports a +wide variety of compilers, contains a lot of fixes and will do all the hard +patching and configuration work for any combination you want or need. + + +Suggestions and comments +======================== + +If you find mistakes or want to send comments about this document, please mail +me at . + diff --git a/Makefile b/Makefile index 9fea153..48ce53d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 -SUBLEVEL = 36 -EXTRAVERSION = +SUBLEVEL = 37 +EXTRAVERSION = -rc1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c index 557a73f..ad489cb 100644 --- a/arch/i386/kernel/acpi.c +++ b/arch/i386/kernel/acpi.c @@ -440,7 +440,7 @@ acpi_boot_init (void) return result; } -#ifdef CONFIG_X86_IOAPIC +#ifdef CONFIG_X86_IO_APIC check_acpi_pci(); #endif diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index 07c6d94..9c27ca2 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -524,6 +524,7 @@ static __init int disable_acpi_pci(struct dmi_blacklist *d) */ +#ifdef CONFIG_VT /* IBM bladeservers have a USB console switch. The keyboard type is USB * and the hardware does not have a console keyboard. We disable the * console keyboard so the kernel does not try to initialize one and @@ -538,6 +539,7 @@ static __init int disable_console_keyboard(struct dmi_blacklist *d) keyboard_controller_present = 0; return 0; } +#endif /* * This will be expanded over time to force things like the APM @@ -825,7 +827,8 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={ MATCH(DMI_SYS_VENDOR, "IBM"), NO_MATCH, NO_MATCH, NO_MATCH } }, - /* +#ifdef CONFIG_VT + /* * IBM Bladeservers */ @@ -834,6 +837,7 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={ MATCH(DMI_BOARD_NAME, "Server Blade"), NO_MATCH, NO_MATCH } }, +#endif #ifdef CONFIG_ACPI_BOOT /* diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 7a66b02..7c211ae 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -1146,9 +1146,7 @@ void __init smp_boot_cpus(void) */ Dprintk("Before bogomips.\n"); - if (!cpucount) { - printk(KERN_ERR "Error: only one processor found.\n"); - } else { + { unsigned long bogosum = 0; for (cpu = 0; cpu < NR_CPUS; cpu++) if (cpu_online_map & (1< 0x20000000 || - ((flags & MAP_FIXED) && - addr < 0xe0000000 && addr + len > 0x20000000))) + (addr < 0xe0000000 && addr + len > 0x20000000))) goto out_putf; /* See asm-sparc/uaccess.h */ diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 03750c8..9eccd43 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -300,12 +300,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, if (current->thread.flags & SPARC_FLAG_32BIT) { if (len > 0xf0000000UL || - ((flags & MAP_FIXED) && addr > 0xf0000000UL - len)) + (addr > 0xf0000000UL - len)) goto out_putf; } else { if (len > -PAGE_OFFSET || - ((flags & MAP_FIXED) && - addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET)) + (addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET)) goto out_putf; } diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index 99db1bb..74b4183 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c @@ -964,9 +964,7 @@ void __init smp_boot_cpus(void) */ Dprintk("Before bogomips.\n"); - if (!cpucount) { - printk(KERN_ERR "Only one processor found.\n"); - } else { + { unsigned long bogosum = 0; for (cpu = 0; cpu < NR_CPUS; cpu++) if (cpu_online_map & (1<vm_ops = &drm_vm_dma_ops; vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + vma->vm_flags |= VM_DONTEXPAND; #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ /* In Linux 2.2.3 and above, this is @@ -358,6 +359,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) return -EINVAL; /* This should never happen. */ } vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + vma->vm_flags |= VM_DONTEXPAND; #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ /* In Linux 2.2.3 and above, this is diff --git a/drivers/char/drm/drm_vm.h b/drivers/char/drm/drm_vm.h index 4eadbf0..337a1a8 100644 --- a/drivers/char/drm/drm_vm.h +++ b/drivers/char/drm/drm_vm.h @@ -342,6 +342,7 @@ int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma) vma->vm_ops = &DRM(vm_dma_ops); vma->vm_flags |= VM_RESERVED; /* Don't swap */ + vma->vm_flags |= VM_DONTEXPAND; vma->vm_file = filp; /* Needed for drm_vm_open() */ DRM(vm_open)(vma); return 0; @@ -475,6 +476,7 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) return -EINVAL; /* This should never happen. */ } vma->vm_flags |= VM_RESERVED; /* Don't swap */ + vma->vm_flags |= VM_DONTEXPAND; vma->vm_file = filp; /* Needed for drm_vm_open() */ DRM(vm_open)(vma); diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 341b8e0..b67e8d6 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -1763,7 +1763,7 @@ int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) return -EFAULT; - if(dltmp.cardno < 0 || dltmp.cardno >= MAX_BOARDS) + if(dltmp.cardno < 0 || dltmp.cardno >= MAX_BOARDS || dltmp.len < 0) return -EINVAL; switch(cmd) @@ -2874,6 +2874,8 @@ static int moxaloadbios(int cardno, unsigned char *tmp, int len) unsigned long baseAddr; int i; + if(len < 0 || len > sizeof(moxaBuff)) + return -EINVAL; if(copy_from_user(moxaBuff, tmp, len)) return -EFAULT; baseAddr = moxaBaseAddr[cardno]; @@ -2921,7 +2923,7 @@ static int moxaload320b(int cardno, unsigned char * tmp, int len) unsigned long baseAddr; int i; - if(len > sizeof(moxaBuff)) + if(len < 0 || len > sizeof(moxaBuff)) return -EINVAL; if(copy_from_user(moxaBuff, tmp, len)) return -EFAULT; @@ -2941,6 +2943,8 @@ static int moxaloadcode(int cardno, unsigned char * tmp, int len) unsigned long baseAddr, ofsAddr; int retval, port, i; + if(len < 0 || len > sizeof(moxaBuff)) + return -EINVAL; if(copy_from_user(moxaBuff, tmp, len)) return -EFAULT; baseAddr = moxaBaseAddr[cardno]; diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index d4e1611..b90e659 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -61,6 +61,10 @@ unsigned char pckbd_sysrq_xlate[128] = "\r\000/"; /* 0x60 - 0x6f */ #endif +/* Warning: do not redefine kbd_controller_present on ia64, mips and mips64 */ +#ifndef kbd_controller_present +#define kbd_controller_present() keyboard_controller_present + int keyboard_controller_present __initdata = 1; static int __init removable_keyb(char *str) { @@ -68,6 +72,7 @@ static int __init removable_keyb(char *str) return 0; } __setup("nokeyb", removable_keyb); +#endif static void kbd_write_command_w(int data); static void kbd_write_output_w(int data); @@ -77,8 +82,6 @@ static void __aux_write_ack(int val); static int aux_reconnect = 0; #endif -#define kbd_controller_present() keyboard_controller_present - static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; static unsigned char handle_kbd_event(void); @@ -905,7 +908,7 @@ static char * __init initialize_kbd(void) void __init pckbd_init_hw(void) { - if (!keyboard_controller_present) { + if (!kbd_controller_present()) { kbd_exists = 0; return; } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index ac45ac9..9d42fc6 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -819,7 +819,7 @@ void probe_hwif (ide_hwif_t *hwif) { unsigned int unit; unsigned long flags; - unsigned int irqd; + int irqd; if (hwif->noprobe) return; @@ -855,8 +855,8 @@ void probe_hwif (ide_hwif_t *hwif) * we'll install our IRQ driver much later... */ irqd = hwif->irq; - if (irqd) - disable_irq(hwif->irq); + if (irqd > 0) + disable_irq(irqd); local_irq_set(flags); @@ -909,7 +909,7 @@ void probe_hwif (ide_hwif_t *hwif) * Use cached IRQ number. It might be (and is...) changed by probe * code above */ - if (irqd) + if (irqd > 0) enable_irq(irqd); ide_tune_drives(hwif); diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 0d71383..ac2bfa5 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -69,9 +69,16 @@ static struct amd_ide_chip { { PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, 0x50, AMD_UDMA_133 }, { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, 0x50, AMD_UDMA_133 }, { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, 0x50, AMD_UDMA_133 }, - { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, 0x50, AMD_UDMA_133 }, - { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, 0x50, AMD_UDMA_133 }, - { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE,0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_AMD_CS5536_IDE, 0x40, AMD_UDMA_100 }, { 0 } }; @@ -474,7 +481,14 @@ static struct pci_device_id amd74xx_pci_tbl[] __devinitdata = { #endif { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 19 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 20 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 21 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 22 }, { 0, }, }; diff --git a/drivers/ide/pci/amd74xx.h b/drivers/ide/pci/amd74xx.h index 6cc19a5..04d1522 100644 --- a/drivers/ide/pci/amd74xx.h +++ b/drivers/ide/pci/amd74xx.h @@ -199,6 +199,17 @@ static ide_pci_device_t amd74xx_chipsets[] __devinitdata = { }, { /* 15 */ .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, + .name = "NFORCE-MCP51", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 16 */ + .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, .name = "NFORCE-MCP55", .init_chipset = init_chipset_amd74xx, @@ -208,6 +219,72 @@ static ide_pci_device_t amd74xx_chipsets[] __devinitdata = { .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, .bootable = ON_BOARD, }, + { /* 17 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, + .name = "NFORCE-MCP61", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 18 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, + .name = "NFORCE-MCP65", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 19 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE, + .name = "NFORCE-MCP67", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 20 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE, + .name = "NFORCE-MCP73", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 21 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE, + .name = "NFORCE-MCP77", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 22 */ + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_DEVICE_ID_AMD_CS5536_IDE, + .name = "AMD-CS5536", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, + .bootable = ON_BOARD, + }, { .vendor = 0, .device = 0, diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index bcc5a69..6496b81 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -144,6 +144,9 @@ static struct pci_device_id generic_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_20363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12}, + { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_6101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14}, + { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_6145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15}, { 0, }, }; diff --git a/drivers/ide/pci/generic.h b/drivers/ide/pci/generic.h index 759018b..d2b913e 100644 --- a/drivers/ide/pci/generic.h +++ b/drivers/ide/pci/generic.h @@ -167,6 +167,36 @@ static ide_pci_device_t generic_chipsets[] __devinitdata = { .channels = 1, .autodma = NOAUTODMA, .bootable = ON_BOARD, + },{ /* 13 */ + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_6101, + .name = "MARVELL_6101", + .init_chipset = init_chipset_generic, + .init_hwif = init_hwif_generic, + .init_dma = init_dma_generic, + .channels = 1, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 14 */ + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB368, + .name = "JMICRON_JMB368", + .init_chipset = init_chipset_generic, + .init_hwif = init_hwif_generic, + .init_dma = init_dma_generic, + .channels = 1, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 15 */ + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_6145, + .name = "MARVELL_6145", + .init_chipset = init_chipset_generic, + .init_hwif = init_hwif_generic, + .init_dma = init_dma_generic, + .channels = 1, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, },{ .vendor = 0, .device = 0, diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index d861ef5..a89c59c 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -231,7 +231,7 @@ static u32 global_outstanding_dmas = 0; * enable this define to make use of it. This provides better hotplug * support. The mentioned patch is not part of the kernel proper though, * because it is considered somewhat of a hack. */ -//#define SBP2_USE_SCSI_ADDREM_HACK +#define SBP2_USE_SCSI_ADDREM_HACK /* diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index f49861a..e4dff5e 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -517,7 +517,7 @@ static struct vortex_chip_info { {"3c920B-EMB-WNM (ATI Radeon 9100 IGP)", PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_MII|HAS_HWCKSM, 128, }, {"3c980 Cyclone", - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, }, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM|EXTRA_PREAMBLE, 128, }, {"3c980C Python-T", PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, }, diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 02d05b3..5af4d65 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -350,7 +350,6 @@ int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) struct pppox_opt *relay_po = NULL; if (sk->state & PPPOX_BOUND) { - skb_pull(skb, sizeof(struct pppoe_hdr)); ppp_input(&po->chan, skb); } else if (sk->state & PPPOX_RELAY) { relay_po = get_item_by_addr(&po->pppoe_relay); @@ -361,7 +360,6 @@ int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) if ((relay_po->sk->state & PPPOX_CONNECTED) == 0) goto abort_put; - skb_pull(skb, sizeof(struct pppoe_hdr)); if (!__pppoe_xmit( relay_po->sk , skb)) goto abort_put; } else { @@ -390,16 +388,21 @@ static int pppoe_rcv(struct sk_buff *skb, { struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw; + int len = ntohs(ph->length); struct pppox_opt *po; struct sock *sk ; int ret; + skb_pull(skb, sizeof(*ph)); + if (skb->len < len) + goto drop; + po = get_item((unsigned long) ph->sid, skb->mac.ethernet->h_source); + if (!po) + goto drop; - if (!po) { - kfree_skb(skb); - return NET_RX_DROP; - } + if (pskb_trim(skb, len)) + goto drop; sk = po->sk; bh_lock_sock(sk); @@ -416,6 +419,9 @@ static int pppoe_rcv(struct sk_buff *skb, sock_put(sk); return ret; + drop: + kfree_skb(skb); + return NET_RX_DROP; } /************************************************************************ @@ -901,6 +907,9 @@ int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) * give dev_queue_xmit something it can free. */ skb2 = skb_clone(skb, GFP_ATOMIC); + + if (skb2 == NULL) + goto abort; } ph = (struct pppoe_hdr *) skb_push(skb2, sizeof(struct pppoe_hdr)); @@ -952,8 +961,6 @@ int pppoe_rcvmsg(struct socket *sock, struct msghdr *m, int total_len, int flags struct sock *sk = sock->sk; struct sk_buff *skb = NULL; int error = 0; - int len; - struct pppoe_hdr *ph = NULL; if (sk->state & PPPOX_BOUND) { error = -EIO; @@ -970,17 +977,12 @@ int pppoe_rcvmsg(struct socket *sock, struct msghdr *m, int total_len, int flags m->msg_namelen = 0; if (skb) { - error = 0; - ph = (struct pppoe_hdr *) skb->nh.raw; - len = ntohs(ph->length); - - error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len); - if (error < 0) - goto do_skb_free; - error = len; + total_len = min_t(int, total_len, skb->len); + error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len); + if (error == 0) + error = total_len; } -do_skb_free: if (skb) kfree_skb(skb); end: diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 7830e4d..4883c0f 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -67,7 +67,7 @@ void pppox_unbind_sock(struct sock *sk) { /* Clear connection to ppp device, if attached. */ - if (sk->state & (PPPOX_BOUND|PPPOX_ZOMBIE)) { + if (sk->state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) { ppp_unregister_channel(&sk->protinfo.pppox->chan); sk->state = PPPOX_DEAD; } diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 5f19f61..f232bde 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1157,7 +1157,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) int avail; dma_addr_t mapping; u32 addr64; - u16 mss; + u16 mss = 0; u8 ctrl; spin_lock_irqsave(&sky2->tx_lock, flags); diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 91e12bd..2b35d99 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -530,7 +530,7 @@ struct netdev_private { /* MII transceiver section. */ unsigned char phys[MAX_MII_CNT]; /* MII device addresses. */ unsigned int mii_cnt; /* number of MIIs found, but only the first one is used */ - u16 mii_status; /* last read MII status */ + int last_duplex; /* last checked duplex */ struct mii_if_info mii_if; }; @@ -808,11 +808,11 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, /* The lower four bits are the media type. */ if (option > 0) { if (option & 0x220) - np->mii_if.full_duplex = 1; + np->last_duplex = np->mii_if.full_duplex = 1; np->default_port = option & 15; } if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) - np->mii_if.full_duplex = 1; + np->last_duplex = np->mii_if.full_duplex = 1; if (np->mii_if.full_duplex) { printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" @@ -859,7 +859,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, /* Allow forcing the media type. */ if (option > 0) { if (option & 0x220) - np->mii_if.full_duplex = 1; + np->last_duplex = np->mii_if.full_duplex = 1; np->default_port = option & 0x3ff; if (np->default_port & 0x330) { /* FIXME: shouldn't someone check this variable? */ @@ -1058,6 +1058,7 @@ static void init_registers(struct net_device *dev) np->tx_thresh = 0x20; np->rx_thresh = 0x60; /* Written in via_rhine_set_rx_mode(). */ np->mii_if.full_duplex = 0; + np->last_duplex = 0; if (dev->if_port == 0) dev->if_port = np->default_port; @@ -1119,7 +1120,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value if (value & 0x9000) /* Autonegotiation. */ np->mii_if.force_media = 0; else - np->mii_if.full_duplex = (value & 0x0100) ? 1 : 0; + np->last_duplex = np->mii_if.full_duplex = (value & 0x0100) ? 1 : 0; break; case MII_ADVERTISE: np->mii_if.advertising = value; @@ -1184,20 +1185,20 @@ static void via_rhine_check_duplex(struct net_device *dev) { struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; - int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); - int negotiated = mii_lpa & np->mii_if.advertising; - int duplex; - if (np->mii_if.force_media || mii_lpa == 0xffff) + if (np->mii_if.force_media) return; - duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; - if (np->mii_if.full_duplex != duplex) { - np->mii_if.full_duplex = duplex; + + mii_check_media(&np->mii_if, debug, 0); + + if (np->last_duplex != np->mii_if.full_duplex) { + np->last_duplex = np->mii_if.full_duplex; if (debug) printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner capability of %4.4x.\n", dev->name, - duplex ? "full" : "half", np->phys[0], mii_lpa); - if (duplex) + np->mii_if.full_duplex ? "full" : "half", np->phys[0], + mdio_read(dev, np->phys[0], MII_LPA)); + if (np->mii_if.full_duplex) np->chip_cmd |= CmdFDuplex; else np->chip_cmd &= ~CmdFDuplex; @@ -1211,8 +1212,7 @@ static void via_rhine_timer(unsigned long data) struct net_device *dev = (struct net_device *)data; struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; - int next_tick = 10*HZ; - int mii_status; + int next_tick = 2*HZ/10; if (debug > 3) { printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", @@ -1223,16 +1223,6 @@ static void via_rhine_timer(unsigned long data) via_rhine_check_duplex(dev); - /* make IFF_RUNNING follow the MII status bit "Link established" */ - mii_status = mdio_read(dev, np->phys[0], MII_BMSR); - if ( (mii_status & BMSR_LSTATUS) != (np->mii_status & BMSR_LSTATUS) ) { - if (mii_status & BMSR_LSTATUS) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - } - np->mii_status = mii_status; - spin_unlock_irq (&np->lock); np->timer.expires = jiffies + next_tick; diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 6715e9f..dba80ca 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -1297,7 +1297,7 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd ) break; case SIOCDEVRESINSTATS : - if( current->euid != 0 ) /* root only */ + if (!capable(CAP_NET_ADMIN)) /* root only */ return -EPERM; memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) ); break; @@ -1316,7 +1316,7 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd ) break; case SIOCDEVSHWSTATE : - if( current->euid != 0 ) /* root only */ + if (!capable(CAP_NET_ADMIN)) /* root only */ return -EPERM; spin_lock( &nl->lock ); @@ -1337,7 +1337,7 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd ) #ifdef CONFIG_SBNI_MULTILINE case SIOCDEVENSLAVE : - if( current->euid != 0 ) /* root only */ + if (!capable(CAP_NET_ADMIN)) /* root only */ return -EPERM; if( (error = verify_area( VERIFY_READ, ifr->ifr_data, @@ -1355,7 +1355,7 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd ) return enslave( dev, slave_dev ); case SIOCDEVEMANSIPATE : - if( current->euid != 0 ) /* root only */ + if (!capable(CAP_NET_ADMIN)) /* root only */ return -EPERM; return emancipate( dev ); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 2862039..e24e13f 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -2282,7 +2282,7 @@ EXPORT_SYMBOL(init_airo_card); static int waitbusy (struct airo_info *ai) { int delay = 0; - while ((IN4500 (ai, COMMAND) & COMMAND_BUSY) & (delay < 10000)) { + while ((IN4500 (ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { udelay (10); if (++delay % 20) OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 57d0a40..c6d7bdf 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -683,6 +683,8 @@ static int aac_cfg_release(struct inode * inode, struct file * file ) static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg ) { struct aac_dev *dev = aac_devices[MINOR(inode->i_rdev)]; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; return aac_do_ioctl(dev, cmd, (void *)arg); } diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index d598277..1a59fe4 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -255,6 +255,7 @@ static struct ata_port_info ahci_port_info[] = { }; static const struct pci_device_id ahci_pci_tbl[] = { + /* INTEL */ { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* ICH6 */ { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, @@ -285,14 +286,211 @@ static const struct pci_device_id ahci_pci_tbl[] = { board_ahci }, /* ICH8M */ { PCI_VENDOR_ID_INTEL, 0x282a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* ICH8M */ + { PCI_VENDOR_ID_INTEL, 0x2922, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x2923, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x2924, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x2925, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x2927, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x2929, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x292a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x292b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x292c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x292f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x294d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9 */ + { PCI_VENDOR_ID_INTEL, 0x294e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH9M */ + { PCI_VENDOR_ID_INTEL, 0x502a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* Tolapai */ + { PCI_VENDOR_ID_INTEL, 0x502b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* Tolapai */ + { PCI_VENDOR_ID_INTEL, 0x3a05, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH10 */ + { PCI_VENDOR_ID_INTEL, 0x3a25, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH10 */ + { PCI_VENDOR_ID_INTEL, 0x3b24, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* PCH RAID */ + { PCI_VENDOR_ID_INTEL, 0x3b2b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* PCH RAID */ + + /* JMICRON */ { 0x197b, 0x2360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* JMicron JMB360 */ { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* JMicron JMB363 */ + + /* ATI */ { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 non-raid */ - { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 raid */ + board_ahci }, /* ATI SB600 */ + { PCI_VENDOR_ID_ATI, 0x4390, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + { PCI_VENDOR_ID_ATI, 0x4391, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + { PCI_VENDOR_ID_ATI, 0x4392, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + { PCI_VENDOR_ID_ATI, 0x4393, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + { PCI_VENDOR_ID_ATI, 0x4394, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + { PCI_VENDOR_ID_ATI, 0x4395, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB700/800 */ + + /* NVIDIA */ + { PCI_VENDOR_ID_NVIDIA, 0x044c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x045c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x045d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x045e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x045f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x0550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0556, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x0559, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x055a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x055b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP67 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07fa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x07fb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP73 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ad9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ada, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0adb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP77 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0ab9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0aba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0abb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0abc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0abd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0abe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0abf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP79 */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bc9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bca, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bcb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bcc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bcd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bce, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + { PCI_VENDOR_ID_NVIDIA, 0x0bcf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP7B */ + + /* SIS */ + { PCI_VENDOR_ID_SI, 0x1184, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x1185, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x1186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 968 */ + { } /* terminate list */ }; diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 904787b..bcc2644 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -117,6 +117,9 @@ static const struct pci_device_id piix_pci_tbl[] = { { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, { 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, + { 0x8086, 0x2829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, + { 0x8086, 0x2920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, + { 0x8086, 0x2926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata }, { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb2_sata }, { } /* terminate list */ diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index 98e3e81..89ec9bf 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -535,6 +535,13 @@ void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt); int scsi_register_device(struct Scsi_Device_Template * sdpnt); void scsi_deregister_device(struct Scsi_Device_Template * tpnt); +/* Support for hot plugging and unplugging devices -- safe for + * ieee1394 or USB devices, but probably not for normal SCSI... */ +extern int scsi_add_single_device(struct Scsi_Host *shpnt, + int channel, int id, int lun); +extern int scsi_remove_single_device(struct Scsi_Host *shpnt, + int channel, int id, int lun); + /* These are used by loadable modules */ extern int scsi_register_module(int, void *); extern int scsi_unregister_module(int, void *); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 31f8dc0..5bb383e 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1554,6 +1554,156 @@ void __init scsi_host_no_insert(char *str, int n) } } + +static DECLARE_MUTEX(scsi_host_internals_lock); +/* + * Function: scsi_add_single_device() + * + * Purpose: Support for hotplugging SCSI devices. This function + * implements the actual functionality for + * echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi + * + * Arguments: shpnt - pointer to the SCSI host structure + * channel - channel of the device to add + * id - id of the device to add + * lun - lun of the device to add + * + * Returns: 0 on success or an error code + * + * Lock status: None needed. + * + * Notes: This feature is probably unsafe for standard SCSI devices, + * but is perfectly normal for things like ieee1394 or USB + * drives since these busses are designed for hotplugging. + * Use at your own risk.... + */ +int scsi_add_single_device(struct Scsi_Host *shpnt, int channel, + int id, int lun) +{ + Scsi_Device *scd; + + /* Do a bit of sanity checking */ + if (shpnt==NULL) { + return -ENXIO; + } + + /* We call functions that can sleep, so use a semaphore to + * avoid racing with scsi_remove_single_device(). We probably + * need to also apply this lock to scsi_register*(), + * scsi_unregister*(), sd_open(), sd_release() and anything + * else that might be messing with with the Scsi_Host or other + * fundamental data structures. */ + down(&scsi_host_internals_lock); + + /* Check if they asked us to add an already existing device. + * If so, ignore their misguided efforts. */ + for (scd = shpnt->host_queue; scd; scd = scd->next) { + if ((scd->channel == channel && scd->id == id && scd->lun == lun)) { + break; + } + } + if (scd) { + up(&scsi_host_internals_lock); + return -ENOSYS; + } + + scan_scsis(shpnt, 1, channel, id, lun); + up(&scsi_host_internals_lock); + return 0; +} + +/* + * Function: scsi_remove_single_device() + * + * Purpose: Support for hot-unplugging SCSI devices. This function + * implements the actual functionality for + * echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi + * + * Arguments: shpnt - pointer to the SCSI host structure + * channel - channel of the device to add + * id - id of the device to add + * lun - lun of the device to add + * + * Returns: 0 on success or an error code + * + * Lock status: None needed. + * + * Notes: This feature is probably unsafe for standard SCSI devices, + * but is perfectly normal for things like ieee1394 or USB + * drives since these busses are designed for hotplugging. + * Use at your own risk.... + */ +int scsi_remove_single_device(struct Scsi_Host *shpnt, int channel, + int id, int lun) +{ + Scsi_Device *scd; + struct Scsi_Device_Template *SDTpnt; + + /* Do a bit of sanity checking */ + if (shpnt==NULL) { + return -ENODEV; + } + + /* We call functions that can sleep, so use a semaphore to + * avoid racing with scsi_add_single_device(). We probably + * need to also apply this lock to scsi_register*(), + * scsi_unregister*(), sd_open(), sd_release() and anything + * else that might be messing with with the Scsi_Host or other + * fundamental data structures. */ + down(&scsi_host_internals_lock); + + /* Make sure the specified device is in fact present */ + for (scd = shpnt->host_queue; scd; scd = scd->next) { + if ((scd->channel == channel && scd->id == id && scd->lun == lun)) { + break; + } + } + if (scd==NULL) { + up(&scsi_host_internals_lock); + return -ENODEV; + } + + /* See if the specified device is busy. Doesn't this race with + * sd_open(), sd_release() and similar? Why don't they lock + * things when they increment/decrement the access_count? */ + if (scd->access_count) { + up(&scsi_host_internals_lock); + return -EBUSY; + } + + SDTpnt = scsi_devicelist; + while (SDTpnt != NULL) { + if (SDTpnt->detach) + (*SDTpnt->detach) (scd); + SDTpnt = SDTpnt->next; + } + + if (scd->attached == 0) { + /* Nobody is using this device, so we + * can now free all command structures. */ + if (shpnt->hostt->revoke) + shpnt->hostt->revoke(scd); + devfs_unregister (scd->de); + scsi_release_commandblocks(scd); + + /* Now we can remove the device structure */ + if (scd->next != NULL) + scd->next->prev = scd->prev; + + if (scd->prev != NULL) + scd->prev->next = scd->next; + + if (shpnt->host_queue == scd) { + shpnt->host_queue = scd->next; + } + blk_cleanup_queue(&scd->request_queue); + kfree((char *) scd); + } + + up(&scsi_host_internals_lock); + return 0; +} + #ifdef CONFIG_PROC_FS static int scsi_proc_info(char *buffer, char **start, off_t offset, int length) { @@ -1606,8 +1756,6 @@ stop_output: static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data) { - struct Scsi_Device_Template *SDTpnt; - Scsi_Device *scd; struct Scsi_Host *HBA_ptr; char *p; int host, channel, id, lun; @@ -1722,13 +1870,12 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, /* * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi * with "0 1 2 3" replaced by your "Host Channel Id Lun". - * Consider this feature BETA. + * + * Consider this feature pre-BETA. + * * CAUTION: This is not for hotplugging your peripherals. As * SCSI was not designed for this you could damage your - * hardware ! - * However perhaps it is legal to switch on an - * already connected device. It is perhaps not - * guaranteed this device doesn't corrupt an ongoing data transfer. + * hardware and thoroughly confuse the SCSI subsystem. */ if (!strncmp("add-single-device", buffer + 5, 17)) { p = buffer + 23; @@ -1746,33 +1893,11 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, break; } } - err = -ENXIO; - if (!HBA_ptr) - goto out; - - for (scd = HBA_ptr->host_queue; scd; scd = scd->next) { - if ((scd->channel == channel - && scd->id == id - && scd->lun == lun)) { - break; - } - } - - err = -ENOSYS; - if (scd) - goto out; /* We do not yet support unplugging */ - - scan_scsis(HBA_ptr, 1, channel, id, lun); - - /* FIXME (DB) This assumes that the queue_depth routines can be used - in this context as well, while they were all designed to be - called only once after the detect routine. (DB) */ - /* queue_depth routine moved to inside scan_scsis(,1,,,) so - it is called before build_commandblocks() */ - - err = length; + if ((err=scsi_add_single_device(HBA_ptr, channel, id, lun))==0) + err = length; goto out; } + /* * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi * with "0 1 2 3" replaced by your "Host Channel Id Lun". @@ -1782,7 +1907,6 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, * CAUTION: This is not for hotplugging your peripherals. As * SCSI was not designed for this you could damage your * hardware and thoroughly confuse the SCSI subsystem. - * */ else if (!strncmp("remove-single-device", buffer + 5, 20)) { p = buffer + 26; @@ -1798,58 +1922,8 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, break; } } - err = -ENODEV; - if (!HBA_ptr) - goto out; - - for (scd = HBA_ptr->host_queue; scd; scd = scd->next) { - if ((scd->channel == channel - && scd->id == id - && scd->lun == lun)) { - break; - } - } - - if (scd == NULL) - goto out; /* there is no such device attached */ - - err = -EBUSY; - if (scd->access_count) - goto out; - - SDTpnt = scsi_devicelist; - while (SDTpnt != NULL) { - if (SDTpnt->detach) - (*SDTpnt->detach) (scd); - SDTpnt = SDTpnt->next; - } - - if (scd->attached == 0) { - /* - * Nobody is using this device any more. - * Free all of the command structures. - */ - if (HBA_ptr->hostt->revoke) - HBA_ptr->hostt->revoke(scd); - devfs_unregister (scd->de); - scsi_release_commandblocks(scd); - - /* Now we can remove the device structure */ - if (scd->next != NULL) - scd->next->prev = scd->prev; - - if (scd->prev != NULL) - scd->prev->next = scd->next; - - if (HBA_ptr->host_queue == scd) { - HBA_ptr->host_queue = scd->next; - } - blk_cleanup_queue(&scd->request_queue); - kfree((char *) scd); - } else { - goto out; - } - err = 0; + err=scsi_remove_single_device(HBA_ptr, channel, id, lun); + goto out; } out: diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index f78001d..6e82431 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -104,3 +104,9 @@ extern void scsi_add_timer(Scsi_Cmnd *, int, void ((*) (Scsi_Cmnd *))); extern int scsi_delete_timer(Scsi_Cmnd *); EXPORT_SYMBOL(scsi_add_timer); EXPORT_SYMBOL(scsi_delete_timer); + +/* Support for hot plugging and unplugging devices -- safe for + * ieee1394 or USB devices, but probably not for normal SCSI... */ +EXPORT_SYMBOL(scsi_add_single_device); +EXPORT_SYMBOL(scsi_remove_single_device); + diff --git a/drivers/sound/ad1889.c b/drivers/sound/ad1889.c index ec017e1..d4fecf2 100644 --- a/drivers/sound/ad1889.c +++ b/drivers/sound/ad1889.c @@ -854,7 +854,7 @@ static int ad1889_ac97_init(ad1889_dev_t *dev, int id) } eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID); - if (eid == 0xffffff) { + if (eid == 0xffff) { printk(KERN_WARNING DEVNAME ": no codec attached?\n"); goto out_free; } diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index 864f164..db5da3c 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -2111,6 +2111,7 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma, { struct via_info *card = vma->vm_private_data; struct via_channel *chan = &card->ch_out; + unsigned long max_bufs; struct page *dmapage; unsigned long pgoff; int rd, wr; @@ -2135,14 +2136,11 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma, rd = card->ch_in.is_mapped; wr = card->ch_out.is_mapped; -#ifndef VIA_NDEBUG - { - unsigned long max_bufs = chan->frag_number; - if (rd && wr) max_bufs *= 2; - /* via_dsp_mmap() should ensure this */ - assert (pgoff < max_bufs); - } -#endif + max_bufs = chan->frag_number; + if (rd && wr) + max_bufs *= 2; + if (pgoff >= max_bufs) + return NOPAGE_SIGBUS; /* if full-duplex (read+write) and we have two sets of bufs, * then the playback buffers come first, sez soundcard.c */ diff --git a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h index b7eb834..c37a503 100644 --- a/drivers/usb/pegasus.h +++ b/drivers/usb/pegasus.h @@ -177,6 +177,9 @@ PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", PEGASUS_DEV( "ADMtek ADM8513 \"Pegasus II\" USB Ethernet", VENDOR_ADMTEK, 0x8513, DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "ADMtek ADM8515 \"Pegasus II\" USB Ethernet", + VENDOR_ADMTEK, 0x8515, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (evaluation board)", VENDOR_ADMTEK, 0x0986, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) diff --git a/drivers/usb/pwc-if.c b/drivers/usb/pwc-if.c index e458515..a1c3989 100644 --- a/drivers/usb/pwc-if.c +++ b/drivers/usb/pwc-if.c @@ -1055,11 +1055,16 @@ static int pwc_video_open(struct video_device *vdev, int mode) return 0; } +static void pwc_cleanup(struct pwc_device *pdev) +{ + video_unregister_device(&pdev->vdev); +} + /* Note that all cleanup is done in the reverse order as in _open */ static void pwc_video_close(struct video_device *vdev) { struct pwc_device *pdev; - int i; + int i, hint; Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); @@ -1083,8 +1088,9 @@ static void pwc_video_close(struct video_device *vdev) pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); + lock_kernel(); /* Turn off LEDS and power down camera, but only when not unplugged */ - if (pdev->error_status != EPIPE) { + if (!pdev->unplugged) { if (pwc_set_leds(pdev, 0, 0) < 0) Info("Failed to set LED on/off time.\n"); if (power_save) { @@ -1092,9 +1098,18 @@ static void pwc_video_close(struct video_device *vdev) if (i < 0) Err("Failed to power down camera (%d)\n", i); } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + } else { + pwc_cleanup(pdev); + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; } - pdev->vopen = 0; - Trace(TRACE_OPEN, "<< video_close()\n"); + unlock_kernel(); } /* @@ -1897,19 +1912,20 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) /* Alert waiting processes */ wake_up_interruptible(&pdev->frameq); /* Wait until device is closed */ - while (pdev->vopen) - schedule(); - /* Device is now closed, so we can safely unregister it */ - Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); - video_unregister_device(&pdev->vdev); - - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); - - /* search device_hint[] table if we occupy a slot, by any chance */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) - if (device_hint[hint].pdev == pdev) - device_hint[hint].pdev = NULL; + if(pdev->vopen) { + pdev->unplugged = 1; + } else { + /* Device is closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + pwc_cleanup(pdev); + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; + } unlock_kernel(); } diff --git a/drivers/usb/pwc.h b/drivers/usb/pwc.h index b020769..999daf2 100644 --- a/drivers/usb/pwc.h +++ b/drivers/usb/pwc.h @@ -141,6 +141,7 @@ struct pwc_device char vsnapshot; /* snapshot mode */ char vsync; /* used by isoc handler */ char vmirror; /* for ToUCaM series */ + char unplugged; /* The image acquisition requires 3 to 4 steps: 1. data is gathered in short packets from the USB controller diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 636e024..0279485 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1,17 +1,20 @@ /* * Prolific PL2303 USB to serial adaptor driver * - * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * * Original driver for 2.2.x by anonymous * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. * * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * 2008_Feb)22 dn + * Back-port pl2303.c from linux-2.6.24.1. davidn@davidnewall.com. + * * 2003_Apr_24 gkh * Added line error reporting support. Hopefully it is correct... * @@ -33,6 +36,9 @@ * */ +/* TODO first char received is lost on second open of device. anecdotal evidence + * TODO suggests this might be on all even opens of device. dn. */ + #include #include #include @@ -59,30 +65,64 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.10.1" /* Takes from 2.6's */ #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" +#define PL2303_CLOSING_WAIT (30*HZ) +#define PL2303_BUF_SIZE 1024 +#define PL2303_TMP_BUF_SIZE 1024 + +struct pl2303_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, + { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, + { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, + { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, + { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, + { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) }, + { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, + { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, + { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, + { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, + { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, + { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, + { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, + { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, + { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, + { USB_DEVICE(HL340_VENDOR_ID, HL340_PRODUCT_ID) }, + { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); - +MODULE_DEVICE_TABLE(usb, id_table); #define SET_LINE_REQUEST_TYPE 0x21 #define SET_LINE_REQUEST 0x20 @@ -129,33 +169,12 @@ static void pl2303_read_bulk_callback (struct urb *urb); static void pl2303_write_bulk_callback (struct urb *urb); static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int pl2303_write_room(struct usb_serial_port *port); static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); static int pl2303_startup (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial); -/* All of the device info needed for the PL2303 SIO serial converter */ -static struct usb_serial_device_type pl2303_device = { - .owner = THIS_MODULE, - .name = "PL-2303", - .id_table = id_table, - .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = 1, - .num_bulk_out = 1, - .num_ports = 1, - .open = pl2303_open, - .close = pl2303_close, - .write = pl2303_write, - .ioctl = pl2303_ioctl, - .break_ctl = pl2303_break_ctl, - .set_termios = pl2303_set_termios, - .read_bulk_callback = pl2303_read_bulk_callback, - .read_int_callback = pl2303_read_int_callback, - .write_bulk_callback = pl2303_write_bulk_callback, - .startup = pl2303_startup, - .shutdown = pl2303_shutdown, -}; - enum pl2303_type { type_0, /* don't know the difference between type 0 and */ type_1, /* type 1, until someone from prolific tells us... */ @@ -164,6 +183,8 @@ enum pl2303_type { struct pl2303_private { spinlock_t lock; + struct pl2303_buf *buf; + int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; @@ -171,8 +192,177 @@ struct pl2303_private { enum pl2303_type type; }; +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + struct pl2303_buf *pb; + + if (size == 0) + return NULL; + + pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; + } + + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; + + return pb; +} + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ +static void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb) { + kfree(pb->buf_buf); + kfree(pb); + } +} + +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) + pb->buf_get = pb->buf_put; + /* equivalent to a get of all data available */ +} + +/* + * pl2303_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +{ + if (pb == NULL) + return 0; + + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); +} + +/* + * pl2303_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +{ + if (pb == NULL) + return 0; + + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); +} + +static int copy(char *dest, int from_user, const char *source, unsigned count) +{ + if (!from_user) + memcpy (dest, source, count); + else if (copy_from_user (dest, source, count)) + return -EFAULT; + return count; +} + +/* + * pl2303_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, int from_user, + const char *buf, unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; -static int pl2303_startup (struct usb_serial *serial) + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + if (copy(pb->buf_put, from_user, buf, len) < 0 || + copy(pb->buf_buf, from_user, buf+len, count - len) < 0) + return -EFAULT; + pb->buf_put = pb->buf_buf + count - len; + } else { + if (copy(pb->buf_put, from_user, buf, count) < 0) + return -EFAULT; + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; +} + +/* + * pl2303_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; +} + +static int pl2303_startup(struct usb_serial *serial) { struct pl2303_private *priv; enum pl2303_type type = type_0; @@ -191,65 +381,136 @@ static int pl2303_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); if (!priv) - return -ENOMEM; + goto cleanup; memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); + priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + if (priv->buf == NULL) { + kfree(priv); + goto cleanup; + } init_waitqueue_head(&priv->delta_msr_wait); usb_set_serial_port_data(&serial->port[i], priv); priv->type = type; } return 0; + +cleanup: + for (--i; i>=0; --i) { + priv = usb_get_serial_port_data(&serial->port[i]); + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(&serial->port[i], NULL); + } + return -ENOMEM; } -static int set_control_lines (struct usb_device *dev, u8 value) +static int set_control_lines(struct usb_device *dev, u8 value) { int retval; - retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0), - SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, - value, 0, NULL, 0, 100); + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, + value, 0, NULL, 0, 100); dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval); return retval; } -static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +static void pl2303_send(struct usb_serial_port *port) { - int result; + int count, result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; - dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + dbg("%s - port %d", __FUNCTION__, port->number); - if (!count) - return count; + spin_lock_irqsave(&priv->lock, flags); - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - return 0; + if (priv->write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - if (from_user) { - if (copy_from_user (port->write_urb->transfer_buffer, buf, count)) - return -EFAULT; - } else { - memcpy (port->write_urb->transfer_buffer, buf, count); + count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, + port->bulk_out_size); + + if (count == 0) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - + + priv->write_urb_in_use = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; result = usb_submit_urb (port->write_urb); - if (result) + if (result) { err("%s - failed submitting write urb, error %d", __FUNCTION__, result); - else - result = count; + priv->write_urb_in_use = 0; + // TODO: reschedule pl2303_send + } - return result; + wake_up_interruptible(&port->tty->write_wait); /* this, at very least, wakes up pl2303_close */ } +static int pl2303_write(struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + + if (!count) + return count; + + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, from_user, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + pl2303_send(port); -static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) + return count; +} + +static int pl2303_write_room(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int room = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + room = pl2303_buf_space_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, room); + return room; +} + +static int pl2303_chars_in_buffer(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int chars = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + chars = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, chars); + return chars; +} + +static void pl2303_set_termios(struct usb_serial_port *port, + struct termios *old_termios) { struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -278,6 +539,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol cflag = port->tty->termios->c_cflag; /* check that they really want us to change something */ + /* important to do this as even a null change can corrupt data in transit */ if (old_termios) { if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { @@ -292,13 +554,12 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol return; } memset (buf, 0x00, 0x07); - - i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CSIZE) { switch (cflag & CSIZE) { @@ -311,28 +572,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol dbg("%s - data bits = %d", __FUNCTION__, buf[6]); } - baud = 0; - switch (cflag & CBAUD) { - case B0: baud = 0; break; - case B75: baud = 75; break; - case B150: baud = 150; break; - case B300: baud = 300; break; - case B600: baud = 600; break; - case B1200: baud = 1200; break; - case B1800: baud = 1800; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - case B57600: baud = 57600; break; - case B115200: baud = 115200; break; - case B230400: baud = 230400; break; - case B460800: baud = 460800; break; - default: - err ("pl2303 driver does not support the baudrate requested (fix it)"); - break; - } + baud = tty_get_baud_rate(port->tty); dbg("%s - baud = %d", __FUNCTION__, baud); if (baud) { buf[0] = baud & 0xff; @@ -370,10 +610,10 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol dbg("%s - parity = none", __FUNCTION__); } - i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0x21:0x20:0:0 %d", i); + i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0x21:0x20:0:0 %d", i); /* change control lines if we are switching to or from B0 */ spin_lock_irqsave(&priv->lock, flags); @@ -389,13 +629,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } else { spin_unlock_irqrestore(&priv->lock, flags); } - + buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; - i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, + i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CRTSCTS) { @@ -404,24 +644,121 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol index = 0x61; else index = 0x41; - i = usb_control_msg(serial->dev, + i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, 0x0, index, NULL, 0, 100); - dbg ("0x40:0x1:0x0:0x%x %d", index, i); + dbg("0x40:0x1:0x0:0x%x %d", index, i); + } else { + i = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + VENDOR_WRITE_REQUEST, + VENDOR_WRITE_REQUEST_TYPE, + 0x0, 0x0, NULL, 0, 100); + dbg ("0x40:0x1:0x0:0x0 %d", i); } - kfree (buf); -} + /* FIXME: Need to read back resulting baud rate */ +// 2.6 if (baud) +// 2.6 tty_encode_baud_rate(port->tty, baud, baud); + kfree(buf); +} -static int pl2303_open (struct usb_serial_port *port, struct file *filp) +static void pl2303_close(struct usb_serial_port *port, struct file *filp) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial; + unsigned long flags; + unsigned int c_cflag; + int result, bps; + long timeout; + wait_queue_t wait; + + if (port_paranoia_check (port, __FUNCTION__)) + return; + serial = get_usb_serial (port, __FUNCTION__); + if (!serial) + return; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = PL2303_CLOSING_WAIT; + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (pl2303_buf_data_avail(priv->buf) == 0 || + timeout == 0 || signal_pending(current)) + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); + + if (serial->dev) { + if (port->tty) { + c_cflag = port->tty->termios->c_cflag; + if (c_cflag & HUPCL) { + /* drop DTR and RTS */ + spin_lock_irqsave(&priv->lock, flags); + priv->line_control = 0; + spin_unlock_irqrestore(&priv->lock, flags); + set_control_lines(port->serial->dev, 0); + } + } + + /* shutdown our urbs */ + dbg("%s - shutting down urbs", __FUNCTION__); + result = usb_unlink_urb (port->write_urb); + if (result) + dbg("%s - usb_unlink_urb (write_urb)" + " failed with reason: %d", __FUNCTION__, + result); + + result = usb_unlink_urb (port->read_urb); + if (result) + dbg("%s - usb_unlink_urb (read_urb) " + "failed with reason: %d", __FUNCTION__, + result); + + result = usb_unlink_urb (port->interrupt_in_urb); + if (result) + dbg("%s - usb_unlink_urb (interrupt_in_urb)" + " failed with reason: %d", __FUNCTION__, + result); + } +} + +static int pl2303_open(struct usb_serial_port *port, struct file *filp) { struct termios tmp_termios; struct usb_serial *serial = port->serial; + struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned char *buf; - struct pl2303_private *priv = port->private; int result; if (port_paranoia_check (port, __FUNCTION__)) @@ -429,8 +766,10 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __FUNCTION__, port->number); - usb_clear_halt(serial->dev, port->write_urb->pipe); - usb_clear_halt(serial->dev, port->read_urb->pipe); + if (priv->type != HX) { + usb_clear_halt(serial->dev, port->write_urb->pipe); + usb_clear_halt(serial->dev, port->read_urb->pipe); + } buf = kmalloc(10, GFP_KERNEL); if (buf==NULL) @@ -456,7 +795,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0); - + if (priv->type == HX) { /* HX chip */ SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44); @@ -471,7 +810,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) /* Setup termios */ if (port->tty) { - pl2303_set_termios (port, &tmp_termios); + pl2303_set_termios(port, &tmp_termios); } //FIXME: need to assert RTS and DTR if CRTSCTS off @@ -481,7 +820,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) result = usb_submit_urb (port->read_urb); if (result) { err("%s - failed submitting read urb, error %d", __FUNCTION__, result); - pl2303_close (port, NULL); + pl2303_close(port, NULL); return -EPROTO; } @@ -490,105 +829,35 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) result = usb_submit_urb (port->interrupt_in_urb); if (result) { err("%s - failed submitting interrupt urb, error %d", __FUNCTION__, result); - pl2303_close (port, NULL); + pl2303_close(port, NULL); return -EPROTO; } return 0; } - -static void pl2303_close (struct usb_serial_port *port, struct file *filp) -{ - struct usb_serial *serial; - struct pl2303_private *priv; - unsigned long flags; - unsigned int c_cflag; - int result; - - if (port_paranoia_check (port, __FUNCTION__)) - return; - serial = get_usb_serial (port, __FUNCTION__); - if (!serial) - return; - - dbg("%s - port %d", __FUNCTION__, port->number); - - if (serial->dev) { - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore (&priv->lock, flags); - set_control_lines (port->serial->dev, 0); - } - } - - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __FUNCTION__); - result = usb_unlink_urb (port->write_urb); - if (result) - dbg("%s - usb_unlink_urb (write_urb)" - " failed with reason: %d", __FUNCTION__, - result); - - result = usb_unlink_urb (port->read_urb); - if (result) - dbg("%s - usb_unlink_urb (read_urb) " - "failed with reason: %d", __FUNCTION__, - result); - - result = usb_unlink_urb (port->interrupt_in_urb); - if (result) - dbg("%s - usb_unlink_urb (interrupt_in_urb)" - " failed with reason: %d", __FUNCTION__, - result); - } -} - -static int set_modem_info (struct usb_serial_port *port, unsigned int cmd, unsigned int *value) +static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int arg; u8 control; - if (copy_from_user(&arg, value, sizeof(int))) - return -EFAULT; - - spin_lock_irqsave (&priv->lock, flags); - switch (cmd) { - case TIOCMBIS: - if (arg & TIOCM_RTS) - priv->line_control |= CONTROL_RTS; - if (arg & TIOCM_DTR) - priv->line_control |= CONTROL_DTR; - break; - - case TIOCMBIC: - if (arg & TIOCM_RTS) - priv->line_control &= ~CONTROL_RTS; - if (arg & TIOCM_DTR) - priv->line_control &= ~CONTROL_DTR; - break; - - case TIOCMSET: - /* turn off RTS and DTR and then only turn - on what was asked to */ - priv->line_control &= ~(CONTROL_RTS | CONTROL_DTR); - priv->line_control |= ((arg & TIOCM_RTS) ? CONTROL_RTS : 0); - priv->line_control |= ((arg & TIOCM_DTR) ? CONTROL_DTR : 0); - break; - } + spin_lock_irqsave(&priv->lock, flags); + if (set & TIOCM_RTS) + priv->line_control |= CONTROL_RTS; + if (set & TIOCM_DTR) + priv->line_control |= CONTROL_DTR; + if (clear & TIOCM_RTS) + priv->line_control &= ~CONTROL_RTS; + if (clear & TIOCM_DTR) + priv->line_control &= ~CONTROL_DTR; control = priv->line_control; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - return set_control_lines (port->serial->dev, control); + return set_control_lines(port->serial->dev, control); } -static int get_modem_info (struct usb_serial_port *port, unsigned int *value) +static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -596,10 +865,11 @@ static int get_modem_info (struct usb_serial_port *port, unsigned int *value) unsigned int status; unsigned int result; - spin_lock_irqsave (&priv->lock, flags); + dbg("%s (%d)", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); mcr = priv->line_control; status = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) | ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) @@ -610,9 +880,7 @@ static int get_modem_info (struct usb_serial_port *port, unsigned int *value) dbg("%s - result = %x", __FUNCTION__, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; + return result; } static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) @@ -623,22 +891,22 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) unsigned int status; unsigned int changed; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); prevstatus = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); while (1) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - - spin_lock_irqsave (&priv->lock, flags); + + spin_lock_irqsave(&priv->lock, flags); status = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); - + spin_unlock_irqrestore(&priv->lock, flags); + changed=prevstatus^status; - + if (((arg & TIOCM_RNG) && (changed & UART_RING)) || ((arg & TIOCM_DSR) && (changed & UART_DSR)) || ((arg & TIOCM_CD) && (changed & UART_DCD)) || @@ -651,26 +919,44 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg) { + int value; dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); switch (cmd) { - case TIOCMGET: dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number); - return get_modem_info (port, (unsigned int *)arg); + value = pl2303_tiocmget(port, file); + if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) + return -EFAULT; + return 0; case TIOCMBIS: + dbg("%s (%d) TIOCMBIS", __FUNCTION__, port->number); + if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) + return -EFAULT; + return pl2303_tiocmset(port, file, value, 0); + case TIOCMBIC: + dbg("%s (%d) TIOCMBIC", __FUNCTION__, port->number); + if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) + return -EFAULT; + return pl2303_tiocmset(port, file, 0, value); + case TIOCMSET: - dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__, port->number); - return set_modem_info(port, cmd, (unsigned int *) arg); + dbg("%s (%d) TIOCMSET", __FUNCTION__, port->number); + if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) + return -EFAULT; + /* turn off RTS and DTR and then only turn + on what was asked to */ + return pl2303_tiocmset(port, file, value, value^(TIOCM_RTS|TIOCM_DTR)); case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number); return wait_modem_info(port, arg); - + default: dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); break; @@ -679,7 +965,7 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsign return -ENOIOCTLCMD; } -static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) +static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) { struct usb_serial *serial = port->serial; u16 state; @@ -693,41 +979,75 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) state = BREAK_ON; dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on"); - result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + BREAK_REQUEST, BREAK_REQUEST_TYPE, state, + 0, NULL, 0, 100); if (result) dbg("%s - error sending break = %d", __FUNCTION__, result); } - -static void pl2303_shutdown (struct usb_serial *serial) +static void pl2303_shutdown(struct usb_serial *serial) { int i; + struct pl2303_private *priv; dbg("%s", __FUNCTION__); for (i = 0; i < serial->num_ports; ++i) { - kfree (usb_get_serial_port_data(&serial->port[i])); - usb_set_serial_port_data(&serial->port[i], NULL); - } + priv = usb_get_serial_port_data(&serial->port[i]); + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(&serial->port[i], NULL); + } + } } +static void pl2303_update_line_status(struct usb_serial_port *port, + unsigned char *data, + unsigned int actual_length) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 status_idx = UART_STATE; + u8 length = UART_STATE + 1; + u16 idv, idp; + + idv = le16_to_cpu(port->serial->dev->descriptor.idVendor); + idp = le16_to_cpu(port->serial->dev->descriptor.idProduct); + -static void pl2303_read_int_callback (struct urb *urb) + if (idv == SIEMENS_VENDOR_ID) { + if (idp == SIEMENS_PRODUCT_ID_X65 || + idp == SIEMENS_PRODUCT_ID_SX1 || + idp == SIEMENS_PRODUCT_ID_X75) { + + length = 1; + status_idx = 0; + } + } + + if (actual_length < length) + return; + + /* Save off the uart status for others to look at */ + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = data[status_idx]; + spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible(&priv->delta_msr_wait); +} + +static void pl2303_read_int_callback(struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; - struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; - unsigned long flags; - u8 uart_state; + int status = urb->status; dbg("%s (%d)", __FUNCTION__, port->number); /* ints auto restart... */ - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -735,68 +1055,51 @@ static void pl2303_read_int_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - return; - } - - if (!serial) { + dbg("%s - nonzero urb status received: %d", __FUNCTION__, + status); return; } usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer); - - if (urb->actual_length < UART_STATE) - return; - - /* Save off the uart status for others to look at */ - uart_state = data[UART_STATE]; - spin_lock_irqsave(&priv->lock, flags); - uart_state |= (priv->line_status & UART_STATE_TRANSIENT_MASK); - priv->line_status = uart_state; - spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible (&priv->delta_msr_wait); - - return; + pl2303_update_line_status(port, data, urb->actual_length); } - -static void pl2303_read_bulk_callback (struct urb *urb) +static void pl2303_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; - struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct pl2303_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; unsigned long flags; int i; int result; - u8 status; + int status = urb->status; + u8 line_status; char tty_flag; - if (port_paranoia_check (port, __FUNCTION__)) - return; - dbg("%s - port %d", __FUNCTION__, port->number); - if (!serial) { + if (!get_usb_serial (port, __FUNCTION__)) { dbg("%s - bad serial pointer, exiting", __FUNCTION__); return; } - if (urb->status) { - dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - urb status = %d", __FUNCTION__, status); if (!port->open_count) { dbg("%s - port is closed, exiting.", __FUNCTION__); return; } - if (urb->status == -EPROTO) { - /* PL2303 mysteriously fails with -EPROTO reschedule the read */ - dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); - urb->status = 0; - urb->dev = serial->dev; + if (status == -EPROTO) { + /* PL2303 mysteriously fails with -EPROTO reschedule + * the read */ + dbg("%s - caught -EPROTO, resubmitting the urb", + __FUNCTION__); + urb->dev = port->serial->dev; result = usb_submit_urb(urb); if (result) err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); @@ -812,39 +1115,38 @@ static void pl2303_read_bulk_callback (struct urb *urb) tty_flag = TTY_NORMAL; spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; + line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible (&priv->delta_msr_wait); //AF from 2.6 + wake_up_interruptible(&priv->delta_msr_wait); /* break takes precedence over parity, */ /* which takes precedence over framing errors */ - if (status & UART_BREAK_ERROR ) + if (line_status & UART_BREAK_ERROR ) tty_flag = TTY_BREAK; - else if (status & UART_PARITY_ERROR) + else if (line_status & UART_PARITY_ERROR) tty_flag = TTY_PARITY; - else if (status & UART_FRAME_ERROR) + else if (line_status & UART_FRAME_ERROR) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); tty = port->tty; if (tty && urb->actual_length) { /* overrun is special, not associated with a char */ - if (status & UART_OVERRUN_ERROR) + if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); for (i = 0; i < urb->actual_length; ++i) { - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) tty_flip_buffer_push(tty); - } - tty_insert_flip_char (tty, data[i], tty_flag); + tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push (tty); + tty_flip_buffer_push(tty); } /* Schedule the next read _if_ we are still open */ if (port->open_count) { - urb->dev = serial->dev; + urb->dev = port->serial->dev; result = usb_submit_urb(urb); if (result) err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); @@ -853,60 +1155,96 @@ static void pl2303_read_bulk_callback (struct urb *urb) return; } - - -static void pl2303_write_bulk_callback (struct urb *urb) +static void pl2303_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct pl2303_private *priv = usb_get_serial_port_data(port); int result; + int status = urb->status; if (port_paranoia_check (port, __FUNCTION__)) return; - + dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + status); + priv->write_urb_in_use = 0; + return; + default: /* error in the urb, so we have to resubmit it */ - if (serial_paranoia_check (port->serial, __FUNCTION__)) { + if (serial_paranoia_check (port->serial, __FUNCTION__)) return; - } dbg("%s - Overflow in write", __FUNCTION__); - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, + status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; result = usb_submit_urb (port->write_urb); if (result) err("%s - failed resubmitting write urb, error %d", __FUNCTION__, result); - - return; + else + return; } - queue_task(&port->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); + priv->write_urb_in_use = 0; - return; + /* send any buffered data */ + pl2303_send(port); } +/* All of the device info needed for the PL2303 SIO serial converter */ +static struct usb_serial_device_type pl2303_device = { + .owner = THIS_MODULE, + .name = "PL-2303", + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = pl2303_open, + .close = pl2303_close, + .write = pl2303_write, + .ioctl = pl2303_ioctl, + .break_ctl = pl2303_break_ctl, + .set_termios = pl2303_set_termios, +// 2.6 .tiocmget = pl2303_tiocmget, +// 2.6 .tiocmset = pl2303_tiocmset, + .read_bulk_callback = pl2303_read_bulk_callback, + .read_int_callback = pl2303_read_int_callback, + .write_bulk_callback = pl2303_write_bulk_callback, + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, + .startup = pl2303_startup, + .shutdown = pl2303_shutdown, +}; -static int __init pl2303_init (void) +static int __init pl2303_init(void) { int retval; + retval = usb_serial_register(&pl2303_device); if (retval) goto failed_usb_serial_register; - info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_DESC); return 0; failed_usb_serial_register: return retval; } - -static void __exit pl2303_exit (void) +static void __exit pl2303_exit(void) { - usb_serial_deregister (&pl2303_device); + usb_serial_deregister(&pl2303_device); } - module_init(pl2303_init); module_exit(pl2303_exit); diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 971c5bb..237a41f 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -9,7 +9,10 @@ */ #define PL2303_VENDOR_ID 0x067b #define PL2303_PRODUCT_ID 0x2303 -#define PL2303_PRODUCT_ID_RSAQ2 0x04bb +#define PL2303_PRODUCT_ID_RSAQ2 0x04bb +#define PL2303_PRODUCT_ID_DCU11 0x1234 +#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 +#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 @@ -17,18 +20,22 @@ #define IODATA_VENDOR_ID 0x04bb #define IODATA_PRODUCT_ID 0x0a03 +#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e #define ELCOM_VENDOR_ID 0x056e #define ELCOM_PRODUCT_ID 0x5003 +#define ELCOM_PRODUCT_ID_UCSGT 0x5004 #define ITEGNO_VENDOR_ID 0x0eba #define ITEGNO_PRODUCT_ID 0x1080 +#define ITEGNO_PRODUCT_ID_2080 0x2080 #define MA620_VENDOR_ID 0x0df7 #define MA620_PRODUCT_ID 0x0620 #define RATOC_VENDOR_ID 0x0584 #define RATOC_PRODUCT_ID 0xb000 +#define RATOC_PRODUCT_ID_USB60F 0xb020 #define TRIPP_VENDOR_ID 0x2478 #define TRIPP_PRODUCT_ID 0x2008 @@ -41,3 +48,67 @@ #define SITECOM_VENDOR_ID 0x6189 #define SITECOM_PRODUCT_ID 0x2068 + +/* Alcatel OT535/735 USB cable */ +#define ALCATEL_VENDOR_ID 0x11f7 +#define ALCATEL_PRODUCT_ID 0x02df + +/* Samsung I330 phone cradle */ +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_ID 0x8001 + +#define SIEMENS_VENDOR_ID 0x11f5 +#define SIEMENS_PRODUCT_ID_SX1 0x0001 +#define SIEMENS_PRODUCT_ID_X65 0x0003 +#define SIEMENS_PRODUCT_ID_X75 0x0004 +#define SIEMENS_PRODUCT_ID_EF81 0x0005 + +#define SYNTECH_VENDOR_ID 0x0745 +#define SYNTECH_PRODUCT_ID 0x0001 + +/* Nokia CA-42 Cable */ +#define NOKIA_CA42_VENDOR_ID 0x078b +#define NOKIA_CA42_PRODUCT_ID 0x1234 + +/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ +#define CA_42_CA42_VENDOR_ID 0x10b5 +#define CA_42_CA42_PRODUCT_ID 0xac70 + +#define SAGEM_VENDOR_ID 0x079b +#define SAGEM_PRODUCT_ID 0x0027 + +/* Leadtek GPS 9531 (ID 0413:2101) */ +#define LEADTEK_VENDOR_ID 0x0413 +#define LEADTEK_9531_PRODUCT_ID 0x2101 + +/* USB GSM cable from Speed Dragon Multimedia, Ltd */ +#define SPEEDDRAGON_VENDOR_ID 0x0e55 +#define SPEEDDRAGON_PRODUCT_ID 0x110b + +/* DATAPILOT Universal-2 Phone Cable */ +#define DATAPILOT_U2_VENDOR_ID 0x0731 +#define DATAPILOT_U2_PRODUCT_ID 0x2003 + +/* Belkin "F5U257" Serial Adapter */ +#define BELKIN_VENDOR_ID 0x050d +#define BELKIN_PRODUCT_ID 0x0257 + +/* Alcor Micro Corp. USB 2.0 TO RS-232 */ +#define ALCOR_VENDOR_ID 0x058F +#define ALCOR_PRODUCT_ID 0x9720 + +/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ +#define WS002IN_VENDOR_ID 0x11f6 +#define WS002IN_PRODUCT_ID 0x2001 + +/* Corega CG-USBRS232R Serial Adapter */ +#define COREGA_VENDOR_ID 0x07aa +#define COREGA_PRODUCT_ID 0x002a + +/* HL HL-340 (ID: 4348:5523) */ +#define HL340_VENDOR_ID 0x4348 +#define HL340_PRODUCT_ID 0x5523 + +/* Y.C. Cable U.S.A., Inc - USB to RS-232 */ +#define YCCABLE_VENDOR_ID 0x05ad +#define YCCABLE_PRODUCT_ID 0x0fba diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 417f297..bcd87db 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -744,6 +744,11 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, /* unlock the device pointers */ up(&(ss->dev_semaphore)); + /* Try to re-connect ourselves to the SCSI subsystem */ + if (scsi_add_single_device(ss->host, 0, 0, 0)) + printk(KERN_WARNING "Unable to connect USB device to the SCSI subsystem\n"); + else + printk(KERN_WARNING "USB device connected to the SCSI subsystem\n"); } else { /* New device -- allocate memory and initialize */ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); @@ -1066,6 +1071,12 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) /* lock access to the device data structure */ down(&(ss->dev_semaphore)); + /* Try to un-hook ourselves from the SCSI subsystem */ + if (scsi_remove_single_device(ss->host, 0, 0, 0)) + printk(KERN_WARNING "Unable to disconnect USB device from the SCSI subsystem\n"); + else + printk(KERN_WARNING "USB device disconnected from the SCSI subsystem\n"); + /* release the IRQ, if we have one */ if (ss->irq_urb) { US_DEBUGP("-- releasing irq URB\n"); diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 850ba5e..b6b1d7d 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -9,6 +9,76 @@ #include #include #include +#include + +static loff_t bad_file_llseek(struct file *file, loff_t offset, int origin) +{ + return -EIO; +} + +static ssize_t bad_file_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos) +{ + return -EIO; +} + +static ssize_t bad_file_write(struct file *filp, const char __user *buf, + size_t siz, loff_t *ppos) +{ + return -EIO; +} + +static int bad_file_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + return -EIO; +} + +static unsigned int bad_file_poll(struct file *filp, poll_table *wait) +{ + return POLLERR; +} + +static int bad_file_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return -EIO; +} + +static int bad_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + return -EIO; +} + +static int bad_file_open(struct inode *inode, struct file *filp) +{ + return -EIO; +} + +static int bad_file_flush(struct file *file) +{ + return -EIO; +} + +static int bad_file_release(struct inode *inode, struct file *filp) +{ + return -EIO; +} + +static int bad_file_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + return -EIO; +} + +static int bad_file_fasync(int fd, struct file *filp, int on) +{ + return -EIO; +} + +static int bad_file_lock(struct file *file, int cmd, struct file_lock *fl) +{ + return -EIO; +} /* * The follow_link operation is special: it must behave as a no-op @@ -20,46 +90,107 @@ static int bad_follow_link(struct dentry *dent, struct nameidata *nd) return vfs_follow_link(nd, ERR_PTR(-EIO)); } -static int return_EIO(void) +static struct file_operations bad_file_ops = +{ + llseek: bad_file_llseek, + read: bad_file_read, + write: bad_file_write, + readdir: bad_file_readdir, + poll: bad_file_poll, + ioctl: bad_file_ioctl, + mmap: bad_file_mmap, + open: bad_file_open, + flush: bad_file_flush, + release: bad_file_release, + fsync: bad_file_fsync, + fasync: bad_file_fasync, + lock: bad_file_lock, +}; + +static int bad_inode_create (struct inode *dir, struct dentry *dentry, + int mode) { return -EIO; } + +static struct dentry *bad_inode_lookup(struct inode *dir, + struct dentry *dentry) +{ + return ERR_PTR(-EIO); +} -#define EIO_ERROR ((void *) (return_EIO)) +static int bad_inode_link (struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + return -EIO; +} -static struct file_operations bad_file_ops = +static int bad_inode_unlink(struct inode *dir, struct dentry *dentry) { - llseek: EIO_ERROR, - read: EIO_ERROR, - write: EIO_ERROR, - readdir: EIO_ERROR, - poll: EIO_ERROR, - ioctl: EIO_ERROR, - mmap: EIO_ERROR, - open: EIO_ERROR, - flush: EIO_ERROR, - release: EIO_ERROR, - fsync: EIO_ERROR, - fasync: EIO_ERROR, - lock: EIO_ERROR, -}; + return -EIO; +} + +static int bad_inode_symlink (struct inode *dir, struct dentry *dentry, + const char *symname) +{ + return -EIO; +} + +static int bad_inode_mkdir(struct inode *dir, struct dentry *dentry, + int mode) +{ + return -EIO; +} + +static int bad_inode_rmdir (struct inode *dir, struct dentry *dentry) +{ + return -EIO; +} + +static int bad_inode_mknod (struct inode *dir, struct dentry *dentry, + int mode, int rdev) +{ + return -EIO; +} + +static int bad_inode_rename (struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + return -EIO; +} + +static int bad_inode_readlink(struct dentry *dentry, char __user *buffer, + int buflen) +{ + return -EIO; +} + +static int bad_inode_permission(struct inode *inode, int mask) +{ + return -EIO; +} + +static int bad_inode_revalidate(struct dentry *dentry) +{ + return -EIO; +} struct inode_operations bad_inode_ops = { - create: EIO_ERROR, - lookup: EIO_ERROR, - link: EIO_ERROR, - unlink: EIO_ERROR, - symlink: EIO_ERROR, - mkdir: EIO_ERROR, - rmdir: EIO_ERROR, - mknod: EIO_ERROR, - rename: EIO_ERROR, - readlink: EIO_ERROR, + create: bad_inode_create, + lookup: bad_inode_lookup, + link: bad_inode_link, + unlink: bad_inode_unlink, + symlink: bad_inode_symlink, + mkdir: bad_inode_mkdir, + rmdir: bad_inode_rmdir, + mknod: bad_inode_mknod, + rename: bad_inode_rename, + readlink: bad_inode_readlink, follow_link: bad_follow_link, - truncate: EIO_ERROR, - permission: EIO_ERROR, - revalidate: EIO_ERROR, + /* truncate returns void */ + permission: bad_inode_permission, + revalidate: bad_inode_revalidate, }; diff --git a/fs/dnotify.c b/fs/dnotify.c index 7ccdb31..1602277 100644 --- a/fs/dnotify.c +++ b/fs/dnotify.c @@ -19,6 +19,7 @@ #include #include #include +#include extern void send_sigio(struct fown_struct *fown, int fd, int band); @@ -68,6 +69,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) struct dnotify_struct **prev; struct inode *inode; fl_owner_t id = current->files; + struct file *f; if ((arg & ~DN_MULTISHOT) == 0) { dnotify_flush(filp, id); @@ -93,6 +95,16 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) } prev = &odn->dn_next; } + + /* we'd lost the race with close(), sod off silently */ + read_lock(¤t->files->file_lock); + f = fcheck(fd); + read_unlock(¤t->files->file_lock); + if (f != filp) { + kmem_cache_free(dn_cache, dn); + goto out; + } + filp->f_owner.pid = current->pid; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 58b76dd..593b660 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -240,12 +240,13 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) loff_t pos = filp->f_pos; struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; - unsigned offset = pos & ~PAGE_CACHE_MASK; + unsigned int offset = pos & ~PAGE_CACHE_MASK; unsigned long n = pos >> PAGE_CACHE_SHIFT; unsigned long npages = dir_pages(inode); unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); unsigned char *types = NULL; int need_revalidate = (filp->f_version != inode->i_version); + int ret = 0; if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) goto done; @@ -258,8 +259,14 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) ext2_dirent *de; struct page *page = ext2_get_page(inode, n); - if (IS_ERR(page)) - continue; + if (IS_ERR(page)) { + ext2_error(sb, __FUNCTION__, + "bad page in #%lu", + inode->i_ino); + filp->f_pos += PAGE_CACHE_SIZE - offset; + ret = -EIO; + goto done; + } kaddr = page_address(page); if (need_revalidate) { offset = ext2_validate_entry(kaddr, offset, chunk_mask); @@ -267,7 +274,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) } de = (ext2_dirent *)(kaddr+offset); limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1); - for ( ;(char*)de <= limit; de = ext2_next_entry(de)) + for ( ;(char*)de <= limit; de = ext2_next_entry(de)) { if (de->inode) { int over; unsigned char d_type = DT_UNKNOWN; @@ -284,14 +291,15 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) goto done; } } + filp->f_pos += le16_to_cpu(de->rec_len); + } ext2_put_page(page); } done: - filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; filp->f_version = inode->i_version; UPDATE_ATIME(inode); - return 0; + return ret; } /* @@ -336,7 +344,16 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, } if (++n >= npages) n = 0; + /* next page is past the blocks we've got */ + if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT - 9)))) { + ext2_error(dir->i_sb, __FUNCTION__, + "dir %lu size %lld exceeds block count %llu", + dir->i_ino, dir->i_size, + (unsigned long long)dir->i_blocks); + goto out; + } } while (n != start); +out: return NULL; found: diff --git a/fs/fcntl.c b/fs/fcntl.c index 9447229..abadfdf 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -283,7 +283,7 @@ static long do_fcntl(unsigned int fd, unsigned int cmd, break; case F_SETLK: case F_SETLKW: - err = fcntl_setlk(fd, cmd, (struct flock *) arg); + err = fcntl_setlk(fd, filp, cmd, (struct flock *) arg); break; case F_GETOWN: /* @@ -369,10 +369,12 @@ asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg err = fcntl_getlk64(fd, (struct flock64 *) arg); break; case F_SETLK64: - err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg); + err = fcntl_setlk64(fd, filp, cmd, + (struct flock64 *) arg); break; case F_SETLKW64: - err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg); + err = fcntl_setlk64(fd, filp, cmd, + (struct flock64 *) arg); break; default: err = do_fcntl(fd, cmd, arg, filp); diff --git a/fs/intermezzo/dcache.c b/fs/intermezzo/dcache.c index 12184ae..61e8e9e 100644 --- a/fs/intermezzo/dcache.c +++ b/fs/intermezzo/dcache.c @@ -248,7 +248,7 @@ inline struct presto_dentry_data *izo_alloc_ddata(void) /* This uses the BKL! */ int presto_set_dd(struct dentry * dentry) { - struct presto_file_set *fset; + struct presto_file_set *fset = NULL; struct presto_dentry_data *dd; int is_under_d_izo; int error=0; @@ -325,9 +325,11 @@ out_unlock: dentry->d_fsdata); unlock_kernel(); - filter_setup_dentry_ops(fset->fset_cache->cache_filter, - dentry->d_op, &presto_dentry_ops); - dentry->d_op = filter_c2udops(fset->fset_cache->cache_filter); + if (fset) { + filter_setup_dentry_ops(fset->fset_cache->cache_filter, + dentry->d_op, &presto_dentry_ops); + dentry->d_op = filter_c2udops(fset->fset_cache->cache_filter); + } return error; } diff --git a/fs/locks.c b/fs/locks.c index 2f21d25..412e91b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1477,9 +1477,10 @@ out: /* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ -int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) +int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd, + struct flock *l) { - struct file *filp; + struct file *f; struct file_lock *file_lock = locks_alloc_lock(); struct flock flock; struct inode *inode; @@ -1498,11 +1499,6 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) /* Get arguments and validate them ... */ - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - error = -EINVAL; inode = filp->f_dentry->d_inode; @@ -1515,23 +1511,23 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) if (mapping->i_mmap_shared != NULL) { error = -EAGAIN; - goto out_putf; + goto out; } } error = flock_to_posix_lock(filp, file_lock, &flock); if (error) - goto out_putf; + goto out; error = -EBADF; switch (flock.l_type) { case F_RDLCK: if (!(filp->f_mode & FMODE_READ)) - goto out_putf; + goto out; break; case F_WRLCK: if (!(filp->f_mode & FMODE_WRITE)) - goto out_putf; + goto out; break; case F_UNLCK: break; @@ -1549,23 +1545,29 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } } if (!(filp->f_mode & 3)) - goto out_putf; + goto out; break; #endif default: error = -EINVAL; - goto out_putf; + goto out; } +do_it: if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); if (error < 0) - goto out_putf; + goto out; } error = posix_lock_file(filp, file_lock, cmd == F_SETLKW); - -out_putf: - fput(filp); + read_lock(¤t->files->file_lock); + f = fcheck(fd); + read_unlock(¤t->files->file_lock); + /* lost race with close, kill stuck lock if close didn't get it */ + if (!error && flock.l_type != F_UNLCK && filp != f) { + file_lock->fl_type = F_UNLCK; + goto do_it; + } out: locks_free_lock(file_lock); return error; @@ -1633,9 +1635,10 @@ out: /* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ -int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) +int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd, + struct flock64 *l) { - struct file *filp; + struct file *f; struct file_lock *file_lock = locks_alloc_lock(); struct flock64 flock; struct inode *inode; @@ -1654,11 +1657,6 @@ int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) /* Get arguments and validate them ... */ - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - error = -EINVAL; inode = filp->f_dentry->d_inode; @@ -1671,23 +1669,23 @@ int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) if (mapping->i_mmap_shared != NULL) { error = -EAGAIN; - goto out_putf; + goto out; } } error = flock64_to_posix_lock(filp, file_lock, &flock); if (error) - goto out_putf; + goto out; error = -EBADF; switch (flock.l_type) { case F_RDLCK: if (!(filp->f_mode & FMODE_READ)) - goto out_putf; + goto out; break; case F_WRLCK: if (!(filp->f_mode & FMODE_WRITE)) - goto out_putf; + goto out; break; case F_UNLCK: break; @@ -1695,18 +1693,24 @@ int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) case F_EXLCK: default: error = -EINVAL; - goto out_putf; + goto out; } +do_it: if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); if (error < 0) - goto out_putf; + goto out; } error = posix_lock_file(filp, file_lock, cmd == F_SETLKW64); - -out_putf: - fput(filp); + read_lock(¤t->files->file_lock); + f = fcheck(fd); + read_unlock(¤t->files->file_lock); + /* lost race with close, kill stuck lock if close didn't get it */ + if (!error && flock.l_type != F_UNLCK && filp != f) { + file_lock->fl_type = F_UNLCK; + goto do_it; + } out: locks_free_lock(file_lock); return error; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 7d9bc34..dd8cb1a 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -47,9 +47,6 @@ static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, pos = address - area->vm_start + (area->vm_pgoff << PAGE_SHIFT); count = PAGE_SIZE; - if (address + PAGE_SIZE > area->vm_end) { - count = area->vm_end - address; - } /* what we can read in one go */ bufsize = NCP_SERVER(inode)->buffer_size; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 72cdc30..8e03eea 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1363,9 +1363,13 @@ udf_update_inode(struct inode *inode, int do_sync) if (inode->i_uid != UDF_SB(inode->i_sb)->s_uid) fe->uid = cpu_to_le32(inode->i_uid); + else + fe->uid = cpu_to_le32(-1); if (inode->i_gid != UDF_SB(inode->i_sb)->s_gid) fe->gid = cpu_to_le32(inode->i_gid); + else + fe->uid = cpu_to_le32(-1); udfperms = ((inode->i_mode & S_IRWXO) ) | ((inode->i_mode & S_IRWXG) << 2) | diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 81afef2..4ccfc5d 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -234,8 +234,9 @@ static inline void clear_in_cr4 (unsigned long mask) #define getCx86(reg) ({ outb((reg), 0x22); inb(0x23); }) #define setCx86(reg, data) do { \ + unsigned char _tmp_data = (data); \ outb((reg), 0x22); \ - outb((data), 0x23); \ + outb(_tmp_data, 0x23); \ } while (0) /* @@ -494,7 +495,7 @@ struct extended_sigtable { /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ static inline void rep_nop(void) { - __asm__ __volatile__("rep;nop" ::: "memory"); + __asm__ __volatile__("rep;nop": : :"memory"); } #define cpu_relax() rep_nop() diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 92a6a70..e2ddf28 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -41,6 +41,7 @@ #endif #endif +#ifdef __KERNEL__ #if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2 #error "GCC >= 4.2 miscompiles kernel 2.4, do not use it!" #error "While the resulting kernel may boot, you will encounter random bugs" @@ -48,6 +49,7 @@ #error "To build with another version, for instance 3.3, please do" #error " make bzImage CC=gcc-3.3 " #endif +#endif /* no checker support, so we unconditionally define this as (null) */ #define __user diff --git a/include/linux/fs.h b/include/linux/fs.h index c59e01d..feac534 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -658,10 +658,12 @@ extern struct list_head file_lock_list; #include extern int fcntl_getlk(unsigned int, struct flock *); -extern int fcntl_setlk(unsigned int, unsigned int, struct flock *); +extern int fcntl_setlk(unsigned int, struct file *, unsigned int, + struct flock *); extern int fcntl_getlk64(unsigned int, struct flock64 *); -extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *); +extern int fcntl_setlk64(unsigned int, struct file *, unsigned int, + struct flock64 *); /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); diff --git a/include/linux/netfilter_ipv4/ipt_CLASSIFY.h b/include/linux/netfilter_ipv4/ipt_CLASSIFY.h new file mode 100644 index 0000000..7596e3d --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_CLASSIFY.h @@ -0,0 +1,8 @@ +#ifndef _IPT_CLASSIFY_H +#define _IPT_CLASSIFY_H + +struct ipt_classify_target_info { + u_int32_t priority; +}; + +#endif /*_IPT_CLASSIFY_H */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ef07d5f..52e1dbe 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -434,6 +434,7 @@ #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A #define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 #define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 #define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 @@ -1052,6 +1053,11 @@ #define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 #define PCI_VENDOR_ID_IMS 0x10e0 #define PCI_DEVICE_ID_IMS_8849 0x8849 @@ -2104,4 +2110,9 @@ #define PCI_VENDOR_ID_JMICRON 0x197b #define PCI_DEVICE_ID_JMICRON_20363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 + +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_DEVICE_ID_MARVELL_6101 0x6101 +#define PCI_DEVICE_ID_MARVELL_6145 0x6145 diff --git a/include/linux/signal.h b/include/linux/signal.h index c4e6eb3..e7878d9 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -160,8 +160,8 @@ static inline void sigfillset(sigset_t *set) default: memset(set, -1, sizeof(sigset_t)); break; - case 2: set->sig[1] = -1; - case 1: set->sig[0] = -1; + case 2: set->sig[1] = (unsigned long)-1; + case 1: set->sig[0] = (unsigned long)-1; break; } } @@ -204,7 +204,7 @@ static inline void siginitsetinv(sigset_t *set, unsigned long mask) default: memset(&set->sig[1], -1, sizeof(long)*(_NSIG_WORDS-1)); break; - case 2: set->sig[1] = -1; + case 2: set->sig[1] = (unsigned long)-1; case 1: ; } } diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 258ec3e..62b694c 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -421,7 +421,7 @@ struct video_code #define VID_HARDWARE_VICAM 34 #define VID_HARDWARE_SF16FMR2 35 #define VID_HARDWARE_W9968CF 36 -#define VID_HARDWARE_SAA7114H 36 +#define VID_HARDWARE_SAA7114H 37 #endif /* __LINUX_VIDEODEV_H */ diff --git a/init/do_mounts.c b/init/do_mounts.c index cbdee28..7690951 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -315,8 +316,16 @@ static int __init fs_names_setup(char *str) return 1; } +static unsigned int __initdata root_delay; +static int __init root_delay_setup(char *str) +{ + root_delay = simple_strtoul(str, NULL, 0); + return 1; +} + __setup("rootflags=", root_data_setup); __setup("rootfstype=", fs_names_setup); +__setup("rootdelay=", root_delay_setup); static void __init get_fs_names(char *page) { @@ -888,7 +897,15 @@ static int __init initrd_load(void) */ void prepare_namespace(void) { - int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; + int is_floppy; + + if (root_delay) { + printk(KERN_INFO "Waiting %dsec before mounting root device...\n", + root_delay); + ssleep(root_delay); + } + + is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; #ifdef CONFIG_ALL_PPC extern void arch_discover_root(void); arch_discover_root(); diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index f9fcb3b..71ac51c 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1034,8 +1034,8 @@ static int __init ic_dynamic(void) jiff = jiffies + (d->next ? CONF_INTER_TIMEOUT : timeout); while (time_before(jiffies, jiff) && !ic_got_reply) { - barrier(); - cpu_relax(); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); } #ifdef IPCONFIG_DHCP /* DHCP isn't done until we get a DHCPACK. */ diff --git a/net/ipv4/netfilter/Config.in b/net/ipv4/netfilter/Config.in index 1d8dd7e..7f72487 100644 --- a/net/ipv4/netfilter/Config.in +++ b/net/ipv4/netfilter/Config.in @@ -103,6 +103,7 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then dep_tristate ' DSCP target support' CONFIG_IP_NF_TARGET_DSCP $CONFIG_IP_NF_MANGLE dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE + dep_tristate ' CLASSIFY target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_CLASSIFY $CONFIG_IP_NF_MANGLE fi dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES dep_tristate ' ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_IP_NF_IPTABLES diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 3779d27..b424412 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o # targets obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o +obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index c64a3a7..8ca7334 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -131,8 +131,11 @@ conntrack_iterate(const struct ip_conntrack_tuple_hash *hash, return 0; newlen = print_conntrack(buffer + *len, hash->ctrack); - if (*len + newlen > maxlen) + + if (*len + newlen > maxlen) { + (*upto)--; return 1; + } else *len += newlen; return 0; diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c index efc651c..87d71d4 100644 --- a/net/ipv4/netfilter/ip_nat_snmp_basic.c +++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c @@ -232,6 +232,11 @@ static unsigned char asn1_length_decode(struct asn1_ctx *ctx, } } } + + /* don't trust len bigger than ctx buffer */ + if (*len > ctx->end - ctx->pointer) + return 0; + return 1; } @@ -248,7 +253,11 @@ static unsigned char asn1_header_decode(struct asn1_ctx *ctx, if (!asn1_length_decode(ctx, &def, &len)) return 0; - + + /* primitive shall be definite, indefinite shall be constructed */ + if (*con == ASN1_PRI && !def) + return 0; + if (def) *eoc = ctx->pointer + len; else @@ -433,6 +442,11 @@ static unsigned char asn1_oid_decode(struct asn1_ctx *ctx, unsigned long *optr; size = eoc - ctx->pointer + 1; + + /* first subid actually encodes first two subids */ + if (size < 2 || size > ULONG_MAX/sizeof(unsigned long)) + return 0; + *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC); if (*oid == NULL) { if (net_ratelimit()) diff --git a/net/ipv4/netfilter/ipt_CLASSIFY.c b/net/ipv4/netfilter/ipt_CLASSIFY.c new file mode 100644 index 0000000..a576a01 --- /dev/null +++ b/net/ipv4/netfilter/ipt_CLASSIFY.c @@ -0,0 +1,82 @@ +/* + * This is a module which is used for setting the skb->priority field + * of an skb for qdisc classification. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("iptables qdisc classification target module"); + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ipt_classify_target_info *clinfo = targinfo; + + if((*pskb)->priority != clinfo->priority) { + (*pskb)->priority = clinfo->priority; + (*pskb)->nfcache |= NFC_ALTERED; + } + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_classify_target_info))){ + printk(KERN_ERR "CLASSIFY: invalid size (%u != %u).\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_classify_target_info))); + return 0; + } + + if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) { + printk(KERN_ERR "CLASSIFY: only valid in POST_ROUTING.\n"); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "CLASSIFY: can only be called from " + "\"mangle\" table, not \"%s\".\n", + tablename); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_classify_reg += { { NULL, NULL }, "CLASSIFY", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_classify_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_classify_reg); +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 142eacc..daa40d5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -142,6 +142,10 @@ static __inline__ void tcp_event_ack_sent(struct sock *sk) tcp_clear_xmit_timer(sk, TCP_TIME_DACK); } +/* from 2.6's ALIGN, used in tcp_select_window() */ +#define ALIGN_WIN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) +#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) + /* Chose a new window to advertise, update state in tcp_opt for the * socket, and return result with RFC1323 scaling applied. The return * value can be stuffed directly into th->window for an outgoing @@ -162,7 +166,7 @@ static __inline__ u16 tcp_select_window(struct sock *sk) * * Relax Will Robinson. */ - new_win = cur_win; + new_win = ALIGN_WIN(cur_win, 1 << tp->rcv_wscale); } tp->rcv_wnd = new_win; tp->rcv_wup = tp->rcv_nxt; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 9c7bff5..45c29bd 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -495,7 +495,7 @@ out: static __inline__ void fib6_start_gc(struct rt6_info *rt) { - if (ip6_fib_timer.expires == 0 && + if (!timer_pending(&ip6_fib_timer) && (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 0808d45..204cbb4 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -410,9 +410,9 @@ int ipip6_rcv(struct sk_buff *skb) } icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); - kfree_skb(skb); read_unlock(&ipip6_lock); out: + kfree_skb(skb); return 0; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8d13849..37fb63b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -324,17 +324,18 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; + /* See if the address matches any of the addresses we may have + * already bound before checking against other endpoints. + */ + if (sctp_bind_addr_match(bp, addr, sp)) + return -EINVAL; + /* Make sure we are allowed to bind here. * The function sctp_get_port_local() does duplicate address * detection. */ if ((ret = sctp_get_port_local(sk, addr))) { - if (ret == (long) sk) { - /* This endpoint has a conflicting address. */ - return -EINVAL; - } else { - return -EADDRINUSE; - } + return -EADDRINUSE; } /* Refresh ephemeral port. */ @@ -3174,7 +3175,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) return -EFAULT; - if (getaddrs.addr_num <= 0) return -EINVAL; + if (getaddrs.addr_num <= 0 || + getaddrs.addr_num >= (INT_MAX / sizeof(union sctp_addr))) + return -EINVAL; /* * For UDP-style sockets, id specifies the association to query. * If the id field is set to the value '0' then the locally bound @@ -3797,7 +3800,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) struct sctp_endpoint *ep2; ep2 = sctp_sk(sk2)->ep; - if (reuse && sk2->reuse) + if (sk == sk2 || + (reuse && sk2->reuse && + sk2->state != SCTP_SS_LISTENING)) continue; if (sctp_bind_addr_match(&ep2->base.bind_addr, addr, @@ -3918,6 +3923,11 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) if (!ep->base.bind_addr.port) { if (sctp_autobind(sk)) return -EAGAIN; + } else { + if (sctp_get_port(sk, sk->num)) { + sk->state = SCTP_SS_CLOSED; + return -EADDRINUSE; + } } sk->state = SCTP_SS_LISTENING; sctp_hash_endpoint(ep); @@ -3985,7 +3995,7 @@ int sctp_inet_listen(struct socket *sock, int backlog) goto out; /* Allocate HMAC for generating cookie. */ - if (sctp_hmac_alg) { + if (!sctp_sk(sk)->hmac && sctp_hmac_alg) { tfm = sctp_crypto_alloc_tfm(sctp_hmac_alg, 0); if (!tfm) { err = -ENOSYS; @@ -4007,7 +4017,8 @@ int sctp_inet_listen(struct socket *sock, int backlog) goto cleanup; /* Store away the transform reference. */ - sctp_sk(sk)->hmac = tfm; + if (!sctp_sk(sk)->hmac) + sctp_sk(sk)->hmac = tfm; out: sctp_release_sock(sk); return err;