diff options
| author | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-04 20:33:54 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-04 20:33:54 -0800 |
| commit | 1040c54c3b98ac4f8d91bc313cdc9d6669481da3 (patch) | |
| tree | d2c91b7b7e2aa5ffc88774ce1aa9aef08d4c709f | |
| parent | 5aa875d2cbee34727963bd81aa992b64480045ca (diff) | |
v2.4.14.8 -> v2.4.14.9
- David Brownell: usbnet update
- Greg KH: USB and PCI hotplug update
- Ingo/me: fix SCHED_FIFO for UP/SMP for good (flw).
- Add back direct_IO now that it works again.
43 files changed, 1003 insertions, 776 deletions
diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 9b4a26e9ad1b..48a44d045475 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -12742,6 +12742,17 @@ CONFIG_USB_SERIAL_VISOR The module will be called visor.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +USB IR Dongle Serial Driver +CONFIG_USB_SERIAL_IR + Say Y here if you want to enable simple serial support for USB IrDA + devices. This is useful if you do not want to use the full IrDA + stack. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called ir-usb.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + USB Belkin and Paracom Single Port Serial Driver CONFIG_USB_SERIAL_BELKIN Say Y here if you want to use a Belkin USB Serial single port @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 15 -EXTRAVERSION =-pre8 +EXTRAVERSION =-pre9 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index dbb08b98a884..33ca873c46b2 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -491,9 +491,9 @@ smp_boot_one_cpu(int cpuid, int cpunum) panic("idle process is init_task for CPU %d", cpuid); idle->processor = cpuid; + idle->cpus_runnable = 1 << cpuid; /* we schedule the first task manually */ __cpu_logical_map[cpunum] = cpuid; __cpu_number_map[cpuid] = cpunum; - idle->has_cpu = 1; /* we schedule the first task manually */ del_from_runqueue(idle); unhash_process(idle); diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index a51cbf13be48..0fe86897fb0d 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -181,15 +181,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->p_pptr != current) + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; + switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index b2a797c68ef4..64d6c827cd96 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -800,10 +800,10 @@ static void __init do_boot_cpu (int apicid) panic("No idle process for CPU %d", cpu); idle->processor = cpu; + idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ map_cpu_to_boot_apicid(cpu, apicid); - idle->has_cpu = 1; /* we schedule the first task manually */ idle->thread.eip = (unsigned long) start_secondary; del_from_runqueue(idle); diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index fc69cf6e2a5e..dc932f60d612 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -846,31 +846,11 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - - if (child->p_pptr != current) + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; - if (request != PTRACE_KILL) { - if (child->state != TASK_STOPPED) - goto out_tsk; - -#ifdef CONFIG_SMP - while (child->has_cpu) { - if (child->state != TASK_STOPPED) - goto out_tsk; - barrier(); - } -#endif - } - pt = ia64_task_regs(child); sw = (struct switch_stack *) (child->thread.ksp + 16); diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index abc38103dd9a..bc3dbf7a31a7 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -418,7 +418,7 @@ do_boot_cpu (int sapicid) idle->processor = cpu; ia64_cpu_to_sapicid[cpu] = sapicid; - idle->has_cpu = 1; /* we schedule the first task manually */ + idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ del_from_runqueue(idle); unhash_process(idle); diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 41404069877a..bcfdad667d7a 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -126,7 +126,7 @@ void __init smp_boot_cpus(void) /* Schedule the first task manually */ p->processor = i; - p->has_cpu = 1; + p->cpus_runnable = 1 << i; /* we schedule the first task manually */ /* Attach to the address space of init_task. */ atomic_inc(&init_mm.mm_count); @@ -155,7 +155,7 @@ void __init smp_boot_cpus(void) sprintf(p->comm, "%s%d", "Idle", i); init_tasks[i] = p; p->processor = i; - p->has_cpu = 1; /* we schedule the first task manually */ + p->cpus_runnable = 1 << i; /* we schedule the first task manually * del_from_runqueue(p); unhash_process(p); /* Attach to the address space of init_task. */ diff --git a/arch/mips64/sgi-ip27/ip27-init.c b/arch/mips64/sgi-ip27/ip27-init.c index e676a90369ae..4c5c7439f499 100644 --- a/arch/mips64/sgi-ip27/ip27-init.c +++ b/arch/mips64/sgi-ip27/ip27-init.c @@ -497,7 +497,7 @@ void allowboot(void) alloc_cpupda(cpu, num_cpus); del_from_runqueue(p); p->processor = num_cpus; - p->has_cpu = 1; /* we schedule the first task manually */ + p->cpus_runnable = 1 << num_cpus; /* we schedule the first task manually */ unhash_process(p); /* Attach to the address space of init_task. */ atomic_inc(&init_mm.mm_count); diff --git a/arch/ppc/8xx_io/micropatch.c b/arch/ppc/8xx_io/micropatch.c index f0485c361446..68a51ee22661 100644 --- a/arch/ppc/8xx_io/micropatch.c +++ b/arch/ppc/8xx_io/micropatch.c @@ -17,7 +17,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/8xx_immap.h> -#include "commproc.h" +#include <asm/commproc.h> /* Define this to get SMC patches as well. You need to modify the uart * driver as well...... diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index b69f593bc41b..fe503e74a123 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.setup.c 1.63 11/13/01 21:26:07 paulus + * BK Id: SCCS/s.setup.c 1.65 11/18/01 20:57:25 trini */ /* * Common prep/pmac/chrp boot and setup code. @@ -472,8 +472,8 @@ int parse_bootinfo(void) break; #ifdef CONFIG_BLK_DEV_INITRD case BI_INITRD: - initrd_start = data[0]; - initrd_end = data[0] + data[1]; + initrd_start = data[0] + KERNELBASE; + initrd_end = data[0] + data[1] + KERNELBASE; break; #endif /* CONFIG_BLK_DEV_INITRD */ #ifdef CONFIG_ALL_PPC diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index c27da90efc9c..cefcc690d252 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -349,7 +349,7 @@ void __init smp_boot_cpus(void) init_tasks[i] = p; p->processor = i; - p->has_cpu = 1; + p->cpus_runnable = 1 << i; /* we schedule the first task manually */ current_set[i] = p; /* diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index a7e6c959c7e5..0171e0d4cb26 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -531,7 +531,7 @@ static void __init do_boot_cpu(int cpu) if (!idle) panic("No idle process for CPU %d",cpu); idle->processor = cpu; - idle->has_cpu = 1; /* we schedule the first task manually */ + idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ del_from_runqueue(idle); unhash_process(idle); diff --git a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c index 0232aedbbc6a..21912c7734b8 100644 --- a/arch/s390x/kernel/smp.c +++ b/arch/s390x/kernel/smp.c @@ -510,7 +510,7 @@ static void __init do_boot_cpu(int cpu) if (!idle) panic("No idle process for CPU %d",cpu); idle->processor = cpu; - idle->has_cpu = 1; /* we schedule the first task manually */ + idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ del_from_runqueue(idle); unhash_process(idle); diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 098b2e58dc18..e8f5fb37b01e 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -225,7 +225,7 @@ void __init smp4d_boot_cpus(void) init_tasks[i] = p; p->processor = i; - p->has_cpu = 1; /* we schedule the first task manually */ + p->cpus_runnable = 1 << i; /* we schedule the first task manually */ current_set[i] = p; diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 04e3d65fa38a..676b9f261591 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -198,7 +198,7 @@ void __init smp4m_boot_cpus(void) init_tasks[i] = p; p->processor = i; - p->has_cpu = 1; /* we schedule the first task manually */ + p->cpus_runnable = 1 << i; /* we schedule the first task manually */ current_set[i] = p; diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 69a21837e2a5..1b44bc0abf79 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -276,7 +276,7 @@ void __init smp_boot_cpus(void) init_tasks[cpucount] = p; p->processor = i; - p->has_cpu = 1; /* we schedule the first task manually */ + p->cpus_runnable = 1 << i; /* we schedule the first task manually */ del_from_runqueue(p); unhash_process(p); diff --git a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c index 0b851a24c374..1df5e3735adc 100644 --- a/drivers/hotplug/pci_hotplug_core.c +++ b/drivers/hotplug/pci_hotplug_core.c @@ -622,7 +622,7 @@ exit: static ssize_t power_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset) { struct hotplug_slot *slot = file->private_data; - const char *buff; + char *buff; unsigned long lpower; u8 power; int retval = 0; @@ -639,10 +639,11 @@ static ssize_t power_write_file (struct file *file, const char *ubuff, size_t co return -ENODEV; } - buff = kmalloc (count, GFP_KERNEL); + buff = kmalloc (count + 1, GFP_KERNEL); if (!buff) return -ENOMEM; - + memset (buff, 0x00, count + 1); + if (copy_from_user ((void *)buff, (void *)ubuff, count)) { retval = -EFAULT; goto exit; @@ -732,7 +733,7 @@ exit: static ssize_t attention_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset) { struct hotplug_slot *slot = file->private_data; - const char *buff; + char *buff; unsigned long lattention; u8 attention; int retval = 0; @@ -749,9 +750,10 @@ static ssize_t attention_write_file (struct file *file, const char *ubuff, size_ return -ENODEV; } - buff = kmalloc (count, GFP_KERNEL); + buff = kmalloc (count + 1, GFP_KERNEL); if (!buff) return -ENOMEM; + memset (buff, 0x00, count + 1); if (copy_from_user ((void *)buff, (void *)ubuff, count)) { retval = -EFAULT; @@ -868,7 +870,7 @@ exit: static ssize_t test_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset) { struct hotplug_slot *slot = file->private_data; - const char *buff; + char *buff; unsigned long ltest; u32 test; int retval = 0; @@ -885,9 +887,10 @@ static ssize_t test_write_file (struct file *file, const char *ubuff, size_t cou return -ENODEV; } - buff = kmalloc (count, GFP_KERNEL); + buff = kmalloc (count + 1, GFP_KERNEL); if (!buff) return -ENOMEM; + memset (buff, 0x00, count + 1); if (copy_from_user ((void *)buff, (void *)ubuff, count)) { retval = -EFAULT; diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c index dfbbda448903..bf2a097e59d3 100644 --- a/drivers/pcmcia/yenta.c +++ b/drivers/pcmcia/yenta.c @@ -770,6 +770,7 @@ static void yenta_close(pci_socket_t *sock) { /* Disable all events so we don't die in an IRQ storm */ cb_writel(sock, CB_SOCKET_MASK, 0x0); + exca_writeb(sock, I365_CSCINT, 0); if (sock->cb_irq) free_irq(sock->cb_irq, sock); diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index dc7fa3ae3cce..e78f2978d9e8 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -175,6 +175,8 @@ cpqfc.o: $(cpqfc-objs) 53c8xx_u.h: 53c8xx_d.h +53c7,8xx.o: 53c8xx_u.h + 53c7xx_d.h: 53c7xx.scr script_asm.pl ln -sf 53c7xx.scr fake7.c $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | $(PERL) -s script_asm.pl -ncr7x0_family diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux.c b/drivers/scsi/aic7xxx/aic7xxx_linux.c index 48c6716716f3..345e2546c63e 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_linux.c +++ b/drivers/scsi/aic7xxx/aic7xxx_linux.c @@ -129,6 +129,7 @@ #include "../sd.h" /* For geometry detection */ #include <linux/mm.h> /* For fetching system memory size */ +#include <linux/blk.h> /* * To generate the correct addresses for the controller to issue @@ -2742,7 +2743,7 @@ ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) struct buffer_head *bh; ahc = *((struct ahc_softc **)disk->device->host->hostdata); - bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024); + bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev)); if (bh) { ret = scsi_partsize(bh, disk->capacity, diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 0882f2faf171..b5b8b3945351 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -238,6 +238,7 @@ #include <linux/init.h> #include <linux/spinlock.h> #include <linux/smp.h> +#include <linux/blk.h> #include "sd.h" #include "scsi.h" #include "hosts.h" @@ -11739,7 +11740,7 @@ aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[]) struct buffer_head *bh; p = (struct aic7xxx_host *) disk->device->host->hostdata; - bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024); + bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, block_size(dev)); if ( bh ) { diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index de1a523ce5d3..9d898e8924b6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -25,6 +25,14 @@ * * Version history: * + * 2.2 2001_11_14 greg kroah-hartman + * - fixed bug in edge_close that kept the port from being used more + * than once. + * - fixed memory leak on device removal. + * - fixed potential double free of memory when command urb submitting + * failed. + * - other small cleanups when the device is removed + * * 2.1 2001_07_09 greg kroah-hartman * - added support for TIOCMBIS and TIOCMBIC. * @@ -263,7 +271,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v2.1" +#define DRIVER_VERSION "v2.2" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli" #define DRIVER_DESC "Edgeport USB Serial Driver" @@ -433,7 +441,7 @@ static int CmdUrbs = 0; /* Number of outstanding Command Write Urbs */ /* function prototypes for all URB callbacks */ static void edge_interrupt_callback (struct urb *urb); -static void edge_bulk_in_callback (struct urb *urb); +static void edge_bulk_in_callback (struct urb *urb); static void edge_bulk_out_data_callback (struct urb *urb); static void edge_bulk_out_cmd_callback (struct urb *urb); @@ -646,12 +654,6 @@ static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_de } #endif - - -/************************************************************************ - * * - * * - ************************************************************************/ static void get_product_info(struct edgeport_serial *edge_serial) { struct edgeport_product_info *product_info = &edge_serial->product_info; @@ -933,7 +935,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) dbg(__FUNCTION__" - FREE URB %p (outstanding %d)", urb, CmdUrbs); - /* if this urb had a transfer buffer already (old transfer) free it */ + /* clean up the transfer buffer */ if (urb->transfer_buffer != NULL) { kfree(urb->transfer_buffer); } @@ -957,7 +959,6 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) /* tell the tty driver that something has changed */ wake_up_interruptible(&tty->write_wait); - /* we have completed the command */ edge_port->commandPending = FALSE; wake_up_interruptible(&edge_port->wait_command); @@ -987,6 +988,9 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) dbg(__FUNCTION__ " - port %d", port->number); + if (edge_port == NULL) + return -ENODEV; + ++port->open_count; MOD_INC_USE_COUNT; @@ -1002,6 +1006,12 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) as the structures were not set up at that time.) */ serial = port->serial; edge_serial = (struct edgeport_serial *)serial->private; + if (edge_serial == NULL) { + port->active = 0; + port->open_count = 0; + MOD_DEC_USE_COUNT; + return -ENODEV; + } if (edge_serial->interrupt_in_buffer == NULL) { struct usb_serial_port *port0 = &serial->port[0]; @@ -1015,27 +1025,22 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress; /* set up our interrupt urb */ - /* Like to use FILL_INT_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */ - edge_serial->interrupt_read_urb->complete = edge_interrupt_callback; - edge_serial->interrupt_read_urb->context = edge_serial; - edge_serial->interrupt_read_urb->dev = serial->dev; - /* FILL_INT_URB(edge_serial->interrupt_read_urb, serial->dev, - usb_rcvintpipe (serial->dev, edge_serial->interrupt_in_endpoint), - edge_serial->interrupt_in_buffer, edge_serial->interrupt_in_endpoint.wMaxPacketSize, - edge_interrupt_callback, edge_serial, edge_serial->interrupt_in_endpoint.bInterval); - */ + FILL_INT_URB(edge_serial->interrupt_read_urb, + serial->dev, + usb_rcvintpipe(serial->dev, + port0->interrupt_in_endpointAddress), + port0->interrupt_in_buffer, + edge_serial->interrupt_read_urb->transfer_buffer_length, + edge_interrupt_callback, edge_serial, + edge_serial->interrupt_read_urb->interval); /* set up our bulk in urb */ - /* Like to use FILL_BULK_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */ - edge_serial->read_urb->complete = edge_bulk_in_callback; - edge_serial->read_urb->context = edge_serial; - edge_serial->read_urb->dev = serial->dev; - /* FILL_BULK_URB(edge_serial->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, port->bulk_in_endpointAddress), - edge_serial->bulk_in_buffer, edge_serial->bulk_in_endpoint->wMaxPacketSize, + FILL_BULK_URB(edge_serial->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress), + port0->bulk_in_buffer, + edge_serial->read_urb->transfer_buffer_length, edge_bulk_in_callback, edge_serial); - */ - + /* start interrupt read for this edgeport * this interrupt will continue as long as the edgeport is connected */ response = usb_submit_urb (edge_serial->interrupt_read_urb); @@ -1239,7 +1244,9 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_serial = (struct edgeport_serial *)serial->private; edge_port = (struct edgeport_port *)port->private; - + if ((edge_serial == NULL) || (edge_port == NULL)) + return; + --port->open_count; if (port->open_count <= 0) { @@ -1285,15 +1292,14 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) if (edge_port->txfifo.fifo) { kfree(edge_port->txfifo.fifo); } + port->active = 0; + port->open_count = 0; } MOD_DEC_USE_COUNT; dbg(__FUNCTION__" exited"); } - - - /***************************************************************************** * SerialWrite * this function is called by the tty driver when data should be written to @@ -1312,6 +1318,9 @@ static int edge_write (struct usb_serial_port *port, int from_user, const unsign dbg(__FUNCTION__ " - port %d", port->number); + if (edge_port == NULL) + return -ENODEV; + // get a pointer to the Tx fifo fifo = &edge_port->txfifo; @@ -1509,9 +1518,10 @@ static int edge_write_room (struct usb_serial_port *port) dbg(__FUNCTION__); - if (edge_port->closePending == TRUE) { + if (edge_port == NULL) + return -ENODEV; + if (edge_port->closePending == TRUE) return -ENODEV; - } dbg(__FUNCTION__" - port %d", port->number); @@ -1544,6 +1554,11 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) dbg(__FUNCTION__); + if (edge_port == NULL) + return -ENODEV; + if (edge_port->closePending == TRUE) + return -ENODEV; + if (!edge_port->open) { dbg (__FUNCTION__" - port not opened"); return -EINVAL; @@ -1571,6 +1586,9 @@ static void edge_throttle (struct usb_serial_port *port) dbg(__FUNCTION__" - port %d", port->number); + if (edge_port == NULL) + return; + if (!edge_port->open) { dbg (__FUNCTION__" - port not opened"); return; @@ -1613,6 +1631,9 @@ static void edge_unthrottle (struct usb_serial_port *port) dbg(__FUNCTION__" - port %d", port->number); + if (edge_port == NULL) + return; + if (!edge_port->open) { dbg (__FUNCTION__" - port not opened"); return; @@ -1670,6 +1691,9 @@ static void edge_set_termios (struct usb_serial_port *port, struct termios *old_ dbg(__FUNCTION__" - port %d", port->number); + if (edge_port == NULL) + return; + if (!edge_port->open) { dbg (__FUNCTION__" - port not opened"); return; @@ -2457,15 +2481,12 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer /* Allocate our next urb */ urb = usb_alloc_urb (0); + if (!urb) + return -ENOMEM; CmdUrbs++; - dbg(__FUNCTION__" - ALLOCATE URB %p (outstanding %d)", urb, CmdUrbs); - if (!urb) { - return -ENOMEM; - } - FILL_BULK_URB (urb, edge_serial->serial->dev, usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint), buffer, length, edge_bulk_out_cmd_callback, edge_port); @@ -2474,17 +2495,11 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer urb->transfer_flags |= USB_QUEUE_BULK; edge_port->commandPending = TRUE; - urb->dev = edge_serial->serial->dev; status = usb_submit_urb(urb); if (status) { /* something went wrong */ dbg(__FUNCTION__" - usb_submit_urb(write bulk) failed"); - - /* if this urb had a transfer buffer already (old transfer) free it */ - if (urb->transfer_buffer != NULL) { - kfree(urb->transfer_buffer); - } usb_unlink_urb (urb); usb_free_urb (urb); return status; @@ -2548,6 +2563,10 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, LCR, edge_port->shadowLCR); status = write_cmd_usb(edge_port, cmdBuffer, cmdLen ); + if (status) { + /* something bad happened, let's free up the memory */ + kfree (cmdBuffer); + } return status; } @@ -2623,6 +2642,10 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->number, regNum, regValue); status = write_cmd_usb(edge_port, cmdBuffer, cmdLen); + if (status) { + /* something bad happened, let's free up the memory */ + kfree (cmdBuffer); + } return status; } @@ -2707,10 +2730,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct termio unsigned char stop_char = STOP_CHAR(tty); unsigned char start_char = START_CHAR(tty); - { - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char); - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); - } + send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char); + send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); /* if we are implementing INBOUND XON/XOFF */ if (I_IXOFF(tty)) { @@ -2962,10 +2983,11 @@ static int edge_startup (struct usb_serial *serial) get_product_info(edge_serial); /* set the number of ports from the manufacturing description */ - // FIXME should we override this??? - //serial->num_ports = serial->product_info.NumPorts; + /* serial->num_ports = serial->product_info.NumPorts; */ if (edge_serial->product_info.NumPorts != serial->num_ports) { - warn(__FUNCTION__ " - Device Reported %d serial ports vs core thinking we have %d ports, email greg@kroah.com this info.", edge_serial->product_info.NumPorts, serial->num_ports); + warn(__FUNCTION__ " - Device Reported %d serial ports vs core " + "thinking we have %d ports, email greg@kroah.com this info.", + edge_serial->product_info.NumPorts, serial->num_ports); } dbg(__FUNCTION__ " - time 1 %ld", jiffies); @@ -3005,10 +3027,9 @@ static int edge_startup (struct usb_serial *serial) /**************************************************************************** - * usb_edgeport_disconnect + * edge_shutdown * This function is called whenever the device is removed from the usb bus. ****************************************************************************/ -//static void usb_edgeport_disconnect (struct usb_device *dev, void *ptr) static void edge_shutdown (struct usb_serial *serial) { int i; @@ -3020,11 +3041,11 @@ static void edge_shutdown (struct usb_serial *serial) while (serial->port[i].open_count > 0) { edge_close (&serial->port[i], NULL); } + kfree (serial->port[i].private); + serial->port[i].private = NULL; } - - /* free up any memory that we allocated */ - // FIXME - + kfree (serial->private); + serial->private = NULL; } diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index c580b6082e07..af4bd2c9eb00 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1019,7 +1019,7 @@ void usb_free_urb(urb_t* urb) /*-------------------------------------------------------------------*/ int usb_submit_urb(urb_t *urb) { - if (urb && urb->dev) + if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) return urb->dev->bus->op->submit_urb(urb); else return -ENODEV; @@ -1028,7 +1028,7 @@ int usb_submit_urb(urb_t *urb) /*-------------------------------------------------------------------*/ int usb_unlink_urb(urb_t *urb) { - if (urb && urb->dev) + if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) return urb->dev->bus->op->unlink_urb(urb); else return -ENODEV; diff --git a/drivers/usb/usbnet.c b/drivers/usb/usbnet.c index 724defed3d82..96608c0d9106 100644 --- a/drivers/usb/usbnet.c +++ b/drivers/usb/usbnet.c @@ -16,11 +16,10 @@ * * - AnchorChip 2720 * - Belkin, eTEK (interops with Win32 drivers) + * - GeneSys GL620USB-A * - "Linux Devices" (like iPaq and similar SA-1100 based PDAs) * - NetChip 1080 (interoperates with NetChip Win32 drivers) * - Prolific PL-2301/2302 (replaces "plusb" driver) - * - GeneSys GL620USB-A - * * USB devices can implement their side of this protocol at the cost * of two bulk endpoints; it's not restricted to "cable" applications. @@ -78,9 +77,11 @@ * 17-oct-2001 Handle "Advance USBNET" product, like Belkin/eTEK devices, * from Ioannis Mavroukakis <i.mavroukakis@btinternet.com>; * rx unlinks somehow weren't async; minor cleanup. - * 25-oct-2001 Merged GeneSys driver, using code from - * Jiun-Jie Huang <huangjj@genesyslogic.com.tw> - * by Stanislav Brabec <utx@penguin.cz> + * 03-nov-2001 Merged GeneSys driver; original code from Jiun-Jie Huang + * <huangjj@genesyslogic.com.tw>, updated by Stanislav Brabec + * <utx@penguin.cz>. Made framing options (NetChip/GeneSys) + * tie mostly to (sub)driver info. Workaround some PL-2302 + * chips that seem to reject SET_INTERFACE requests. * *-------------------------------------------------------------------------*/ @@ -106,10 +107,10 @@ #define CONFIG_USB_AN2720 #define CONFIG_USB_BELKIN +#define CONFIG_USB_GENESYS #define CONFIG_USB_LINUXDEV #define CONFIG_USB_NET1080 #define CONFIG_USB_PL2301 -#define CONFIG_USB_GENELINK /*-------------------------------------------------------------------------*/ @@ -163,7 +164,10 @@ struct usbnet { // protocol/interface state struct net_device net; struct net_device_stats stats; + +#ifdef CONFIG_USB_NET1080 u16 packet_id; +#endif // various kinds of pending driver work struct sk_buff_head rxq; @@ -171,9 +175,6 @@ struct usbnet { struct sk_buff_head done; struct tasklet_struct bh; struct tq_struct ctrl_task; - - // various data structure may be needed - void *priv_data; }; // device-specific info used by the driver @@ -182,7 +183,7 @@ struct driver_info { int flags; #define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */ -#define FLAG_GENELINK 0x0002 /* genelink flag */ +#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */ #define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */ /* reset device ... can sleep */ @@ -191,13 +192,15 @@ struct driver_info { /* see if peer is connected ... can sleep */ int (*check_connect)(struct usbnet *); - /* allocate and initialize the private resources per device */ - int (*initialize_private)(struct usbnet *); + /* fixup rx packet (strip framing) */ + int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb); - /* free the private resources per device */ - int (*release_private)(struct usbnet *); + /* fixup tx packet (add framing) */ + struct sk_buff *(*tx_fixup)(struct usbnet *dev, + struct sk_buff *skb, int flags); // FIXME -- also an interrupt mechanism + // useful for at least PL2301/2302 and GL620USB-A /* framework currently "knows" bulk EPs talk packets */ int in; /* rx endpoint */ @@ -240,47 +243,6 @@ struct skb_data { // skb->cb is one of these #define devinfo(usbnet, fmt, arg...) \ printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg) -/*------------------------------------------------------------------------- - * - * NetChip framing of ethernet packets, supporting additional error - * checks for links that may drop bulk packets from inside messages. - * Odd USB length == always short read for last usb packet. - * - nc_header - * - Ethernet header (14 bytes) - * - payload - * - (optional padding byte, if needed so length becomes odd) - * - nc_trailer - * - * This framing is to be avoided for non-NetChip devices. - */ - -struct nc_header { // packed: - u16 hdr_len; // sizeof nc_header (LE, all) - u16 packet_len; // payload size (including ethhdr) - u16 packet_id; // detects dropped packets -#define MIN_HEADER 6 - - // all else is optional, and must start with: - // u16 vendorId; // from usb-if - // u16 productId; -} __attribute__((__packed__)); - -#define PAD_BYTE ((unsigned char)0xAC) - -struct nc_trailer { - u16 packet_id; -} __attribute__((__packed__)); - -// packets may use FLAG_FRAMING_NC and optional pad -#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ - + sizeof (struct ethhdr) \ - + (mtu) \ - + 1 \ - + sizeof (struct nc_trailer)) - -#define MIN_FRAMED FRAMED_SIZE(0) - - #ifdef CONFIG_USB_AN2720 @@ -329,6 +291,330 @@ static const struct driver_info belkin_info = { +#ifdef CONFIG_USB_GENESYS + +/*------------------------------------------------------------------------- + * + * GeneSys GL620USB-A (www.genesyslogic.com.tw) + * + * ... should partially interop with the Win32 driver for this hardware + * The GeneSys docs imply there's some NDIS issue motivating this framing. + * + *-------------------------------------------------------------------------*/ + +// control msg write command +#define GENELINK_CONNECT_WRITE 0xF0 +// interrupt pipe index +#define GENELINK_INTERRUPT_PIPE 0x03 +// interrupt read buffer size +#define INTERRUPT_BUFSIZE 0x08 +// interrupt pipe interval value +#define GENELINK_INTERRUPT_INTERVAL 0x10 +// max transmit packet number per transmit +#define GL_MAX_TRANSMIT_PACKETS 32 +// max packet length +#define GL_MAX_PACKET_LEN 1514 +// max receive buffer size +#define GL_RCV_BUF_SIZE \ + (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) + +struct gl_packet { + u32 packet_length; + char packet_data [1]; +}; + +struct gl_header { + u32 packet_count; + struct gl_packet packets; +}; + +#ifdef GENLINK_ACK + +// FIXME: this code is incomplete, not debugged; it doesn't +// handle interrupts correctly. interrupts should be generic +// code like all other device I/O, anyway. + +struct gl_priv { + struct urb *irq_urb; + char irq_buf [INTERRUPT_BUFSIZE]; +}; + +static inline int gl_control_write (struct usbnet *dev, u8 request, u16 value) +{ + int retval; + + retval = usb_control_msg (dev->udev, + usb_sndctrlpipe (dev->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + value, + 0, // index + 0, // data buffer + 0, // size + CONTROL_TIMEOUT_JIFFIES); + return retval; +} + +static void gl_interrupt_complete (struct urb *urb) +{ + int status = urb->status; + + if (status) + dbg ("gl_interrupt_complete fail - %X", status); + else + dbg ("gl_interrupt_complete success..."); +} + +static int gl_interrupt_read (struct usbnet *dev) +{ + struct gl_priv *priv = dev->priv_data; + int retval; + + // issue usb interrupt read + if (priv && priv->irq_urb) { + // submit urb + if ((retval = usb_submit_urb (priv->irq_urb)) != 0) + dbg ("gl_interrupt_read: submit fail - %X...", retval); + else + dbg ("gl_interrupt_read: submit success..."); + } + + return 0; +} + +// check whether another side is connected +static int genelink_check_connect (struct usbnet *dev) +{ + int retval; + + dbg ("genelink_check_connect..."); + + // detect whether another side is connected + if ((retval = gl_control_write (dev, GENELINK_CONNECT_WRITE, 0)) != 0) { + dbg ("%s: genelink_check_connect write fail - %X", + dev->net.name, retval); + return retval; + } + + // usb interrupt read to ack another side + if ((retval = gl_interrupt_read (dev)) != 0) { + dbg ("%s: genelink_check_connect read fail - %X", + dev->net.name, retval); + return retval; + } + + dbg ("%s: genelink_check_connect read success", dev->net.name); + return 0; +} + +// allocate and initialize the private data for genelink +static int genelink_init (struct usbnet *dev) +{ + struct gl_priv *priv; + + // allocate the private data structure + if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) { + dbg ("%s: cannot allocate private data per device", + dev->net.name); + return -ENOMEM; + } + + // allocate irq urb + if ((priv->irq_urb = usb_alloc_urb (0)) == 0) { + dbg ("%s: cannot allocate private irq urb per device", + dev->net.name); + kfree (priv); + return -ENOMEM; + } + + // fill irq urb + FILL_INT_URB (priv->irq_urb, dev->udev, + usb_rcvintpipe (dev->udev, GENELINK_INTERRUPT_PIPE), + priv->irq_buf, INTERRUPT_BUFSIZE, + gl_interrupt_complete, 0, + GENELINK_INTERRUPT_INTERVAL); + + // set private data pointer + dev->priv_data = priv; + + return 0; +} + +// release the private data +static int genelink_free (struct usbnet *dev) +{ + struct gl_priv *priv = dev->priv_data; + + if (!priv) + return 0; + +// FIXME: can't cancel here; it's synchronous, and +// should have happened earlier in any case (interrupt +// handling needs to be generic) + + // cancel irq urb first + usb_unlink_urb (priv->irq_urb); + + // free irq urb + usb_free_urb (priv->irq_urb); + + // free the private data structure + kfree (priv); + + return 0; +} + +#else + +static int genelink_check_connect (struct usbnet *dev) +{ + dbg ("%s: assuming peer is connected", dev->net.name); + return 0; +} + +#endif + +// reset the device status +static int genelink_reset (struct usbnet *dev) +{ + // we don't need to reset, just return 0 + return 0; +} + +static int genelink_rx_fixup (struct usbnet *dev, struct sk_buff *skb) +{ + struct gl_header *header; + struct gl_packet *packet; + struct sk_buff *gl_skb; + int status; + u32 size; + + header = (struct gl_header *) skb->data; + + // get the packet count of the received skb + le32_to_cpus (&header->packet_count); + if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS) + || (header->packet_count < 0)) { + dbg ("genelink: illegal received packet count %d", + header->packet_count); + return 0; + } + + // set the current packet pointer to the first packet + packet = &header->packets; + + // decrement the length for the packet count size 4 bytes + skb_pull (skb, 4); + + while (header->packet_count > 1) { + // get the packet length + size = packet->packet_length; + + // this may be a broken packet + if (size > GL_MAX_PACKET_LEN) { + dbg ("genelink: illegal rx length %d", size); + return 0; + } + + // allocate the skb for the individual packet + gl_skb = alloc_skb (size, GFP_ATOMIC); + if (gl_skb == 0) + return 0; + + // copy the packet data to the new skb + memcpy (gl_skb->data, packet->packet_data, size); + + // set skb data size + gl_skb->len = size; + gl_skb->dev = &dev->net; + + // determine the packet's protocol ID + gl_skb->protocol = eth_type_trans (gl_skb, &dev->net); + + // update the status + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; + + // notify os of the received packet + status = netif_rx (gl_skb); + + // advance to the next packet + packet = (struct gl_packet *) + &packet->packet_data [size]; + header->packet_count--; + + // shift the data pointer to the next gl_packet + skb_pull (skb, size + 4); + } + + // skip the packet length field 4 bytes + skb_pull (skb, 4); + + if (skb->len > GL_MAX_PACKET_LEN) { + dbg ("genelink: illegal rx length %d", skb->len); + return 0; + } + return 1; +} + +static struct sk_buff * +genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) +{ + int padlen; + int length = skb->len; + int headroom = skb_headroom (skb); + int tailroom = skb_tailroom (skb); + u32 *packet_count; + u32 *packet_len; + + // FIXME: magic numbers, bleech + padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; + + if ((!skb_cloned (skb)) + && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { + if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { + skb->data = memmove (skb->head + (4 + 4*1), + skb->data, skb->len); + skb->tail = skb->data + skb->len; + } + } else { + struct sk_buff *skb2; + skb2 = skb_copy_expand (skb, (4 + 4*1) , padlen, flags); + dev_kfree_skb_any (skb); + skb = skb2; + } + + // attach the packet count to the header + packet_count = (u32 *) skb_push (skb, (4 + 4*1)); + packet_len = packet_count + 1; + + // FIXME little endian? + *packet_count = 1; + *packet_len = length; + + // add padding byte + if ((skb->len % EP_SIZE (dev)) == 0) + skb_put (skb, 1); + + return skb; +} + +static const struct driver_info genelink_info = { + description: "Genesys GeneLink", + flags: FLAG_FRAMING_GL | FLAG_NO_SETINT, + reset: genelink_reset, + check_connect: genelink_check_connect, + rx_fixup: genelink_rx_fixup, + tx_fixup: genelink_tx_fixup, + + in: 1, out: 2, + epsize: 64, +}; + +#endif /* CONFIG_USB_GENESYS */ + + + #ifdef CONFIG_USB_LINUXDEV /*------------------------------------------------------------------------- @@ -364,10 +650,51 @@ static const struct driver_info linuxdev_info = { /*------------------------------------------------------------------------- * * Netchip 1080 driver ... http://www.netchip.com + * Used in LapLink cables * *-------------------------------------------------------------------------*/ /* + * NetChip framing of ethernet packets, supporting additional error + * checks for links that may drop bulk packets from inside messages. + * Odd USB length == always short read for last usb packet. + * - nc_header + * - Ethernet header (14 bytes) + * - payload + * - (optional padding byte, if needed so length becomes odd) + * - nc_trailer + * + * This framing is to be avoided for non-NetChip devices. + */ + +struct nc_header { // packed: + u16 hdr_len; // sizeof nc_header (LE, all) + u16 packet_len; // payload size (including ethhdr) + u16 packet_id; // detects dropped packets +#define MIN_HEADER 6 + + // all else is optional, and must start with: + // u16 vendorId; // from usb-if + // u16 productId; +} __attribute__((__packed__)); + +#define PAD_BYTE ((unsigned char)0xAC) + +struct nc_trailer { + u16 packet_id; +} __attribute__((__packed__)); + +// packets may use FLAG_FRAMING_NC and optional pad +#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ + + sizeof (struct ethhdr) \ + + (mtu) \ + + 1 \ + + sizeof (struct nc_trailer)) + +#define MIN_FRAMED FRAMED_SIZE(0) + + +/* * Zero means no timeout; else, how long a 64 byte bulk packet may be queued * before the hardware drops it. If that's done, the driver will need to * frame network packets to guard against the dropped USB packets. The win32 @@ -652,11 +979,113 @@ static int net1080_check_connect (struct usbnet *dev) return 0; } +static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) +{ + struct nc_header *header; + struct nc_trailer *trailer; + + if (!(skb->len & 0x01) + || MIN_FRAMED > skb->len + || skb->len > FRAMED_SIZE (dev->net.mtu)) { + dev->stats.rx_frame_errors++; + dbg ("rx framesize %d range %d..%d mtu %d", skb->len, + MIN_FRAMED, FRAMED_SIZE (dev->net.mtu), + dev->net.mtu + ); + return 0; + } + + header = (struct nc_header *) skb->data; + le16_to_cpus (&header->hdr_len); + le16_to_cpus (&header->packet_len); + if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) { + dev->stats.rx_frame_errors++; + dbg ("packet too big, %d", header->packet_len); + return 0; + } else if (header->hdr_len < MIN_HEADER) { + dev->stats.rx_frame_errors++; + dbg ("header too short, %d", header->hdr_len); + return 0; + } else if (header->hdr_len > MIN_HEADER) { + // out of band data for us? + dbg ("header OOB, %d bytes", + header->hdr_len - MIN_HEADER); + // switch (vendor/product ids) { ... } + } + skb_pull (skb, header->hdr_len); + + trailer = (struct nc_trailer *) + (skb->data + skb->len - sizeof *trailer); + skb_trim (skb, skb->len - sizeof *trailer); + + if ((header->packet_len & 0x01) == 0) { + if (skb->data [header->packet_len] != PAD_BYTE) { + dev->stats.rx_frame_errors++; + dbg ("bad pad"); + return 0; + } + skb_trim (skb, skb->len - 1); + } + if (skb->len != header->packet_len) { + dev->stats.rx_frame_errors++; + dbg ("bad packet len %d (expected %d)", + skb->len, header->packet_len); + return 0; + } + if (header->packet_id != get_unaligned (&trailer->packet_id)) { + dev->stats.rx_fifo_errors++; + dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", + header->packet_id, trailer->packet_id); + return 0; + } +#if 0 + devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len, + header->packet_len, header->packet_id); +#endif + return 1; +} + +static struct sk_buff * +net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) +{ + int padlen; + struct sk_buff *skb2; + + padlen = ((skb->len + sizeof (struct nc_header) + + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1; + if (!skb_cloned (skb)) { + int headroom = skb_headroom (skb); + int tailroom = skb_tailroom (skb); + + if ((padlen + sizeof (struct nc_trailer)) <= tailroom + && sizeof (struct nc_header) <= headroom) + return skb; + + if ((sizeof (struct nc_header) + padlen + + sizeof (struct nc_trailer)) < + (headroom + tailroom)) { + skb->data = memmove (skb->head + + sizeof (struct nc_header), + skb->data, skb->len); + skb->tail = skb->data + skb->len; + return skb; + } + } + skb2 = skb_copy_expand (skb, + sizeof (struct nc_header), + sizeof (struct nc_trailer) + padlen, + flags); + dev_kfree_skb_any (skb); + return skb2; +} + static const struct driver_info net1080_info = { description: "NetChip TurboCONNECT", flags: FLAG_FRAMING_NC, reset: net1080_reset, check_connect: net1080_check_connect, + rx_fixup: net1080_rx_fixup, + tx_fixup: net1080_tx_fixup, in: 1, out: 1, // direction distinguishes these epsize: 64, @@ -730,6 +1159,8 @@ static int pl_check_connect (struct usbnet *dev) static const struct driver_info prolific_info = { description: "Prolific PL-2301/PL-2302", + flags: FLAG_NO_SETINT, + /* some PL-2302 versions seem to fail usb_set_interface() */ reset: pl_reset, check_connect: pl_check_connect, @@ -741,199 +1172,6 @@ static const struct driver_info prolific_info = { -#ifdef CONFIG_USB_GENELINK - -/*------------------------------------------------------------------------- - * - * GeneSys GL620USB-A (www.genesyslogic.com.tw) - * - *-------------------------------------------------------------------------*/ - -// control msg write command -#define GENELINK_CONNECT_WRITE 0xF0 -// interrupt pipe index -#define GENELINK_INTERRUPT_PIPE 0x03 -// interrupt read buffer size -#define INTERRUPT_BUFSIZE 0x08 -// interrupt pipe interval value -#define GENELINK_INTERRUPT_INTERVAL 0x10 -// max transmit packet number per transmit -#define GL_MAX_TRANSMIT_PACKETS 32 -// max packet length -#define GL_MAX_PACKET_LEN 1514 -// max receive buffer size -#define GL_RCV_BUF_SIZE (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) - -struct gl_priv -{ - struct urb *irq_urb; - char irq_buf[INTERRUPT_BUFSIZE]; -}; - -struct gl_packet -{ - u32 packet_length; - char packet_data[1]; -}; - -struct gl_header -{ - u32 packet_count; - - struct gl_packet packets; -}; - -static inline int gl_control_write(struct usbnet *dev, u8 request, u16 value) -{ - int retval; - - retval = usb_control_msg (dev->udev, - usb_sndctrlpipe (dev->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - value, - 0, // index - 0, // data buffer - 0, // size - CONTROL_TIMEOUT_JIFFIES); - - return retval; -} - -static void gl_interrupt_complete (struct urb *urb) -{ - int status = urb->status; - - if (status) - dbg("gl_interrupt_complete fail - %X\n", status); - else - dbg("gl_interrupt_complete success...\n"); -} - -static inline int gl_interrupt_read(struct usbnet *dev) -{ - struct gl_priv *priv = dev->priv_data; - int retval; - - // issue usb interrupt read - if (priv && priv->irq_urb) { - // submit urb - if ((retval = usb_submit_urb (priv->irq_urb)) != 0) - dbg("gl_interrupt_read: submit interrupt read urb fail - %X...\n", retval); - else - dbg("gl_interrupt_read: submit interrupt read urb success...\n"); - } - - return 0; -} - -// check whether another side is connected -static int genelink_check_connect (struct usbnet *dev) -{ - dbg ("%s: assuming peer is connected", dev->net.name); - return 0; - - /* - // FIXME Uncomment this code after genelink_check_connect - // control hanshaking will be implemented - - int retval; - - dbg("genelink_check_connect...\n"); - - // issue a usb control write command to detect whether another side is connected - if ((retval = gl_control_write(dev, GENELINK_CONNECT_WRITE, 0)) != 0) { - dbg ("%s: genelink_check_connect control write fail - %X\n", dev->net.name, retval); - return retval; - } else { - dbg("%s: genelink_check_conntect control write success\n",dev->net.name); - - // issue a usb interrupt read command to ack another side - - if ((retval = gl_interrupt_read(dev)) != 0) { - dbg("%s: genelink_check_connect interrupt read fail - %X\n", dev->net.name, retval); - return retval; - } else { - dbg("%s: genelink_check_connect interrupt read success\n", dev->net.name); - } - - } - */ - - return 0; -} - -// allocate and initialize the private data for genelink -static int genelink_init_priv(struct usbnet *dev) -{ - struct gl_priv *priv; - - // allocate the private data structure - if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) { - dbg("%s: cannot allocate private data per device", dev->net.name); - return -ENOMEM; - } - - // allocate irq urb - if ((priv->irq_urb = usb_alloc_urb(0)) == 0) { - dbg("%s: cannot allocate private irq urb per device", dev->net.name); - kfree(priv); - return -ENOMEM; - } - - // fill irq urb - FILL_INT_URB(priv->irq_urb, dev->udev, usb_rcvintpipe(dev->udev, GENELINK_INTERRUPT_PIPE), - priv->irq_buf, INTERRUPT_BUFSIZE, gl_interrupt_complete, 0, GENELINK_INTERRUPT_INTERVAL); - - // set private data pointer - dev->priv_data = priv; - - return 0; -} - -// release the private data -static int genelink_release_priv(struct usbnet *dev) -{ - struct gl_priv *priv = dev->priv_data; - - if (!priv) - return 0; - - // cancel irq urb first - usb_unlink_urb(priv->irq_urb); - - // free irq urb - usb_free_urb(priv->irq_urb); - - // free the private data structure - kfree(priv); - - return 0; -} - -// reset the device status -static int genelink_reset (struct usbnet *dev) -{ - // we don't need to reset, just return 0 - return 0; -} - -static const struct driver_info genelink_info = { - description: "Genesys GeneLink", - flags: FLAG_GENELINK | FLAG_NO_SETINT, - reset: genelink_reset, - check_connect: genelink_check_connect, - initialize_private: genelink_init_priv, - release_private: genelink_release_priv, - - in: 1, out: 2, // direction distinguishes these - epsize: 64, -}; - -#endif /* CONFIG_USB_GENELINK */ - - - /*------------------------------------------------------------------------- * * Network Device Driver (peer link to "Host Device", from USB host) @@ -946,11 +1184,19 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu) if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET) return -EINVAL; +#ifdef CONFIG_USB_NET1080 if (((dev->driver_info->flags) & FLAG_FRAMING_NC)) { if (FRAMED_SIZE (new_mtu) > MAX_PACKET) return -EINVAL; + } +#endif +#ifdef CONFIG_USB_GENESYS + if (((dev->driver_info->flags) & FLAG_FRAMING_GL) + && new_mtu > GL_MAX_PACKET_LEN) + return -EINVAL; +#endif // no second zero-length packet read wanted after mtu-sized packets - } else if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0) + if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0) return -EDOM; net->mtu = new_mtu; return 0; @@ -994,14 +1240,16 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) unsigned long lockflags; size_t size; -#ifdef CONFIG_USB_GENELINK - if (dev->driver_info->flags & FLAG_GENELINK) - size = GL_RCV_BUF_SIZE; - else -#endif +#ifdef CONFIG_USB_NET1080 if (dev->driver_info->flags & FLAG_FRAMING_NC) size = FRAMED_SIZE (dev->net.mtu); else +#endif +#ifdef CONFIG_USB_GENESYS + if (dev->driver_info->flags & FLAG_FRAMING_GL) + size = GL_RCV_BUF_SIZE; + else +#endif size = (sizeof (struct ethhdr) + dev->net.mtu); if ((skb = alloc_skb (size, flags)) == 0) { @@ -1055,181 +1303,14 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) { - if (dev->driver_info->flags & FLAG_FRAMING_NC) { - struct nc_header *header; - struct nc_trailer *trailer; - - if (!(skb->len & 0x01) - || MIN_FRAMED > skb->len - || skb->len > FRAMED_SIZE (dev->net.mtu)) { - dev->stats.rx_frame_errors++; - dbg ("rx framesize %d range %d..%d mtu %d", skb->len, - MIN_FRAMED, FRAMED_SIZE (dev->net.mtu), - dev->net.mtu - ); - goto error; - } - - header = (struct nc_header *) skb->data; - le16_to_cpus (&header->hdr_len); - le16_to_cpus (&header->packet_len); - if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) { - dev->stats.rx_frame_errors++; - dbg ("packet too big, %d", header->packet_len); - goto error; - } else if (header->hdr_len < MIN_HEADER) { - dev->stats.rx_frame_errors++; - dbg ("header too short, %d", header->hdr_len); - goto error; - } else if (header->hdr_len > MIN_HEADER) { - // out of band data for us? - dbg ("header OOB, %d bytes", - header->hdr_len - MIN_HEADER); - // switch (vendor/product ids) { ... } - } - skb_pull (skb, header->hdr_len); - - trailer = (struct nc_trailer *) - (skb->data + skb->len - sizeof *trailer); - skb_trim (skb, skb->len - sizeof *trailer); - - if ((header->packet_len & 0x01) == 0) { - if (skb->data [header->packet_len] != PAD_BYTE) { - dev->stats.rx_frame_errors++; - dbg ("bad pad"); - goto error; - } - skb_trim (skb, skb->len - 1); - } - if (skb->len != header->packet_len) { - dev->stats.rx_frame_errors++; - dbg ("bad packet len %d (expected %d)", - skb->len, header->packet_len); - goto error; - } - if (header->packet_id != get_unaligned (&trailer->packet_id)) { - dev->stats.rx_fifo_errors++; - dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", - header->packet_id, trailer->packet_id); - goto error; - } -#if 0 - devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len, - header->packet_len, header->packet_id); -#endif - } else { - // we trust the network stack to remove - // the extra byte we may have appended - } - -#ifdef CONFIG_USB_GENELINK - if (dev->driver_info->flags & FLAG_GENELINK) { - struct gl_header *header; - struct gl_packet *current_packet; - struct sk_buff *gl_skb; - int status; - u32 size; - - header = (struct gl_header *)skb->data; - - // get the packet count of the received skb - le32_to_cpus(&header->packet_count); - -// dbg("receive packet count = %d", header->packet_count); - - if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS) || - (header->packet_count < 0)) { - dbg("genelink: illegal received packet count %d", header->packet_count); - goto error; - } - - // set the current packet pointer to the first packet - current_packet = &(header->packets); - - // decrement the length for the packet count size 4 bytes - skb_pull(skb, 4); - - while (header->packet_count > 1) { - // get the packet length - size = current_packet->packet_length; - - // this may be a broken packet - if (size > GL_MAX_PACKET_LEN) { - dbg("genelink: illegal received packet length %d, maybe a broken packet", size); - goto error; - } - - // allocate the skb for the individual packet - gl_skb = alloc_skb (size, in_interrupt () ? GFP_ATOMIC : GFP_KERNEL); - - if (gl_skb == 0) - goto error; - - // copy the packet data to the new skb - memcpy(gl_skb->data,current_packet->packet_data,size); - - // set skb data size - gl_skb->len = size; -/* - dbg("rx_process one gl_packet, size = %d...", size); - - dbg("%02X %02X %02X %02X %02X %02X", - (u8)gl_skb->data[0],(u8)gl_skb->data[1],(u8)gl_skb->data[2], - (u8)gl_skb->data[3],(u8)gl_skb->data[4],(u8)gl_skb->data[5]); - dbg("%02X %02X %02X %02X %02X %02X\n", - (u8)gl_skb->data[6],(u8)gl_skb->data[7],(u8)gl_skb->data[8], - (u8)gl_skb->data[9],(u8)gl_skb->data[10],(u8)gl_skb->data[11]); -*/ - gl_skb->dev = &dev->net; - - // determine the packet's protocol ID - gl_skb->protocol = eth_type_trans(gl_skb, &dev->net); - - // update the status - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - // notify os of the received packet - status = netif_rx (gl_skb); - -// dev_kfree_skb (gl_skb); // just for debug purpose, delete this line for normal operation - - // advance to the next packet - current_packet = (struct gl_packet *)(current_packet->packet_data + size); - - header->packet_count --; - - // shift the data pointer to the next gl_packet - skb_pull(skb, size + 4); - } // while (header->packet_count > 1) - - // skip the packet length field 4 bytes - skb_pull(skb, 4); - } -#endif + if (dev->driver_info->rx_fixup + && !dev->driver_info->rx_fixup (dev, skb)) + goto error; + // else network stack removes extra byte if we forced a short packet if (skb->len) { int status; -#ifdef CONFIG_USB_GENELINK -/* - dbg("rx_process one packet, size = %d", skb->len); - - dbg("%02X %02X %02X %02X %02X %02X", - (u8)skb->data[0],(u8)skb->data[1],(u8)skb->data[2], - (u8)skb->data[3],(u8)skb->data[4],(u8)skb->data[5]); - dbg("%02X %02X %02X %02X %02X %02X\n", - (u8)skb->data[6],(u8)skb->data[7],(u8)skb->data[8], - (u8)skb->data[9],(u8)skb->data[10],(u8)skb->data[11]); -*/ - - if ((dev->driver_info->flags & FLAG_GENELINK) && - (skb->len > GL_MAX_PACKET_LEN)) { - dbg("genelink: illegal received packet length %d, maybe a broken packet", skb->len); - goto error; - } -#endif - // FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? skb->dev = &dev->net; @@ -1241,7 +1322,7 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) devdbg (dev, "< rx, len %d, type 0x%x", skb->len + sizeof (struct ethhdr), skb->protocol); #endif - memset (skb->cb,0,sizeof(struct skb_data)); + memset (skb->cb, 0, sizeof (struct skb_data)); status = netif_rx (skb); if (status != NET_RX_SUCCESS) devdbg (dev, "netif_rx status %d", status); @@ -1359,7 +1440,7 @@ static int usbnet_stop (struct net_device *net) DECLARE_WAITQUEUE (wait, current); mutex_lock (&dev->mutex); - netif_stop_queue(net); + netif_stop_queue (net); devdbg (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld", dev->stats.rx_packets, dev->stats.tx_packets, @@ -1375,16 +1456,13 @@ static int usbnet_stop (struct net_device *net) while (skb_queue_len (&dev->rxq) && skb_queue_len (&dev->txq) && skb_queue_len (&dev->done)) { - set_current_state(TASK_UNINTERRUPTIBLE); + set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (UNLINK_TIMEOUT_JIFFIES); dbg ("waited for %d urb completions", temp); } dev->wait = 0; remove_wait_queue (&unlink_wakeup, &wait); - if (dev->driver_info->release_private) - dev->driver_info->release_private(dev); - mutex_unlock (&dev->mutex); return 0; } @@ -1412,14 +1490,6 @@ static int usbnet_open (struct net_device *net) goto done; } - // initialize the private resources - if (info->initialize_private) { - if ((retval = info->initialize_private(dev)) < 0) { - dbg("%s: open initialize private fail", dev->net.name); - goto done; - } - } - // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { devdbg (dev, "can't open; %d", retval); @@ -1429,8 +1499,10 @@ static int usbnet_open (struct net_device *net) netif_start_queue (net); devdbg (dev, "open: enable queueing (rx %d, tx %d) mtu %d %s framing", RX_QLEN, TX_QLEN, dev->net.mtu, - (info->flags & FLAG_FRAMING_NC) - ? "NetChip" + (info->flags & (FLAG_FRAMING_NC | FLAG_FRAMING_GL)) + ? ((info->flags & FLAG_FRAMING_NC) + ? "NetChip" + : "GeneSys") : "raw" ); @@ -1446,7 +1518,7 @@ done: /* usb_clear_halt cannot be called in interrupt context */ static void -tx_clear_halt(void *data) +tx_clear_halt (void *data) { struct usbnet *dev = data; @@ -1467,7 +1539,7 @@ static void tx_complete (struct urb *urb) if (dev->ctrl_task.sync == 0) { dev->ctrl_task.routine = tx_clear_halt; dev->ctrl_task.data = dev; - schedule_task(&dev->ctrl_task); + schedule_task (&dev->ctrl_task); } else { dbg ("Cannot clear TX stall"); } @@ -1491,79 +1563,6 @@ static void usbnet_tx_timeout (struct net_device *net) /*-------------------------------------------------------------------------*/ -static inline struct sk_buff *fixup_skb (struct sk_buff *skb, int flags) -{ - int padlen; - struct sk_buff *skb2; - - padlen = ((skb->len + sizeof (struct nc_header) - + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1; - if (!skb_cloned (skb)) { - int headroom = skb_headroom (skb); - int tailroom = skb_tailroom (skb); - - if ((padlen + sizeof (struct nc_trailer)) <= tailroom - && sizeof (struct nc_header) <= headroom) - return skb; - - if ((sizeof (struct nc_header) + padlen - + sizeof (struct nc_trailer)) < - (headroom + tailroom)) { - skb->data = memmove (skb->head - + sizeof (struct nc_header), - skb->data, skb->len); - skb->tail = skb->data + skb->len; - return skb; - } - } - skb2 = skb_copy_expand (skb, - sizeof (struct nc_header), - sizeof (struct nc_trailer) + padlen, - flags); - dev_kfree_skb_any (skb); - return skb2; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_GENELINK -static struct sk_buff *gl_build_skb (struct sk_buff *skb) -{ - struct sk_buff *skb2; - int padlen; - - int headroom = skb_headroom (skb); - int tailroom = skb_tailroom (skb); - -// dbg("headroom = %d, tailroom = %d", headroom, tailroom); - - padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; - - if ((!skb_cloned (skb)) && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { - if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { - skb->data = memmove (skb->head + (4 + 4*1), - skb->data, skb->len); - skb->tail = skb->data + skb->len; - } - skb2 = skb; - } else { - skb2 = skb_copy_expand (skb, (4 + 4*1) , padlen, in_interrupt () ? GFP_ATOMIC : GFP_KERNEL); - - if (!skb2) { - dbg("genelink: skb_copy_expand fail"); - return 0; - } - - // free the original skb - dev_kfree_skb_any (skb); - } - - return skb2; -} -#endif - -/*-------------------------------------------------------------------------*/ - static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) { struct usbnet *dev = (struct usbnet *) net->priv; @@ -1571,30 +1570,25 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) int retval = NET_XMIT_SUCCESS; struct urb *urb = 0; struct skb_data *entry; - struct nc_header *header = 0; - struct nc_trailer *trailer = 0; struct driver_info *info = dev->driver_info; int flags; +#ifdef CONFIG_USB_NET1080 + struct nc_header *header = 0; + struct nc_trailer *trailer = 0; +#endif /* CONFIG_USB_NET1080 */ flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL; - if (info->flags & FLAG_FRAMING_NC) { - struct sk_buff *skb2; - skb2 = fixup_skb (skb, flags); - if (!skb2) { - dbg ("can't fixup skb"); + // some devices want funky USB-level framing, for + // win32 driver (usually) and/or hardware quirks + if (info->tx_fixup) { + skb = info->tx_fixup (dev, skb, flags); + if (!skb) { + dbg ("can't tx_fixup skb"); goto drop; } - skb = skb2; } -#ifdef CONFIG_USB_GENELINK - if ((info->flags & FLAG_GENELINK) && (skb = gl_build_skb(skb)) == 0) { - dbg("can't build skb for genelink transmit"); - goto drop; - } -#endif - if (!(urb = usb_alloc_urb (0))) { dbg ("no urb"); goto drop; @@ -1606,6 +1600,10 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) entry->state = tx_start; entry->length = length; + // FIXME: reorganize a bit, so that fixup() fills out NetChip + // framing too. (Packet ID update needs the spinlock...) + +#ifdef CONFIG_USB_NET1080 if (info->flags & FLAG_FRAMING_NC) { header = (struct nc_header *) skb_push (skb, sizeof *header); header->hdr_len = cpu_to_le16 (sizeof (*header)); @@ -1613,40 +1611,12 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) if (!((skb->len + sizeof *trailer) & 0x01)) *skb_put (skb, 1) = PAD_BYTE; trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer); - } -#ifdef CONFIG_USB_GENELINK - else if (info->flags & FLAG_GENELINK) { - u32 *packet_count, *packet_len; - - // attach the packet count to the header - packet_count = (u32 *)skb_push(skb, (4 + 4*1)); - packet_len = packet_count + 1; - - // set packet to 1 - *packet_count = 1; - - // set packet length - *packet_len = length; + } else +#endif /* CONFIG_USB_NET1080 */ - // add padding byte - if ((skb->len % EP_SIZE(dev)) == 0) - skb_put(skb, 1); - } -#endif - else if ((length % EP_SIZE (dev)) == 0) { - // not all hardware behaves with USB_ZERO_PACKET, - // so we add an extra one-byte packet - if (skb_shared (skb)) { - struct sk_buff *skb2; - skb2 = skb_unshare (skb, flags); - if (!skb2) { - dbg ("can't unshare skb"); - goto drop; - } - skb = skb2; - } + /* don't assume the hardware handles USB_ZERO_PACKET */ + if ((length % EP_SIZE (dev)) == 0) skb->len++; - } FILL_BULK_URB (urb, dev->udev, usb_sndbulkpipe (dev->udev, info->out), @@ -1658,6 +1628,8 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) // FIXME urb->timeout = ... jiffies ... ; spin_lock_irqsave (&dev->txq.lock, flags); + +#ifdef CONFIG_USB_NET1080 if (info->flags & FLAG_FRAMING_NC) { header->packet_id = cpu_to_le16 (dev->packet_id++); put_unaligned (header->packet_id, &trailer->packet_id); @@ -1667,6 +1639,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) header->packet_id); #endif } +#endif /* CONFIG_USB_NET1080 */ netif_stop_queue (net); if ((retval = usb_submit_urb (urb)) != 0) { @@ -1685,7 +1658,8 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) drop: retval = NET_XMIT_DROP; dev->stats.tx_dropped++; - dev_kfree_skb_any (skb); + if (skb) + dev_kfree_skb_any (skb); usb_free_urb (urb); #ifdef VERBOSE } else { @@ -1911,6 +1885,13 @@ static const struct usb_device_id products [] = { }, #endif +#ifdef CONFIG_USB_GENESYS +{ + USB_DEVICE (0x05e3, 0x0502), // GL620USB-A + driver_info: (unsigned long) &genelink_info, +}, +#endif + #ifdef CONFIG_USB_LINUXDEV /* * for example, this can be a host side talk-to-PDA driver. @@ -1944,12 +1925,7 @@ static const struct usb_device_id products [] = { }, #endif -#ifdef CONFIG_USB_GENELINK -{ - USB_DEVICE (0x05e3, 0x0502), // GL620USB-A - driver_info: (unsigned long) &genelink_info, -}, -#endif +/* KC2190 from www.sepoong.co.kr "InstaNET" */ { }, // END }; diff --git a/fs/block_dev.c b/fs/block_dev.c index b61910674788..485e0bffed80 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -113,6 +113,11 @@ static int blkdev_get_block(struct inode * inode, long iblock, struct buffer_hea return 0; } +static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) +{ + return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block); +} + static int blkdev_writepage(struct page * page) { return block_write_full_page(page, blkdev_get_block); @@ -640,6 +645,7 @@ struct address_space_operations def_blk_aops = { sync_page: block_sync_page, prepare_write: blkdev_prepare_write, commit_write: blkdev_commit_write, + direct_IO: blkdev_direct_IO, }; struct file_operations def_blk_fops = { diff --git a/fs/buffer.c b/fs/buffer.c index fc7638503437..e0ba7b3d077b 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1172,6 +1172,7 @@ struct buffer_head * bread(kdev_t dev, int block, int size) struct buffer_head * bh; bh = getblk(dev, block, size); + touch_buffer(bh); if (buffer_uptodate(bh)) return bh; ll_rw_block(READ, 1, &bh); @@ -1997,6 +1998,47 @@ int generic_block_bmap(struct address_space *mapping, long block, get_block_t *g return tmp.b_blocknr; } +int generic_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize, get_block_t * get_block) +{ + int i, nr_blocks, retval; + unsigned long * blocks = iobuf->blocks; + + nr_blocks = iobuf->length / blocksize; + /* build the blocklist */ + for (i = 0; i < nr_blocks; i++, blocknr++) { + struct buffer_head bh; + + bh.b_state = 0; + bh.b_dev = inode->i_dev; + bh.b_size = blocksize; + + retval = get_block(inode, blocknr, &bh, rw == READ ? 0 : 1); + if (retval) + goto out; + + if (rw == READ) { + if (buffer_new(&bh)) + BUG(); + if (!buffer_mapped(&bh)) { + /* there was an hole in the filesystem */ + blocks[i] = -1UL; + continue; + } + } else { + if (buffer_new(&bh)) + unmap_underlying_metadata(&bh); + if (!buffer_mapped(&bh)) + BUG(); + } + blocks[i] = bh.b_blocknr; + } + + retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->blocks, blocksize); + + out: + return retval; +} + /* * IO completion routine for a buffer_head being used for kiobuf IO: we * can't dispatch the kiobuf callback until io_count reaches 0. @@ -2350,10 +2392,13 @@ static int grow_buffers(kdev_t dev, unsigned long block, int size) unsigned long index; int sizebits; - if ((size & 511) || (size > PAGE_SIZE)) { - printk(KERN_ERR "VFS: grow_buffers: size = %d\n",size); - return 0; - } + /* Size must be multiple of hard sectorsize */ + if (size & (get_hardsect_size(dev)-1)) + BUG(); + /* Size must be within 512 bytes and PAGE_SIZE */ + if (size < 512 || size > PAGE_SIZE) + BUG(); + sizebits = -1; do { sizebits++; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 8e138cc9a2ad..45eeaa675c46 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -592,13 +592,18 @@ static int ext2_bmap(struct address_space *mapping, long block) { return generic_block_bmap(mapping,block,ext2_get_block); } +static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) +{ + return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block); +} struct address_space_operations ext2_aops = { readpage: ext2_readpage, writepage: ext2_writepage, sync_page: block_sync_page, prepare_write: ext2_prepare_write, commit_write: generic_commit_write, - bmap: ext2_bmap + bmap: ext2_bmap, + direct_IO: ext2_direct_IO, }; /* diff --git a/fs/inode.c b/fs/inode.c index 5e2d697efa6c..42d13e7afcf0 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -571,8 +571,7 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru continue; invalidate_inode_buffers(inode); if (!atomic_read(&inode->i_count)) { - list_del(&inode->i_hash); - INIT_LIST_HEAD(&inode->i_hash); + list_del_init(&inode->i_hash); list_del(&inode->i_list); list_add(&inode->i_list, dispose); inode->i_state |= I_FREEING; @@ -1029,13 +1028,14 @@ void remove_inode_hash(struct inode *inode) void iput(struct inode *inode) { if (inode) { + struct super_block *sb = inode->i_sb; struct super_operations *op = NULL; if (inode->i_state == I_CLEAR) BUG(); - if (inode->i_sb && inode->i_sb->s_op) - op = inode->i_sb->s_op; + if (sb && sb->s_op) + op = sb->s_op; if (op && op->put_inode) op->put_inode(inode); @@ -1065,7 +1065,7 @@ void iput(struct inode *inode) if (inode->i_state != I_CLEAR) BUG(); } else { - if (!list_empty(&inode->i_hash)) { + if (!list_empty(&inode->i_hash) && sb && sb->s_root) { if (!(inode->i_state & (I_DIRTY|I_LOCK))) { list_del(&inode->i_list); list_add(&inode->i_list, &inode_unused); @@ -1074,9 +1074,8 @@ void iput(struct inode *inode) spin_unlock(&inode_lock); return; } else { - /* magic nfs path */ - list_del(&inode->i_list); - INIT_LIST_HEAD(&inode->i_list); + list_del_init(&inode->i_list); + list_del_init(&inode->i_hash); inode->i_state|=I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); diff --git a/fs/reiserfs/Makefile b/fs/reiserfs/Makefile index af4bdcbf322d..a52020c22ed5 100644 --- a/fs/reiserfs/Makefile +++ b/fs/reiserfs/Makefile @@ -13,13 +13,13 @@ lbalance.o ibalance.o stree.o hashes.o buffer2.o tail_conversion.o journal.o res obj-m := $(O_TARGET) -# gcc -O2 (the kernel default) is overaggressive on ppc when many inline +# gcc -O2 (the kernel default) is overaggressive on ppc32 when many inline # functions are used. This causes the compiler to advance the stack # pointer out of the available stack space, corrupting kernel space, -# and causing a panic. Since this behavior only affects ppc, this ifeq +# and causing a panic. Since this behavior only affects ppc32, this ifeq # will work around it. If any other architecture displays this behavior, # add it here. -ifeq ($(shell uname -m),ppc) +ifeq ($(CONFIG_PPC32),y) EXTRA_CFLAGS := -O1 endif diff --git a/fs/super.c b/fs/super.c index 9df5268060b3..c74d32726726 100644 --- a/fs/super.c +++ b/fs/super.c @@ -739,6 +739,7 @@ void kill_super(struct super_block *sb) dput(root); fsync_super(sb); lock_super(sb); + invalidate_inodes(sb); /* bad name - it should be evict_inodes() */ if (sop) { if (sop->write_super && sb->s_dirt) sop->write_super(sb); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9dbef2980770..cdb196ca50fb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -195,11 +195,15 @@ extern void drive_stat_acct (kdev_t dev, int rw, static inline int get_hardsect_size(kdev_t dev) { - extern int *hardsect_size[]; - if (hardsect_size[MAJOR(dev)] != NULL) - return hardsect_size[MAJOR(dev)][MINOR(dev)]; - else - return 512; + int retval = 512; + int major = MAJOR(dev); + + if (hardsect_size[major]) { + int minor = MINOR(dev); + if (hardsect_size[major][minor]) + retval = hardsect_size[major][minor]; + } + return retval; } #define blk_finished_io(nsects) do { } while (0) diff --git a/include/linux/fs.h b/include/linux/fs.h index f170f9e036ec..867aa6ce8f5b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1386,6 +1386,7 @@ extern int block_sync_page(struct page *); int generic_block_bmap(struct address_space *, long, get_block_t *); int generic_commit_write(struct file *, struct page *, unsigned, unsigned); int block_truncate_page(struct address_space *, loff_t, get_block_t *); +extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *); extern int waitfor_one_page(struct page*); extern int generic_file_mmap(struct file *, struct vm_area_struct *); diff --git a/include/linux/mm.h b/include/linux/mm.h index 7737d585b0a2..5a85039279b0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -416,6 +416,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long d extern int ptrace_attach(struct task_struct *tsk); extern int ptrace_detach(struct task_struct *, unsigned int); extern void ptrace_disable(struct task_struct *); +extern int ptrace_check_attach(struct task_struct *task, int kill); /* * On a two-level page table, this ends up being trivial. Thus the diff --git a/include/linux/sched.h b/include/linux/sched.h index 9cdc5e05d025..97c574fe85d2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -304,8 +304,16 @@ struct task_struct { long nice; unsigned long policy; struct mm_struct *mm; - int has_cpu, processor; - unsigned long cpus_allowed; + int processor; + /* + * cpus_runnable is ~0 if the process is not running on any + * CPU. It's (1 << cpu) if it's running on a CPU. This mask + * is updated under the runqueue lock. + * + * To determine whether a process might run on a CPU, this + * mask is AND-ed with cpus_allowed. + */ + unsigned long cpus_runnable, cpus_allowed; /* * (only the 'next' pointer fits into the cacheline, but * that's just fine.) @@ -464,6 +472,7 @@ extern struct exec_domain default_exec_domain; policy: SCHED_OTHER, \ mm: NULL, \ active_mm: &init_mm, \ + cpus_runnable: -1, \ cpus_allowed: -1, \ run_list: LIST_HEAD_INIT(tsk.run_list), \ next_task: &tsk, \ @@ -541,6 +550,19 @@ static inline struct task_struct *find_task_by_pid(int pid) return p; } +#define task_has_cpu(tsk) ((tsk)->cpus_runnable != ~0UL) + +static inline void task_set_cpu(struct task_struct *tsk, unsigned int cpu) +{ + tsk->processor = cpu; + tsk->cpus_runnable = 1UL << cpu; +} + +static inline void task_release_cpu(struct task_struct *tsk) +{ + tsk->cpus_runnable = ~0UL; +} + /* per-UID process charging. */ extern struct user_struct * alloc_uid(uid_t); extern void free_uid(struct user_struct *); diff --git a/kernel/exit.c b/kernel/exit.c index 708cad8597f5..2650ac328e4a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -35,13 +35,13 @@ static void release_task(struct task_struct * p) */ for (;;) { task_lock(p); - if (!p->has_cpu) + if (!task_has_cpu(p)) break; task_unlock(p); do { cpu_relax(); barrier(); - } while (p->has_cpu); + } while (task_has_cpu(p)); } task_unlock(p); #endif diff --git a/kernel/fork.c b/kernel/fork.c index 91aeda9ef591..3c28fc2a9777 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -638,7 +638,7 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start, #ifdef CONFIG_SMP { int i; - p->has_cpu = 0; + p->cpus_runnable = ~0UL; p->processor = current->processor; /* ?? should we just memset this ?? */ for(i = 0; i < smp_num_cpus; i++) diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 6ecd30f3d323..bd2d762baf94 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -199,6 +199,7 @@ EXPORT_SYMBOL(submit_bh); EXPORT_SYMBOL(unlock_buffer); EXPORT_SYMBOL(__wait_on_buffer); EXPORT_SYMBOL(___wait_on_page); +EXPORT_SYMBOL(generic_direct_IO); EXPORT_SYMBOL(block_write_full_page); EXPORT_SYMBOL(block_read_full_page); EXPORT_SYMBOL(block_prepare_write); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index b9cf12d4c02d..da45fa2be7a3 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -16,6 +16,42 @@ #include <asm/pgtable.h> #include <asm/uaccess.h> +/* + * Check that we have indeed attached to the thing.. + */ +int ptrace_check_attach(struct task_struct *child, int kill) +{ + if (!(child->ptrace & PT_PTRACED)) + return -ESRCH; + + if (child->p_pptr != current) + return -ESRCH; + + if (!kill) { + if (child->state != TASK_STOPPED) + return -ESRCH; +#ifdef CONFIG_SMP + /* Make sure the child gets off its CPU.. */ + for (;;) { + task_lock(child); + if (!task_has_cpu(child)) + break; + task_unlock(child); + do { + if (child->state != TASK_STOPPED) + return -ESRCH; + barrier(); + cpu_relax(); + } while (task_has_cpu(child)); + } + task_unlock(child); +#endif + } + + /* All systems go.. */ + return 0; +} + int ptrace_attach(struct task_struct *task) { task_lock(task); diff --git a/kernel/sched.c b/kernel/sched.c index 11de5d6951a9..db3e42f74c42 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -28,6 +28,7 @@ #include <linux/kernel_stat.h> #include <linux/completion.h> #include <linux/prefetch.h> +#include <linux/compiler.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> @@ -114,8 +115,8 @@ extern struct task_struct *child_reaper; #ifdef CONFIG_SMP #define idle_task(cpu) (init_tasks[cpu_number_map(cpu)]) -#define can_schedule(p,cpu) ((!(p)->has_cpu) && \ - ((p)->cpus_allowed & (1 << cpu))) +#define can_schedule(p,cpu) \ + ((p)->cpus_runnable & (p)->cpus_allowed & (1 << cpu)) #else @@ -455,11 +456,11 @@ static inline void __schedule_tail(struct task_struct *prev) /* * prev->policy can be written from here only before `prev' - * can be scheduled (before setting prev->has_cpu to zero). + * can be scheduled (before setting prev->cpus_runnable to ~0UL). * Of course it must also be read before allowing prev * to be rescheduled, but since the write depends on the read * to complete, wmb() is enough. (the spin_lock() acquired - * before setting has_cpu is not enough because the spin_lock() + * before setting cpus_runnable is not enough because the spin_lock() * common code semantics allows code outside the critical section * to enter inside the critical section) */ @@ -468,12 +469,12 @@ static inline void __schedule_tail(struct task_struct *prev) wmb(); /* - * fast path falls through. We have to clear has_cpu before - * checking prev->state to avoid a wakeup race - thus we - * also have to protect against the task exiting early. + * fast path falls through. We have to clear cpus_runnable before + * checking prev->state to avoid a wakeup race. Protect against + * the task exiting early. */ task_lock(prev); - prev->has_cpu = 0; + task_release_cpu(prev); mb(); if (prev->state == TASK_RUNNING) goto needs_resched; @@ -505,7 +506,7 @@ needs_resched: goto out_unlock; spin_lock_irqsave(&runqueue_lock, flags); - if ((prev->state == TASK_RUNNING) && !prev->has_cpu) + if ((prev->state == TASK_RUNNING) && !task_has_cpu(prev)) reschedule_idle(prev); spin_unlock_irqrestore(&runqueue_lock, flags); goto out_unlock; @@ -545,8 +546,10 @@ need_resched_back: prev = current; this_cpu = prev->processor; - if (in_interrupt()) - goto scheduling_in_interrupt; + if (unlikely(in_interrupt())) { + printk("Scheduling in interrupt\n"); + BUG(); + } release_kernel_lock(prev, this_cpu); @@ -559,9 +562,11 @@ need_resched_back: spin_lock_irq(&runqueue_lock); /* move an exhausted RR process to be last.. */ - if (prev->policy == SCHED_RR) - goto move_rr_last; -move_rr_back: + if (unlikely(prev->policy == SCHED_RR)) + if (!prev->counter) { + prev->counter = NICE_TO_TICKS(prev->nice); + move_last_runqueue(prev); + } switch (prev->state) { case TASK_INTERRUPTIBLE: @@ -585,10 +590,6 @@ repeat_schedule: */ next = idle_task(this_cpu); c = -1000; - if (prev->state == TASK_RUNNING) - goto still_running; - -still_running_back: list_for_each(tmp, &runqueue_head) { p = list_entry(tmp, struct task_struct, run_list); if (can_schedule(p, this_cpu)) { @@ -599,21 +600,28 @@ still_running_back: } /* Do we need to re-calculate counters? */ - if (!c) - goto recalculate; + if (unlikely(!c)) { + struct task_struct *p; + + spin_unlock_irq(&runqueue_lock); + read_lock(&tasklist_lock); + for_each_task(p) + p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice); + read_unlock(&tasklist_lock); + spin_lock_irq(&runqueue_lock); + goto repeat_schedule; + } + /* * from this point on nothing can prevent us from * switching to the next task, save this fact in * sched_data. */ sched_data->curr = next; -#ifdef CONFIG_SMP - next->has_cpu = 1; - next->processor = this_cpu; -#endif + task_set_cpu(next, this_cpu); spin_unlock_irq(&runqueue_lock); - if (prev == next) { + if (unlikely(prev == next)) { /* We won't go through the normal tail, so do this by hand */ prev->policy &= ~SCHED_YIELD; goto same_process; @@ -678,38 +686,6 @@ same_process: reacquire_kernel_lock(current); if (current->need_resched) goto need_resched_back; - - return; - -recalculate: - { - struct task_struct *p; - spin_unlock_irq(&runqueue_lock); - read_lock(&tasklist_lock); - for_each_task(p) - p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice); - read_unlock(&tasklist_lock); - spin_lock_irq(&runqueue_lock); - } - goto repeat_schedule; - -still_running: - if (!(prev->cpus_allowed & (1UL << this_cpu))) - goto still_running_back; - c = goodness(prev, this_cpu, prev->active_mm); - next = prev; - goto still_running_back; - -move_rr_last: - if (!prev->counter) { - prev->counter = NICE_TO_TICKS(prev->nice); - move_last_runqueue(prev); - } - goto move_rr_back; - -scheduling_in_interrupt: - printk("Scheduling in interrupt\n"); - BUG(); return; } @@ -1072,6 +1048,10 @@ asmlinkage long sys_sched_yield(void) if (current->policy == SCHED_OTHER) current->policy |= SCHED_YIELD; current->need_resched = 1; + + spin_lock_irq(&runqueue_lock); + move_last_runqueue(current); + spin_unlock_irq(&runqueue_lock); } return 0; } @@ -1176,13 +1156,10 @@ static void show_task(struct task_struct * p) else printk(" (NOTLB)\n"); -#if defined(CONFIG_X86) || defined(CONFIG_SPARC64) || defined(CONFIG_ARM) || defined(CONFIG_ALPHA) -/* This is very useful, but only works on ARM, x86 and sparc64 right now */ { extern void show_trace_task(struct task_struct *tsk); show_trace_task(p); } -#endif } char * render_sigset_t(sigset_t *set, char *buffer) diff --git a/kernel/signal.c b/kernel/signal.c index 7f4d2e71c751..44acecd851c7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -479,7 +479,7 @@ static inline void signal_wake_up(struct task_struct *t) * other than doing an extra (lightweight) IPI interrupt. */ spin_lock(&runqueue_lock); - if (t->has_cpu && t->processor != smp_processor_id()) + if (task_has_cpu(t) && t->processor != smp_processor_id()) smp_send_reschedule(t->processor); spin_unlock(&runqueue_lock); #endif /* CONFIG_SMP */ diff --git a/mm/filemap.c b/mm/filemap.c index 1e2665a20f52..16f8da3d64af 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -555,8 +555,13 @@ int generic_buffer_fdatasync(struct inode *inode, unsigned long start_idx, unsig */ int fail_writepage(struct page *page) { - activate_page(page); - SetPageReferenced(page); + /* Only activate on memory-pressure, not fsync.. */ + if (PageLaunder(page)) { + activate_page(page); + SetPageReferenced(page); + } + + /* Set the page dirty again, unlock */ SetPageDirty(page); UnlockPage(page); return 0; @@ -1476,6 +1481,87 @@ no_cached_page: UPDATE_ATIME(inode); } +static ssize_t generic_file_direct_IO(int rw, struct file * filp, char * buf, size_t count, loff_t offset) +{ + ssize_t retval; + int new_iobuf, chunk_size, blocksize_mask, blocksize, blocksize_bits, iosize, progress; + struct kiobuf * iobuf; + struct inode * inode = filp->f_dentry->d_inode; + struct address_space * mapping = inode->i_mapping; + + new_iobuf = 0; + iobuf = filp->f_iobuf; + if (test_and_set_bit(0, &filp->f_iobuf_lock)) { + /* + * A parallel read/write is using the preallocated iobuf + * so just run slow and allocate a new one. + */ + retval = alloc_kiovec(1, &iobuf); + if (retval) + goto out; + new_iobuf = 1; + } + + blocksize = 1 << inode->i_blkbits; + blocksize_bits = inode->i_blkbits; + blocksize_mask = blocksize - 1; + chunk_size = KIO_MAX_ATOMIC_IO << 10; + + retval = -EINVAL; + if ((offset & blocksize_mask) || (count & blocksize_mask)) + goto out_free; + if (!mapping->a_ops->direct_IO) + goto out_free; + + /* + * Flush to disk exlusively the _data_, metadata must remains + * completly asynchronous or performance will go to /dev/null. + */ + filemap_fdatasync(mapping); + retval = fsync_inode_data_buffers(inode); + filemap_fdatawait(mapping); + if (retval < 0) + goto out_free; + + progress = retval = 0; + while (count > 0) { + iosize = count; + if (iosize > chunk_size) + iosize = chunk_size; + + retval = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize); + if (retval) + break; + + retval = mapping->a_ops->direct_IO(rw, inode, iobuf, (offset+progress) >> blocksize_bits, blocksize); + + if (rw == READ && retval > 0) + mark_dirty_kiobuf(iobuf, retval); + + if (retval >= 0) { + count -= retval; + buf += retval; + progress += retval; + } + + unmap_kiobuf(iobuf); + + if (retval != iosize) + break; + } + + if (progress) + retval = progress; + + out_free: + if (!new_iobuf) + clear_bit(0, &filp->f_iobuf_lock); + else + free_kiovec(1, &iobuf); + out: + return retval; +} + int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size) { char *kaddr; @@ -1509,6 +1595,9 @@ ssize_t generic_file_read(struct file * filp, char * buf, size_t count, loff_t * if ((ssize_t) count < 0) return -EINVAL; + if (filp->f_flags & O_DIRECT) + goto o_direct; + retval = -EFAULT; if (access_ok(VERIFY_WRITE, buf, count)) { retval = 0; @@ -1527,7 +1616,29 @@ ssize_t generic_file_read(struct file * filp, char * buf, size_t count, loff_t * retval = desc.error; } } + out: return retval; + + o_direct: + { + loff_t pos = *ppos, size; + struct address_space *mapping = filp->f_dentry->d_inode->i_mapping; + struct inode *inode = mapping->host; + + retval = 0; + if (!count) + goto out; /* skip atime */ + size = inode->i_size; + if (pos < size) { + if (pos + count > size) + count = size - pos; + retval = generic_file_direct_IO(READ, filp, buf, count, pos); + if (retval > 0) + *ppos = pos + retval; + } + UPDATE_ATIME(filp->f_dentry->d_inode); + goto out; + } } static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset , unsigned long size) @@ -2765,7 +2876,8 @@ generic_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos) written = 0; - if (file->f_flags & O_APPEND) + /* FIXME: this is for backwards compatibility with 2.4 */ + if (!S_ISBLK(inode->i_mode) && file->f_flags & O_APPEND) pos = inode->i_size; /* @@ -2845,6 +2957,9 @@ generic_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos) inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty_sync(inode); + if (file->f_flags & O_DIRECT) + goto o_direct; + do { unsigned long index, offset; long page_fault; @@ -2921,6 +3036,7 @@ unlock: status = generic_osync_inode(inode, OSYNC_METADATA|OSYNC_DATA); } +out_status: err = written ? written : status; out: @@ -2929,6 +3045,25 @@ out: fail_write: status = -EFAULT; goto unlock; + +o_direct: + written = generic_file_direct_IO(WRITE, file, (char *) buf, count, pos); + if (written > 0) { + loff_t end = pos + written; + if (end > inode->i_size && !S_ISBLK(inode->i_mode)) { + inode->i_size = end; + mark_inode_dirty(inode); + } + *ppos = end; + invalidate_inode_pages2(mapping); + } + /* + * Sync the fs metadata but not the minor inode changes and + * of course not the data as we did direct DMA for the IO. + */ + if (written >= 0 && file->f_flags & O_SYNC) + status = generic_osync_inode(inode, OSYNC_METADATA); + goto out_status; } void __init page_cache_init(unsigned long mempages) diff --git a/mm/shmem.c b/mm/shmem.c index 63e6838976c7..ddcac7a48a93 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -428,6 +428,8 @@ static int shmem_writepage(struct page * page) if (!PageLocked(page)) BUG(); + if (!PageLaunder(page)) + return fail_writepage(page); mapping = page->mapping; index = page->index; |
