called sc.o. See Documentation/isdn/README.sc and
http://www.spellcast.com for more information.
-Eicon.Diehl active card support
+Eicon active card support
CONFIG_ISDN_DRV_EICON
Say Y here if you have an Eicon active ISDN card. In order to use
this card, additional firmware is necessary, which has to be loaded
latest isdn4k-utils package. Please read the file
Documentation/isdn/README.eicon for more information.
+Eicon legacy driver
+CONFIG_ISDN_DRV_EICON_OLD
+ Say Y here to use your Eicon active ISDN card with ISDN4Linux
+ isdn module.
+
+Eicon Diva Server card support
+CONFIG_ISDN_DRV_EICON_PCI
+ Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN card.
+ Please read Documentation/isdn/README.eicon for more information.
+
Eicon old-type card support
CONFIG_ISDN_DRV_EICON_ISA
Say Y here if you have an old-type Eicon active ISDN card. In order
the latest isdn4k-utils package. Please read the file
Documentation/isdn/README.eicon for more information.
+Eicon driver type standalone
+CONFIG_ISDN_DRV_EICON_DIVAS
+ Enable this option if you want the eicon driver as standalone
+ version with no interface to the ISDN4Linux isdn module. If you
+ say Y here, the eicon module only supports the Diva Server PCI
+ cards and will provide its own IDI interface. You should say N
+ here.
+
Support AT-Fax Class 2 commands
CONFIG_ISDN_TTY_FAX
If you say Y here, the modem-emulator will support a subset of the
the necessary D-Channel traces for isdnlog.
-FILECHECK:
-A part of the eicon driver source code files are provided
-by Eicon Technology. In order to get the best support from Eicon,
-these files are tested with a checksum, just to know if the files
-were modified. This does *not* mean, you are not allowed to modify the
-driver. If you want to improve the driver or you fix a bug, please do
-so and let me (or Eicon) know, about the necessary changes. So
-every user knows, if the driver he uses is modified or checked with
-Eicon files. When the driver has been loaded, in the syslog you will
-find something like "verified" or "modified" right after the version.
-
Thanks to
Deutsche Mailbox Saar-Lor-Lux GmbH
L: isdn4linux@listserv.isdn4linux.de
S: Maintained
+ISDN SUBSYSTEM (Eicon active card driver)
+P: Armin Schindler
+M: mac@melware.de
+L: isdn4linux@listserv.isdn4linux.de
+W: http://www.melware.net
+S: Maintained
+
JOYSTICK DRIVER
P: Vojtech Pavlik
M: vojtech@suse.cz
VERSION = 2
PATCHLEVEL = 2
SUBLEVEL = 19
-EXTRAVERSION = pre16
+EXTRAVERSION = pre17
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
* Problem: This can schedule. I moved the block device
* wakeup almost late by priority because of that.
*/
- DRIVER(drive)->media_change(drive);
+ if (DRIVER(drive))
+ DRIVER(drive)->media_change(drive);
/* We kick the VFS too (see fix in ide.c revalidate) */
check_disk_change(MKDEV(HWIF(drive)->major, (drive->select.b.unit) << PARTN_BITS));
static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
{
- int ret, i;
- u_char buf[4 + 4 * 20], *base;
+ unsigned char buf[20], *base;
struct dvd_layer *layer;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
+ int ret, layer_num = s->physical.layer_num;
+
+ if (layer_num >= DVD_LAYERS)
+ return -EINVAL;
init_cdrom_command(&cgc, buf, sizeof(buf));
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[6] = s->physical.layer_num;
+ cgc.cmd[6] = layer_num;
cgc.cmd[7] = s->type;
cgc.cmd[9] = cgc.buflen & 0xff;
return ret;
base = &buf[4];
- layer = &s->physical.layer[0];
-
- /* place the data... really ugly, but at least we won't have to
- worry about endianess in userspace or here. */
- for (i = 0; i < 4; ++i, base += 20, ++layer) {
- memset(layer, 0, sizeof(*layer));
- layer->book_version = base[0] & 0xf;
- layer->book_type = base[0] >> 4;
- layer->min_rate = base[1] & 0xf;
- layer->disc_size = base[1] >> 4;
- layer->layer_type = base[2] & 0xf;
- layer->track_path = (base[2] >> 4) & 1;
- layer->nlayers = (base[2] >> 5) & 3;
- layer->track_density = base[3] & 0xf;
- layer->linear_density = base[3] >> 4;
- layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
- layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
- layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
- layer->bca = base[16] >> 7;
- }
+ layer = &s->physical.layer[layer_num];
+
+ /*
+ * place the data... really ugly, but at least we won't have to
+ * worry about endianess in userspace.
+ */
+ memset(layer, 0, sizeof(*layer));
+ layer->book_version = base[0] & 0xf;
+ layer->book_type = base[0] >> 4;
+ layer->min_rate = base[1] & 0xf;
+ layer->disc_size = base[1] >> 4;
+ layer->layer_type = base[2] & 0xf;
+ layer->track_path = (base[2] >> 4) & 1;
+ layer->nlayers = (base[2] >> 5) & 3;
+ layer->track_density = base[3] & 0xf;
+ layer->linear_density = base[3] >> 4;
+ layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
+ layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
+ layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
+ layer->bca = base[16] >> 7;
return 0;
}
endif
endif
-obj-$(CONFIG_SX) += sx.o generic_serial.o
+ifeq ($(CONFIG_SX),y)
+O_OBJS += sx.o generic_serial.o
+else
+ ifeq ($(CONFIG_SX),m)
+ M_OBJS += sx.o generic_serial.o
+ endif
+endif
ifeq ($(CONFIG_RIO),y)
O_OBJS += rio/rio.o generic_serial.o
DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
vm.width, vm.height);
#endif
- if (vm.frame<0||vm.frame>FRAME_NUM) {
+ if (vm.frame<0||vm.frame>=FRAME_NUM) {
retval = -EINVAL;
break;
}
int misc_register(struct miscdevice * misc)
{
+ struct miscdevice *c;
+
if (misc->next || misc->prev)
return -EBUSY;
+ c = misc_list.next;
+
+ while ((c != &misc_list) && (c->minor != misc->minor))
+ c = c->next;
+ if (c != &misc_list)
+ return -EBUSY;
+
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = DYNAMIC_MINORS;
while (--i >= 0)
}
+static void sx_break (struct tty_struct * tty, int flag)
+{
+ struct sx_port *port = tty->driver_data;
+ int rv;
+
+ if (flag)
+ rv = sx_send_command (port, HS_START, -1, HS_IDLE_BREAK);
+ else
+ rv = sx_send_command (port, HS_STOP, -1, HS_IDLE_OPEN);
+ if (rv != 1) printk (KERN_ERR "sx: couldn't send break (%x).\n",
+ read_sx_byte (port->board, CHAN_OFFSET (port, hi_hstat)));
+}
+
+
static int sx_ioctl (struct tty_struct * tty, struct file * filp,
unsigned int cmd, unsigned long arg)
{
sx_reconfigure_port(port);
}
break;
-
default:
rc = -ENOIOCTLCMD;
break;
sx_driver.table = sx_table;
sx_driver.termios = sx_termios;
sx_driver.termios_locked = sx_termios_locked;
+ sx_driver.break_ctl = sx_break;
sx_driver.open = sx_open;
sx_driver.close = gs_close;
-/* 3c527.c: 3Com Etherlink/MC32 driver for Linux
+/* 3c527.c: 3Com Etherlink/MC32 driver for Linux 2.2
*
* (c) Copyright 1998 Red Hat Software Inc
- * Written by Alan Cox.
+ * Written by Alan Cox.
+ * Modified by Richard Procter (rnp@netlink.co.nz)
*
* Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
* (for the MCA stuff) written by Wim Dumon.
* documentation.
*
* This software may be used and distributed according to the terms
- * of the GNU Public License, incorporated herein by reference.
+ * of the GNU General Public License, incorporated herein by reference.
*
*/
static const char *version =
- "3c527.c:v0.06 1999/09/16 Alan Cox (alan@redhat.com)\n";
+ "3c527.c:v0.5 1999/09/16 Alan Cox (alan@redhat.com)\n";
-/* Modified by Richard Procter (rprocter@mcs.vuw.ac.nz, rnp@netlink.co.nz) */
-
-/*
- * Things you need
- * o The databook.
- *
- * Traps for the unwary
+/**
+ * DOC: Traps for the unwary
*
* The diagram (Figure 1-1) and the POS summary disagree with the
* "Interrupt Level" section in the manual.
*
+ * The manual contradicts itself when describing the minimum number
+ * buffers in the 'configure lists' command.
+ * My card accepts a buffer config of 4/4.
+ *
+ * Setting the SAV BP bit does not save bad packets, but
+ * only enables RX on-card stats collection.
+ *
* The documentation in places seems to miss things. In actual fact
* I've always eventually found everything is documented, it just
* requires careful study.
*
- * The manual contradicts itself when describing the minimum number
- * buffers in the 'configure lists' command.
- * My card accepts a buffer config of 4/4.
+ * DOC: Theory Of Operation
*
- */
+ * The 3com 3c527 is a 32bit MCA bus mastering adapter with a large
+ * amount of on board intelligence that housekeeps a somewhat dumber
+ * Intel NIC. For performance we want to keep the transmit queue deep
+ * as the card can transmit packets while fetching others from main
+ * memory by bus master DMA. Transmission and reception are driven by
+ * circular buffer queues.
+ *
+ * The mailboxes can be used for controlling how the card traverses
+ * its buffer rings, but are used only for inital setup in this
+ * implementation. The exec mailbox allows a variety of commands to
+ * be executed. Each command must complete before the next is
+ * executed. Primarily we use the exec mailbox for controlling the
+ * multicast lists. We have to do a certain amount of interesting
+ * hoop jumping as the multicast list changes can occur in interrupt
+ * state when the card has an exec command pending. We defer such
+ * events until the command completion interrupt.
+ *
+ * A copy break scheme (taken from 3c59x.c) is employed whereby
+ * received frames exceeding a configurable length are passed
+ * directly to the higher networking layers without incuring a copy,
+ * in what amounts to a time/space trade-off.
+ *
+ * The card also keeps a large amount of statistical information
+ * on-board. In a perfect world, these could be used safely at no
+ * cost. However, lacking information to the contrary, processing
+ * them without races would involve so much extra complexity as to
+ * make it unworthwhile to do so. In the end, a hybrid SW/HW
+ * implementation was made necessary --- see mc32_update_stats().
+ *
+ * DOC: Notes
+ *
+ * It should be possible to use two or more cards, but at this stage
+ * only by loading two copies of the same module.
+ *
+ * The on-board 82586 NIC has trouble receiving multiple
+ * back-to-back frames and so is likely to drop packets from fast
+ * senders.
+**/
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-
-#include <linux/proc_fs.h>
+#include <linux/if_ether.h>
#include "3c527.h"
static unsigned int mc32_debug = NET_DEBUG;
/* The number of low I/O ports used by the ethercard. */
-#define NETCARD_IO_EXTENT 8
+#define MC32_IO_EXTENT 8
/* As implemented, values must be a power-of-2 -- 4/8/16/32 */
-
-#define TX_RING_LEN 16 /* Typically the card supports 37 */
+#define TX_RING_LEN 32 /* Typically the card supports 37 */
#define RX_RING_LEN 8 /* " " " */
+/* Copy break point, see above for details.
+ * Setting to > 1512 effectively disables this feature. */
#define RX_COPYBREAK 200 /* Value from 3c59x.c */
+/* Issue the 82586 workaround command - this is for "busy lans", but
+ * basically means for all lans now days - has a performance (latency)
+ * cost, but best set. */
+static const int WORKAROUND_82586=1;
/* Pointers to buffers and their on-card records */
struct mc32_ring_desc
u16 rx_chain; /* Receive list start offset */
u16 tx_len; /* Transmit list count */
u16 rx_len; /* Receive list count */
+
u32 base;
- u16 rx_halted;
- u16 tx_halted;
u16 exec_pending;
u16 mc_reload_wait; /* a multicast load request is pending */
+ u32 mc_list_valid; /* True when the mclist is set */
+ u16 xceiver_state; /* Current transceiver state. bitmapped */
+ u16 desired_state; /* The state we want the transceiver to be in */
atomic_t tx_count; /* buffers left */
struct wait_queue *event;
-
+
struct mc32_ring_desc tx_ring[TX_RING_LEN]; /* Host Transmit ring */
struct mc32_ring_desc rx_ring[RX_RING_LEN]; /* Host Receive ring */
u16 tx_ring_head; /* index to tx en-queue end */
u16 rx_ring_tail; /* index to rx de-queue end */
-
- u32 mc_list_valid; /* True when the mclist is set */
};
/* The station (ethernet) address prefix, used for a sanity check. */
};
-/* Index to functions, as function prototypes. */
+/* Macros for ring index manipulations */
+static inline u16 next_rx(u16 rx) { return (rx+1)&(RX_RING_LEN-1); };
+static inline u16 prev_rx(u16 rx) { return (rx-1)&(RX_RING_LEN-1); };
+
+static inline u16 next_tx(u16 tx) { return (tx+1)&(TX_RING_LEN-1); };
+
+/* Index to functions, as function prototypes. */
extern int mc32_probe(struct device *dev);
static int mc32_probe1(struct device *dev, int ioaddr);
static struct net_device_stats *mc32_get_stats(struct device *dev);
static void mc32_set_multicast_list(struct device *dev);
static void mc32_reset_multicast_list(struct device *dev);
-static void mc32_flush_tx_ring(struct mc32_local *lp);
-/*
- * Check for a network adaptor of this type, and return '0' iff one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr == 1, always return failure.
- * If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
+/**
+ * mc32_probe:
+ * @dev: device to probe
+ *
+ * Because MCA bus is a real bus and we can scan for cards we could do a
+ * single scan for all boards here. Right now we use the passed in device
+ * structure and scan for only one board. This needs fixing for modules
+ * in paticular.
*/
__initfunc(int mc32_probe(struct device *dev))
return -ENODEV;
}
-/*
- * This is the real probe routine. Linux has a history of friendly device
- * probes on the ISA bus. A good device probes avoids doing writes, and
- * verifies that the correct device exists and functions.
+/**
+ * mc32_probe1:
+ * @dev: Device structure to fill in
+ * @slot: The MCA bus slot being used by this card
+ *
+ * Decode the slot data and configure the card structures. Having done this we
+ * can reset the card and configure it. The card does a full self test cycle
+ * in firmware so we have to wait for it to return and post us either a
+ * failure case or some addresses we use to find the board internals.
*/
+
__initfunc(static int mc32_probe1(struct device *dev, int slot))
{
static unsigned version_printed = 0;
- int i;
+ int i, err;
u8 POS;
u32 base;
struct mc32_local *lp;
* Grab the IRQ
*/
- if(request_irq(dev->irq, &mc32_interrupt, 0, cardname, dev))
- {
- printk("%s: unable to get IRQ %d.\n",
- dev->name, dev->irq);
- return -EAGAIN;
+ i = request_irq(dev->irq, &mc32_interrupt, SA_SHIRQ, dev->name, dev);
+ if(i) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return i;
}
+ request_region(dev->base_addr, MC32_IO_EXTENT, cardname);
+
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL);
- if (dev->priv == NULL)
+ if (dev->priv == NULL)
{
- free_irq(dev->irq, dev);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_exit_irq;
}
}
if(i==1000)
{
printk("%s: failed to boot adapter.\n", dev->name);
- free_irq(dev->irq, dev);
- return -ENODEV;
+ err = -ENODEV;
+ goto err_exit_free;
}
udelay(1000);
if(inb(dev->base_addr+2)&(1<<5))
base<0x0A?" test failure":"");
else
printk("%s: unknown failure %d.\n", dev->name, base);
- free_irq(dev->irq, dev);
- return -ENODEV;
+ err = -ENODEV;
+ goto err_exit_free;
}
base=0;
if(n>100)
{
printk(KERN_ERR "%s: mailbox read fail (%d).\n", dev->name, i);
- free_irq(dev->irq, dev);
- return -ENODEV;
+ err = -ENODEV;
+ goto err_exit_free;
}
}
dev->get_stats = mc32_get_stats;
dev->set_multicast_list = mc32_set_multicast_list;
- lp->rx_halted = 1;
- lp->tx_halted = 1;
+ lp->xceiver_state = HALTED;
+
+ lp->tx_ring_tail=lp->tx_ring_head=0;
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
return 0;
+
+err_exit_free:
+ kfree(dev->priv);
+err_exit_irq:
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MC32_IO_EXTENT);
+ return err;
}
-/*
- * Polled command stuff
+
+/**
+ * mc32_ready_poll:
+ * @dev: The device to wait for
+ *
+ * Wait until the card becomes ready to accept a command via the
+ * command register. This tells us nothing about the completion
+ * status of any pending commands and takes very little time at all.
*/
-static void mc32_ring_poll(struct device *dev)
+static void mc32_ready_poll(struct device *dev)
{
int ioaddr = dev->base_addr;
while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
}
-/*
- * Send exec commands. This requires a bit of explaining.
+/**
+ * mc32_command_nowait:
+ * @dev: The 3c527 to issue the command to
+ * @cmd: The command word to write to the mailbox
+ * @data: A data block if the command expects one
+ * @len: Length of the data block
*
- * You feed the card a command, you wait, it interrupts you get a
- * reply. All well and good. The complication arises because you use
- * commands for filter list changes which come in at bh level from things
- * like IPV6 group stuff.
- *
- * We have a simple state machine
- *
- * 0 - nothing issued
- * 1 - command issued, wait reply
- * 2 - reply waiting - reader then goes to state 0
- * 3 - command issued, trash reply. In which case the irq
- * takes it back to state 0
- */
-
-
-/*
- * Send command from interrupt state
+ * Send a command from interrupt state. If there is a command
+ * currently being executed then we return an error of -1. It simply
+ * isn't viable to wait around as commands may be slow. Providing we
+ * get in, we busy wait for the board to become ready to accept the
+ * command and issue it. We do not wait for the command to complete
+ * --- the card will interrupt us when it's done.
*/
static int mc32_command_nowait(struct device *dev, u16 cmd, void *data, int len)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
int ioaddr = dev->base_addr;
-
+
if(lp->exec_pending)
return -1;
-
+
lp->exec_pending=3;
lp->exec_box->mbox=0;
lp->exec_box->mbox=cmd;
}
-/*
- * Send command and block for results. On completion spot and reissue
- * multicasts
+/**
+ * mc32_command:
+ * @dev: The 3c527 card to issue the command to
+ * @cmd: The command word to write to the mailbox
+ * @data: A data block if the command expects one
+ * @len: Length of the data block
+ *
+ * Sends exec commands in a user context. This permits us to wait around
+ * for the replies and also to wait for the command buffer to complete
+ * from a previous command before we execute our command. After our
+ * command completes we will complete any pending multicast reload
+ * we blocked off by hogging the exec buffer.
+ *
+ * You feed the card a command, you wait, it interrupts you get a
+ * reply. All well and good. The complication arises because you use
+ * commands for filter list changes which come in at bh level from things
+ * like IPV6 group stuff.
+ *
+ * We have a simple state machine
+ *
+ * 0 - nothing issued
+ *
+ * 1 - command issued, wait reply
+ *
+ * 2 - reply waiting - reader then goes to state 0
+ *
+ * 3 - command issued, trash reply. In which case the irq
+ * takes it back to state 0
+ *
*/
static int mc32_command(struct device *dev, u16 cmd, void *data, int len)
cli();
while(lp->exec_pending!=2)
- sleep_on(&lp->event);
+ sleep_on(&lp->event);
lp->exec_pending=0;
restore_flags(flags);
*/
if(lp->mc_reload_wait)
+ {
mc32_reset_multicast_list(dev);
+ }
return ret;
}
-/*
- * RX abort
- */
-
-static void mc32_rx_abort(struct device *dev)
-{
- struct mc32_local *lp = (struct mc32_local *)dev->priv;
- int ioaddr = dev->base_addr;
+/**
+ * mc32_start_transceiver:
+ * @dev: The 3c527 card to issue the command to
+ *
+ * This may be called from the interrupt state, where it is used
+ * to restart the rx ring if the card runs out of rx buffers.
+ *
+ * First, we check if it's ok to start the transceiver. We then show
+ * the card where to start in the rx ring and issue the
+ * commands to start reception and transmission. We don't wait
+ * around for these to complete.
+ */
- mc32_ring_poll(dev);
-
- lp->rx_box->mbox=0;
- outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD); /* Suspend reception */
-
- mc32_ring_poll(dev);
-}
+static void mc32_start_transceiver(struct device *dev) {
-
-/*
- * RX enable
- */
-
-static void mc32_rx_begin(struct device *dev)
-{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
int ioaddr = dev->base_addr;
- /* Tell the card start reception at the first descriptor */
- lp->rx_box->data[0]=lp->rx_chain;
-
- mc32_ring_poll(dev);
-
- lp->rx_box->mbox=0;
- outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); /* GO */
+ /* Ignore RX overflow on device closure */
+ if (lp->desired_state==HALTED)
+ return;
- mc32_ring_poll(dev);
- lp->rx_halted=0;
-}
+ mc32_ready_poll(dev);
-static void mc32_tx_abort(struct device *dev)
-{
- struct mc32_local *lp = (struct mc32_local *)dev->priv;
- int ioaddr = dev->base_addr;
-
- mc32_ring_poll(dev);
-
lp->tx_box->mbox=0;
- outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD); /* Suspend */
+ lp->rx_box->mbox=0;
+
+ /* Give the card the offset to the post-EOL-bit RX descriptor */
+ lp->rx_box->data[0]=lp->rx_ring[prev_rx(lp->rx_ring_tail)].p->next;
- mc32_flush_tx_ring(lp);
+ outb(HOST_CMD_START_RX, ioaddr+HOST_CMD);
- mc32_ring_poll(dev);
+ mc32_ready_poll(dev);
+ outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD); /* card ignores this on RX restart */
+
+ /* We are not interrupted on start completion */
+ lp->xceiver_state=RUNNING;
}
-/*
- * TX enable
- */
-
-static void mc32_tx_begin(struct device *dev)
+
+/**
+ * mc32_halt_transceiver:
+ * @dev: The 3c527 card to issue the command to
+ *
+ * We issue the commands to halt the card's transceiver. In fact,
+ * after some experimenting we now simply tell the card to
+ * suspend. When issuing aborts occasionally odd things happened.
+ *
+ * We then sleep until the card has notified us that both rx and
+ * tx have been suspended.
+ */
+
+static void mc32_halt_transceiver(struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
int ioaddr = dev->base_addr;
-
- mc32_ring_poll(dev);
+ unsigned long flags;
+
+ mc32_ready_poll(dev);
lp->tx_box->mbox=0;
- outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD); /* GO */
+ lp->rx_box->mbox=0;
- mc32_ring_poll(dev);
- lp->tx_halted=0;
-}
+ outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD);
+ mc32_ready_poll(dev);
+ outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD);
+
+ save_flags(flags);
+ cli();
+
+ while(lp->xceiver_state!=HALTED)
+ sleep_on(&lp->event);
+
+ restore_flags(flags);
+}
-
-/*
- * Load the rx ring.
+
+/**
+ * mc32_load_rx_ring:
+ * @dev: 3c527 to build the ring for
+ *
+ * This initalises the on-card and driver datastructures to
+ * the point where mc32_start_transceiver() can be called.
+ *
+ * The card sets up the receive ring for us. We are required to use the
+ * ring it provides although we can change the size of the ring.
+ *
+ * We allocate an sk_buff for each ring entry in turn and
+ * initalise its house-keeping info. At the same time, we read
+ * each 'next' pointer in our rx_ring array. This reduces slow
+ * shared-memory reads and makes it easy to access predecessor
+ * descriptors.
+ *
+ * We then set the end-of-list bit for the last entry so that the
+ * card will know when it has run out of buffers.
*/
-
+
static int mc32_load_rx_ring(struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
lp->rx_ring[i-1].p->control |= CONTROL_EOL;
- lp->rx_ring_tail=0;
+ lp->rx_ring_tail=0;
- lp->rx_box->mbox=0; /* check: needed ? */
return 0;
}
-static void mc32_flush_rx_ring(struct mc32_local *lp)
+
+/**
+ * mc32_flush_rx_ring:
+ * @lp: Local data of 3c527 to flush the rx ring of
+ *
+ * Free the buffer for each ring slot. This may be called
+ * before mc32_load_rx_ring(), eg. on error in mc32_open().
+ */
+
+static void mc32_flush_rx_ring(struct device *dev)
{
+ struct mc32_local *lp = (struct mc32_local *)dev->priv;
+
+ struct sk_buff *skb;
int i;
- for(i=0; i < RX_RING_LEN; i++) {
- kfree_skb(lp->rx_ring[i].skb);
+
+ for(i=0; i < RX_RING_LEN; i++)
+ {
+ skb = lp->rx_ring[i].skb;
+ if (skb!=NULL) {
+ kfree_skb(skb);
+ skb=NULL;
+ }
lp->rx_ring[i].p=NULL;
}
}
-/* Load the addresses of the on-card buffer descriptors into main memory */
+/**
+ * mc32_load_tx_ring:
+ * @dev: The 3c527 card to issue the command to
+ *
+ * This sets up the host transmit data-structures.
+ *
+ * First, we obtain from the card it's current postion in the tx
+ * ring, so that we will know where to begin transmitting
+ * packets.
+ *
+ * Then, we read the 'next' pointers from the on-card tx ring into
+ * our tx_ring array to reduce slow shared-mem reads. Finally, we
+ * intitalise the tx house keeping variables.
+ *
+ */
+
static void mc32_load_tx_ring(struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
tx_base=lp->tx_box->data[0];
- /* Read the 'next' pointers from the on-card list into */
- /* our tx_ring array so we can reduce slow shared-mem reads */
-
for(i=0;i<lp->tx_len;i++)
{
p=bus_to_virt(lp->base+tx_base);
tx_base=p->next;
}
+ /* -1 so that tx_ring_head cannot "lap" tx_ring_tail, */
+ /* which would be bad news for mc32_tx_ring as cur. implemented */
+
+ atomic_set(&lp->tx_count, TX_RING_LEN-1);
lp->tx_ring_head=lp->tx_ring_tail=0;
}
-static void mc32_flush_tx_ring(struct mc32_local *lp)
+
+/**
+ * mc32_flush_tx_ring:
+ * @lp: Local data of 3c527 to flush the tx ring of
+ *
+ * We have to consider two cases here. We want to free the pending
+ * buffers only. If the ring buffer head is past the start then the
+ * ring segment we wish to free wraps through zero. The tx ring
+ * house-keeping variables are then reset.
+ */
+
+static void mc32_flush_tx_ring(struct device *dev)
{
+ struct mc32_local *lp = (struct mc32_local *)dev->priv;
if(lp->tx_ring_tail!=lp->tx_ring_head)
{
- int i;
-
+ int i;
if(lp->tx_ring_tail < lp->tx_ring_head)
{
for(i=lp->tx_ring_tail;i<lp->tx_ring_head;i++)
}
}
- /* -1 so that tx_ring_head cannot "lap" tx_ring_tail, */
- /* which would be bad news for mc32_tx_ring as cur. implemented */
- atomic_set(&lp->tx_count, TX_RING_LEN-1);
+ atomic_set(&lp->tx_count, 0);
lp->tx_ring_tail=lp->tx_ring_head=0;
}
-/*
- * Open/initialize the board. This is called (in the current kernel)
- * sometime after booting when the 'ifconfig' program is run.
+
+/**
+ * mc32_open
+ * @dev: device to open
+ *
+ * The user is trying to bring the card into ready state. This requires
+ * a brief dialogue with the card. Firstly we enable interrupts and then
+ * 'indications'. Without these enabled the card doesn't bother telling
+ * us what it has done. This had me puzzled for a week.
+ *
+ * We configure the number of card descriptors, then load the network
+ * address and multicast filters. Turn on the workaround mode. This
+ * works around a bug in the 82586 - it asks the firmware to do
+ * so. It has a performance (latency) hit but is needed on busy
+ * [read most] lans. We load the ring with buffers then we kick it
+ * all off.
*/
static int mc32_open(struct device *dev)
{
int ioaddr = dev->base_addr;
struct mc32_local *lp = (struct mc32_local *)dev->priv;
- u16 zero_word=0;
u8 one=1;
u8 regs;
u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN};
-
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
+
+ MOD_INC_USE_COUNT;
/*
* Interrupts enabled
mc32_command(dev, 4, &one, 2);
/*
- * Send the command sequence "abort, resume" for RX and TX.
- * The abort cleans up the buffer chains if needed.
+ * Poke it to make sure it's really dead.
*/
- mc32_rx_abort(dev);
- mc32_tx_abort(dev);
-
- /* Ask card to set up on-card descriptors to our spec */
+ mc32_halt_transceiver(dev);
+ mc32_flush_tx_ring(dev);
+
+ /*
+ * Ask card to set up on-card descriptors to our spec
+ */
if(mc32_command(dev, 8, descnumbuffs, 4)) {
printk("%s: %s rejected our buffer configuration!\n",
dev->name, cardname);
+ mc32_close(dev);
return -ENOBUFS;
}
/* Set the filters */
mc32_set_multicast_list(dev);
-
- /* Issue the 82586 workaround command - this is for "busy lans",
- but basically means for all lans now days - has a performance
- cost but best set */
-
- /* mc32_command(dev, 0x0D, &zero_word, 2); */ /* 82586 bug workaround on */
-
- /* Load the ring we just initialised */
+
+ if (WORKAROUND_82586) {
+ u16 zero_word=0;
+ mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */
+ }
mc32_load_tx_ring(dev);
- if(mc32_load_rx_ring(dev))
+ if(mc32_load_rx_ring(dev))
{
mc32_close(dev);
return -ENOBUFS;
}
-
- /* And the resume command goes last */
- mc32_rx_begin(dev);
- mc32_tx_begin(dev);
+ lp->desired_state = RUNNING;
- MOD_INC_USE_COUNT;
+ /* And finally, set the ball rolling... */
+
+ mc32_start_transceiver(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
return 0;
}
+/**
+ * mc32_send_packet:
+ * @skb: buffer to transmit
+ * @dev: 3c527 to send it out of
+ *
+ * Transmit a buffer. This normally means throwing the buffer onto
+ * the transmit queue as the queue is quite large. If the queue is
+ * full then we set tx_busy and return. Once the interrupt handler
+ * gets messages telling it to reclaim transmit queue entries we will
+ * clear tx_busy and the kernel will start calling this again.
+ *
+ * We use cli rather than spinlocks. Since I have no access to an SMP
+ * MCA machine I don't plan to change it. It is probably the top
+ * performance hit for this driver on SMP however.
+ */
+
static int mc32_send_packet(struct sk_buff *skb, struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
/* P is the last sending/sent buffer as a pointer */
p=lp->tx_ring[lp->tx_ring_head].p;
- lp->tx_ring_head++;
- lp->tx_ring_head&=(TX_RING_LEN-1);
+ lp->tx_ring_head=next_tx(lp->tx_ring_head);
/* NP is the buffer we will be loading */
np=lp->tx_ring[lp->tx_ring_head].p;
/* We will need this to flush the buffer out */
lp->tx_ring[lp->tx_ring_head].skb=skb;
- np->length = (skb->len < 60) ? 60 : skb->len;
+ np->length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
np->data = virt_to_bus(skb->data);
np->status = 0;
return 0;
}
+
+/**
+ * mc32_update_stats:
+ * @dev: 3c527 to service
+ *
+ *
+ * Query and reset the on-card stats. There's the small possibility
+ * of a race here, which would result in an underestimation of
+ * actual errors. As such, we'd prefer to keep all our stats
+ * collection in software. As a rule, we do. However it can't be
+ * used for rx errors and collisions as, by default, the card discards
+ * bad rx packets.
+ *
+ * Setting the SAV BP in the rx filter command supposedly
+ * stops this behaviour. However, testing shows that it only seems to
+ * enable the collation of on-card rx statistics --- the driver
+ * never sees an RX descriptor with an error status set.
+ *
+ */
+
static void mc32_update_stats(struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
volatile struct mc32_stats *st = lp->stats;
-
- u32 rx_errors=0;
-
- /* The databook isn't at all clear about whether */
- /* the host should suspend rx/tx here to avoid races. */
- /* Assuming that it should, my code here is probably */
- /* a bit dodgey - RP */
-
- /* TO DO: count rx_errors, tx_errors, figure out how to measure colisions */
-
- /* RX */
- rx_errors += lp->net_stats.rx_crc_errors += st->rx_crc_errors;
- st->rx_crc_errors = 0;
-
- rx_errors += lp->net_stats.rx_length_errors += st->rx_tooshort_errors;
- st->rx_tooshort_errors = 0;
-
- rx_errors += lp->net_stats.rx_length_errors += st->rx_toolong_errors;
- st->rx_toolong_errors = 0;
-
- rx_errors += lp->net_stats.rx_fifo_errors += st->rx_overrun_errors;
- st->rx_overrun_errors = 0;
-
- rx_errors += lp->net_stats.rx_frame_errors += st->rx_alignment_errors;
- st->rx_alignment_errors = 0;
-
- rx_errors += lp->net_stats.rx_missed_errors += st->rx_outofresource_errors;
- st->rx_outofresource_errors = 0;
-
- lp->net_stats.rx_errors = rx_errors;
-
- /* TX */
-
- /* How to count collisions when you're only
- told frames which had 1 or 2-15? - RP */
-
- lp->net_stats.collisions += (st->tx_max_collisions * 16);
- st->tx_max_collisions = 0;
-
- lp->net_stats.tx_carrier_errors += st->tx_carrier_errors;
- st->tx_carrier_errors = 0;
+ u32 rx_errors=0;
+
+ rx_errors+=lp->net_stats.rx_crc_errors +=st->rx_crc_errors;
+ st->rx_crc_errors=0;
+ rx_errors+=lp->net_stats.rx_fifo_errors +=st->rx_overrun_errors;
+ st->rx_overrun_errors=0;
+ rx_errors+=lp->net_stats.rx_frame_errors +=st->rx_alignment_errors;
+ st->rx_alignment_errors=0;
+ rx_errors+=lp->net_stats.rx_length_errors+=st->rx_tooshort_errors;
+ st->rx_tooshort_errors=0;
+ rx_errors+=lp->net_stats.rx_missed_errors+=st->rx_outofresource_errors;
+ st->rx_outofresource_errors=0;
+ lp->net_stats.rx_errors=rx_errors;
+
+ /* Number of packets which saw one collision */
+ lp->net_stats.collisions+=st->dataC[10];
+ st->dataC[10]=0;
+
+ /* Number of packets which saw 2--15 collisions */
+ lp->net_stats.collisions+=st->dataC[11];
+ st->dataC[11]=0;
+}
- lp->net_stats.tx_fifo_errors += st->tx_underrun_errors;
- st->tx_underrun_errors = 0;
-
-}
+/**
+ * mc32_rx_ring:
+ * @dev: 3c527 that needs its receive ring processing
+ *
+ *
+ * We have received one or more indications from the card that a
+ * receive has completed. The buffer ring thus contains dirty
+ * entries. We walk the ring by iterating over the circular rx_ring
+ * array, starting at the next dirty buffer (which happens to be the
+ * one we finished up at last time around).
+ *
+ * For each completed packet, we will either copy it and pass it up
+ * the stack or, if the packet is near MTU sized, we allocate
+ * another buffer and flip the old one up the stack.
+ *
+ * We must succeed in keeping a buffer on the ring. If neccessary we
+ * will toss a received packet rather than lose a ring entry. Once
+ * the first uncompleted descriptor is found, we move the
+ * End-Of-List bit to include the buffers just processed.
+ *
+ */
static void mc32_rx_ring(struct device *dev)
{
struct sk_buff *skb;
struct sk_buff *newskb;
-#ifdef DEBUG_IRQ
- printk("skb_header %p has frame, x==%d\n", p, x);
-#endif
-
/* Try to save time by avoiding a copy on big frames */
if ((length > RX_COPYBREAK)
{
skb=dev_alloc_skb(length+2);
- if(skb==NULL)
- {
+ if(skb==NULL) {
lp->net_stats.rx_dropped++;
-
- p->length = 1532;
- p->status = 0;
- p->control = 0;
-
- rx_ring_tail++;
- rx_ring_tail&=(RX_RING_LEN-1);
-
- continue; /* better to use a goto? */
+ goto dropped;
}
skb_reserve(skb,2);
lp->net_stats.rx_bytes+=skb->len;
netif_rx(skb);
}
- else /* NOT COMPLETED_OK */
- {
- /* There was some sort of reception error. */
- /* This case should never occur unless */
- /* the card is asked to upload damaged frames. */
-
- /* do nothing */
- }
+ dropped:
p->length = 1532;
p->status = 0;
- p->control = 0;
-
- rx_ring_tail++;
- rx_ring_tail&=(RX_RING_LEN-1);
+
+ rx_ring_tail=next_rx(rx_ring_tail);
}
while(x++<48);
- /* If there was actually a frame to be processed, */
- /* place the EL bit at the descriptor prior to the one to be filled next */
+ /* If there was actually a frame to be processed, place the EOL bit */
+ /* at the descriptor prior to the one to be filled next */
- if (rx_ring_tail != rx_old_tail) {
- lp->rx_ring[(rx_ring_tail-1)&(RX_RING_LEN-1)].p->control |= CONTROL_EOL;
- lp->rx_ring[(rx_old_tail-1)&(RX_RING_LEN-1)].p->control &= ~CONTROL_EOL;
+ if (rx_ring_tail != rx_old_tail)
+ {
+ lp->rx_ring[prev_rx(rx_ring_tail)].p->control |= CONTROL_EOL;
+ lp->rx_ring[prev_rx(rx_old_tail)].p->control &= ~CONTROL_EOL;
lp->rx_ring_tail=rx_ring_tail;
}
}
+/**
+ * mc32_tx_ring:
+ * @dev: 3c527 that needs its transmit ring processing
+ *
+ *
+ * This operates in a similar fashion to mc32_rx_ring. We iterate
+ * over the transmit ring. For each descriptor which has been
+ * processed by the card, we free its associated buffer and note
+ * any errors. This continues until the transmit ring is emptied
+ * or we reach a descriptor that hasn't yet been processed by the
+ * card.
+ *
+ */
+
static void mc32_tx_ring(struct device *dev) {
struct mc32_local *lp=(struct mc32_local *)dev->priv;
{
u16 t;
- t=(lp->tx_ring_tail+1)&(TX_RING_LEN-1);
+ t=next_tx(lp->tx_ring_tail);
np=lp->tx_ring[t].p;
if(!(np->status & (1<<7))) { /* Not COMPLETED */
if(!(np->status & (1<<6))) /* Not COMPLETED_OK */
{
lp->net_stats.tx_errors++;
-
- /* Error stats are stored on-card to be picked up by
- mc32_update_stats() - RP */
+
+ switch(np->status&0x0F)
+ {
+ case 1:
+ lp->net_stats.tx_aborted_errors++;break; /* Max collisions */
+ case 2:
+ lp->net_stats.tx_fifo_errors++;break;
+ case 3:
+ lp->net_stats.tx_carrier_errors++;break;
+ case 4:
+ lp->net_stats.tx_window_errors++;break; /* CTS Lost */
+ case 5:
+ lp->net_stats.tx_aborted_errors++;break; /* Transmit timeout */
+ }
}
lp->tx_ring_tail=t;
}
+
}
-/*
- * The typical workload of the driver:
- * Handle the network interface interrupts.
+
+/**
+ * mc32_interrupt:
+ * @irq: Interrupt number
+ * @dev_id: 3c527 that requires servicing
+ * @regs: Registers (unused)
+ *
+ *
+ * An interrupt is raised whenever the 3c527 writes to the command
+ * register. This register contains the message it wishes to send us
+ * packed into a single byte field. We keep reading status entries
+ * until we have processed all the control items, but simply count
+ * transmit and receive reports. When all reports are in we empty the
+ * transceiver rings as appropriate. This saves the overhead of
+ * multiple command requests.
+ *
+ * Because MCA is level-triggered, we shouldn't miss indications.
+ * Therefore, we needn't ask the card to suspend interrupts within
+ * this handler. The card receives an implicit acknowledgment of the
+ * current interrupt when we read the command register.
+ *
*/
+
static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct device *dev = dev_id;
int ioaddr, status, boguscount = 0;
int rx_event = 0;
int tx_event = 0;
- int must_restart = 0;
if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
return;
}
+
dev->interrupt = 1;
ioaddr = dev->base_addr;
break;
case 3: /* Halt */
case 4: /* Abort */
- lp->tx_halted=1;
- wake_up(&lp->event);
- break;
- case 5:
- lp->tx_halted=0;
+ lp->xceiver_state |= TX_HALTED;
wake_up(&lp->event);
break;
default:
case 2: /* RX */
rx_event=1;
break;
- case 3:
- case 4:
- lp->rx_halted=1;
- wake_up(&lp->event);
- break;
- case 5:
- lp->rx_halted=0;
+ case 3: /* Halt */
+ case 4: /* Abort */
+ lp->xceiver_state |= RX_HALTED;
wake_up(&lp->event);
break;
case 6:
/* Out of RX buffers stat */
- /* Must restart */
+ /* Must restart rx */
lp->net_stats.rx_dropped++;
- must_restart = 1; /* To restart */
- rx_event = 1;
-
+ mc32_rx_ring(dev);
+ mc32_start_transceiver(dev);
break;
default:
printk("%s: strange rx ack %d\n",
status>>=3;
if(status&1)
{
-
+
/* 0=no 1=yes 2=replied, get cmd, 3 = wait reply & dump it */
- if(lp->exec_pending!=3)
- {
+ if(lp->exec_pending!=3) {
lp->exec_pending=2;
wake_up(&lp->event);
}
else
- {
- lp->exec_pending=0;
- wake_up(&lp->event);
+ {
+ lp->exec_pending=0;
+
+ /* A new multicast set may have been
+ blocked while the old one was
+ running. If so, do it now. */
+
+ if (lp->mc_reload_wait)
+ mc32_reset_multicast_list(dev);
+ else
+ wake_up(&lp->event);
}
}
if(status&2)
{
/*
- * Update the stats as soon as
- * we have it flagged and can
- * send an immediate reply (CRR set)
+ * We get interrupted once per
+ * counter that is about to overflow.
*/
-
- if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)
- {
- mc32_update_stats(dev);
- outb(0, ioaddr+HOST_CMD);
- }
-
+
+ mc32_update_stats(dev);
}
}
- if(tx_event) {
- mc32_tx_ring(dev);
- }
-
/*
- * Process the receive ring and restart if the card
- * stopped due to a shortage of free buffers.
+ * Process the transmit and receive rings
*/
+
+ if(tx_event)
+ mc32_tx_ring(dev);
- if(rx_event) {
+ if(rx_event)
mc32_rx_ring(dev);
- if (must_restart) {
- u16 rx_prev_tail=(lp->rx_ring_tail-1)&(RX_RING_LEN-1);
-
- /* Restart at the current rx_ring_tail */
- lp->rx_box->data[0]=lp->rx_ring[rx_prev_tail].p->next;
- lp->rx_box->mbox=0;
-
- while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
- outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); /* Re-enable receives */
- }
- }
dev->interrupt = 0;
}
-/* The inverse routine to mc32_open(). */
+/**
+ * mc32_close:
+ * @dev: 3c527 card to shut down
+ *
+ * The 3c527 is a bus mastering device. We must be careful how we
+ * shut it down. It may also be running shared interrupt so we have
+ * to be sure to silence it properly
+ *
+ * We indicate that the card is closing to the rest of the
+ * driver. Otherwise, it is possible that the card may run out
+ * of receive buffers and restart the transceiver while we're
+ * trying to close it.
+ *
+ * We abort any receive and transmits going on and then wait until
+ * any pending exec commands have completed in other code threads.
+ * In theory we can't get here while that is true, in practice I am
+ * paranoid
+ *
+ * We turn off the interrupt enable for the board to be sure it can't
+ * intefere with other devices.
+ */
static int mc32_close(struct device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
+
int ioaddr = dev->base_addr;
u8 regs;
u16 one=1;
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ lp->desired_state = HALTED;
+
/*
* Send the indications on command (handy debug check)
*/
mc32_command(dev, 4, &one, 2);
- /* Abort RX and Abort TX */
-
- mc32_rx_abort(dev);
- mc32_tx_abort(dev);
+ /* Shut down the transceiver */
+
+ mc32_halt_transceiver(dev);
/* Catch any waiting commands */
while(lp->exec_pending==1)
sleep_on(&lp->event);
-
+
/* Ok the card is now stopping */
regs=inb(ioaddr+HOST_CTRL);
regs&=~HOST_CTRL_INTE;
outb(regs, ioaddr+HOST_CTRL);
- mc32_flush_rx_ring(lp);
- mc32_flush_tx_ring(lp);
-
- dev->tbusy = 1;
- dev->start = 0;
-
- /* Update the statistics here. */
+ mc32_flush_rx_ring(dev);
+ mc32_flush_tx_ring(dev);
+
+ mc32_update_stats(dev);
MOD_DEC_USE_COUNT;
return 0;
-
}
-/*
- * Get the current statistics.
- * This may be called with the card open or closed.
+
+/**
+ * mc32_get_stats:
+ * @dev: The 3c527 card to handle
+ *
+ * We've collected all the stats we can in software already. Now
+ * it's time to update those kept on-card and return the lot.
+ *
*/
static struct net_device_stats *mc32_get_stats(struct device *dev)
return &lp->net_stats;
}
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets,
- * and do best-effort filtering.
+
+/**
+ * do_mc32_set_multicast_list:
+ * @dev: 3c527 device to load the list on
+ * @retry: indicates this is not the first call.
+ *
+ *
+ * Actually set or clear the multicast filter for this adaptor. The
+ * locking issues are handled by this routine. We have to track
+ * state as it may take multiple calls to get the command sequence
+ * completed. We just keep trying to schedule the loads until we
+ * manage to process them all.
+ *
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ *
+ * num_addrs == 0 Normal mode, clear multicast list
+ *
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ *
+ * See mc32_update_stats() regards setting the SAV BP bit.
+ *
*/
+
static void do_mc32_set_multicast_list(struct device *dev, int retry)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
- u16 filt;
+ u16 filt = (1<<2); /* Save Bad Packets, for stats purposes */
if (dev->flags&IFF_PROMISC)
/* Enable promiscuous mode */
- filt = 1;
+ filt |= 1;
else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10)
{
dev->flags|=IFF_PROMISC;
- filt = 1;
+ filt |= 1;
}
else if(dev->mc_count)
{
struct dev_mc_list *dmc=dev->mc_list;
int i;
-
- filt = 0;
-
+
if(retry==0)
lp->mc_list_valid = 0;
if(!lp->mc_list_valid)
lp->mc_list_valid=1;
}
}
- else
- {
- filt = 0;
- }
- if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
+
+ if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
{
lp->mc_reload_wait = 1;
+ }
+ else {
+ lp->mc_reload_wait = 0;
}
}
+
+/**
+ * mc32_set_multicast_list:
+ * @dev: The 3c527 to use
+ *
+ * Commence loading the multicast list. This is called when the kernel
+ * changes the lists. It will override any pending list we are trying to
+ * load.
+ */
+
static void mc32_set_multicast_list(struct device *dev)
{
do_mc32_set_multicast_list(dev,0);
}
+
+/**
+ * mc32_reset_multicast_list:
+ * @dev: The 3c527 to use
+ *
+ * Attempt the next step in loading the multicast lists. If this attempt
+ * fails to complete then it will be scheduled and this function called
+ * again later from elsewhere.
+ */
+
static void mc32_reset_multicast_list(struct device *dev)
{
do_mc32_set_multicast_list(dev,1);
0, 0, /* I/O address, IRQ */
0, 0, 0, NULL, mc32_probe };
+/**
+ * init_module:
+ *
+ * Probe and locate a 3c527 card. This really should probe and locate
+ * all the 3c527 cards in the machine not just one of them. Yes you can
+ * insmod multiple modules for now but it's a hack.
+ */
+
int init_module(void)
{
int result;
return 0;
}
+/**
+ * cleanup_module:
+ *
+ * Unloading time. We release the MCA bus resources and the interrupt
+ * at which point everything is ready to unload. The card must be stopped
+ * at this point or we would not have been called. When we unload we
+ * leave the card stopped but not totally shut down. When the card is
+ * initialized it must be rebooted or the rings reloaded before any
+ * transmit operations are allowed to start scribbling into memory.
+ */
+
void cleanup_module(void)
{
int slot;
/*
* If we don't do this, we can't re-insmod it later.
- * Release irq/dma here, when you have jumpered versions and
- * allocate them in mc32_probe1().
*/
if (this_device.priv)
kfree_s(this_device.priv, sizeof(struct mc32_local));
}
free_irq(this_device.irq, &this_device);
+ release_region(this_device.base_addr, MC32_IO_EXTENT);
}
#endif /* MODULE */
#define HOST_RAMPAGE 8
+#define RX_HALTED (1<<0)
+#define TX_HALTED (1<<1)
+#define HALTED (RX_HALTED | TX_HALTED)
+#define RUNNING 0
struct mc32_mailbox
{
u32 rx_toolong_errors __attribute((packed));
u32 rx_outofresource_errors __attribute((packed));
- u32 rx_discarded __attribute((packed)); // via card pattern match filter
+ u32 rx_discarded __attribute((packed)); /* via card pattern match filter */
/* TX Errors */
u32 tx_max_collisions __attribute((packed));
tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
tty->disc_data = 0;
+ /* We are not using any dongle anymore! */
+ if (self->dongle)
+ irda_device_dongle_cleanup(self->dongle);
+ self->dongle = NULL;
+
/* Remove netdevice */
if (self->netdev) {
rtnl_lock();
kfree(self->netdev);
}
- /* We are not using any dongle anymore! */
- if (self->dongle)
- irda_device_dongle_cleanup(self->dongle);
- self->dongle = NULL;
-
/* Remove speed changing task if any */
if (self->task)
irda_task_delete(self->task);
- Removed unused code
- Made more functions static and __init
+ LK1.2.8 (Ion Badulescu)
+ - Quell bogus error messages, inform about the Tx threshold
+ - Removed #ifdef CONFIG_PCI, this driver is PCI only
+
TODO:
- implement tx_timeout() properly
- support ethtool
" Updates and info at http://www.scyld.com/network/starfire.html\n";
static const char version3[] =
-" (unofficial 2.2.x kernel port, version 1.2.7, February 26, 2001)\n";
+" (unofficial 2.2.x kernel port, version 1.2.8, March 7, 2001)\n";
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
{
struct pci_dev *dev;
int count = 0, found, i;
-#ifdef CONFIG_PCI
list_add_tail(&drv->node, &pci_drivers);
for (dev = pci_devices; dev; dev = dev->next) {
found = 0;
if (!found)
count += pci_announce_device(drv, dev);
}
-#endif
return count;
}
{
struct pci_dev *dev;
int i, found;
-#ifdef CONFIG_PCI
list_del(&drv->node);
for (dev = pci_devices; dev; dev = dev->next) {
found = 0;
drvmap[i].dev = NULL;
}
}
-#endif
}
static inline int pci_module_init(struct pci_driver *drv)
/* not quite bits */
IntrRxDone=IntrRxQ2Done | IntrRxQ1Done,
IntrRxEmpty=IntrRxDescQ1Low | IntrRxDescQ2Low,
+ IntrNormalMask=0xf0, IntrAbnormalMask=0x3f0e,
};
/* Bits in the RxFilterMode register. */
get_stats(dev);
}
/* Came close to underrunning the Tx FIFO, increase threshold. */
- if (intr_status & IntrTxDataLow)
+ if (intr_status & IntrTxDataLow) {
writel(++np->tx_threshold, dev->base_addr + TxThreshold);
- if ((intr_status & ~(IntrAbnormalSummary|IntrLinkChange|IntrStatsMax|IntrTxDataLow|1)) && debug)
+ printk(KERN_NOTICE "%s: Increasing Tx FIFO threshold to %d bytes\n",
+ np->tx_threshold * 16);
+ }
+ if ((intr_status & ~(IntrNormalMask | IntrAbnormalSummary | IntrLinkChange | IntrStatsMax | IntrTxDataLow | IntrPCIPad)) && debug)
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status);
/* Hmmmmm, it's not clear how to recover from DMA faults. */
Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- Copyright (C) 1999-2000 3ware Inc.
+ Copyright (C) 1999-2001 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
systems.
12/09/2000 - release previously allocated resources on failure at
tw_allocate_memory (acme)
+ 1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
+ controller status is non-zero.
+ Added handling of request_sense opcode.
+ Fix possible null pointer dereference in
+ tw_reset_device_extension()
+ 1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
+ Make tw_setfeature() call with interrupts disabled.
+ Register interrupt handler before enabling interrupts.
+ Clear attention interrupt before draining aen queue.
*/
#include <linux/module.h>
};
/* Globals */
-char *tw_driver_version="1.02.00.002";
+char *tw_driver_version="1.02.00.004";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
struct pci_dev *tw_pci_dev = pci_devices;
u32 status_reg_value;
unsigned char c = 1;
+ int i;
+ u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID, TW_DEVICE_ID2 };
dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
- while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) {
- /* Prepare temporary device extension */
- tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
- if (tw_dev == NULL) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
- continue;
- }
- memset(tw_dev, 0, sizeof(TW_Device_Extension));
-
- error = tw_initialize_device_extension(tw_dev);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Calculate the cards register addresses */
- tw_dev->registers.base_addr = tw_pci_dev->base_address[0];
- tw_dev->registers.control_reg_addr = (tw_pci_dev->base_address[0] & ~15);
- tw_dev->registers.status_reg_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x4);
- tw_dev->registers.command_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x8);
- tw_dev->registers.response_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0xC);
- /* Save pci_dev struct to device extension */
- tw_dev->tw_pci_dev = tw_pci_dev;
-
- /* Poll status register for 60 secs for 'Controller Ready' flag */
- if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Disable interrupts on the card */
- tw_disable_interrupts(tw_dev);
-
- while (tries < TW_MAX_RESET_TRIES) {
- /* Do soft reset */
- tw_soft_reset(tw_dev);
-
- error = tw_aen_drain_queue(tw_dev);
+
+ for (i=0;i<TW_NUMDEVICES;i++) {
+ while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {
+ /* Prepare temporary device extension */
+ tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
+ if (tw_dev == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
+ continue;
+ }
+ memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+ error = tw_initialize_device_extension(tw_dev);
if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
- tries++;
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
continue;
}
-
- /* Check for controller errors */
- if (tw_check_errors(tw_dev)) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
- tries++;
+
+ /* Calculate the cards register addresses */
+ tw_dev->registers.base_addr = tw_pci_dev->base_address[0];
+ tw_dev->registers.control_reg_addr = (tw_pci_dev->base_address[0] & ~15);
+ tw_dev->registers.status_reg_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x4);
+ tw_dev->registers.command_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x8);
+ tw_dev->registers.response_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0xC);
+ /* Save pci_dev struct to device extension */
+ tw_dev->tw_pci_dev = tw_pci_dev;
+
+ /* Poll status register for 60 secs for 'Controller Ready' flag */
+ if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
continue;
}
-
- /* Empty the response queue */
- error = tw_empty_response_que(tw_dev);
+
+ /* Disable interrupts on the card */
+ tw_disable_interrupts(tw_dev);
+
+ while (tries < TW_MAX_RESET_TRIES) {
+ /* Do soft reset */
+ tw_soft_reset(tw_dev);
+
+ error = tw_aen_drain_queue(tw_dev);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+ tries++;
+ continue;
+ }
+
+ /* Check for controller errors */
+ if (tw_check_errors(tw_dev)) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+ tries++;
+ continue;
+ }
+
+ /* Empty the response queue */
+ error = tw_empty_response_que(tw_dev);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+ tries++;
+ continue;
+ }
+
+ /* Now the controller is in a good state */
+ break;
+ }
+
+ if (tries >= TW_MAX_RESET_TRIES) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
+ continue;
+ }
+
+ /* Make sure that io region isn't already taken */
+ if (check_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE)) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n",
+ (tw_dev->tw_pci_dev->base_address[0]),
+ (tw_dev->tw_pci_dev->base_address[0]) +
+ TW_IO_ADDRESS_RANGE, numcards);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
+ continue;
+ }
+
+ /* Reserve the io address space */
+ request_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
+ error = tw_initialize_units(tw_dev);
if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
- tries++;
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
+ release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
continue;
}
-
- /* Now the controller is in a good state */
- break;
- }
-
- if (tries >= TW_MAX_RESET_TRIES) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Make sure that io region isn't already taken */
- if (check_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE)) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n",
- (tw_dev->tw_pci_dev->base_address[0]),
- (tw_dev->tw_pci_dev->base_address[0]) +
- TW_IO_ADDRESS_RANGE, numcards);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Reserve the io address space */
- request_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
- error = tw_initialize_units(tw_dev);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
- release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
- release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Calculate max cmds per lun */
- if (tw_dev->num_units > 0)
- tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units;
-
- /* Register the card with the kernel SCSI layer */
- host = scsi_register(tw_host, sizeof(TW_Device_Extension));
-
- /* FIXME - check for NULL */
-
- status_reg_value = inl(tw_dev->registers.status_reg_addr);
-
- dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no,
+
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+ release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
+ continue;
+ }
+
+ /* Calculate max cmds per lun */
+ if (tw_dev->num_units > 0)
+ tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units;
+
+ /* Register the card with the kernel SCSI layer */
+ host = scsi_register(tw_host, sizeof(TW_Device_Extension));
+
+ /* FIXME - check for NULL */
+
+ status_reg_value = inl(tw_dev->registers.status_reg_addr);
+
+ dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no,
(u32)(tw_pci_dev->base_address[0]), tw_pci_dev->irq,
(status_reg_value & TW_STATUS_MAJOR_VERSION_MASK) >> 28,
(status_reg_value & TW_STATUS_MINOR_VERSION_MASK) >> 24);
+
+ if (host->hostdata) {
+ tw_dev2 = (TW_Device_Extension *)host->hostdata;
+ memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
+ tw_device_extension_list[tw_device_extension_count] = tw_dev2;
+ numcards++;
+ tw_device_extension_count = numcards;
+ tw_dev2->host = host;
+ } else {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+ scsi_unregister(host);
+ release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
+ continue;
+ }
+
+ /* Tell the firmware we support shutdown notification*/
+ tw_setfeature(tw_dev2, 2, 1, &c);
+
+ /* Now setup the interrupt handler */
+ error = tw_setup_irq(tw_dev2);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
+ scsi_unregister(host);
+ release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+
+ tw_free_device_extension(tw_dev);
+ kfree(tw_dev);
+ numcards--;
+ continue;
+ }
- if (host->hostdata) {
- tw_dev2 = (TW_Device_Extension *)host->hostdata;
- memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
- tw_device_extension_list[tw_device_extension_count] = tw_dev2;
- numcards++;
- tw_device_extension_count = numcards;
- tw_dev2->host = host;
- } else {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
- scsi_unregister(host);
- release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- continue;
- }
-
- /* Re-enable interrupts on the card */
- tw_enable_interrupts(tw_dev2);
-
- /* Now setup the interrupt handler */
- error = tw_setup_irq(tw_dev2);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
- scsi_unregister(host);
- release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
-
- tw_free_device_extension(tw_dev);
- kfree(tw_dev);
- numcards--;
- continue;
+ /* Re-enable interrupts on the card */
+ tw_enable_interrupts(tw_dev2);
+
+ /* Free the temporary device extension */
+ if (tw_dev)
+ kfree(tw_dev);
}
-
- /* Free the temporary device extension */
- if (tw_dev)
- kfree(tw_dev);
- /* Tell the firmware we support shutdown notification*/
- tw_setfeature(tw_dev2, 2, 1, &c);
}
-
if (numcards == 0)
printk(KERN_WARNING "3w-xxxx: tw_findcards(): No cards found.\n");
else
- register_reboot_notifier(&tw_notifier);
-
+ register_reboot_notifier(&tw_notifier);
+
return numcards;
} /* End tw_findcards() */
/* Handle attention interrupt */
if (do_attention_interrupt) {
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
+ tw_clear_attention_interrupt(tw_dev);
tw_state_request_start(tw_dev, &request_id);
error = tw_aen_read_queue(tw_dev, request_id);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error reading aen queue.\n");
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
- } else {
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
- tw_clear_attention_interrupt(tw_dev);
}
}
response_que.value = inl(response_que_addr);
request_id = response_que.u.response_id;
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ error = 0;
if (command_packet->status != 0) {
- printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x, unit = 0x%x.\n", command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ error = 1;
}
if (tw_dev->state[request_id] != TW_S_POSTED) {
printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode);
+ error = 1;
}
- error = 0;
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
/* Check for internal command */
if (tw_dev->srb[request_id] == 0) {
}
if (error) {
/* Tell scsi layer there was an error */
- printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
- tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
+ tw_dev->srb[request_id]->result = (DID_RESET << 16);
} else {
/* Tell scsi layer command was a success */
tw_dev->srb[request_id]->result = (DID_OK << 16);
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
srb = tw_dev->srb[i];
- srb->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ if (srb != NULL) {
+ srb->result = (DID_RESET << 16);
+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ }
}
}
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
error = tw_scsiop_read_capacity(tw_dev, request_id);
break;
+ case REQUEST_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
+ error = tw_scsiop_request_sense(tw_dev, request_id);
+ break;
case TW_IOCTL:
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TW_SCSI_IOCTL.\n");
error = tw_ioctl(tw_dev, request_id);
return 0;
} /* End tw_scsiop_read_write() */
+/* This function will handle the request sense scsi command */
+int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
+{
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
+
+ /* For now we just zero the sense buffer */
+ memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+
+ /* If we got a request_sense, we probably want a reset, return error */
+ tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+ return 0;
+} /* End tw_scsiop_request_sense() */
+
/* This function will handle test unit ready scsi command */
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
{
Written By: Adam Radford <linux@3ware.com>
Modifications By: Joel Jacobson <linux@3ware.com>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- Copyright (C) 1999, 2000 3ware Inc.
+ Copyright (C) 1999-2001 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
#define TW_DEVICE_NAME "3ware Storage Controller"
#define TW_VENDOR_ID (0x13C1) /* 3ware */
#define TW_DEVICE_ID (0x1000) /* Storage Controller */
+#define TW_DEVICE_ID2 (0x1001) /* 7000 series controller */
+#define TW_NUMDEVICES 2
/* Command packet opcodes */
#define TW_OP_NOP 0x0
int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, unsigned char *val);
int tw_setup_irq(TW_Device_Extension *tw_dev);
/*
- * bluetooth.c Version 0.6
+ * bluetooth.c Version 0.7
*
* Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
*
* USB Bluetooth driver, based on the Bluetooth Spec version 1.0B
*
+ * (11/29/2000) Version 0.7 gkh
+ * Fixed problem with overrunning the tty flip buffer.
+ * Removed unneeded NULL pointer initialization.
+ *
* (10/05/2000) Version 0.6 gkh
* Fixed bug with urb->dev not being set properly, now that the usb
* core needs it.
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
static struct tty_struct * bluetooth_tty[BLUETOOTH_TTY_MINORS];
static struct termios * bluetooth_termios[BLUETOOTH_TTY_MINORS];
static struct termios * bluetooth_termios_locked[BLUETOOTH_TTY_MINORS];
-static struct usb_bluetooth *bluetooth_table[BLUETOOTH_TTY_MINORS] = {NULL, };
-
+static struct usb_bluetooth *bluetooth_table[BLUETOOTH_TTY_MINORS];
static inline int bluetooth_paranoia_check (struct usb_bluetooth *bluetooth, const char *function)
tty->driver_data = bluetooth;
bluetooth->tty = tty;
+ /* force low_latency on so that our tty_push actually forces the data through,
+ * otherwise it is scheduled, and with high data rates (like with OHCI) data
+ * can get lost. */
+ bluetooth->tty->low_latency = 1;
+
bluetooth->active = 1;
/* Reset the packet position counters */
return;
}
- if (packet_size + EVENT_HDR_SIZE == bluetooth->int_packet_pos){
- for (i = 0; i < bluetooth->int_packet_pos; ++i)
+ if (packet_size + EVENT_HDR_SIZE == bluetooth->int_packet_pos) {
+ for (i = 0; i < bluetooth->int_packet_pos; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them */
+ if (bluetooth->tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(bluetooth->tty);
+ }
tty_insert_flip_char(bluetooth->tty, bluetooth->int_buffer[i], 0);
+ }
tty_flip_buffer_push(bluetooth->tty);
bluetooth->int_packet_pos = 0;
}
if (packet_size + ACL_HDR_SIZE == bluetooth->bulk_packet_pos) {
- for (i = 0; i < bluetooth->bulk_packet_pos; ++i)
+ for (i = 0; i < bluetooth->bulk_packet_pos; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if (bluetooth->tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(bluetooth->tty);
+ }
tty_insert_flip_char(bluetooth->tty, bluetooth->bulk_buffer[i], 0);
+ }
tty_flip_buffer_push(bluetooth->tty);
bluetooth->bulk_packet_pos = 0;
}
/*
- * Copyright (C) 1999-2000 by David Brownell <david-b@pacbell.net>
+ * Copyright (C) 1999-2000 by David Brownell <dbrownell@users.sourceforge.net>
*
* 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
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/module.h>
-#undef DEBUG
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
#include <linux/usb.h>
/* current USB framework handles max of 16 USB devices per driver */
-#define MAX_CAMERAS 8
+#define MAX_CAMERAS 16
/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */
#define USB_CAMERA_MINOR_BASE 80
/* These have the same application level protocol */
{ 0x040a, 0x0120 }, // Kodak DC-240
{ 0x040a, 0x0130 }, // Kodak DC-280
+ { 0x040a, 0x0131 }, // Kodak DC-5000
+ { 0x040a, 0x0132 }, // Kodak DC-3400
/* These have a different application level protocol which
* is part of the Flashpoint "DigitaOS". That supports some
goto error;
}
- info ("USB Camera #%d connected", camera->subminor);
+ info ("USB Camera #%d connected, major/minor %d/%d", camera->subminor,
+ USB_MAJOR, USB_CAMERA_MINOR_BASE + camera->subminor);
camera->dev = dev;
usb_inc_dev_use (dev);
}
static /* const */ struct usb_driver camera_driver = {
- "dc2xx",
- camera_probe,
- camera_disconnect,
- { NULL, NULL },
- &usb_camera_fops,
- USB_CAMERA_MINOR_BASE
+ name: "dc2xx",
+
+ probe: camera_probe,
+ disconnect: camera_disconnect,
+
+ fops: &usb_camera_fops,
+ minor: USB_CAMERA_MINOR_BASE
};
}
-MODULE_AUTHOR("David Brownell, david-b@pacbell.net");
+MODULE_AUTHOR("David Brownell, dbrownell@users.sourceforge.net");
MODULE_DESCRIPTION("USB Camera Driver for Kodak DC-2xx series cameras");
module_init (usb_dc2xx_init);
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (01/22/2001) gb
+ * Added write_room() and chars_in_buffer() support.
+ *
+ * (12/21/2000) gb
+ * Moved termio stuff inside the port->active check.
+ * Moved MOD_DEC_USE_COUNT to end of empeg_close().
+ *
+ * (12/03/2000) gb
+ * Added port->tty->ldisc.set_termios(port->tty, NULL) to empeg_open()
+ * This notifies the tty driver that the termios have changed.
+ *
* (11/13/2000) gb
* Moved tty->low_latency = 1 from empeg_read_bulk_callback() to empeg_open()
* (It only needs to be set once - Doh!)
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
#include "usb-serial.h"
-#define EMPEG_VENDOR_ID 0x084f
-#define EMPEG_PRODUCT_ID 0x0001
+#define EMPEG_VENDOR_ID 0x084f
+#define EMPEG_PRODUCT_ID 0x0001
#define MIN(a,b) (((a)<(b))?(a):(b))
/* function prototypes for an empeg-car player */
-static int empeg_open (struct usb_serial_port *port, struct file *filp);
-static void empeg_close (struct usb_serial_port *port, struct file *filp);
-static int empeg_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
-static void empeg_throttle (struct usb_serial_port *port);
-static void empeg_unthrottle (struct usb_serial_port *port);
-static int empeg_startup (struct usb_serial *serial);
-static void empeg_shutdown (struct usb_serial *serial);
-static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
-static void empeg_set_termios (struct usb_serial_port *port, struct termios *old_termios);
+static int empeg_open (struct usb_serial_port *port, struct file *filp);
+static void empeg_close (struct usb_serial_port *port, struct file *filp);
+static int empeg_write (struct usb_serial_port *port,
+ int from_user,
+ const unsigned char *buf,
+ int count);
+static int empeg_write_room (struct usb_serial_port *port);
+static int empeg_chars_in_buffer (struct usb_serial_port *port);
+static void empeg_throttle (struct usb_serial_port *port);
+static void empeg_unthrottle (struct usb_serial_port *port);
+static int empeg_startup (struct usb_serial *serial);
+static void empeg_shutdown (struct usb_serial *serial);
+static int empeg_ioctl (struct usb_serial_port *port,
+ struct file * file,
+ unsigned int cmd,
+ unsigned long arg);
+static void empeg_set_termios (struct usb_serial_port *port, struct termios *old_termios);
static void empeg_write_bulk_callback (struct urb *urb);
static void empeg_read_bulk_callback (struct urb *urb);
ioctl: empeg_ioctl,
set_termios: empeg_set_termios,
write: empeg_write,
+ write_room: empeg_write_room,
+ chars_in_buffer: empeg_chars_in_buffer,
write_bulk_callback: empeg_write_bulk_callback,
read_bulk_callback: empeg_read_bulk_callback,
};
******************************************************************************/
static int empeg_open (struct usb_serial_port *port, struct file *filp)
{
+ struct usb_serial *serial = port->serial;
unsigned long flags;
int result;
++port->open_count;
MOD_INC_USE_COUNT;
- /* gb - 2000/11/05
- *
- * personally, I think these termios should be set in
- * empeg_startup(), but it appears doing so leads to one
- * of those chicken/egg problems. :)
- *
- */
- port->tty->termios->c_iflag
- &= ~(IGNBRK
- | BRKINT
- | PARMRK
- | ISTRIP
- | INLCR
- | IGNCR
- | ICRNL
- | IXON);
-
- port->tty->termios->c_oflag
- &= ~OPOST;
-
- port->tty->termios->c_lflag
- &= ~(ECHO
- | ECHONL
- | ICANON
- | ISIG
- | IEXTEN);
-
- port->tty->termios->c_cflag
- &= ~(CSIZE
- | PARENB);
-
- port->tty->termios->c_cflag
- |= CS8;
-
- /* gb - 2000/11/05
- *
- * force low_latency on
- *
- * The tty_flip_buffer_push()'s in empeg_read_bulk_callback() will actually
- * force the data through if low_latency is set. Otherwise the pushes are
- * scheduled; this is bad as it opens up the possibility of dropping bytes
- * on the floor. We are trying to sustain high data transfer rates; and
- * don't want to drop bytes on the floor.
- * Moral: use low_latency - drop no bytes - life is good. :)
- *
- */
- port->tty->low_latency = 1;
-
if (!port->active) {
+
+ /* gb - 2000/11/05
+ * personally, I think these termios should be set in
+ * empeg_startup(), but it appears doing so leads to one
+ * of those chicken/egg problems. :)
+ */
+ port->tty->termios->c_iflag
+ &= ~(IGNBRK
+ | BRKINT
+ | PARMRK
+ | ISTRIP
+ | INLCR
+ | IGNCR
+ | ICRNL
+ | IXON);
+
+ port->tty->termios->c_oflag
+ &= ~OPOST;
+
+ port->tty->termios->c_lflag
+ &= ~(ECHO
+ | ECHONL
+ | ICANON
+ | ISIG
+ | IEXTEN);
+
+ port->tty->termios->c_cflag
+ &= ~(CSIZE
+ | PARENB);
+
+ port->tty->termios->c_cflag
+ |= CS8;
+
+ /* gb - 2000/12/03
+ * Contributed by Borislav Deianov
+ * Notify the tty driver that the termios have changed!!
+ */
+ port->tty->ldisc.set_termios(port->tty, NULL);
+
+ /* gb - 2000/11/05
+ * force low_latency on
+ *
+ * The tty_flip_buffer_push()'s in empeg_read_bulk_callback() will actually
+ * force the data through if low_latency is set. Otherwise the pushes are
+ * scheduled; this is bad as it opens up the possibility of dropping bytes
+ * on the floor. We are trying to sustain high data transfer rates; and
+ * don't want to drop bytes on the floor.
+ * Moral: use low_latency - drop no bytes - life is good. :)
+ */
+ port->tty->low_latency = 1;
+
port->active = 1;
bytes_in = 0;
bytes_out = 0;
/* Start reading from the device */
+ FILL_BULK_URB(
+ port->read_urb,
+ serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ empeg_read_bulk_callback,
+ port);
+
port->read_urb->transfer_flags |= USB_QUEUE_BULK;
result = usb_submit_urb(port->read_urb);
spin_lock_irqsave (&port->port_lock, flags);
--port->open_count;
- MOD_DEC_USE_COUNT;
if (port->open_count <= 0) {
transfer_buffer = kmalloc (0x12, GFP_KERNEL);
/* Uncomment the following line if you want to see some statistics in your syslog */
/* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */
+ MOD_DEC_USE_COUNT;
+
}
memcpy (urb->transfer_buffer, current_position, transfer_size);
}
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
/* build up our urb */
FILL_BULK_URB (
urb,
}
+static int empeg_write_room (struct usb_serial_port *port)
+{
+ unsigned long flags;
+ int i;
+ int room = 0;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ spin_lock_irqsave (&port->port_lock, flags);
+
+ /* tally up the number of bytes available */
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (write_urb_pool[i]->status != -EINPROGRESS) {
+ room += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&port->port_lock, flags);
+
+ dbg(__FUNCTION__ " - returns %d", room);
+
+ return (room);
+
+}
+
+
+static int empeg_chars_in_buffer (struct usb_serial_port *port)
+{
+ unsigned long flags;
+ int i;
+ int chars = 0;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ spin_lock_irqsave (&port->port_lock, flags);
+
+ /* tally up the number of bytes waiting */
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&port->port_lock, flags);
+
+ dbg (__FUNCTION__ " - returns %d", chars);
+
+ return (chars);
+
+}
+
+
static void empeg_write_bulk_callback (struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
}
- /* gb - 2000/11/13
- * This doesn't push the data through unless tty->low_latency is set.
- */
tty_insert_flip_char(tty, data[i], 0);
}
/* gb - 2000/11/13
}
/* Continue trying to always read */
+ FILL_BULK_URB(
+ port->read_urb,
+ serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ empeg_read_bulk_callback,
+ port);
+
port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+
result = usb_submit_urb(port->read_urb);
if (result)
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
p_priv = (struct keyspan_port_private *)(port->private);
/* Set some sane defaults */
- p_priv->baud = 9600;
- p_priv->cflag = CREAD | CLOCAL;
- p_priv->flow_control = flow_none;
p_priv->rts_state = 1;
p_priv->dtr_state = 1;
dbg(__FUNCTION__ " submit urb %d failed (%d)", i, err);
}
}
-/* Now done in startup routine
- if (atomic_inc_return(&s_priv->active_count) == 1) {
- s_priv->instat_urb->dev = serial->dev;
- if ((err = usb_submit_urb(s_priv->instat_urb)) != 0) {
- dbg(__FUNCTION__ " submit instat urb failed %d", err);
- }
- }
-*/
- keyspan_send_setup(port);
+ keyspan_set_termios(port, NULL);
return (0);
}
* based on a driver by Brad Keryan)
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
- *
+ *
+ * 2001_02_05 gkh
+ * Fixed buffer overflows bug with the generic serial driver. Thanks to
+ * Todd Squires <squirest@ct0.com> for fixing this.
+ *
+ * (12/12/2000) gkh
+ * Removed MOD_INC and MOD_DEC from poll and disconnect functions, and
+ * moved them to the serial_open and serial_close functions.
+ * Also fixed bug with there not being a MOD_DEC for the generic driver
+ * (thanks to Gary Brubaker for finding this.)
+ *
* (12/29/2000) gkh
* Small NULL pointer initialization cleanup which saves a bit of disk image
*
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/list.h>
return -ENODEV;
}
+ MOD_INC_USE_COUNT;
+
/* set up our port structure making the tty driver remember our port object, and us it */
portNumber = MINOR(tty->device) - serial->minor;
port = &serial->port[portNumber];
} else {
generic_close(port, filp);
}
+
+ MOD_DEC_USE_COUNT;
}
if (port_paranoia_check (port, __FUNCTION__))
return -ENODEV;
+ MOD_INC_USE_COUNT;
+
dbg(__FUNCTION__ " - port %d", port->number);
spin_lock_irqsave (&port->port_lock, flags);
++port->open_count;
- MOD_INC_USE_COUNT;
if (!port->active) {
port->active = 1;
+ /* force low_latency on so that our tty_push actually forces the data through,
+ otherwise it is scheduled, and with high data rates (like with OHCI) data
+ can get lost. */
+ port->tty->low_latency = 1;
+
/* if we have a bulk interrupt, start reading from it */
if (serial->num_bulk_in) {
/* Start reading from the device */
}
spin_unlock_irqrestore (&port->port_lock, flags);
+ MOD_DEC_USE_COUNT;
}
tty = port->tty;
if (urb->actual_length) {
for (i = 0; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless tty->low_latency is set */
+ tty_insert_flip_char(tty, data[i], 0);
+ }
tty_flip_buffer_push(tty);
}
}
/* found all that we need */
- MOD_INC_USE_COUNT;
info("%s converter detected", type->name);
#ifdef CONFIG_USB_SERIAL_GENERIC
num_ports = num_bulk_out;
if (num_ports == 0) {
err("Generic device with no bulk out, not allowed.");
- MOD_DEC_USE_COUNT;
return NULL;
}
} else
serial = get_free_serial (num_ports, &minor);
if (serial == NULL) {
err("No more free serial devices");
- MOD_DEC_USE_COUNT;
return NULL;
}
/* free up any memory that we allocated */
kfree (serial);
- MOD_DEC_USE_COUNT;
return NULL;
}
info("device disconnected");
}
- MOD_DEC_USE_COUNT;
}
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (01/21/2000) gkh
+ * Added write_room and chars_in_buffer, as they were previously using the
+ * generic driver versions which is all wrong now that we are using an urb
+ * pool. Thanks to Wolfgang Grandegger for pointing this out to me.
+ * Removed count assignment in the write function, which was not needed anymore
+ * either. Thanks to Al Borchers for pointing this out.
+ *
+ * (12/12/2000) gkh
+ * Moved MOD_DEC to end of visor_close to be nicer, as the final write
+ * message can sleep.
*
* (11/12/2000) gkh
* Fixed bug with data being dropped on the floor by forcing tty->low_latency
* to be on. This fixes the OHCI issue!
*
+ * (10/05/2000) gkh
+ * Fixed bug with urb->dev not being set properly, now that the usb
+ * core needs it.
+ *
* (09/11/2000) gkh
* Got rid of always calling kmalloc for every urb we wrote out to the
* device.
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
static int visor_open (struct usb_serial_port *port, struct file *filp);
static void visor_close (struct usb_serial_port *port, struct file *filp);
static int visor_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int visor_write_room (struct usb_serial_port *port);
+static int visor_chars_in_buffer (struct usb_serial_port *port);
static void visor_throttle (struct usb_serial_port *port);
static void visor_unthrottle (struct usb_serial_port *port);
static int visor_startup (struct usb_serial *serial);
ioctl: visor_ioctl,
set_termios: visor_set_termios,
write: visor_write,
+ write_room: visor_write_room,
+ chars_in_buffer: visor_chars_in_buffer,
write_bulk_callback: visor_write_bulk_callback,
read_bulk_callback: visor_read_bulk_callback,
};
******************************************************************************/
static int visor_open (struct usb_serial_port *port, struct file *filp)
{
+ struct usb_serial *serial = port->serial;
unsigned long flags;
+ int result;
if (port_paranoia_check (port, __FUNCTION__))
return -ENODEV;
bytes_out = 0;
/* force low_latency on so that our tty_push actually forces the data through,
- otherwise it is scheduled, and with high data rates (like with OHCI) data
- can get lost. */
+ otherwise it is scheduled, and with high data rates (like with OHCI) data
+ can get lost. */
port->tty->low_latency = 1;
-
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+
+ /* Start reading from the device */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ visor_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed submitting read urb, error %d", result);
}
spin_unlock_irqrestore (&port->port_lock, flags);
spin_lock_irqsave (&port->port_lock, flags);
--port->open_count;
- MOD_DEC_USE_COUNT;
if (port->open_count <= 0) {
transfer_buffer = kmalloc (0x12, GFP_KERNEL);
/* Uncomment the following line if you want to see some statistics in your syslog */
/* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */
+
+ MOD_DEC_USE_COUNT;
}
else
memcpy (urb->transfer_buffer, current_position, transfer_size);
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
/* build up our urb */
FILL_BULK_URB (urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
urb->transfer_buffer, transfer_size, visor_write_bulk_callback, port);
}
+static int visor_write_room (struct usb_serial_port *port)
+{
+ unsigned long flags;
+ int i;
+ int room = 0;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ spin_lock_irqsave (&port->port_lock, flags);
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (write_urb_pool[i]->status != -EINPROGRESS) {
+ room += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&port->port_lock, flags);
+
+ dbg(__FUNCTION__ " - returns %d", room);
+ return (room);
+}
+
+
+static int visor_chars_in_buffer (struct usb_serial_port *port)
+{
+ unsigned long flags;
+ int i;
+ int chars = 0;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ spin_lock_irqsave (&port->port_lock, flags);
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&port->port_lock, flags);
+
+ dbg (__FUNCTION__ " - returns %d", chars);
+ return (chars);
+}
+
+
static void visor_write_bulk_callback (struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
static void visor_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 tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int i;
+ int result;
if (port_paranoia_check (port, __FUNCTION__))
return;
dbg(__FUNCTION__ " - port %d", port->number);
+ if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
+ return;
+ }
+
if (urb->status) {
dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
return;
}
/* Continue trying to always read */
- if (usb_submit_urb(urb))
- dbg(__FUNCTION__ " - failed resubmitting read urb");
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ visor_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
return;
}
static void visor_unthrottle (struct usb_serial_port *port)
{
unsigned long flags;
+ int result;
dbg(__FUNCTION__ " - port %d", port->number);
spin_lock_irqsave (&port->port_lock, flags);
- if (usb_submit_urb (port->read_urb))
- dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+ port->read_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed submitting read urb, error %d", result);
spin_unlock_irqrestore (&port->port_lock, flags);
#error You really need /proc support for binfmt_misc. Please reconfigure!
#endif
-#define VERBOSE_STATUS /* undef this to save 400 bytes kernel memory */
+enum {
+ VERBOSE_STATUS = 1 /* define as zero to save 400 bytes kernel memory */
+};
-struct binfmt_entry {
+typedef struct binfmt_entry {
struct binfmt_entry *next;
long id;
int flags; /* type, status, etc. */
char *magic; /* magic or filename extension */
char *mask; /* mask, NULL for exact match */
char *interpreter; /* filename of interpreter */
- char *proc_name;
+ char *name;
struct proc_dir_entry *proc_dir;
-};
+} Node;
-#define ENTRY_ENABLED 1 /* the old binfmt_entry.enabled */
-#define ENTRY_MAGIC 8 /* not filename detection */
+enum { Enabled, Magic };
static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs);
-static void entry_proc_cleanup(struct binfmt_entry *e);
-static int entry_proc_setup(struct binfmt_entry *e);
+static void entry_proc_cleanup(Node *e);
+static int entry_proc_setup(Node *e);
static struct linux_binfmt misc_format = {
#ifndef MODULE
static struct proc_dir_entry *bm_dir = NULL;
-static struct binfmt_entry *entries = NULL;
+static Node *entries = NULL;
static int free_id = 1;
static int enabled = 1;
*/
static void clear_entry(int id)
{
- struct binfmt_entry **ep, *e;
+ Node **ep, *e;
write_lock(&entries_lock);
ep = &entries;
*/
static void clear_entries(void)
{
- struct binfmt_entry *e, *n;
+ Node *e, *n;
write_lock(&entries_lock);
n = entries;
/*
* Find entry through id and lock it
*/
-static struct binfmt_entry *get_entry(int id)
+static Node *get_entry(int id)
{
- struct binfmt_entry *e;
+ Node *e;
read_lock(&entries_lock);
e = entries;
/*
* unlock entry
*/
-static inline void put_entry(struct binfmt_entry *e)
+static inline void put_entry(Node *e)
{
if (e)
read_unlock(&entries_lock);
/*
* Check if we support the binfmt
- * if we do, return the binfmt_entry, else NULL
+ * if we do, return the node, else NULL
* locking is done in load_misc_binary
*/
-static struct binfmt_entry *check_file(struct linux_binprm *bprm)
+static Node *check_file(struct linux_binprm *bprm)
{
- struct binfmt_entry *e;
+ Node *e;
char *p = strrchr(bprm->filename, '.');
int j;
e = entries;
while (e) {
- if (e->flags & ENTRY_ENABLED) {
- if (!(e->flags & ENTRY_MAGIC)) {
+ if (test_bit(Enabled, &e->flags)) {
+ if (!test_bit(Magic, &e->flags)) {
if (p && !strcmp(e->magic, p + 1))
return e;
} else {
*/
static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
- struct binfmt_entry *fmt;
+ Node *fmt;
struct dentry * dentry;
char iname[128];
char *iname_addr = iname;
return retval;
}
-
-
-/*
- * /proc handling routines
- */
-
/*
* parses and copies one argument enclosed in del from *sp to *dp,
* recognising the \x special.
* returns pointer to the copied argument or NULL in case of an
* error (and sets err) or null argument length.
*/
-static char *copyarg(char **dp, const char **sp, int *count,
- char del, int special, int *err)
+static char *scanarg(char *s, char del)
{
- char c = 0, *res = *dp;
-
- while (!*err && ((c = *((*sp)++)), (*count)--) && (c != del)) {
- switch (c) {
- case '\\':
- if (special && (**sp == 'x')) {
- if (!isxdigit(c = toupper(*(++*sp))))
- *err = -EINVAL;
- **dp = (c - (isdigit(c) ? '0' : 'A' - 10)) * 16;
- if (!isxdigit(c = toupper(*(++*sp))))
- *err = -EINVAL;
- *((*dp)++) += c - (isdigit(c) ? '0' : 'A' - 10);
- ++*sp;
- *count -= 3;
- break;
- }
- default:
- *((*dp)++) = c;
+ char c;
+
+ while ((c = *s++) != del) {
+ if (c == '\\' && *s == 'x') {
+ s++;
+ if (!isxdigit(*s++))
+ return NULL;
+ if (!isxdigit(*s++))
+ return NULL;
}
}
- if (*err || (c != del) || (res == *dp))
- res = NULL;
- else if (!special)
- *((*dp)++) = '\0';
- return res;
+ return s;
+}
+
+static int unquote(char *from)
+{
+ char c = 0, *s = from, *p = from;
+
+ while ((c = *s++) != '\0') {
+ if (c == '\\' && *s == 'x') {
+ s++;
+ c = toupper(*s++);
+ *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
+ c = toupper(*s++);
+ *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
+ continue;
+ }
+ *p++ = c;
+ }
+ return p - from;
}
/*
* ':name:type:offset:magic:mask:interpreter:'
* where the ':' is the IFS, that can be chosen with the first char
*/
-static int proc_write_register(struct file *file, const char *buffer,
- unsigned long count, void *data)
+static Node *create_entry(const char *buffer, size_t count)
{
- const char *sp;
- char del, *dp;
- struct binfmt_entry *e;
- int memsize, cnt = count - 1, err;
+ Node *e;
+ int memsize, err;
+ char *buf, *p;
+ char del;
/* some sanity checks */
err = -EINVAL;
if ((count < 11) || (count > 256))
- goto _err;
+ goto out;
err = -ENOMEM;
- memsize = sizeof(struct binfmt_entry) + count;
- if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER)))
- goto _err;
+ memsize = sizeof(Node) + count + 8;
+ e = (Node *) kmalloc(memsize, GFP_USER);
+ if (!e)
+ goto out;
+
+ p = buf = (char *)e + sizeof(Node);
+
+ memset(e, 0, sizeof(Node));
+ if (copy_from_user(buf, buffer, count))
+ goto Efault;
+
+ del = *p++; /* delimeter */
+
+ memset(buf+count, del, 8);
+
+ e->name = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->name[0] ||
+ !strcmp(e->name, ".") ||
+ !strcmp(e->name, "..") ||
+ strchr(e->name, '/'))
+ goto Einval;
+ switch (*p++) {
+ case 'E': e->flags = 1<<Enabled; break;
+ case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
+ default: goto Einval;
+ }
+ if (*p++ != del)
+ goto Einval;
+ if (test_bit(Magic, &e->flags)) {
+ char *s = strchr(p, del);
+ if (!s)
+ goto Einval;
+ *s++ = '\0';
+ e->offset = simple_strtoul(p, &p, 10);
+ if (*p++)
+ goto Einval;
+ e->magic = p;
+ p = scanarg(p, del);
+ if (!p)
+ goto Einval;
+ p[-1] = '\0';
+ if (!e->magic[0])
+ goto Einval;
+ e->mask = p;
+ p = scanarg(p, del);
+ if (!p)
+ goto Einval;
+ p[-1] = '\0';
+ if (!e->mask[0])
+ e->mask = NULL;
+ e->size = unquote(e->magic);
+ if (e->mask && unquote(e->mask) != e->size)
+ goto Einval;
+ if (e->size + e->offset > 128)
+ goto Einval;
+ } else {
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ e->magic = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->magic[0] || strchr(e->magic, '/'))
+ goto Einval;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ }
+ e->interpreter = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->interpreter[0])
+ goto Einval;
+
+ if (*p == '\n')
+ p++;
+ if (p != buf + count)
+ goto Einval;
+ return e;
- err = 0;
- sp = buffer + 1;
- del = buffer[0];
- dp = (char *)e + sizeof(struct binfmt_entry);
+out:
+ return ERR_PTR(err);
- e->proc_name = copyarg(&dp, &sp, &cnt, del, 0, &err);
+Efault:
+ kfree(e);
+ return ERR_PTR(-EFAULT);
+Einval:
+ kfree(e);
+ return ERR_PTR(-EINVAL);
+}
- /* we can use bit 3 of type for ext/magic
- flag due to the nice encoding of E and M */
- if ((*sp & ~('E' | 'M')) || (sp[1] != del))
- err = -EINVAL;
- else
- e->flags = (*sp++ & (ENTRY_MAGIC | ENTRY_ENABLED));
- cnt -= 2; sp++;
-
- e->offset = 0;
- while (cnt-- && isdigit(*sp))
- e->offset = e->offset * 10 + *sp++ - '0';
- if (*sp++ != del)
- err = -EINVAL;
-
- e->magic = copyarg(&dp, &sp, &cnt, del, (e->flags & ENTRY_MAGIC), &err);
- e->size = dp - e->magic;
- e->mask = copyarg(&dp, &sp, &cnt, del, 1, &err);
- if (e->mask && ((dp - e->mask) != e->size))
- err = -EINVAL;
- e->interpreter = copyarg(&dp, &sp, &cnt, del, 0, &err);
- e->id = free_id++;
+/*
+ * Set status of entry/binfmt_misc:
+ * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
+ */
+static int parse_command(const char *buffer, size_t count)
+{
+ char s[4];
+
+ if (!count)
+ return 0;
+ if (count > 3)
+ return -EINVAL;
+ if (copy_from_user(s, buffer, count))
+ return -EFAULT;
+ if (s[count-1] == '\n')
+ count--;
+ if (count == 1 && s[0] == '0')
+ return 1;
+ if (count == 1 && s[0] == '1')
+ return 2;
+ if (count == 2 && s[0] == '-' && s[1] == '1')
+ return 3;
+ return -EINVAL;
+}
+
+static void entry_status(Node *e, char *page)
+{
+ char *dp;
+ char *status = "disabled";
+
+ if (test_bit(Enabled, &e->flags))
+ status = "enabled";
+
+ if (!VERBOSE_STATUS) {
+ sprintf(page, "%s\n", status);
+ return;
+ }
- /* more sanity checks */
- if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) ||
- (e->size < 1) || ((e->size + e->offset) > 127) ||
- !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e))
+ sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
+ dp = page + strlen(page);
+ if (!test_bit(Magic, &e->flags)) {
+ sprintf(dp, "extension .%s\n", e->magic);
+ } else {
+ int i;
+
+ sprintf(dp, "offset %i\nmagic ", e->offset);
+ dp = page + strlen(page);
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
+ dp += 2;
+ }
+ if (e->mask) {
+ sprintf(dp, "\nmask ");
+ dp += 6;
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
+ dp += 2;
+ }
+ }
+ *dp++ = '\n';
+ *dp = '\0';
+ }
+}
+
+static int proc_write_register(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ Node *e = create_entry(buffer, count);
+ int err;
+
+ if (IS_ERR(e))
+ return PTR_ERR(e);
+
+ if (entry_proc_setup(e))
goto free_err;
+ e->id = free_id++;
write_lock(&entries_lock);
e->next = entries;
entries = e;
static int proc_read_status(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
- struct binfmt_entry *e;
- char *dp;
- int elen, i, err;
+ Node *e;
+ int elen;
-#ifndef VERBOSE_STATUS
- if (data) {
- if (!(e = get_entry((int) data))) {
- err = -ENOENT;
- goto _err;
- }
- i = e->flags & ENTRY_ENABLED;
- put_entry(e);
- } else {
- i = enabled;
- }
- sprintf(page, "%s\n", (i ? "enabled" : "disabled"));
-#else
- if (!data)
+ if (!data) {
sprintf(page, "%s\n", (enabled ? "enabled" : "disabled"));
- else {
- if (!(e = get_entry((long) data))) {
- err = -ENOENT;
- goto _err;
- }
- sprintf(page, "%s\ninterpreter %s\n",
- (e->flags & ENTRY_ENABLED ? "enabled" : "disabled"),
- e->interpreter);
- dp = page + strlen(page);
- if (!(e->flags & ENTRY_MAGIC)) {
- sprintf(dp, "extension .%s\n", e->magic);
- dp = page + strlen(page);
- } else {
- sprintf(dp, "offset %i\nmagic ", e->offset);
- dp = page + strlen(page);
- for (i = 0; i < e->size; i++) {
- sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
- dp += 2;
- }
- if (e->mask) {
- sprintf(dp, "\nmask ");
- dp += 6;
- for (i = 0; i < e->size; i++) {
- sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
- dp += 2;
- }
- }
- *dp++ = '\n';
- *dp = '\0';
- }
+ } else {
+ if (!(e = get_entry((long) data)))
+ return -ENOENT;
+ entry_status(e, page);
put_entry(e);
}
-#endif
elen = strlen(page) - off;
if (elen < 0)
elen = 0;
*eof = (elen <= count) ? 1 : 0;
*start = page + off;
- err = elen;
-
-_err:
- return err;
+ return elen;
}
/*
static int proc_write_status(struct file *file, const char *buffer,
unsigned long count, void *data)
{
- struct binfmt_entry *e;
- int res = count;
-
- if (buffer[count-1] == '\n')
- count--;
- if ((count == 1) && !(buffer[0] & ~('0' | '1'))) {
- if (data) {
- if ((e = get_entry((long) data)))
- e->flags = (e->flags & ~ENTRY_ENABLED)
- | (int)(buffer[0] - '0');
- put_entry(e);
- } else {
- enabled = buffer[0] - '0';
- }
- } else if ((count == 2) && (buffer[0] == '-') && (buffer[1] == '1')) {
- if (data)
- clear_entry((long) data);
- else
- clear_entries();
- } else {
- res = -EINVAL;
+ Node *e;
+ int res = parse_command(buffer, count);
+
+ switch(res) {
+ case 1: if (data) {
+ if ((e = get_entry((long) data)))
+ clear_bit(Enabled, &e->flags);
+ put_entry(e);
+ } else {
+ enabled = 0;
+ }
+ break;
+ case 2: if (data) {
+ if ((e = get_entry((long) data)))
+ set_bit(Enabled, &e->flags);
+ put_entry(e);
+ } else {
+ enabled = 1;
+ }
+ break;
+ case 3: if (data)
+ clear_entry((long) data);
+ else
+ clear_entries();
+ break;
+ default: return res;
}
- return res;
+ return count;
}
/*
* Remove the /proc-dir entries of one binfmt
*/
-static void entry_proc_cleanup(struct binfmt_entry *e)
+static void entry_proc_cleanup(Node *e)
{
- remove_proc_entry(e->proc_name, bm_dir);
+ remove_proc_entry(e->name, bm_dir);
}
/*
* Create the /proc-dir entry for binfmt
*/
-static int entry_proc_setup(struct binfmt_entry *e)
+static int entry_proc_setup(Node *e)
{
- if (!(e->proc_dir = create_proc_entry(e->proc_name,
+ if (!(e->proc_dir = create_proc_entry(e->name,
S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
{
printk(KERN_WARNING "Unable to create /proc entry.\n");
remove_proc_entry("sys/fs/binfmt_misc", NULL);
}
#endif
-#undef VERBOSE_STATUS
* table to check for several different types of binary formats. We keep
* trying until we recognize the file or we run out of supported binary
* formats.
- *
- * (current->executable doesn't exist anymore, procfs searches for the vma
- * that corresponds to the executable and uses its dentry.)
*/
#include <linux/config.h>
current->sas_ss_sp = current->sas_ss_size = 0;
- current->dumpable = 0;
bprm->dumpable = 0;
if (current->euid == current->uid && current->egid == current->gid)
bprm->dumpable = !bprm->priv_change;
{
struct linux_binprm bprm;
struct dentry * dentry;
+ int was_dumpable;
int retval;
int i;
return bprm.envc;
}
+ was_dumpable = current->dumpable;
+ current->dumpable = 0;
+
retval = prepare_binprm(&bprm);
if (retval >= 0) {
if (retval >= 0)
retval = search_binary_handler(&bprm,regs);
- current->dumpable = bprm.dumpable;
-
- if (retval >= 0)
+ if (retval >= 0) {
/* execve success */
+ current->dumpable = bprm.dumpable;
return retval;
+ }
/* Something went wrong, return the inode and free the argument pages*/
if (bprm.dentry)
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
+ current->dumpable = was_dumpable;
+
return retval;
}
__u32 end_sector_l0;
};
+#define DVD_LAYERS 4
+
struct dvd_physical {
__u8 type;
__u8 layer_num;
- struct dvd_layer layer[4];
+ struct dvd_layer layer[DVD_LAYERS];
};
struct dvd_copyright {
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_input.c,v 1.164.2.20 2001/02/23 20:20:22 davem Exp $
+ * Version: $Id: tcp_input.c,v 1.164.2.21 2001/03/06 05:39:39 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
tp->snd_wnd = htons(th->window);
tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
tp->snd_wl2 = TCP_SKB_CB(skb)->ack_seq;
+ tp->syn_seq = TCP_SKB_CB(skb)->seq;
tp->fin_seq = TCP_SKB_CB(skb)->seq;
tcp_set_state(sk, TCP_ESTABLISHED);