All debug logs have an an actual debug level (range from 0 to 6).
The default level is 3. Event and Exception functions have a 'level'
parameter. Only debug entries with a level that is lower or equal
-than the actual level are written to the log. This means that high
-priority log entries should have a low level value whereas low priority
-entries should have a high one.
+than the actual level are written to the log. This means, when
+writing events, high priority log entries should have a low level
+value whereas low priority entries should have a high one.
The actual debug level can be changed with the help of the proc-filesystem
through writing a number string "x" to the 'level' proc file which is
provided for every debug log. Debugging can be switched off completely
boot := arch/$(ARCH)/boot
-all: image
+all: image kerntypes.o
install: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $@
-image: vmlinux
+image kerntypes.o: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
archclean:
EXTRA_CFLAGS := -DCOMPILE_VERSION=$(COMPILE_VERSION) -gstabs -I.
-targets := image
+targets := image kerntypes.o
$(obj)/image: vmlinux FORCE
$(call if_changed,objcopy)
--- /dev/null
+/*
+ * kerntypes.c
+ *
+ * Dummy module that includes headers for all kernel types of interest.
+ * The kernel type information is used by the lcrash utility when
+ * analyzing system crash dumps or the live system. Using the type
+ * information for the running system, rather than kernel header files,
+ * makes for a more flexible and robust analysis tool.
+ *
+ * This source code is released under the GNU GPL.
+ */
+
+/* generate version for this file */
+typedef char *COMPILE_VERSION;
+
+/* General linux types */
+
+#include <linux/autoconf.h>
+#include <linux/compile.h>
+#include <linux/config.h>
+#include <linux/utsname.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/lowcore.h>
+#include <asm/debug.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <asm/qdio.h>
+
+/* channel subsystem driver */
+#include "drivers/s390/cio/cio.h"
+#include "drivers/s390/cio/chsc.h"
+#include "drivers/s390/cio/css.h"
+#include "drivers/s390/cio/device.h"
+#include "drivers/s390/cio/qdio.h"
+
+/* dasd device driver */
+#include "drivers/s390/block/dasd_int.h"
+#include "drivers/s390/block/dasd_diag.h"
+#include "drivers/s390/block/dasd_eckd.h"
+#include "drivers/s390/block/dasd_fba.h"
+
+/* networking drivers */
+#include "drivers/s390/net/fsm.h"
+#include "drivers/s390/net/iucv.h"
+#include "drivers/s390/net/lcs.h"
+
+/* zfcp device driver */
+#include "drivers/s390/scsi/zfcp_def.h"
+#include "drivers/s390/scsi/zfcp_fsf.h"
+
+/* include sched.c for types:
+ * - struct prio_array
+ * - struct runqueue
+ */
+#include "kernel/sched.c"
# CONFIG_SHARED_KERNEL is not set
# CONFIG_CMM is not set
# CONFIG_VIRT_TIMER is not set
+# CONFIG_NO_IDLE_HZ is not set
# CONFIG_PCMCIA is not set
#
}
#endif
+
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
- * $Revision: 1.136 $
+ * $Revision: 1.137.2.1 $
*/
#include <linux/config.h>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
- * $Revision: 1.28 $
+ * $Revision: 1.29.2.1 $
*/
#include <linux/timer.h>
/*
- * linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.6 $)
+ * linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.7.2.1 $)
*
* Linux on zSeries Channel Measurement Facility support
* (dasd device driver interface)
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
- * $Revision: 1.32 $
+ * $Revision: 1.33 $
*/
#include <linux/config.h>
return dasd_era_none;
cdev = device->cdev;
- switch (cdev->id.dev_model) {
+ switch (cdev->id.dev_type) {
case 0x3370:
return dasd_3370_erp_examine(cqr, irb);
case 0x9336:
*
* gendisk related functions for the dasd driver.
*
- * $Revision: 1.46 $
+ * $Revision: 1.47.2.1 $
*/
#include <linux/config.h>
dev_info->gd->queue = NULL;
put_disk(dev_info->gd);
device_unregister(&dev_info->dev);
- put_device(&dev_info->dev);
segment_unload(dev_info->segment_name);
PRINT_DEBUG("Segment %s unloaded successfully\n",
dev_info->segment_name);
+ put_device(&dev_info->dev);
rc = count;
out_buf:
kfree(local_buf);
switch(p->op) {
case TO_MSEN:
tape_34xx_medium_sense(p->device);
+ break;
default:
DBF_EVENT(3, "T34XX: internal error: unknown work\n");
}
{
int rc;
- DBF_EVENT(3, "34xx init: $Revision: 1.19 $\n");
+ DBF_EVENT(3, "34xx init: $Revision: 1.20 $\n");
/* Register driver for 3480/3490 tapes. */
rc = ccw_driver_register(&tape_34xx_driver);
if (rc)
MODULE_DEVICE_TABLE(ccw, tape_34xx_ids);
MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape "
- "device driver ($Revision: 1.19 $)");
+ "device driver ($Revision: 1.20 $)");
MODULE_LICENSE("GPL");
module_init(tape_34xx_init);
/*
* (C) Copyright IBM Corp. 2004
- * tape_class.c ($Revision: 1.6 $)
+ * tape_class.c ($Revision: 1.8 $)
*
* Tape class device support
*
MODULE_AUTHOR("Stefan Bader <shbader@de.ibm.com>");
MODULE_DESCRIPTION(
"(C) Copyright IBM Corp. 2004 All Rights Reserved.\n"
- "tape_class.c ($Revision: 1.6 $)"
+ "tape_class.c ($Revision: 1.8 $)"
);
MODULE_LICENSE("GPL");
return tcd;
fail_with_cdev:
- cdev_del(&tcd->char_device);
+ cdev_del(tcd->char_device);
fail_with_tcd:
kfree(tcd);
return rc;
}
+static inline void
+tape_cleanup_device(struct tape_device *device)
+{
+ tapeblock_cleanup_device(device);
+ tapechar_cleanup_device(device);
+ device->discipline->cleanup_device(device);
+ tape_remove_minor(device);
+ tape_med_state_set(device, MS_UNKNOWN);
+}
+
/*
* Set device offline.
*
switch (device->tape_state) {
case TS_INIT:
case TS_NOT_OPER:
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
break;
case TS_UNUSED:
- tapeblock_cleanup_device(device);
- tapechar_cleanup_device(device);
- device->discipline->cleanup_device(device);
- tape_remove_minor(device);
+ tape_state_set(device, TS_INIT);
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ tape_cleanup_device(device);
+ break;
default:
DBF_EVENT(3, "(%08x): Set offline failed "
"- drive in use.\n",
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return -EBUSY;
}
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
-
- tape_med_state_set(device, MS_UNKNOWN);
DBF_LH(3, "(%08x): Drive set offline.\n", device->cdev_id);
return 0;
return 0;
}
-/*
- * Driverfs tape remove function.
- *
- * This function is called whenever the common I/O layer detects the device
- * gone. This can happen at any time and we cannot refuse.
- */
-void
-tape_generic_remove(struct ccw_device *cdev)
+static inline void
+__tape_discard_requests(struct tape_device *device)
{
- struct tape_device * device;
struct tape_request * request;
struct list_head * l, *n;
- device = cdev->dev.driver_data;
- DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev);
-
- /*
- * No more requests may be processed. So just post them as i/o errors.
- */
- spin_lock_irq(get_ccwdev_lock(device->cdev));
list_for_each_safe(l, n, &device->req_queue) {
request = list_entry(l, struct tape_request, list);
if (request->status == TAPE_REQUEST_IN_IO)
if (request->callback != NULL)
request->callback(request, request->callback_data);
}
+}
+
+/*
+ * Driverfs tape remove function.
+ *
+ * This function is called whenever the common I/O layer detects the device
+ * gone. This can happen at any time and we cannot refuse.
+ */
+void
+tape_generic_remove(struct ccw_device *cdev)
+{
+ struct tape_device * device;
+
+ device = cdev->dev.driver_data;
+ if (!device) {
+ PRINT_ERR("No device pointer in tape_generic_remove!\n");
+ return;
+ }
+ DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev);
- if (device->tape_state != TS_UNUSED && device->tape_state != TS_INIT) {
- DBF_EVENT(3, "(%08x): Drive in use vanished!\n",
- device->cdev_id);
- PRINT_WARN("(%s): Drive in use vanished - expect trouble!\n",
- device->cdev->dev.bus_id);
- PRINT_WARN("State was %i\n", device->tape_state);
- device->tape_state = TS_NOT_OPER;
- tapeblock_cleanup_device(device);
- tapechar_cleanup_device(device);
- device->discipline->cleanup_device(device);
- tape_remove_minor(device);
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
+ switch (device->tape_state) {
+ case TS_INIT:
+ tape_state_set(device, TS_NOT_OPER);
+ case TS_NOT_OPER:
+ /*
+ * Nothing to do.
+ */
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ break;
+ case TS_UNUSED:
+ /*
+ * Need only to release the device.
+ */
+ tape_state_set(device, TS_NOT_OPER);
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ tape_cleanup_device(device);
+ break;
+ default:
+ /*
+ * There may be requests on the queue. We will not get
+ * an interrupt for a request that was running. So we
+ * just post them all as I/O errors.
+ */
+ DBF_EVENT(3, "(%08x): Drive in use vanished!\n",
+ device->cdev_id);
+ PRINT_WARN("(%s): Drive in use vanished - "
+ "expect trouble!\n",
+ device->cdev->dev.bus_id);
+ PRINT_WARN("State was %i\n", device->tape_state);
+ tape_state_set(device, TS_NOT_OPER);
+ __tape_discard_requests(device);
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ tape_cleanup_device(device);
}
- device->tape_state = TS_NOT_OPER;
- tape_med_state_set(device, MS_UNKNOWN);
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
if (cdev->dev.driver_data != NULL) {
sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);
cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data);
}
-
}
/*
#ifdef DBF_LIKE_HELL
debug_set_level(tape_dbf_area, 6);
#endif
- DBF_EVENT(3, "tape init: ($Revision: 1.48 $)\n");
+ DBF_EVENT(3, "tape init: ($Revision: 1.49.2.1 $)\n");
tape_proc_init();
tapechar_init ();
tapeblock_init ();
MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
"Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)");
MODULE_DESCRIPTION("Linux on zSeries channel attached "
- "tape device driver ($Revision: 1.48 $)");
+ "tape device driver ($Revision: 1.49.2.1 $)");
MODULE_LICENSE("GPL");
module_init(tape_init);
/*
* drivers/s390/cio/ccwgroup.c
* bus driver for ccwgroup
- * $Revision: 1.25 $
+ * $Revision: 1.27 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
return driver_register(&cdriver->driver);
}
+static inline struct device *
+__get_next_ccwgroup_device(struct device_driver *drv)
+{
+ struct device *dev, *d;
+
+ down_read(&drv->bus->subsys.rwsem);
+ dev = NULL;
+ list_for_each_entry(d, &drv->devices, driver_list) {
+ dev = get_device(d);
+ if (dev)
+ break;
+ }
+ up_read(&drv->bus->subsys.rwsem);
+ return dev;
+}
+
void
ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
{
+ struct device *dev;
+
+ /* We don't want ccwgroup devices to live longer than their driver. */
+ get_driver(&cdriver->driver);
+ while ((dev = __get_next_ccwgroup_device(&cdriver->driver))) {
+ __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+ device_unregister(dev);
+ put_device(dev);
+ };
+ put_driver(&cdriver->driver);
driver_unregister(&cdriver->driver);
}
if (cdev->dev.driver_data) {
gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
- if (get_device(&gdev->dev))
- return gdev;
+ if (get_device(&gdev->dev)) {
+ if (!list_empty(&gdev->dev.node))
+ return gdev;
+ put_device(&gdev->dev);
+ }
return NULL;
}
return NULL;
/*
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
- * $Revision: 1.105 $
+ * $Revision: 1.107 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
struct schib schib;
sch = get_subchannel_by_schid(irq);
- if (sch)
+ if (sch) {
+ put_device(&sch->dev);
continue;
+ }
if (stsch(irq, &schib))
/* We're through */
break;
/*
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
- * $Revision: 1.117 $
+ * $Revision: 1.117.2.1 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
- * $Revision: 1.69 $
+ * $Revision: 1.72 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
struct device, bus_list));
if (!dev)
continue;
- /* Skip channel paths. */
- if (dev->release != &css_subchannel_release) {
- put_device(dev);
- continue;
- }
sch = to_subchannel(dev);
if (sch->irq == irq)
break;
sch = get_subchannel_by_schid(irq);
disc = sch ? device_is_disconnected(sch) : 0;
- if (disc && slow)
+ if (disc && slow) {
+ if (sch)
+ put_device(&sch->dev);
return 0; /* Already processed. */
- if (!disc && !slow)
+ }
+ if (!disc && !slow) {
+ if (sch)
+ put_device(&sch->dev);
return -EAGAIN; /* Will be done on the slow path. */
+ }
event = css_get_subchannel_status(sch, irq);
switch (event) {
case CIO_GONE:
/*
* drivers/s390/cio/device.c
* bus driver for ccw devices
- * $Revision: 1.110 $
+ * $Revision: 1.113 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
static int io_subchannel_notify(struct device *, int);
static void io_subchannel_verify(struct device *);
static void io_subchannel_ioterm(struct device *);
+static void io_subchannel_shutdown(struct device *);
struct css_driver io_subchannel_driver = {
.subchannel_type = SUBCHANNEL_TYPE_IO,
.bus = &css_bus_type,
.probe = &io_subchannel_probe,
.remove = &io_subchannel_remove,
+ .shutdown = &io_subchannel_shutdown,
},
.irq = io_subchannel_irq,
.notify = io_subchannel_notify,
ERR_PTR(-EIO));
}
+static void
+io_subchannel_shutdown(struct device *dev)
+{
+ struct subchannel *sch;
+ struct ccw_device *cdev;
+ int ret;
+
+ sch = to_subchannel(dev);
+ cdev = dev->driver_data;
+
+ if (cio_is_console(sch->irq))
+ return;
+ if (!sch->schib.pmcw.ena)
+ /* Nothing to do. */
+ return;
+ ret = cio_disable_subchannel(sch);
+ if (ret != -EBUSY)
+ /* Subchannel is disabled, we're done. */
+ return;
+ cdev->private->state = DEV_STATE_QUIESCE;
+ if (cdev->handler)
+ cdev->handler(cdev, cdev->private->intparm,
+ ERR_PTR(-EIO));
+ ret = ccw_device_cancel_halt_clear(cdev);
+ if (ret == -EBUSY) {
+ ccw_device_set_timeout(cdev, HZ/10);
+ wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
+ }
+ cio_disable_subchannel(sch);
+}
+
#ifdef CONFIG_CCW_CONSOLE
static struct ccw_device console_cdev;
static struct ccw_device_private console_private;
DEV_STATE_CLEAR_VERIFY,
DEV_STATE_TIMEOUT_KILL,
DEV_STATE_WAIT4IO,
+ DEV_STATE_QUIESCE,
/* special states for devices gone not operational */
DEV_STATE_DISCONNECTED,
DEV_STATE_DISCONNECTED_SENSE_ID,
void io_subchannel_recog_done(struct ccw_device *cdev);
+int ccw_device_cancel_halt_clear(struct ccw_device *);
+
int ccw_device_register(struct ccw_device *);
void ccw_device_do_unreg_rereg(void *);
* -EBUSY if an interrupt is expected (either from halt/clear or from a
* status pending).
*/
-static int
+int
ccw_device_cancel_halt_clear(struct ccw_device *cdev)
{
struct subchannel *sch;
ret = (sch->driver && sch->driver->notify) ?
sch->driver->notify(&sch->dev, CIO_NO_PATH) : 0;
if (!ret) {
- /* Driver doesn't want to keep device. */
- device_unregister(&sch->dev);
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ if (get_device(&sch->dev)) {
+ /* Driver doesn't want to keep device. */
+ device_unregister(&sch->dev);
+ sch->schib.pmcw.intparm = 0;
+ cio_modify(sch);
+ put_device(&sch->dev);
+ }
} else {
ccw_device_set_timeout(cdev, 0);
cdev->private->state = DEV_STATE_DISCONNECTED;
cdev->private->state = DEV_STATE_TIMEOUT_KILL;
return;
}
- if (ret == -ENODEV)
- dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
- else if (cdev->handler)
+ if (ret == -ENODEV) {
+ struct subchannel *sch;
+
+ sch = to_subchannel(cdev->dev.parent);
+ if (!sch->lpm) {
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_nopath_notify, (void *)cdev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ } else
+ dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
+ } else if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-ETIMEDOUT));
}
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
- }
- dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
+ } else
+ dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return;
}
//FIXME: Can we get here?
int ret;
struct subchannel *sch;
+ sch = to_subchannel(cdev->dev.parent);
ccw_device_set_timeout(cdev, 0);
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
return;
}
if (ret == -ENODEV) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_nopath_notify, (void *)cdev);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
+ if (!sch->lpm) {
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_nopath_notify, (void *)cdev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ } else
+ dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return;
}
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-ETIMEDOUT));
- sch = to_subchannel(cdev->dev.parent);
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev);
}
+static void
+ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
+{
+ ccw_device_set_timeout(cdev, 0);
+ if (dev_event == DEV_EVENT_NOTOPER)
+ cdev->private->state = DEV_STATE_NOT_OPER;
+ else
+ cdev->private->state = DEV_STATE_OFFLINE;
+ wake_up(&cdev->private->wait_q);
+}
+
+static void
+ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
+{
+ int ret;
+
+ ret = ccw_device_cancel_halt_clear(cdev);
+ switch (ret) {
+ case 0:
+ cdev->private->state = DEV_STATE_OFFLINE;
+ wake_up(&cdev->private->wait_q);
+ break;
+ case -ENODEV:
+ cdev->private->state = DEV_STATE_NOT_OPER;
+ wake_up(&cdev->private->wait_q);
+ break;
+ default:
+ ccw_device_set_timeout(cdev, HZ/10);
+ }
+}
+
/*
* No operation action. This is used e.g. to ignore a timeout event in
* state offline.
[DEV_EVENT_TIMEOUT] ccw_device_wait4io_timeout,
[DEV_EVENT_VERIFY] ccw_device_wait4io_verify,
},
+ [DEV_STATE_QUIESCE] {
+ [DEV_EVENT_NOTOPER] ccw_device_quiesce_done,
+ [DEV_EVENT_INTERRUPT] ccw_device_quiesce_done,
+ [DEV_EVENT_TIMEOUT] ccw_device_quiesce_timeout,
+ [DEV_EVENT_VERIFY] ccw_device_nop,
+ },
/* special states for devices gone not operational */
[DEV_STATE_DISCONNECTED] {
[DEV_EVENT_NOTOPER] ccw_device_nop,
/*
* drivers/s390/cio/device_ops.c
*
- * $Revision: 1.34 $
+ * $Revision: 1.45.2.1 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
#include "ioasm.h"
#include "chsc.h"
-#define VERSION_QDIO_C "$Revision: 1.78 $"
+#define VERSION_QDIO_C "$Revision: 1.79 $"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
if ((q->is_thinint_q)&&(q->is_input_q)) {
/* iQDIO */
spin_lock_irqsave(&ttiq_list_lock,flags);
+ /* in case cleanup has done this already and simultanously
+ * qdio_unmark_q is called from the interrupt handler, we've
+ * got to check this in this specific case again */
+ if ((!q->list_prev)||(!q->list_next))
+ goto out;
if (q->list_next==q) {
/* q was the only interesting q */
tiq_list=NULL;
q->list_next=NULL;
q->list_prev=NULL;
}
+out:
spin_unlock_irqrestore(&ttiq_list_lock,flags);
}
}
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+
#ifndef _Z90COMMON_
#define _Z90COMMON_
-#define VERSION_Z90COMMON_H "$Revision: 1.6 $"
+
+#define VERSION_Z90COMMON_H "$Revision: 1.8 $"
+
+
#define RESPBUFFSIZE 256
#define PCI_FUNC_KEY_DECRYPT 0x5044
#define PCI_FUNC_KEY_ENCRYPT 0x504B
+
enum devstat {
DEV_GONE,
DEV_ONLINE,
DEV_SEN_EXCEPTION,
DEV_REC_EXCEPTION
};
+
enum hdstat {
HD_NOT_THERE,
HD_BUSY,
HD_ONLINE,
HD_TSQ_EXCEPTION
};
+
#define Z90C_AMBIGUOUS_DOMAIN 2
#define Z90C_INCORRECT_DOMAIN 3
#define ENOTINIT 4
+
#define SEN_BUSY 7
#define SEN_USER_ERROR 8
#define SEN_QUEUE_FULL 11
#define SEN_PAD_ERROR 17
#define SEN_RETRY 18
#define SEN_RELEASED 24
+
#define REC_EMPTY 4
#define REC_BUSY 6
#define REC_OPERAND_INV 8
#define REC_BAD_MESSAGE 16
#define REC_INVALID_PAD 17
#define REC_RELEASED 28
+
#define WRONG_DEVICE_TYPE 20
+
#define REC_FATAL_ERROR 32
#define SEN_FATAL_ERROR 33
#define TSQ_FATAL_ERROR 34
#define RSQ_FATAL_ERROR 35
+
#define PCICA 0
#define PCICC 1
#define PCIXCC 2
#define NILDEV -1
#define ANYDEV -1
+
enum hdevice_type {
PCICC_HW = 3,
PCICA_HW = 4,
PCIXCC_HW = 5,
- OTHER_HW = 6
+ OTHER_HW = 6,
+ OTHER2_HW = 7
};
+
#ifndef DEV_NAME
#define DEV_NAME "z90crypt"
#endif
printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
#define PRINTKC(fmt, args...) \
printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
+
#ifdef Z90CRYPT_DEBUG
#define PDEBUG(fmt, args...) \
printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
#else
#define PDEBUG(fmt, args...) do {} while (0)
#endif
-#define UMIN(a,b) ((a) > (b) ? (a) : (b))
+
+#define UMIN(a,b) ((a) < (b) ? (a) : (b))
#define IS_EVEN(x) ((x) == (2 * ((x) / 2)))
+
+
#endif
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+
#include <asm/uaccess.h>
#include <linux/compiler.h>
#include <linux/delay.h>
+#include <linux/init.h>
#include <linux/module.h>
#include "z90crypt.h"
#include "z90common.h"
-#define VERSION_Z90HARDWARE_C "$Revision: 1.13 $"
-static const char version[] __attribute_used__ =
- "z90crypt.o: z90hardware.o ("
- "z90hardware.c " VERSION_Z90HARDWARE_C "/"
- "z90common.h " VERSION_Z90COMMON_H "/"
- "z90crypt.h " VERSION_Z90CRYPT_H ")";
+
+#define VERSION_Z90HARDWARE_C "$Revision: 1.19 $"
+
+char z90chardware_version[] __initdata =
+ "z90hardware.o (" VERSION_Z90HARDWARE_C "/"
+ VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
+
struct cca_token_hdr {
unsigned char token_identifier;
unsigned char version;
unsigned short token_length;
unsigned char reserved[4];
};
+
#define CCA_TKN_HDR_ID_EXT 0x1E
+
struct cca_private_ext_ME_sec {
unsigned char section_identifier;
unsigned char version;
unsigned char exponent[128];
unsigned char modulus[128];
};
+
#define CCA_PVT_USAGE_ALL 0x80
+
struct cca_public_sec {
unsigned char section_identifier;
unsigned char version;
unsigned short modulus_byte_len;
unsigned char exponent[3];
};
+
struct cca_private_ext_ME {
struct cca_token_hdr pvtMEHdr;
struct cca_private_ext_ME_sec pvtMESec;
struct cca_public_sec pubMESec;
};
+
struct cca_public_key {
struct cca_token_hdr pubHdr;
struct cca_public_sec pubSec;
};
+
struct cca_pvt_ext_CRT_sec {
unsigned char section_identifier;
unsigned char version;
unsigned char reserved4[52];
unsigned char confounder[8];
};
+
#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
+
struct cca_private_ext_CRT {
struct cca_token_hdr pvtCrtHdr;
struct cca_pvt_ext_CRT_sec pvtCrtSec;
struct cca_public_sec pubCrtSec;
};
+
struct ap_status_word {
unsigned char q_stat_flags;
unsigned char response_code;
unsigned char reserved[2];
};
+
#define AP_Q_STATUS_EMPTY 0x80
#define AP_Q_STATUS_REPLIES_WAITING 0x40
#define AP_Q_STATUS_ARRAY_FULL 0x20
+
#define AP_RESPONSE_NORMAL 0x00
#define AP_RESPONSE_Q_NOT_AVAIL 0x01
#define AP_RESPONSE_RESET_IN_PROGRESS 0x02
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
#define AP_RESPONSE_NO_FIRST_PART 0x13
#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
+
#define AP_MAX_CDX_BITL 4
#define AP_RQID_RESERVED_BITL 4
#define SKIP_BITL (AP_MAX_CDX_BITL + AP_RQID_RESERVED_BITL)
+
struct type4_hdr {
unsigned char reserved1;
unsigned char msg_type_code;
unsigned char msg_fmt;
unsigned short reserved2;
};
+
#define TYPE4_TYPE_CODE 0x04
#define TYPE4_REQU_CODE 0x40
+
#define TYPE4_SME_LEN 0x0188
#define TYPE4_LME_LEN 0x0308
#define TYPE4_SCR_LEN 0x01E0
#define TYPE4_LCR_LEN 0x03A0
+
#define TYPE4_SME_FMT 0x00
#define TYPE4_LME_FMT 0x10
#define TYPE4_SCR_FMT 0x40
#define TYPE4_LCR_FMT 0x50
+
struct type4_sme {
struct type4_hdr header;
unsigned char message[128];
unsigned char exponent[128];
unsigned char modulus[128];
};
+
struct type4_lme {
struct type4_hdr header;
unsigned char message[256];
unsigned char exponent[256];
unsigned char modulus[256];
};
+
struct type4_scr {
struct type4_hdr header;
unsigned char message[128];
unsigned char q[64];
unsigned char u[72];
};
+
struct type4_lcr {
struct type4_hdr header;
unsigned char message[256];
unsigned char q[128];
unsigned char u[136];
};
+
union type4_msg {
struct type4_sme sme;
struct type4_lme lme;
struct type4_scr scr;
struct type4_lcr lcr;
};
+
struct type84_hdr {
unsigned char reserved1;
unsigned char code;
unsigned short len;
unsigned char reserved2[4];
};
+
#define TYPE84_RSP_CODE 0x84
+
struct type6_hdr {
unsigned char reserved1;
unsigned char type;
unsigned int offset3;
unsigned int offset4;
unsigned char agent_id[16];
-
-
-
-
-
-
unsigned char rqid[2];
unsigned char reserved5[2];
unsigned char function_code[2];
unsigned int FromCardLen3;
unsigned int FromCardLen4;
};
+
struct CPRB {
unsigned char cprb_len[2];
unsigned char cprb_ver_id;
unsigned char checkpoint_flag;
unsigned char resv2;
unsigned char req_parml[2];
-
unsigned char req_parmp[4];
unsigned char req_datal[4];
-
unsigned char req_datap[4];
-
unsigned char rpl_parml[2];
-
unsigned char pad_001[2];
unsigned char rpl_parmp[4];
unsigned char rpl_datal[4];
unsigned char rpl_datap[4];
-
unsigned char ccp_rscode[2];
unsigned char ccp_rtcode[2];
unsigned char repd_parml[2];
unsigned char svr_namel[2];
unsigned char svr_name[8];
};
+
struct CPRBX {
unsigned short cprb_len;
unsigned char cprb_ver_id;
unsigned char pad_003[12];
unsigned char pad_004[36];
};
+
struct type6_msg {
struct type6_hdr header;
struct CPRB CPRB;
};
+
union request_msg {
union type4_msg t4msg;
struct type6_msg t6msg;
};
+
struct request_msg_ext {
int q_nr;
unsigned char *psmid;
union request_msg reqMsg;
};
+
struct type82_hdr {
unsigned char reserved1;
unsigned char type;
unsigned char reply_code;
unsigned char reserved3[3];
};
+
#define TYPE82_RSP_CODE 0x82
+
#define REPLY_ERROR_MACHINE_FAILURE 0x10
#define REPLY_ERROR_PREEMPT_FAILURE 0x12
#define REPLY_ERROR_CHECKPT_FAILURE 0x14
#define REPLY_ERROR_TRANSPORT_FAIL 0x90
#define REPLY_ERROR_PACKET_TRUNCATED 0xA0
#define REPLY_ERROR_ZERO_BUFFER_LEN 0xB0
+
struct type86_hdr {
unsigned char reserved1;
unsigned char type;
unsigned char reply_code;
unsigned char reserved3[3];
};
+
#define TYPE86_RSP_CODE 0x86
#define TYPE86_FMT2 0x02
+
struct type86_fmt2_msg {
struct type86_hdr hdr;
unsigned char reserved[4];
unsigned int ount4;
unsigned int offset4;
};
+
static struct type6_hdr static_type6_hdr = {
0x00,
0x06,
0x00000000,
0x00000000
};
+
static struct type6_hdr static_type6_hdrX = {
0x00,
0x06,
0x00000000,
0x00000000
};
+
static struct CPRB static_cprb = {
{0x70,0x00},
0x41,
{0x08,0x00},
{0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20}
};
+
struct function_and_rules_block {
unsigned char function_code[2];
unsigned char ulen[2];
unsigned char only_rule[8];
};
+
static struct function_and_rules_block static_pkd_function_and_rules = {
{0x50,0x44},
{0x0A,0x00},
{'P','K','C','S','-','1','.','2'}
};
+
static struct function_and_rules_block static_pke_function_and_rules = {
{0x50,0x4B},
{0x0A,0x00},
{'P','K','C','S','-','1','.','2'}
};
+
struct T6_keyBlock_hdr {
unsigned char blen[2];
unsigned char ulen[2];
unsigned char flags[2];
};
+
static struct T6_keyBlock_hdr static_T6_keyBlock_hdr = {
{0x89,0x01},
{0x87,0x01},
{0x00}
};
+
static struct CPRBX static_cprbx = {
0x00DC,
0x02,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
};
+
static struct function_and_rules_block static_pkd_function_and_rulesX = {
{0x50,0x44},
{0x00,0x0A},
{'P','K','C','S','-','1','.','2'}
};
+
static struct function_and_rules_block static_pke_function_and_rulesX = {
{0x50,0x4B},
{0x00,0x0A},
{'Z','E','R','O','-','P','A','D'}
};
+
struct T6_keyBlock_hdrX {
unsigned short blen;
unsigned short ulen;
unsigned char flags[2];
};
-static struct T6_keyBlock_hdrX static_T6_keyBlock_hdrX = {
- 0x0189,
- 0x0187,
- {0x00}
-};
+
static unsigned char static_pad[256] = {
0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
};
+
static struct cca_private_ext_ME static_pvt_me_key = {
{
0x1E,
0x0183,
{0x00,0x00,0x00,0x00}
},
+
{
0x02,
0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
},
+
{
0x04,
0x00,
{0x01,0x00,0x01}
}
};
+
static struct cca_public_key static_public_key = {
{
0x1E,
0x0000,
{0x00,0x00,0x00,0x00}
},
+
{
0x04,
0x00,
{0x01,0x00,0x01}
}
};
+
#define FIXED_TYPE6_ME_LEN 0x0000025F
+
#define FIXED_TYPE6_ME_EN_LEN 0x000000F0
+
#define FIXED_TYPE6_ME_LENX 0x000002CB
+
#define FIXED_TYPE6_ME_EN_LENX 0x0000015C
+
static struct cca_public_sec static_cca_pub_sec = {
0x04,
0x00,
0x0000,
{0x01,0x00,0x01}
};
+
#define FIXED_TYPE6_CR_LEN 0x00000177
+
#define FIXED_TYPE6_CR_LENX 0x000001E3
+
#ifndef MAX_RESPONSE_SIZE
#define MAX_RESPONSE_SIZE 0x00000710
+
#define MAX_RESPONSEX_SIZE 0x0000077C
#endif
+
#define RESPONSE_CPRB_SIZE 0x000006B8
#define RESPONSE_CPRBX_SIZE 0x00000724
+
#define CALLER_HEADER 12
+
static unsigned char static_PKE_function_code[2] = {0x50, 0x4B};
+
static inline int
testq(int q_nr, int *q_depth, int *dev_type, struct ap_status_word *stat)
{
int ccode;
+
asm volatile
#ifdef __s390x__
- (" lgr 0,%4 \n"
+ (" llgfr 0,%4 \n"
" slgr 1,1 \n"
" lgr 2,1 \n"
"0: .long 0xb2af0000 \n"
".section .fixup,\"ax\" \n"
"3: \n"
" lhi %0,%h5 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
+ " jg 2b \n"
".previous \n"
".section __ex_table,\"a\" \n"
" .align 8 \n"
".previous"
:"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
:"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
- :"cc","0","1","2");
+ :"cc","0","1","2","memory");
#else
(" lr 0,%4 \n"
" slr 1,1 \n"
".previous"
:"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
:"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
- :"cc","0","1","2");
+ :"cc","0","1","2","memory");
#endif
return ccode;
}
+
static inline int
resetq(int q_nr, struct ap_status_word *stat_p)
{
int ccode;
+
asm volatile
#ifdef __s390x__
- (" lgr 0,%2 \n"
+ (" llgfr 0,%2 \n"
" lghi 1,1 \n"
" sll 1,24 \n"
" or 0,1 \n"
".section .fixup,\"ax\" \n"
"3: \n"
" lhi %0,%h3 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
+ " jg 2b \n"
".previous \n"
".section __ex_table,\"a\" \n"
" .align 8 \n"
".previous"
:"=d" (ccode),"=d" (*stat_p)
:"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
- :"cc","0","1","2");
+ :"cc","0","1","2","memory");
#else
(" lr 0,%2 \n"
" lhi 1,1 \n"
".previous"
:"=d" (ccode),"=d" (*stat_p)
:"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
- :"cc","0","1","2");
+ :"cc","0","1","2","memory");
#endif
return ccode;
}
+
static inline int
sen(int msg_len, unsigned char *msg_ext, struct ap_status_word *stat)
{
int ccode;
+
asm volatile
#ifdef __s390x__
(" lgr 6,%3 \n"
- " lgr 7,%2 \n"
+ " llgfr 7,%2 \n"
" llgt 0,0(6) \n"
" lghi 1,64 \n"
" sll 1,24 \n"
".section .fixup,\"ax\" \n"
"3: \n"
" lhi %0,%h4 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
+ " jg 2b \n"
".previous \n"
".section __ex_table,\"a\" \n"
" .align 8 \n"
".previous"
:"=d" (ccode),"=d" (*stat)
:"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
- :"cc","0","1","2","3","6","7");
+ :"cc","0","1","2","3","6","7","memory");
#else
(" lr 6,%3 \n"
" lr 7,%2 \n"
".previous"
:"=d" (ccode),"=d" (*stat)
:"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
- :"cc","0","1","2","3","6","7");
+ :"cc","0","1","2","3","6","7","memory");
#endif
return ccode;
}
+
static inline int
rec(int q_nr, int buff_l, unsigned char *rsp, unsigned char *id,
struct ap_status_word *st)
{
int ccode;
+
asm volatile
#ifdef __s390x__
- (" lgr 0,%2 \n"
+ (" llgfr 0,%2 \n"
" lgr 3,%4 \n"
" lgr 6,%3 \n"
- " lgr 7,%5 \n"
+ " llgfr 7,%5 \n"
" lghi 1,128 \n"
" sll 1,24 \n"
" or 0,1 \n"
".section .fixup,\"ax\" \n"
"3: \n"
" lhi %0,%h6 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
+ " jg 2b \n"
".previous \n"
".section __ex_table,\"a\" \n"
" .align 8 \n"
#endif
return ccode;
}
+
static inline void
itoLe2(int *i_p, unsigned char *lechars)
{
*lechars = *((unsigned char *) i_p + sizeof(int) - 1);
*(lechars + 1) = *((unsigned char *) i_p + sizeof(int) - 2);
}
+
static inline void
le2toI(unsigned char *lechars, int *i_p)
{
*(ic_p + 2) = *(lechars + 1);
*(ic_p + 3) = *(lechars);
}
+
static inline int
is_empty(unsigned char *ptr, int len)
{
return !memcmp(ptr, (unsigned char *) &static_pvt_me_key+60, len);
}
+
enum hdstat
query_online(int deviceNr, int cdx, int resetNr, int *q_depth, int *dev_type)
{
struct ap_status_word stat_word;
enum hdstat stat;
int break_out;
+
q_nr = (deviceNr << SKIP_BITL) + cdx;
stat = HD_BUSY;
ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
*q_depth = t_depth + 1;
switch (t_dev_type) {
case OTHER_HW:
+ case OTHER2_HW:
stat = HD_NOT_THERE;
*dev_type = NILDEV;
break;
}
if (break_out)
break;
-
+
udelay(5);
-
+
ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
}
return stat;
}
+
enum devstat
reset_device(int deviceNr, int cdx, int resetNr)
{
struct ap_status_word stat_word;
enum devstat stat;
int break_out;
+
q_nr = (deviceNr << SKIP_BITL) + cdx;
stat = DEV_GONE;
ccode = resetq(q_nr, &stat_word);
if (ccode > 3)
return DEV_RSQ_EXCEPTION;
+
break_out = 0;
for (i = 0; i < resetNr; i++) {
switch (ccode) {
if (break_out == 1)
break;
udelay(5);
+
ccode = testq(q_nr, &dummy_qdepth, &dummy_devType, &stat_word);
if (ccode > 3) {
stat = DEV_TSQ_EXCEPTION;
}
}
PDEBUG("Number of testq's needed for reset: %d\n", i);
+
if (i >= resetNr) {
stat = DEV_GONE;
}
+
return stat;
}
+
#ifdef DEBUG_HYDRA_MSGS
static inline void
print_buffer(unsigned char *buffer, int bufflen)
}
}
#endif
+
enum devstat
send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext)
{
struct ap_status_word stat_word;
enum devstat stat;
int ccode;
+
((struct request_msg_ext *) msg_ext)->q_nr =
(dev_nr << SKIP_BITL) + cdx;
PDEBUG("msg_len passed to sen: %d\n", msg_len);
PDEBUG("q number passed to sen: %02x%02x%02x%02x\n",
msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3]);
stat = DEV_GONE;
+
#ifdef DEBUG_HYDRA_MSGS
PRINTK("Request header: %02X%02X%02X%02X %02X%02X%02X%02X "
"%02X%02X%02X%02X\n",
msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3],
msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7],
msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]);
-
print_buffer(msg_ext+12, msg_len);
#endif
+
ccode = sen(msg_len, msg_ext, &stat_word);
if (ccode > 3)
return DEV_SEN_EXCEPTION;
+
PDEBUG("nq cc: %u, st: %02x%02x%02x%02x\n",
ccode, stat_word.q_stat_flags, stat_word.response_code,
stat_word.reserved[0], stat_word.reserved[1]);
default:
stat = DEV_GONE;
}
+
return stat;
}
+
enum devstat
receive_from_AP(int dev_nr, int cdx, int resplen,
unsigned char *resp, unsigned char *psmid)
int ccode;
struct ap_status_word stat_word;
enum devstat stat;
+
memset(resp, 0x00, 8);
+
ccode = rec((dev_nr << SKIP_BITL) + cdx, resplen, resp, psmid,
&stat_word);
if (ccode > 3)
return DEV_REC_EXCEPTION;
+
PDEBUG("dq cc: %u, st: %02x%02x%02x%02x\n",
ccode, stat_word.q_stat_flags, stat_word.response_code,
stat_word.reserved[0], stat_word.reserved[1]);
+
stat = DEV_GONE;
switch (ccode) {
case 0:
default:
break;
}
+
return stat;
}
+
static inline int
pad_msg(unsigned char *buffer, int totalLength, int msgLength)
{
int pad_len;
+
for (pad_len = 0; pad_len < (totalLength - msgLength); pad_len++)
if (buffer[pad_len] != 0x00)
break;
pad_len -= 3;
if (pad_len < 8)
return SEN_PAD_ERROR;
+
buffer[0] = 0x00;
buffer[1] = 0x02;
+
memcpy(buffer+2, static_pad, pad_len);
+
buffer[pad_len + 2] = 0x00;
+
return 0;
}
+
static inline int
is_common_public_key(unsigned char *key, int len)
{
int i;
+
for (i = 0; i < len; i++)
if (key[i])
break;
if (((len == 1) && (key[0] == 3)) ||
((len == 3) && (key[0] == 1) && (key[1] == 0) && (key[2] == 1)))
return 1;
+
return 0;
}
+
static int
ICAMEX_msg_to_type4MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p,
union type4_msg *z90cMsg_p)
int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len;
unsigned char *mod_tgt, *exp_tgt, *inp_tgt;
union type4_msg *tmp_type4_msg;
+
mod_len = icaMex_p->inputdatalength;
+
msg_size = ((mod_len <= 128) ? TYPE4_SME_LEN : TYPE4_LME_LEN) +
CALLER_HEADER;
+
memset(z90cMsg_p, 0, msg_size);
+
tmp_type4_msg = (union type4_msg *)
((unsigned char *) z90cMsg_p + CALLER_HEADER);
+
tmp_type4_msg->sme.header.msg_type_code = TYPE4_TYPE_CODE;
tmp_type4_msg->sme.header.request_code = TYPE4_REQU_CODE;
+
if (mod_len <= 128) {
tmp_type4_msg->sme.header.msg_fmt = TYPE4_SME_FMT;
tmp_type4_msg->sme.header.msg_len = TYPE4_SME_LEN;
inp_tgt = tmp_type4_msg->lme.message;
inp_tgt_len = sizeof(tmp_type4_msg->lme.message);
}
+
mod_tgt += (mod_tgt_len - mod_len);
if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len))
return SEN_RELEASED;
return SEN_RELEASED;
if (is_empty(inp_tgt, mod_len))
return SEN_USER_ERROR;
+
*z90cMsg_l_p = msg_size - CALLER_HEADER;
+
return 0;
}
+
static int
ICACRT_msg_to_type4CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p,
int *z90cMsg_l_p, union type4_msg *z90cMsg_p)
dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len;
unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt;
union type4_msg *tmp_type4_msg;
+
mod_len = icaMsg_p->inputdatalength;
short_len = mod_len / 2;
long_len = mod_len / 2 + 8;
+
tmp_size = ((mod_len <= 128) ? TYPE4_SCR_LEN : TYPE4_LCR_LEN) +
CALLER_HEADER;
+
memset(z90cMsg_p, 0, tmp_size);
+
tmp_type4_msg = (union type4_msg *)
((unsigned char *) z90cMsg_p + CALLER_HEADER);
+
tmp_type4_msg->scr.header.msg_type_code = TYPE4_TYPE_CODE;
tmp_type4_msg->scr.header.request_code = TYPE4_REQU_CODE;
if (mod_len <= 128) {
inp_tgt = tmp_type4_msg->lcr.message;
inp_tgt_len = sizeof(tmp_type4_msg->lcr.message);
}
+
p_tgt += (p_tgt_len - long_len);
if (copy_from_user(p_tgt, icaMsg_p->np_prime, long_len))
return SEN_RELEASED;
return SEN_RELEASED;
if (is_empty(inp_tgt, mod_len))
return SEN_USER_ERROR;
+
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
static int
ICAMEX_msg_to_type6MEX_de_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
struct type6_hdr *tp6Hdr_p;
struct CPRB *cprb_p;
struct cca_private_ext_ME *key_p;
+
mod_len = icaMsg_p->inputdatalength;
tmp_size = FIXED_TYPE6_ME_LEN + mod_len;
total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
+
memset(z90cMsg_p, 0, tmp_size);
-
+
temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
tp6Hdr_p = (struct type6_hdr *)temp;
tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-
+
temp += sizeof(struct type6_hdr);
memcpy(temp, &static_cprb, sizeof(struct CPRB));
cprb_p = (struct CPRB *) temp;
cprb_p->usage_domain[0]= (unsigned char)cdx;
itoLe2(&parmBlock_l, cprb_p->req_parml);
itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
-
+
temp += sizeof(struct CPRB);
memcpy(temp, &static_pkd_function_and_rules,
sizeof(struct function_and_rules_block));
-
+
temp += sizeof(struct function_and_rules_block);
vud_len = 2 + icaMsg_p->inputdatalength;
itoLe2(&vud_len, temp);
-
+
temp += 2;
if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
return SEN_RELEASED;
if (is_empty(temp, mod_len))
return SEN_USER_ERROR;
-
+
temp += mod_len;
memcpy(temp, &static_T6_keyBlock_hdr, sizeof(struct T6_keyBlock_hdr));
-
+
temp += sizeof(struct T6_keyBlock_hdr);
memcpy(temp, &static_pvt_me_key, sizeof(struct cca_private_ext_ME));
key_p = (struct cca_private_ext_ME *)temp;
return SEN_RELEASED;
if (is_empty(temp, mod_len))
return SEN_USER_ERROR;
+
if (is_common_public_key(temp, mod_len)) {
PRINTK("Common public key used for modex decrypt\n");
return SEN_NOT_AVAIL;
}
+
temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus)
- mod_len;
if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len) != 0)
return SEN_RELEASED;
if (is_empty(temp, mod_len))
return SEN_USER_ERROR;
-
+
key_p->pubMESec.modulus_bit_len = 8 * mod_len;
+
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
static int
ICAMEX_msg_to_type6MEX_en_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
struct CPRB *cprb_p;
struct cca_public_key *key_p;
struct T6_keyBlock_hdr *keyb_p;
+
mod_len = icaMsg_p->inputdatalength;
if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
return SEN_RELEASED;
if (is_empty(temp_exp, mod_len))
return SEN_USER_ERROR;
+
exp_p = temp_exp;
for (i = 0; i < mod_len; i++)
if (exp_p[i])
break;
if (i >= mod_len)
return SEN_USER_ERROR;
+
exp_len = mod_len - i;
exp_p += i;
+
PDEBUG("exp_len after computation: %08x\n", exp_len);
tmp_size = FIXED_TYPE6_ME_EN_LEN + 2 * mod_len + exp_len;
total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
+
vud_len = 2 + mod_len;
memset(z90cMsg_p, 0, tmp_size);
-
+
temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
tp6Hdr_p = (struct type6_hdr *)temp;
tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
sizeof(static_PKE_function_code));
-
temp += sizeof(struct type6_hdr);
memcpy(temp, &static_cprb, sizeof(struct CPRB));
cprb_p = (struct CPRB *) temp;
cprb_p->usage_domain[0]= (unsigned char)cdx;
itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
-
temp += sizeof(struct CPRB);
memcpy(temp, &static_pke_function_and_rules,
sizeof(struct function_and_rules_block));
-
temp += sizeof(struct function_and_rules_block);
temp += 2;
if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
return SEN_RELEASED;
if (is_empty(temp, mod_len))
return SEN_USER_ERROR;
-
if (temp[0] != 0x00 || temp[1] != 0x02)
return SEN_NOT_AVAIL;
for (i = 2; i < mod_len; i++)
pad_len = i + 1;
vud_len = mod_len - pad_len;
memmove(temp, temp+pad_len, vud_len);
-
temp -= 2;
vud_len += 2;
itoLe2(&vud_len, temp);
-
temp += (vud_len);
keyb_p = (struct T6_keyBlock_hdr *)temp;
-
temp += sizeof(struct T6_keyBlock_hdr);
memcpy(temp, &static_public_key, sizeof(static_public_key));
key_p = (struct cca_public_key *)temp;
parmBlock_l -= pad_len;
itoLe2(&parmBlock_l, cprb_p->req_parml);
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
static int
ICACRT_msg_to_type6CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
struct cca_token_hdr *keyHdr_p;
struct cca_pvt_ext_CRT_sec *pvtSec_p;
struct cca_public_sec *pubSec_p;
+
mod_len = icaMsg_p->inputdatalength;
short_len = mod_len / 2;
long_len = 8 + short_len;
parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
vud_len = 2 + mod_len;
tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
+
memset(z90cMsg_p, 0, tmp_size);
-
tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
memcpy(tgt_p, &static_type6_hdr, sizeof(struct type6_hdr));
tp6Hdr_p = (struct type6_hdr *)tgt_p;
tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-
tgt_p += sizeof(struct type6_hdr);
cprb_p = (struct CPRB *) tgt_p;
memcpy(tgt_p, &static_cprb, sizeof(struct CPRB));
itoLe2(&parmBlock_l, cprb_p->req_parml);
memcpy(cprb_p->rpl_parml, cprb_p->req_parml,
sizeof(cprb_p->req_parml));
-
tgt_p += sizeof(struct CPRB);
memcpy(tgt_p, &static_pkd_function_and_rules,
sizeof(struct function_and_rules_block));
-
tgt_p += sizeof(struct function_and_rules_block);
itoLe2(&vud_len, tgt_p);
-
tgt_p += 2;
if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
return SEN_RELEASED;
if (is_empty(tgt_p, mod_len))
return SEN_USER_ERROR;
-
tgt_p += mod_len;
tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
itoLe2(&tmp_l, tgt_p);
-
temp = tgt_p + 2;
tmp_l -= 2;
itoLe2(&tmp_l, temp);
-
tgt_p += sizeof(struct T6_keyBlock_hdr);
keyHdr_p = (struct cca_token_hdr *)tgt_p;
keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
tmp_l -= 4;
keyHdr_p->token_length = tmp_l;
-
tgt_p += sizeof(struct cca_token_hdr);
pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
- pvtSec_p->section_length =
+ pvtSec_p->section_length =
sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
pvtSec_p->u_len = long_len;
pvtSec_p->mod_len = mod_len;
pvtSec_p->pad_len = pad_len;
-
tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
return SEN_RELEASED;
tgt_p += long_len;
tgt_p += pad_len;
memset(tgt_p, 0xFF, mod_len);
-
tgt_p += mod_len;
memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
pubSec_p = (struct cca_public_sec *) tgt_p;
pubSec_p->modulus_bit_len = 8 * mod_len;
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
static int
-ICAMEX_msg_to_type6MEX_de_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-{
- int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
- unsigned char *tgt_p, *temp;
- struct type6_hdr *tp6Hdr_p;
- struct CPRBX *cprbx_p;
- struct cca_private_ext_ME *key_p;
- mod_len = icaMsg_p->inputdatalength;
- tmp_size = FIXED_TYPE6_ME_LENX + mod_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
- tmp_size += CALLER_HEADER;
- vud_len = 2 + mod_len;
- memset(z90cMsg_p, 0, tmp_size);
-
- tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)tgt_p;
- tp6Hdr_p->ToCardLen1 = total_CPRB_len;
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
-
- tgt_p += sizeof(struct type6_hdr);
- memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
- cprbx_p = (struct CPRBX *) tgt_p;
- cprbx_p->domain = (unsigned short)cdx;
- cprbx_p->req_parml = parmBlock_l;
- cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
-
- tgt_p += sizeof(struct CPRBX);
- memcpy(tgt_p, &static_pkd_function_and_rulesX,
- sizeof(struct function_and_rules_block));
-
- tgt_p += sizeof(struct function_and_rules_block);
- *((short *)tgt_p) = (short) vud_len;
-
- tgt_p += 2;
- if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, mod_len))
- return SEN_USER_ERROR;
-
- tgt_p += mod_len;
- memcpy(tgt_p, &static_T6_keyBlock_hdrX,
- sizeof(struct T6_keyBlock_hdrX));
-
- tgt_p += sizeof(struct T6_keyBlock_hdrX);
- memcpy(tgt_p, &static_pvt_me_key,
- sizeof(struct cca_private_ext_ME));
- key_p = (struct cca_private_ext_ME *)tgt_p;
- temp = key_p->pvtMESec.exponent + sizeof(key_p->pvtMESec.exponent) -
- mod_len;
- if (copy_from_user(temp, icaMsg_p->b_key, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
-
- if (is_common_public_key(temp, mod_len)) {
- PRINTK("Common public key used for modex decrypt\n");
- return SEN_NOT_AVAIL;
- }
- temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus) -
- mod_len;
- if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
- key_p->pubMESec.modulus_bit_len = 8 * mod_len;
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
- return 0;
-}
-static int
-ICAMEX_msg_to_type6MEX_en_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
+ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
+ int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
{
int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
int key_len, i;
struct CPRBX *cprbx_p;
struct cca_public_key *key_p;
struct T6_keyBlock_hdrX *keyb_p;
+
mod_len = icaMsg_p->inputdatalength;
if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
return SEN_RELEASED;
tmp_size = tmp_size + CALLER_HEADER;
vud_len = 2 + mod_len;
memset(z90cMsg_p, 0, tmp_size);
-
tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
tp6Hdr_p = (struct type6_hdr *)tgt_p;
tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
sizeof(static_PKE_function_code));
-
tgt_p += sizeof(struct type6_hdr);
memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
cprbx_p = (struct CPRBX *) tgt_p;
cprbx_p->domain = (unsigned short)cdx;
cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
-
tgt_p += sizeof(struct CPRBX);
memcpy(tgt_p, &static_pke_function_and_rulesX,
sizeof(struct function_and_rules_block));
-
tgt_p += sizeof(struct function_and_rules_block);
-
+
tgt_p += 2;
if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
return SEN_RELEASED;
if (is_empty(tgt_p, mod_len))
return SEN_USER_ERROR;
-
tgt_p -= 2;
*((short *)tgt_p) = (short) vud_len;
tgt_p += vud_len;
keyb_p = (struct T6_keyBlock_hdrX *)tgt_p;
-
tgt_p += sizeof(struct T6_keyBlock_hdrX);
memcpy(tgt_p, &static_public_key, sizeof(static_public_key));
key_p = (struct cca_public_key *)tgt_p;
keyb_p->blen = (unsigned short)key_len;
cprbx_p->req_parml = parmBlock_l;
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
static int
ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
struct cca_token_hdr *keyHdr_p;
struct cca_pvt_ext_CRT_sec *pvtSec_p;
struct cca_public_sec *pubSec_p;
+
mod_len = icaMsg_p->inputdatalength;
short_len = mod_len / 2;
long_len = 8 + short_len;
vud_len = 2 + mod_len;
tmp_size = tmp_size + CALLER_HEADER;
memset(z90cMsg_p, 0, tmp_size);
-
tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
tp6Hdr_p = (struct type6_hdr *)tgt_p;
tp6Hdr_p->ToCardLen1 = total_CPRB_len;
tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
-
tgt_p += sizeof(struct type6_hdr);
cprbx_p = (struct CPRBX *) tgt_p;
memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
cprbx_p->domain = (unsigned short)cdx;
cprbx_p->req_parml = parmBlock_l;
cprbx_p->rpl_msgbl = parmBlock_l;
-
tgt_p += sizeof(struct CPRBX);
memcpy(tgt_p, &static_pkd_function_and_rulesX,
sizeof(struct function_and_rules_block));
-
tgt_p += sizeof(struct function_and_rules_block);
*((short *)tgt_p) = (short) vud_len;
-
tgt_p += 2;
if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
return SEN_RELEASED;
if (is_empty(tgt_p, mod_len))
return SEN_USER_ERROR;
-
tgt_p += mod_len;
tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
temp = tgt_p + 2;
tmp_l -= 2;
*((short *)temp) = (short) tmp_l;
-
tgt_p += sizeof(struct T6_keyBlock_hdr);
keyHdr_p = (struct cca_token_hdr *)tgt_p;
keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
tmp_l -= 4;
keyHdr_p->token_length = tmp_l;
-
tgt_p += sizeof(struct cca_token_hdr);
pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
pvtSec_p->u_len = long_len;
pvtSec_p->mod_len = mod_len;
pvtSec_p->pad_len = pad_len;
-
tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
return SEN_RELEASED;
tgt_p += long_len;
tgt_p += pad_len;
memset(tgt_p, 0xFF, mod_len);
-
tgt_p += mod_len;
memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
pubSec_p = (struct cca_public_sec *) tgt_p;
pubSec_p->modulus_bit_len = 8 * mod_len;
*z90cMsg_l_p = tmp_size - CALLER_HEADER;
+
return 0;
}
+
int
convert_request(unsigned char *buffer, int func, unsigned short function,
int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p)
return ICACRT_msg_to_type6CRT_msgX(
(struct ica_rsa_modexpo_crt *) buffer,
cdx, msg_l_p, (struct type6_msg *) msg_p);
- if (function == PCI_FUNC_KEY_ENCRYPT)
- return ICAMEX_msg_to_type6MEX_en_msgX(
- (struct ica_rsa_modexpo *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p);
else
- return ICAMEX_msg_to_type6MEX_de_msgX(
+ return ICAMEX_msg_to_type6MEX_msgX(
(struct ica_rsa_modexpo *) buffer,
cdx, msg_l_p, (struct type6_msg *) msg_p);
}
+
return 0;
}
+
int
-convert_response(unsigned char *response, int dev_type, unsigned char *buffer,
+convert_response(unsigned char *response, unsigned char *buffer,
int *respbufflen_p, unsigned char *resp_buff)
{
struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer;
unsigned char *src_p, *tgt_p;
struct CPRB *cprb_p;
struct CPRBX *cprbx_p;
+
src_p = 0;
reply_code = 0;
service_rc = 0;
le2toI(cprb_p->ccp_rtcode, &service_rc);
if (service_rc != 0) {
le2toI(cprb_p->ccp_rscode, &service_rs);
- PDEBUG("service rc/rc: %d/%d\n",
- service_rc, service_rs);
+ if ((service_rc == 8) && (service_rs == 66))
+ PDEBUG("8/66 on PCICC\n");
+ else
+ PRINTK("service rc/rs: %d/%d\n",
+ service_rc, service_rs);
rv = 8;
}
src_p = (unsigned char *)cprb_p + sizeof(struct CPRB);
service_rc = (int)cprbx_p->ccp_rtcode;
if (service_rc != 0) {
service_rs = (int) cprbx_p->ccp_rscode;
- PRINTK("service rc:%d; service rs:%d\n",
- service_rc, service_rs);
+ if ((service_rc == 8) && (service_rs == 66))
+ PDEBUG("8/66 on PCIXCC\n");
+ else
+ PRINTK("service rc/rs: %d/%d\n",
+ service_rc, service_rs);
rv = 8;
}
src_p = (unsigned char *)
default:
break;
}
-
-
+
if (rv == 8)
return 8;
if (rv == 4)
default:
return 12;
}
-
+
if (service_rc != 0)
return REC_OPERAND_INV;
+
if ((src_l > icaMsg_p->outputdatalength) ||
(src_l > RESPBUFFSIZE) ||
(src_l <= 0))
return REC_OPERAND_SIZE;
+
PDEBUG("Length returned = %d\n", src_l);
-
tgt_p = resp_buff + icaMsg_p->outputdatalength - src_l;
memcpy(tgt_p, src_p, src_l);
-
if ((t82h_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) {
memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l);
rv = pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l);
*respbufflen_p = icaMsg_p->outputdatalength;
if (*respbufflen_p == 0)
PRINTK("Zero *respbufflen_p\n");
+
return rv;
}
+
*/
#include <asm/uaccess.h> // copy_(from|to)_user
+#include <linux/compat.h>
#include <linux/compiler.h>
#include <linux/delay.h> // mdelay
+#include <linux/init.h>
#include <linux/interrupt.h> // for tasklets
+#include <linux/ioctl32.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/proc_fs.h>
+#include <linux/syscalls.h>
#include <linux/version.h>
#include "z90crypt.h"
#include "z90common.h"
# error "This kernel is too recent: not supported by this file"
#endif
-#define VERSION_Z90MAIN_C "$Revision: 1.15 $"
+#define VERSION_Z90MAIN_C "$Revision: 1.31 $"
-static const char version[] __attribute_used__ =
- "z90crypt.o: z90main.o ("
- "z90main.c " VERSION_Z90MAIN_C "/"
- "z90common.h " VERSION_Z90COMMON_H "/"
- "z90crypt.h " VERSION_Z90CRYPT_H ")";
+static char z90cmain_version[] __initdata =
+ "z90main.o (" VERSION_Z90MAIN_C "/"
+ VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
+
+extern char z90chardware_version[];
/**
* Defaults that may be modified.
#define STAT_FAILED 0x40 // bit 6: this bit is set if the request failed
// before being sent to the hardware.
#define STAT_WRITTEN 0x30 // bits 5-4: work to be done, not sent to device
-#define STAT_QUEUED 0x20 // bits 5-4: work has been sent to a device
+// 0x20 // UNUSED state
#define STAT_READPEND 0x10 // bits 5-4: work done, we're returning data now
#define STAT_NOWORK 0x00 // bits off: no work on any queue
#define STAT_RDWRMASK 0x30 // mask for bits 5-4
enum devstat receive_from_AP(int, int, int, unsigned char *, unsigned char *);
int convert_request(unsigned char *, int, short, int, int, int *,
unsigned char *);
-int convert_response(unsigned char *, int, unsigned char *, int *,
- unsigned char *);
+int convert_response(unsigned char *, unsigned char *, int *, unsigned char *);
/**
* Low level function prototypes
/**
* proc fs definitions
*/
-struct proc_dir_entry *z90crypt_entry;
+static struct proc_dir_entry *z90crypt_entry;
/**
* data structures
static atomic_t total_open;
static atomic_t z90crypt_step;
-struct file_operations z90crypt_fops = {
+static struct file_operations z90crypt_fops = {
.owner = THIS_MODULE,
.read = z90crypt_read,
.write = z90crypt_write,
};
#ifndef Z90CRYPT_USE_HOTPLUG
-struct miscdevice z90crypt_misc_device = {
+static struct miscdevice z90crypt_misc_device = {
.minor = Z90CRYPT_MINOR,
.name = DEV_NAME,
.fops = &z90crypt_fops,
module_param(domain, int, 0);
MODULE_PARM_DESC(domain, "domain index for device");
+#ifdef CONFIG_COMPAT
+/**
+ * ioctl32 conversion routines
+ */
+struct ica_rsa_modexpo_32 { // For 32-bit callers
+ compat_uptr_t inputdata;
+ unsigned int inputdatalength;
+ compat_uptr_t outputdata;
+ unsigned int outputdatalength;
+ compat_uptr_t b_key;
+ compat_uptr_t n_modulus;
+};
+
+static int
+trans_modexpo32(unsigned int fd, unsigned int cmd, unsigned long arg,
+ struct file *file)
+{
+ struct ica_rsa_modexpo_32 *mex32u = compat_ptr(arg);
+ struct ica_rsa_modexpo_32 mex32k;
+ struct ica_rsa_modexpo *mex64;
+ int ret = 0;
+ unsigned int i;
+
+ if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32)))
+ return -EFAULT;
+ mex64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo));
+ if (!access_ok(VERIFY_WRITE, mex64, sizeof(struct ica_rsa_modexpo)))
+ return -EFAULT;
+ if (copy_from_user(&mex32k, mex32u, sizeof(struct ica_rsa_modexpo_32)))
+ return -EFAULT;
+ if (__put_user(compat_ptr(mex32k.inputdata), &mex64->inputdata) ||
+ __put_user(mex32k.inputdatalength, &mex64->inputdatalength) ||
+ __put_user(compat_ptr(mex32k.outputdata), &mex64->outputdata) ||
+ __put_user(mex32k.outputdatalength, &mex64->outputdatalength) ||
+ __put_user(compat_ptr(mex32k.b_key), &mex64->b_key) ||
+ __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus))
+ return -EFAULT;
+ ret = sys_ioctl(fd, cmd, (unsigned long)mex64);
+ if (!ret)
+ if (__get_user(i, &mex64->outputdatalength) ||
+ __put_user(i, &mex32u->outputdatalength))
+ ret = -EFAULT;
+ return ret;
+}
+
+struct ica_rsa_modexpo_crt_32 { // For 32-bit callers
+ compat_uptr_t inputdata;
+ unsigned int inputdatalength;
+ compat_uptr_t outputdata;
+ unsigned int outputdatalength;
+ compat_uptr_t bp_key;
+ compat_uptr_t bq_key;
+ compat_uptr_t np_prime;
+ compat_uptr_t nq_prime;
+ compat_uptr_t u_mult_inv;
+};
+
+static int
+trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
+ struct file *file)
+{
+ struct ica_rsa_modexpo_crt_32 *crt32u = compat_ptr(arg);
+ struct ica_rsa_modexpo_crt_32 crt32k;
+ struct ica_rsa_modexpo_crt *crt64;
+ int ret = 0;
+ unsigned int i;
+
+ if (!access_ok(VERIFY_WRITE, crt32u,
+ sizeof(struct ica_rsa_modexpo_crt_32)))
+ return -EFAULT;
+ crt64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo_crt));
+ if (!access_ok(VERIFY_WRITE, crt64, sizeof(struct ica_rsa_modexpo_crt)))
+ return -EFAULT;
+ if (copy_from_user(&crt32k, crt32u,
+ sizeof(struct ica_rsa_modexpo_crt_32)))
+ return -EFAULT;
+ if (__put_user(compat_ptr(crt32k.inputdata), &crt64->inputdata) ||
+ __put_user(crt32k.inputdatalength, &crt64->inputdatalength) ||
+ __put_user(compat_ptr(crt32k.outputdata), &crt64->outputdata) ||
+ __put_user(crt32k.outputdatalength, &crt64->outputdatalength) ||
+ __put_user(compat_ptr(crt32k.bp_key), &crt64->bp_key) ||
+ __put_user(compat_ptr(crt32k.bq_key), &crt64->bq_key) ||
+ __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime) ||
+ __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime) ||
+ __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv))
+ ret = -EFAULT;
+ if (!ret)
+ ret = sys_ioctl(fd, cmd, (unsigned long)crt64);
+ if (!ret)
+ if (__get_user(i, &crt64->outputdatalength) ||
+ __put_user(i, &crt32u->outputdatalength))
+ ret = -EFAULT;
+ return ret;
+}
+
+static int compatible_ioctls[] = {
+ ICAZ90STATUS, Z90QUIESCE, Z90STAT_TOTALCOUNT, Z90STAT_PCICACOUNT,
+ Z90STAT_PCICCCOUNT, Z90STAT_PCIXCCCOUNT, Z90STAT_REQUESTQ_COUNT,
+ Z90STAT_PENDINGQ_COUNT, Z90STAT_TOTALOPEN_COUNT, Z90STAT_DOMAIN_INDEX,
+ Z90STAT_STATUS_MASK, Z90STAT_QDEPTH_MASK, Z90STAT_PERDEV_REQCNT,
+};
+
+static void z90_unregister_ioctl32s(void)
+{
+ int i;
+
+ unregister_ioctl32_conversion(ICARSAMODEXPO);
+ unregister_ioctl32_conversion(ICARSACRT);
+
+ for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++)
+ unregister_ioctl32_conversion(compatible_ioctls[i]);
+}
+
+static int z90_register_ioctl32s(void)
+{
+ int result, i;
+
+ result = register_ioctl32_conversion(ICARSAMODEXPO, trans_modexpo32);
+ if (result)
+ return result;
+ result = register_ioctl32_conversion(ICARSACRT, trans_modexpo_crt32);
+ if (result)
+ return result;
+
+ for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++) {
+ result = register_ioctl32_conversion(compatible_ioctls[i],NULL);
+ if (result) {
+ z90_unregister_ioctl32s();
+ return result;
+ }
+ }
+ return result;
+}
+#else // !CONFIG_COMPAT
+static inline void z90_unregister_ioctl32s(void)
+{
+}
+
+static inline int z90_register_ioctl32s(void)
+{
+ return 0;
+}
+#endif
+
/**
* The module initialization code.
*/
PRINTKN("Version %d.%d.%d loaded, built on %s %s\n",
z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT,
__DATE__, __TIME__);
+ PRINTKN("%s\n", z90cmain_version);
+ PRINTKN("%s\n", z90chardware_version);
PDEBUG("create_z90crypt (domain index %d) successful.\n",
domain);
} else
reader_timer.expires = jiffies + (READERTIME * HZ / 1000);
add_timer(&reader_timer);
+ if ((result = z90_register_ioctl32s()))
+ goto init_module_cleanup;
+
return 0; // success
init_module_cleanup:
+ z90_unregister_ioctl32s();
+
#ifndef Z90CRYPT_USE_HOTPLUG
if ((nresult = misc_deregister(&z90crypt_misc_device)))
PRINTK("misc_deregister failed with %d.\n", nresult);
PDEBUG("PID %d\n", PID());
+ z90_unregister_ioctl32s();
+
remove_proc_entry("driver/z90crypt", 0);
#ifndef Z90CRYPT_USE_HOTPLUG
del_timer(&cleanup_timer);
destroy_z90crypt();
+
+ PRINTKN("Unloaded.\n");
}
/**
return -EQUIESCE;
private_data_p = kmalloc(sizeof(struct priv_data), GFP_KERNEL);
- if (!private_data_p)
+ if (!private_data_p) {
+ PRINTK("Memory allocate failed\n");
return -ENOMEM;
+ }
memset((void *)private_data_p, 0, sizeof(struct priv_data));
private_data_p->status = STAT_OPEN;
if (count == 0)
return 0;
temp_buff = kmalloc(RESPBUFFSIZE, GFP_KERNEL);
- if (!temp_buff)
+ if (!temp_buff) {
+ PRINTK("Memory allocate failed\n");
return -ENOMEM;
+ }
get_random_bytes(temp_buff, count);
if (copy_to_user(buf, temp_buff, count) != 0) {
}
static inline int
-send_to_crypto_device(unsigned char *psmid, int func, int buff_len,
- unsigned char *buff_ptr, int devType,
- int *devNr_p, unsigned char *reqBuff)
+send_to_crypto_device(struct work_element *we_p)
{
struct caller *caller_p;
struct device *device_p;
int dev_nr;
- if (!reqBuff)
+ if (!we_p->requestptr)
return SEN_FATAL_ERROR;
- caller_p = (struct caller *)reqBuff;
- dev_nr = *devNr_p;
- if (select_device(&devType, &dev_nr) == -1) {
+ caller_p = (struct caller *)we_p->requestptr;
+ dev_nr = we_p->devindex;
+ if (select_device(&we_p->devtype, &dev_nr) == -1) {
if (z90crypt.hdware_info->hdware_mask.st_count != 0)
return SEN_RETRY;
else
return SEN_NOT_AVAIL;
}
- *devNr_p = dev_nr;
+ we_p->devindex = dev_nr;
device_p = z90crypt.device_p[dev_nr];
if (!device_p)
return SEN_NOT_AVAIL;
- if (device_p->dev_type != devType)
+ if (device_p->dev_type != we_p->devtype)
return SEN_RETRY;
if (device_p->dev_caller_count >= device_p->dev_q_depth)
return SEN_QUEUE_FULL;
}
we_p->devindex = -1; // Reset device number
spin_lock_irq(&queuespinlock);
- rv = send_to_crypto_device(we_p->caller_id, we_p->funccode,
- we_p->buff_size, we_p->buffer, we_p->devtype,
- &we_p->devindex, we_p->requestptr);
+ rv = send_to_crypto_device(we_p);
switch (rv) {
case 0:
we_p->requestsent = jiffies;
* function is PCI_FUNC_KEY_ENCRYPT or PCI_FUNC_KEY_DECRYPT
*/
static inline int
-build_caller(char *psmid, int func, short function, int buff_len,
- char *buff_ptr, int dev_type, struct caller *caller_p)
+build_caller(struct work_element *we_p, short function)
{
int rv;
+ struct caller *caller_p = (struct caller *)we_p->requestptr;
- if ((dev_type != PCICC) && (dev_type != PCICA) && (dev_type != PCIXCC))
+ if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) &&
+ (we_p->devtype != PCIXCC))
return SEN_NOT_AVAIL;
- memcpy(caller_p->caller_id, psmid, sizeof(caller_p->caller_id));
+ memcpy(caller_p->caller_id, we_p->caller_id,
+ sizeof(caller_p->caller_id));
caller_p->caller_dev_dep_req_p = caller_p->caller_dev_dep_req;
caller_p->caller_dev_dep_req_l = MAX_RESPONSE_SIZE;
- caller_p->caller_buf_p = buff_ptr;
+ caller_p->caller_buf_p = we_p->buffer;
INIT_LIST_HEAD(&(caller_p->caller_liste));
- rv = convert_request(buff_ptr, func, function, z90crypt.cdx, dev_type,
+ rv = convert_request(we_p->buffer, we_p->funccode, function,
+ z90crypt.cdx, we_p->devtype,
&caller_p->caller_dev_dep_req_l,
caller_p->caller_dev_dep_req_p);
- if (rv)
- PRINTK("Error from convert_request: %d\n", rv);
+ if (rv) {
+ if (rv == SEN_NOT_AVAIL)
+ PDEBUG("request can't be processed on hdwr avail\n");
+ else
+ PRINTK("Error from convert_request: %d\n", rv);
+ }
else
- memcpy(&(caller_p->caller_dev_dep_req_p[4]), psmid, 8);
+ memcpy(&(caller_p->caller_dev_dep_req_p[4]), we_p->caller_id,8);
return rv;
}
}
static inline int
-get_crypto_request_buffer(unsigned char *psmid, int func, int buff_len,
- unsigned char *buff_ptr, int *dev_type_p,
- unsigned char *caller_p)
+get_crypto_request_buffer(struct work_element *we_p)
{
struct ica_rsa_modexpo *mex_p;
struct ica_rsa_modexpo_crt *crt_p;
short function;
int rv;
- mex_p = (struct ica_rsa_modexpo *) buff_ptr;
- crt_p = (struct ica_rsa_modexpo_crt *) buff_ptr;
+ mex_p = (struct ica_rsa_modexpo *) we_p->buffer;
+ crt_p = (struct ica_rsa_modexpo_crt *) we_p->buffer;
- PDEBUG("device type input = %d\n", *dev_type_p);
+ PDEBUG("device type input = %d\n", we_p->devtype);
if (z90crypt.terminating)
return REC_NO_RESPONSE;
-
- temp_buffer = kmalloc(256, GFP_KERNEL);
- if (!temp_buffer) {
- PRINTK("kmalloc for temp_buffer failed!\n");
- return SEN_NOT_AVAIL;
- }
- if (memcmp(psmid, NULL_psmid, 8) == 0) {
+ if (memcmp(we_p->caller_id, NULL_psmid, 8) == 0) {
PRINTK("psmid zeroes\n");
- kfree(temp_buffer);
return SEN_FATAL_ERROR;
}
- if (!buff_ptr) {
+ if (!we_p->buffer) {
PRINTK("buffer pointer NULL\n");
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
- if (!caller_p) {
+ if (!we_p->requestptr) {
PRINTK("caller pointer NULL\n");
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
- if ((*dev_type_p != PCICA) && (*dev_type_p != PCICC) &&
- (*dev_type_p != PCIXCC) && (*dev_type_p != ANYDEV)) {
+ if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) &&
+ (we_p->devtype != PCIXCC) && (we_p->devtype != ANYDEV)) {
PRINTK("invalid device type\n");
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
(mex_p->inputdatalength > MAX_MOD_SIZE)) {
PRINTK("inputdatalength[%d] is not valid\n",
mex_p->inputdatalength);
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
if (mex_p->outputdatalength < mex_p->inputdatalength) {
PRINTK("outputdatalength[%d] < inputdatalength[%d]\n",
mex_p->outputdatalength, mex_p->inputdatalength);
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
if (!mex_p->inputdata || !mex_p->outputdata) {
PRINTK("inputdata[%p] or outputdata[%p] is NULL\n",
mex_p->outputdata, mex_p->inputdata);
- kfree(temp_buffer);
return SEN_USER_ERROR;
}
mex_p->outputdatalength = mex_p->inputdatalength;
rv = 0;
- switch (func) {
+ switch (we_p->funccode) {
case ICARSAMODEXPO:
if (!mex_p->b_key || !mex_p->n_modulus)
rv = SEN_USER_ERROR;
}
break;
default:
- PRINTK("bad func = %d\n", func);
+ PRINTK("bad func = %d\n", we_p->funccode);
rv = SEN_USER_ERROR;
break;
}
- if (rv != 0) {
- kfree(temp_buffer);
+ if (rv != 0)
return rv;
- }
- if (select_device_type(dev_type_p) < 0) {
- kfree(temp_buffer);
+ if (select_device_type(&we_p->devtype) < 0)
return SEN_NOT_AVAIL;
- }
+ temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) +
+ sizeof(struct caller);
if (copy_from_user(temp_buffer, mex_p->inputdata,
- mex_p->inputdatalength) != 0) {
- kfree(temp_buffer);
+ mex_p->inputdatalength) != 0)
return SEN_RELEASED;
- }
function = PCI_FUNC_KEY_ENCRYPT;
- switch (*dev_type_p) {
+ switch (we_p->devtype) {
/* PCICA does everything with a simple RSA mod-expo operation */
case PCICA:
function = PCI_FUNC_KEY_ENCRYPT;
*/
case PCIXCC:
/* Anything less than MIN_MOD_SIZE MUST go to a PCICA */
- if (mex_p->inputdatalength < MIN_MOD_SIZE) {
- kfree(temp_buffer);
+ if (mex_p->inputdatalength < MIN_MOD_SIZE)
return SEN_NOT_AVAIL;
- }
- if (func == ICARSAMODEXPO)
+ if (we_p->funccode == ICARSAMODEXPO)
function = PCI_FUNC_KEY_ENCRYPT;
else
function = PCI_FUNC_KEY_DECRYPT;
case PCICC:
/* Anything less than MIN_MOD_SIZE MUST go to a PCICA */
if (mex_p->inputdatalength < MIN_MOD_SIZE) {
- kfree(temp_buffer);
return SEN_NOT_AVAIL;
}
/* Anythings over MAX_PCICC_MOD_SIZE MUST go to a PCICA */
if (mex_p->inputdatalength > MAX_PCICC_MOD_SIZE) {
- kfree(temp_buffer);
return SEN_NOT_AVAIL;
}
/* PCICC cannot handle input that is is PKCS#1.1 padded */
if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) {
- kfree(temp_buffer);
return SEN_NOT_AVAIL;
}
- if (func == ICARSAMODEXPO) {
+ if (we_p->funccode == ICARSAMODEXPO) {
if (is_PKCS12_padded(temp_buffer,
mex_p->inputdatalength))
function = PCI_FUNC_KEY_ENCRYPT;
break;
}
PDEBUG("function: %04x\n", function);
- rv = build_caller(psmid, func, function, buff_len, buff_ptr,
- *dev_type_p, (struct caller *)caller_p);
+ rv = build_caller(we_p, function);
PDEBUG("rv from build_caller = %d\n", rv);
- kfree(temp_buffer);
return rv;
}
we_p->funccode = funccode;
we_p->devtype = -1;
we_p->audit[0] |= FP_BUFFREQ;
- rv = get_crypto_request_buffer(we_p->caller_id, we_p->funccode,
- we_p->buff_size, we_p->buffer,
- &we_p->devtype, we_p->requestptr);
+ rv = get_crypto_request_buffer(we_p);
switch (rv) {
case 0:
we_p->audit[0] |= FP_BUFFGOT;
rv = -ENODEV;
break;
case SEN_NOT_AVAIL:
+ rv = -EGETBUFF;
+ break;
default:
PRINTK("rv = %d\n", rv);
rv = -EGETBUFF;
unsigned int cmd, unsigned long arg)
{
struct work_element *we_p;
- int keep_work_element;
int rv;
- if ((rv = allocate_work_element(&we_p, private_data_p, pid)))
- return rv;
- if ((rv = z90crypt_prepare(we_p, cmd, (const char *)arg))) {
- PDEBUG("PID %d: rv = %d from z90crypt_prepare\n",
- pid, rv);
+ if ((rv = allocate_work_element(&we_p, private_data_p, pid))) {
+ PDEBUG("PID %d: allocate_work_element returned ENOMEM\n", pid);
return rv;
}
+ if ((rv = z90crypt_prepare(we_p, cmd, (const char *)arg)))
+ PDEBUG("PID %d: rv = %d from z90crypt_prepare\n", pid, rv);
if (!rv)
if ((rv = z90crypt_send(we_p, (const char *)arg)))
PDEBUG("PID %d: rv %d from z90crypt_send.\n", pid, rv);
if (!rv)
rv = z90crypt_process_results(we_p, (char *)arg);
- keep_work_element = 0;
if ((we_p->status[0] & STAT_FAILED)) {
switch (rv) {
/**
case STAT_WRITTEN:
purge_work_element(we_p);
break;
- case STAT_QUEUED:
- keep_work_element = 1;
- break;
case STAT_READPEND:
case STAT_NOWORK:
default:
break;
}
}
- if (!keep_work_element)
- free_page((long)we_p);
+ free_page((long)we_p);
return rv;
}
unsigned int *reqcnt;
struct ica_z90_status *pstat;
int ret, i, loopLim, tempstat;
+ static int deprecated_msg_count = 0;
PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd);
PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n",
if (ret == -ERESTARTSYS)
ret = -ENODEV;
break;
-
+
case Z90STAT_TOTALCOUNT:
tempstat = get_status_totalcount();
if (copy_to_user((int *)arg, &tempstat,sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_PCICACOUNT:
tempstat = get_status_PCICAcount();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_PCICCCOUNT:
tempstat = get_status_PCICCcount();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_PCIXCCCOUNT:
tempstat = get_status_PCIXCCcount();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_REQUESTQ_COUNT:
tempstat = get_status_requestq_count();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_PENDINGQ_COUNT:
tempstat = get_status_pendingq_count();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_TOTALOPEN_COUNT:
tempstat = get_status_totalopen_count();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_DOMAIN_INDEX:
tempstat = get_status_domain_index();
if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
ret = -EFAULT;
break;
-
+
case Z90STAT_STATUS_MASK:
status = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
if (!status) {
ret = -EFAULT;
kfree(status);
break;
-
+
case Z90STAT_QDEPTH_MASK:
qdepth = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
if (!qdepth) {
ret = -EFAULT;
kfree(qdepth);
break;
-
+
case Z90STAT_PERDEV_REQCNT:
reqcnt = kmalloc(sizeof(int) * Z90CRYPT_NUM_APS, GFP_KERNEL);
if (!reqcnt) {
ret = -EFAULT;
kfree(reqcnt);
break;
-
+
/* THIS IS DEPRECATED. USE THE NEW STATUS CALLS */
case ICAZ90STATUS:
- PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
-
+ if (deprecated_msg_count < 100) {
+ PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
+ deprecated_msg_count++;
+ if (deprecated_msg_count == 100)
+ PRINTK("No longer issuing messages related to "
+ "deprecated call to ICAZ90STATUS.\n");
+ }
+
pstat = kmalloc(sizeof(struct ica_z90_status), GFP_KERNEL);
if (!pstat) {
PRINTK("kmalloc for pstat failed!\n");
ret = -ENOMEM;
break;
}
-
+
pstat->totalcount = get_status_totalcount();
pstat->leedslitecount = get_status_PCICAcount();
pstat->leeds2count = get_status_PCICCcount();
pstat->cryptoDomain = get_status_domain_index();
get_status_status_mask(pstat->status);
get_status_qdepth_mask(pstat->qdepth);
-
+
if (copy_to_user((struct ica_z90_status *) arg, pstat,
sizeof(struct ica_z90_status)) != 0)
ret = -EFAULT;
kfree(pstat);
break;
-
+
case Z90QUIESCE:
if (current->euid != 0) {
PRINTK("QUIESCE fails: euid %d\n",
quiesce_z90crypt = 1;
}
break;
-
+
default:
/* user passed an invalid IOCTL number */
PDEBUG("cmd 0x%08X contains invalid ioctl code\n", cmd);
ret = -ENOTTY;
break;
}
-
+
return ret;
}
rv = REC_NO_RESPONSE;
break;
}
- if (dev_ptr->dev_caller_count <= 0)
- rv = REC_USER_GONE;
if (rv)
break;
+ if (dev_ptr->dev_caller_count <= 0) {
+ rv = REC_USER_GONE;
+ break;
+ }
list_for_each_safe(ptr, tptr, &dev_ptr->dev_caller_list) {
caller_p = list_entry(ptr, struct caller, caller_liste);
}
PDEBUG("caller_p after successful receive: %p\n", caller_p);
- rv = convert_response(dev_ptr->dev_resp_p, dev_ptr->dev_type,
+ rv = convert_response(dev_ptr->dev_resp_p,
caller_p->caller_buf_p, buff_len_p, buff);
switch (rv) {
case REC_OPERAND_INV:
remove_device(dev_ptr);
break;
}
-
+
if (caller_p)
unbuild_caller(dev_ptr, caller_p);
rq_p->audit[1] |= FP_REMREQUEST;
if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) {
rq_p->devindex = SHRT2LONG(index);
- rv = send_to_crypto_device(rq_p->caller_id,
- rq_p->funccode,
- rq_p->buff_size,
- rq_p->buffer,
- rq_p->devtype,
- &rq_p->devindex,
- rq_p->requestptr);
+ rv = send_to_crypto_device(rq_p);
if (rv == 0) {
rq_p->requestsent = jiffies;
rq_p->audit[0] |= FP_SENT;
case REC_EVEN_MOD:
case REC_INVALID_PAD:
return 1;
-
+
case REC_BUSY:
case REC_NO_WORK:
case REC_EMPTY:
case REC_RETRY_DEV:
case REC_FATAL_ERROR:
break;
-
+
case REC_NO_RESPONSE:
*workavail_p = 0;
break;
-
+
default:
PRINTK("rc %d, device %d\n", *rc_p, SHRT2LONG(index));
*rc_p = REC_NO_RESPONSE;
PRINTK("Timer pending while modifying config timer\n");
}
-static void
+static void
z90crypt_config_task(unsigned long ptr)
{
int rc;
q_depth = dev_type = k = 0;
for (i = 0; i < z90crypt.max_count; i++) {
hd_stat = HD_NOT_THERE;
- for (j = 0; j < 15; cdx_array[j++] = -1);
+ for (j = 0; j <= 15; cdx_array[j++] = -1);
k = 0;
- for (j = 0; j < 15; j++) {
+ for (j = 0; j <= 15; j++) {
hd_stat = query_online(i, j, MAX_RESET,
&q_depth, &dev_type);
if (hd_stat == HD_TSQ_EXCEPTION) {
if (dev_ptr->dev_type == NILDEV) {
rv = probe_device_type(dev_ptr);
if (rv) {
+ PRINTK("rv = %d from probe_device_type %d\n",
+ rv, index);
kfree(dev_ptr->dev_resp_p);
kfree(dev_ptr);
return rv;
probe_device_type(struct device *devPtr)
{
int rv, dv, i, index, length;
- unsigned char psmid[8], *dyn_testmsg;
-
- dyn_testmsg = kmalloc(384, GFP_KERNEL);
- if (!dyn_testmsg) {
- PRINTK("kmalloc for dyn_testmsg failed in probe_device_type\n");
- /**
- * Strange to return 0, but it will work. Since we didn't
- * update the device type, the next time around, we will
- * reprobe and hopefully have enough memory.
- */
- return 0;
- }
+ unsigned char psmid[8];
+ static unsigned char loc_testmsg[384];
index = devPtr->dev_self_x;
rv = 0;
do {
- memcpy(dyn_testmsg, static_testmsg, sizeof(static_testmsg));
+ memcpy(loc_testmsg, static_testmsg, sizeof(static_testmsg));
length = sizeof(static_testmsg) - 24;
/* the -24 allows for the header */
- dv = send_to_AP(index, z90crypt.cdx, length, dyn_testmsg);
+ dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
if (dv) {
PDEBUG("dv returned by send during probe: %d\n", dv);
if (dv == DEV_SEN_EXCEPTION) {
break;
case DEV_QUEUE_FULL:
rv = SEN_QUEUE_FULL;
- break;
+ break;
default:
+ PRINTK("unknown dv=%d for dev %d\n", dv, index);
+ rv = SEN_NOT_AVAIL;
break;
}
}
if (rv)
break;
rv = (devPtr->dev_resp_p[0] == 0x00) &&
- (devPtr->dev_resp_p[0] == 0x86);
- if (rv) {
+ (devPtr->dev_resp_p[1] == 0x86);
+ if (rv)
devPtr->dev_type = PCICC;
- break;
- }
- devPtr->dev_type = PCICA;
+ else
+ devPtr->dev_type = PCICA;
rv = 0;
} while (0);
/* In a general error case, the card is not marked online */
- kfree(dyn_testmsg);
return rv;
}
<http://www10.software.ibm.com/developerworks/opensource/linux390>
To compile this driver as a module, choose M here: the
- module will be called qeth.
+ module will be called qeth.ko.
comment "Gigabit Ethernet default settings"
obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
obj-$(CONFIG_LCS) += lcs.o cu3088.o
-qeth_mod-objs := qeth.o qeth_mpc.o
-obj-$(CONFIG_QETH) += qeth_mod.o
-
+qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o
+qeth-$(CONFIG_PROC_FS) += qeth_proc.o
+obj-$(CONFIG_QETH) += qeth.o
/*
- * $Id: ctctty.c,v 1.16 2004/02/05 12:39:55 felfert Exp $
+ * $Id: ctctty.c,v 1.17 2004/03/31 17:06:34 ptiedem Exp $
*
* CTC / ESCON network driver, tty interface.
*
ctc_tty_shuttingdown = 1;
spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
tty_unregister_driver(driver->ctc_tty_device);
- kfree(driver);
put_tty_driver(driver->ctc_tty_device);
+ kfree(driver);
driver = NULL;
}
* Frank Pavlic (pavlic@de.ibm.com) and
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*
- * $Revision: 1.72 $ $Date: 2004/03/22 09:34:27 $
+ * $Revision: 1.74 $ $Date: 2004/04/05 00:01:04 $
*
* 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
/**
* initialization string for output
*/
-#define VERSION_LCS_C "$Revision: 1.72 $"
+#define VERSION_LCS_C "$Revision: 1.74 $"
static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
static char debug_buffer[255];
return NULL;
}
+#ifdef CONFIG_IP_MULTICAST
+ INIT_LIST_HEAD(&card->ipm_list);
+#endif
LCS_DBF_HEX(2, setup, &card, sizeof(void*));
return card;
}
if (ccwgdev->state == CCWGROUP_ONLINE) {
lcs_shutdown_device(ccwgdev);
}
- unregister_netdev(card->dev);
+ if (card->dev)
+ unregister_netdev(card->dev);
sysfs_remove_group(&ccwgdev->dev.kobj, &lcs_attr_group);
lcs_cleanup_card(card);
lcs_free_card(card);
/*
- * $Id: netiucv.c,v 1.47 2004/03/22 07:41:42 braunu Exp $
+ * $Id: netiucv.c,v 1.48 2004/04/01 13:42:09 braunu Exp $
*
* IUCV network driver
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * RELEASE-TAG: IUCV network driver $Revision: 1.47 $
+ * RELEASE-TAG: IUCV network driver $Revision: 1.48 $
*
*/
\f
int max_buffsize;
int flags;
fsm_timer timer;
- int retry;
fsm_instance *fsm;
struct net_device *netdev;
struct connection_profile prof;
return test_and_set_bit(0, &((struct netiucv_priv *)dev->priv)->tbusy);
}
-#define SET_DEVICE_START(device, value)
-
static __u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static __u8 iucvMagic[16] = {
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
DEV_STATE_STARTWAIT,
DEV_STATE_STOPWAIT,
DEV_STATE_RUNNING,
- DEV_STATE_STARTRETRY,
/**
* MUST be always the last element!!
*/
"StartWait",
"StopWait",
"Running",
- "StartRetry",
};
/**
pr_debug("%s() called\n", __FUNCTION__);
- fsm_deltimer(&conn->timer);
if (conn && conn->netdev && conn->netdev->priv)
privptr = (struct netiucv_priv *)conn->netdev->priv;
conn->prof.tx_pending--;
memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
NETIUCV_HDRLEN);
- fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
- CONN_EVENT_TIMER, conn);
conn->prof.send_stamp = xtime;
rc = iucv_send(conn->pathid, NULL, 0, 0, 0, 0,
conn->tx_buff->data, conn->tx_buff->len);
if (conn->prof.tx_pending > conn->prof.tx_max_pending)
conn->prof.tx_max_pending = conn->prof.tx_pending;
if (rc != 0) {
- fsm_deltimer(&conn->timer);
conn->prof.tx_pending--;
fsm_newstate(fi, CONN_STATE_IDLE);
if (privptr)
pr_debug("%s() called\n", __FUNCTION__);
+ fsm_deltimer(&conn->timer);
fsm_newstate(fi, CONN_STATE_IDLE);
conn->pathid = eib->ippathid;
netdev->tx_queue_len = eib->ipmsglim;
}
static void
+conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
+{
+ struct iucv_connection *conn = (struct iucv_connection *)arg;
+ __u8 udata[16];
+
+ pr_debug("%s() called\n", __FUNCTION__);
+
+ fsm_deltimer(&conn->timer);
+ iucv_sever(conn->pathid, udata);
+ fsm_newstate(fi, CONN_STATE_STARTWAIT);
+}
+
+static void
conn_action_connsever(fsm_instance *fi, int event, void *arg)
{
struct iucv_event *ev = (struct iucv_event *)arg;
struct iucv_connection *conn = ev->conn;
- // iucv_ConnectionSevered *eib = (iucv_ConnectionSevered *)ev->data;
struct net_device *netdev = conn->netdev;
struct netiucv_priv *privptr = (struct netiucv_priv *)netdev->priv;
- int state = fsm_getstate(fi);
+ __u8 udata[16];
pr_debug("%s() called\n", __FUNCTION__);
- switch (state) {
- case CONN_STATE_SETUPWAIT:
- printk(KERN_INFO "%s: Remote dropped connection\n",
- netdev->name);
- fsm_newstate(fi, CONN_STATE_STOPPED);
- fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
- break;
- case CONN_STATE_IDLE:
- case CONN_STATE_TX:
- printk(KERN_INFO "%s: Remote dropped connection\n",
- netdev->name);
- if (conn->handle)
- iucv_unregister_program(conn->handle);
- conn->handle = 0;
- fsm_newstate(fi, CONN_STATE_STOPPED);
- fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
- break;
- }
+ fsm_deltimer(&conn->timer);
+ iucv_sever(conn->pathid, udata);
+ printk(KERN_INFO "%s: Remote dropped connection\n",
+ netdev->name);
+ fsm_newstate(fi, CONN_STATE_STARTWAIT);
+ fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
}
static void
switch (rc) {
case 0:
conn->netdev->tx_queue_len = msglimit;
+ fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
+ CONN_EVENT_TIMER, conn);
return;
case 11:
printk(KERN_NOTICE
pr_debug("%s() called\n", __FUNCTION__);
+ fsm_deltimer(&conn->timer);
fsm_newstate(fi, CONN_STATE_STOPPED);
netiucv_purge_skb_queue(&conn->collect_queue);
if (conn->handle)
static const fsm_node conn_fsm[] = {
{ CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval },
{ CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start },
- { CONN_STATE_STARTWAIT, CONN_EVENT_START, conn_action_start },
{ CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop },
{ CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop },
{ CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject },
{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack },
+ { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev },
{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever },
{ CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever },
pr_debug("%s() called\n", __FUNCTION__);
- fsm_deltimer(&privptr->timer);
ev.conn = privptr->conn;
fsm_newstate(fi, DEV_STATE_STARTWAIT);
fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev);
ev.conn = privptr->conn;
- fsm_deltimer(&privptr->timer);
fsm_newstate(fi, DEV_STATE_STOPWAIT);
fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev);
}
dev_action_connup(fsm_instance *fi, int event, void *arg)
{
struct net_device *dev = (struct net_device *)arg;
- struct netiucv_priv *privptr = dev->priv;
pr_debug("%s() called\n", __FUNCTION__);
switch (fsm_getstate(fi)) {
- case DEV_STATE_STARTRETRY:
- fsm_deltimer(&privptr->timer);
case DEV_STATE_STARTWAIT:
fsm_newstate(fi, DEV_STATE_RUNNING);
printk(KERN_INFO
static void
dev_action_conndown(fsm_instance *fi, int event, void *arg)
{
- struct net_device *dev = (struct net_device *)arg;
- struct netiucv_priv *privptr = dev->priv;
- struct iucv_event ev;
-
pr_debug("%s() called\n", __FUNCTION__);
switch (fsm_getstate(fi)) {
case DEV_STATE_RUNNING:
fsm_newstate(fi, DEV_STATE_STARTWAIT);
- ev.conn = privptr->conn;
- fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev);
- break;
- case DEV_STATE_STARTWAIT:
- fsm_addtimer(&privptr->timer, NETIUCV_TIMEOUT_5SEC,
- DEV_EVENT_TIMER, dev);
- fsm_newstate(fi, DEV_STATE_STARTRETRY);
break;
case DEV_STATE_STOPWAIT:
fsm_newstate(fi, DEV_STATE_STOPPED);
{ DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop },
{ DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup },
- { DEV_STATE_STARTWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
-
- { DEV_STATE_STARTRETRY, DEV_EVENT_TIMER, dev_action_start },
- { DEV_STATE_STARTRETRY, DEV_EVENT_CONUP, dev_action_connup },
- { DEV_STATE_STARTRETRY, DEV_EVENT_STOP, dev_action_stop },
{ DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
{ DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown },
header.next = 0;
memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN);
- conn->retry = 0;
fsm_newstate(conn->fsm, CONN_STATE_TX);
- fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
- CONN_EVENT_TIMER, conn);
conn->prof.send_stamp = xtime;
rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */,
conn->prof.tx_max_pending = conn->prof.tx_pending;
if (rc != 0) {
struct netiucv_priv *privptr;
- fsm_deltimer(&conn->timer);
fsm_newstate(conn->fsm, CONN_STATE_IDLE);
conn->prof.tx_pending--;
privptr = (struct netiucv_priv *)conn->netdev->priv;
*/
static int
netiucv_open(struct net_device *dev) {
- SET_DEVICE_START(dev, 1);
fsm_event(((struct netiucv_priv *)dev->priv)->fsm, DEV_EVENT_START, dev);
return 0;
}
*/
static int
netiucv_close(struct net_device *dev) {
- SET_DEVICE_START(dev, 0);
fsm_event(((struct netiucv_priv *)dev->priv)->fsm, DEV_EVENT_STOP, dev);
return 0;
}
static void
netiucv_banner(void)
{
- char vbuf[] = "$Revision: 1.47 $";
+ char vbuf[] = "$Revision: 1.48 $";
char *version = vbuf;
if ((version = strchr(version, ':'))) {
+++ /dev/null
-/*
- *
- * linux/drivers/s390/net/qeth.c ($Revision: 1.177 $)
- *
- * Linux on zSeries OSA Express and HiperSockets support
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
- * Cornelia Huck <cohuck@de.ibm.com> (2.5 integration,
- * numerous bugfixes)
- * Frank Pavlic <pavlic@de.ibm.com> (query/purge ARP, SNMP, fixes)
- * Andreas Herrmann <aherrman@de.ibm.com> (bugfixes)
- * Thomas Spatzier <tspat@de.ibm.com> (bugfixes)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- * The driver supports in general all QDIO driven network devices on the
- * Hydra card.
- *
- * For all devices, three channels must be available to the driver. One
- * channel is the read channel, one is the write channel and the third
- * one is the channel used to control QDIO.
- *
- * There are several stages from the channel recognition to the running
- * network device:
- * - The channels are scanned and ordered due to the parameters (see
- * MODULE_PARM_DESC)
- * - The card is hardsetup: this means, that the communication channels
- * are prepared
- * - The card is softsetup: this means, that commands are issued
- * to activate the network parameters
- * - After that, data can flow through the card (transported by QDIO)
- *
- *IPA Takeover:
- * /proc/qeth_ipa_takeover provides the possibility to add and remove
- * certain ranges of IP addresses to the driver. As soon as these
- * addresses have to be set by the driver, the driver uses the OSA
- * Address Takeover mechanism.
- * reading out of the proc-file displays the registered addresses;
- * writing into it changes the information. Only one command at one
- * time must be written into the file. Subsequent commands are ignored.
- * The following commands are available:
- * inv4
- * inv6
- * add4 <ADDR>/<mask bits>[:<interface>]
- * add6 <ADDR>/<mask bits>[:<interface>]
- * del4 <ADDR>/<mask bits>[:<interface>]
- * del6 <ADDR>/<mask bits>[:<interface>]
- * inv4 and inv6 toggle the IPA takeover behaviour for all interfaces:
- * when inv4 was input once, all addresses specified with add4 are not
- * set using the takeover mechanism, but all other IPv4 addresses are set so.
- *
- * add# adds an address range, del# deletes an address range. # corresponds
- * to the IP version (4 or 6).
- * <ADDR> is a 8 or 32byte hexadecimal view of the IP address.
- * <mask bits> specifies the number of bits which are set in the network mask.
- * <interface> is optional and specifies the interface name to which the
- * address range is bound.
- * E. g.
- * add4 C0a80100/24
- * activates all addresses in the 192.168.10 subnet for address takeover.
- * Note, that the address is not taken over before an according ifconfig
- * is executed.
- *
- *VIPA:
- * add_vipa4 <ADDR>:<interface>
- * add_vipa6 <ADDR>:<interface>
- * del_vipa4 <ADDR>:<interface>
- * del_vipa6 <ADDR>:<interface>
- *
- * the specified address is set/unset as VIPA on the specified interface.
- * use the src_vipa package to exploit this out of arbitrary applications.
- *
- *Proxy ARP:
- *
- * add_rxip4 <ADDR>:<interface>
- * add_rxip6 <ADDR>:<interface>
- * del_rxip4 <ADDR>:<interface>
- * del_rxip6 <ADDR>:<interface>
- *
- * the specified address is set/unset as "do not fail a gratuitous ARP"
- * on the specified interface. this can be used to act as a proxy ARP.
- */
-
-static void volatile
-qeth_eyecatcher(void)
-{
- return;
-}
-
-#undef DEBUG
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/ebcdic.h>
-#include <linux/ctype.h>
-#include <asm/semaphore.h>
-#include <asm/timex.h>
-#include <linux/if.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <linux/inetdevice.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/tcp.h>
-#include <linux/icmp.h>
-#include <linux/skbuff.h>
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif /* CONFIG_PROC_FS */
-#include <net/route.h>
-#include <net/arp.h>
-#include <linux/in.h>
-#include <linux/igmp.h>
-#include <net/ip.h>
-#include <asm/uaccess.h>
-#include <linux/init.h>
-#include <net/ipv6.h>
-#include <linux/in6.h>
-#include <net/if_inet6.h>
-#include <net/addrconf.h>
-#include <linux/if_tr.h>
-#include <linux/trdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/reboot.h>
-
-#include <linux/if_vlan.h>
-
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-#include <asm/debug.h>
-
-#include "qeth_mpc.h"
-#include "qeth.h"
-
-/****************** MODULE PARAMETER VARIABLES ********************/
-static int qeth_sparebufs = 0;
-module_param(qeth_sparebufs, int, 0);
-MODULE_PARM_DESC(qeth_sparebufs, "the number of pre-allocated spare buffers "
- "reserved for low memory situations");
-
-/****************** MODULE STUFF **********************************/
-#define VERSION_QETH_C "$Revision: 1.177 $"
-static const char *version = "qeth S/390 OSA-Express driver ("
- VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
- QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
-
-MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
-MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
- "Copyright 2000,2003 IBM Corporation\n");
-MODULE_LICENSE("GPL");
-
-/******************** HERE WE GO ***********************************/
-
-#define PROCFILE_SLEEP_SEM_MAX_VALUE 0
-#define PROCFILE_IOCTL_SEM_MAX_VALUE 3
-static struct semaphore qeth_procfile_ioctl_lock;
-static struct semaphore qeth_procfile_ioctl_sem;
-static struct qeth_card *firstcard = NULL;
-
-static struct sparebufs sparebufs[MAX_SPARE_BUFFERS];
-static int sparebuffer_count;
-
-static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
-
-static spinlock_t setup_lock = SPIN_LOCK_UNLOCKED;
-static rwlock_t list_lock = RW_LOCK_UNLOCKED;
-
-static debug_info_t *qeth_dbf_setup = NULL;
-static debug_info_t *qeth_dbf_data = NULL;
-static debug_info_t *qeth_dbf_misc = NULL;
-static debug_info_t *qeth_dbf_control = NULL;
-static debug_info_t *qeth_dbf_trace = NULL;
-static debug_info_t *qeth_dbf_sense = NULL;
-static debug_info_t *qeth_dbf_qerr = NULL;
-
-static int proc_file_registration;
-#ifdef QETH_PERFORMANCE_STATS
-static int proc_perf_file_registration;
-#define NOW qeth_get_micros()
-#endif /* QETH_PERFORMANCE_STATS */
-static int proc_ipato_file_registration;
-
-static int ipato_inv4 = 0, ipato_inv6 = 0;
-static struct ipato_entry *ipato_entries = NULL;
-static spinlock_t ipato_list_lock = SPIN_LOCK_UNLOCKED;
-
-struct tempinfo{
- char *data;
- int len;
-};
-
-/* thought I could get along without forward declarations...
- * just lazyness here */
-static int qeth_reinit_thread(void *);
-static inline void qeth_schedule_recovery(struct qeth_card *card);
-
-static inline int
-QETH_IP_VERSION(struct sk_buff *skb)
-{
- switch (skb->protocol) {
- case ETH_P_IPV6:
- return 6;
- case ETH_P_IP:
- return 4;
- default:
- return 0;
- }
-}
-
-/* not a macro, as one of the arguments is atomic_read */
-static inline int
-qeth_min(int a, int b)
-{
- if (a < b)
- return a;
- else
- return b;
-}
-
-static inline unsigned int
-qeth_get_millis(void)
-{
- return (int) (get_clock() >> 22); /* time>>12 is microseconds, we
- divide it by 1024 */
-}
-
-#ifdef QETH_PERFORMANCE_STATS
-static inline unsigned int
-qeth_get_micros(void)
-{
- return (int) (get_clock() >> 12);
-}
-#endif /* QETH_PERFORMANCE_STATS */
-
-static void
-qeth_delay_millis(unsigned long msecs)
-{
- unsigned int start;
-
- start = qeth_get_millis();
- while (qeth_get_millis() - start < msecs) ;
-}
-
-static void
-qeth_wait_nonbusy(unsigned int timeout)
-{
- unsigned int start;
- char dbf_text[15];
-
- sprintf(dbf_text, "wtnb%4x", timeout);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- start = qeth_get_millis();
- for (;;) {
- set_task_state(current, TASK_INTERRUPTIBLE);
- if (qeth_get_millis() - start > timeout) {
- goto out;
- }
- schedule_timeout(((start + timeout -
- qeth_get_millis()) >> 10) * HZ);
- }
-out:
- set_task_state(current, TASK_RUNNING);
-}
-
-static void
-qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
-{
- if (dev->type == ARPHRD_IEEE802_TR)
- ip_tr_mc_map(ipm, mac);
- else
- ip_eth_mc_map(ipm, mac);
-}
-
-#define atomic_swap(a,b) xchg((int*)a.counter,b)
-
-static int inline
-my_spin_lock_nonbusy(struct qeth_card *card, spinlock_t * lock)
-{
- for (;;) {
- if (card) {
- if (atomic_read(&card->shutdown_phase))
- return -1;
- }
- if (spin_trylock(lock))
- return 0;
- qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
- }
-}
-
-static int inline
-my_down_trylock_nonbusy(struct qeth_card *card, struct semaphore *sema)
-{
- for (;;) {
- if (card) {
- if (atomic_read(&card->shutdown_phase))
- return -1;
- }
- if (down_trylock(sema))
- return 0;
- qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
- }
-}
-
-
-#ifdef CONFIG_ARCH_S390X
-#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x)
-#else /* CONFIG_ARCH_S390X */
-#define QETH_GET_ADDR(x) ((__u32)x)
-#endif /* CONFIG_ARCH_S390X */
-
-static int
-qeth_does_card_exist(struct qeth_card *card)
-{
- struct qeth_card *c = firstcard;
- int rc = 0;
-
- read_lock(&list_lock);
- while (c) {
- if (c == card) {
- rc = 1;
- break;
- }
- c = c->next;
- }
- read_unlock(&list_lock);
- return rc;
-}
-
-static int
-qeth_getxdigit(char c)
-{
- if ((c >= '0') && (c <= '9'))
- return c - '0';
- if ((c >= 'a') && (c <= 'f'))
- return c + 10 - 'a';
- if ((c >= 'A') && (c <= 'F'))
- return c + 10 - 'A';
- return -1;
-}
-
-static struct qeth_card *
-qeth_get_card_by_name(char *name)
-{
- struct qeth_card *card;
-
- read_lock(&list_lock);
- card = firstcard;
- while (card) {
- if (!strncmp(name, card->dev_name, DEV_NAME_LEN))
- break;
- card = card->next;
- }
- read_unlock(&list_lock);
-
- return card;
-}
-
-static void
-qeth_convert_addr_to_text(int version, __u8 * addr, char *text)
-{
- if (version == 4) {
- sprintf(text, "%02x%02x%02x%02x",
- addr[0], addr[1], addr[2], addr[3]);
- } else {
- sprintf(text, "%02x%02x%02x%02x%02x%02x%02x%02x"
- "%02x%02x%02x%02x%02x%02x%02x%02x",
- addr[0], addr[1], addr[2], addr[3],
- addr[4], addr[5], addr[6], addr[7],
- addr[8], addr[9], addr[10], addr[11],
- addr[12], addr[13], addr[14], addr[15]);
- }
-}
-
-static int
-qeth_convert_text_to_addr(int version, char *text, __u8 * addr)
-{
- int olen = (version == 4) ? 4 : 16;
-
- while (olen--) {
- if ((!isxdigit(*text)) || (!isxdigit(*(text + 1))))
- return -EINVAL;
- *addr =
- (qeth_getxdigit(*text) << 4) + qeth_getxdigit(*(text + 1));
- addr++;
- text += 2;
- }
- return 0;
-}
-
-static void
-qeth_add_ipato_entry(int version, __u8 * addr, int mask_bits, char *dev_name)
-{
- struct ipato_entry *entry, *e;
- int len = (version == 4) ? 4 : 16;
-
- entry =
- (struct ipato_entry *) kmalloc(sizeof (struct ipato_entry),
- GFP_KERNEL);
- if (!entry) {
- PRINT_ERR("not enough memory for ipato allocation\n");
- return;
- }
- entry->version = version;
- memcpy(entry->addr, addr, len);
- if (dev_name) {
- strncpy(entry->dev_name, dev_name, DEV_NAME_LEN);
- if (qeth_get_card_by_name(dev_name)->options.ena_ipat !=
- ENABLE_TAKEOVER)
- PRINT_WARN("IP takeover is not enabled on %s! "
- "Ignoring line\n", dev_name);
- } else
- memset(entry->dev_name, 0, DEV_NAME_LEN);
- entry->mask_bits = mask_bits;
- entry->next = NULL;
-
- spin_lock(&ipato_list_lock);
- if (ipato_entries) {
- e = ipato_entries;
- while (e) {
- if ((e->version == version) &&
- (e->mask_bits == mask_bits) &&
- (((dev_name) && !strncmp(e->dev_name, dev_name,
- DEV_NAME_LEN)) ||
- (!dev_name)) && (!memcmp(e->addr, addr, len))) {
- PRINT_INFO("ipato to be added does already "
- "exist\n");
- kfree(entry);
- goto out;
- }
- if (e->next)
- e = e->next;
- else
- break;
- }
- e->next = entry;
- } else
- ipato_entries = entry;
- out:
- spin_unlock(&ipato_list_lock);
-}
-
-static void
-qeth_del_ipato_entry(int version, __u8 * addr, int mask_bits, char *dev_name)
-{
- struct ipato_entry *e, *e_before;
- int len = (version == 4) ? 4 : 16;
- int found = 0;
-
- spin_lock(&ipato_list_lock);
- e = ipato_entries;
- if ((e->version == version) &&
- (e->mask_bits == mask_bits) && (!memcmp(e->addr, addr, len))) {
- ipato_entries = e->next;
- kfree(e);
- } else
- while (e) {
- e_before = e;
- e = e->next;
- if (!e)
- break;
- if ((e->version == version) &&
- (e->mask_bits == mask_bits) &&
- (((dev_name) && !strncmp(e->dev_name, dev_name,
- DEV_NAME_LEN)) ||
- (!dev_name)) && (!memcmp(e->addr, addr, len))) {
- e_before->next = e->next;
- kfree(e);
- found = 1;
- break;
- }
- }
- if (!found)
- PRINT_INFO("ipato to be deleted does not exist\n");
- spin_unlock(&ipato_list_lock);
-}
-
-static void
-qeth_convert_addr_to_bits(__u8 * addr, char *bits, int len)
-{
- int i, j;
- __u8 octet;
-
- for (i = 0; i < len; i++) {
- octet = addr[i];
- for (j = 7; j >= 0; j--) {
- bits[i * 8 + j] = (octet & 1) ? 1 : 0;
- octet >>= 1;
- }
- }
-}
-
-static int
-qeth_is_ipa_covered_by_ipato_entries(int version, __u8 * addr,
- struct qeth_card *card)
-{
- char *memarea, *addr_bits, *entry_bits;
- int len = (version == 4) ? 4 : 16;
- int invert = (version == 4) ? ipato_inv4 : ipato_inv6;
- int result = 0;
- struct ipato_entry *e;
-
- if (card->options.ena_ipat != ENABLE_TAKEOVER) {
- return 0;
- }
-
- memarea = kmalloc(256, GFP_KERNEL);
- if (!memarea) {
- PRINT_ERR("not enough memory to check out whether to "
- "use ipato\n");
- return 0;
- }
- addr_bits = memarea;
- entry_bits = memarea + 128;
- qeth_convert_addr_to_bits(addr, addr_bits, len);
- e = ipato_entries;
- while (e) {
- qeth_convert_addr_to_bits(e->addr, entry_bits, len);
- if ((!memcmp(addr_bits, entry_bits,
- __min(len * 8, e->mask_bits))) &&
- ((e->dev_name[0] &&
- (!strncmp(e->dev_name, card->dev_name, DEV_NAME_LEN))) ||
- (!e->dev_name[0]))) {
- result = 1;
- break;
- }
- e = e->next;
- }
-
- kfree(memarea);
- if (invert)
- return !result;
- else
- return result;
-}
-
-static void
-qeth_set_dev_flag_running(struct qeth_card *card)
-{
- if (card) {
- card->dev->flags |= IFF_RUNNING;
- }
-}
-
-static void
-qeth_set_dev_flag_norunning(struct qeth_card *card)
-{
- if (card) {
- card->dev->flags &= ~IFF_RUNNING;
- }
-}
-
-static void
-qeth_restore_dev_flag_state(struct qeth_card *card)
-{
- if (card) {
- if (card->saved_dev_flags & IFF_RUNNING)
- card->dev->flags |= IFF_RUNNING;
- else
- card->dev->flags &= ~IFF_RUNNING;
- }
-}
-
-static void
-qeth_save_dev_flag_state(struct qeth_card *card)
-{
- if (card) {
- card->saved_dev_flags = card->dev->flags & IFF_RUNNING;
- }
-}
-
-static int
-qeth_open(struct net_device *dev)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_CARD2(0, trace, "open", card);
- QETH_DBF_CARD2(0, setup, "open", card);
-
- qeth_save_dev_flag_state(card);
-
- netif_start_queue(dev);
- atomic_set(&((struct qeth_card *) dev->priv)->is_open, 1);
-
- return 0;
-}
-
-static int
-qeth_set_config(struct net_device *dev, struct ifmap *map)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *)dev->priv;
- QETH_DBF_CARD3(0, trace, "nscf", card);
-
- return -EOPNOTSUPP;
-}
-
-static int
-qeth_is_multicast_skb_at_all(struct sk_buff *skb, int version)
-{
- int i;
- struct qeth_card *card;
-
- i = RTN_UNSPEC;
- card = (struct qeth_card *)skb->dev->priv;
- if (skb->dst && skb->dst->neighbour) {
- i = skb->dst->neighbour->type;
- return ((i == RTN_BROADCAST) ||
- (i == RTN_MULTICAST) || (i == RTN_ANYCAST)) ? i : 0;
- }
- /* ok, we've to try it somehow else */
- if (version == 4) {
- return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
- } else if (version == 6) {
- return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0;
- }
- if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6)) {
- i = RTN_BROADCAST;
- } else {
- __u16 hdr_mac;
-
- hdr_mac = *((__u16*)skb->nh.raw);
- /* tr multicast? */
- switch (card->link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- if ((hdr_mac == QETH_TR_MAC_NC) ||
- (hdr_mac == QETH_TR_MAC_C))
- i = RTN_MULTICAST;
- break;
- /* eth or so multicast? */
- default:
- if ((hdr_mac == QETH_ETH_MAC_V4) ||
- (hdr_mac == QETH_ETH_MAC_V6))
- i = RTN_MULTICAST;
- }
- }
- return ((i == RTN_BROADCAST)||
- (i == RTN_MULTICAST)||
- (i == RTN_ANYCAST)) ? i : 0;
-}
-
-static int
-qeth_get_prioqueue(struct qeth_card *card, struct sk_buff *skb,
- int multicast, int version)
-{
- if (!version && (card->type == QETH_CARD_TYPE_OSAE))
- return QETH_DEFAULT_QUEUE;
- switch (card->no_queues) {
- case 1:
- return 0;
- case 4:
- if (card->is_multicast_different) {
- if (multicast) {
- return card->is_multicast_different &
- (card->no_queues - 1);
- } else {
- return 0;
- }
- }
- if (card->options.do_prio_queueing) {
- if (version == 4) {
- if (card->options.do_prio_queueing ==
- PRIO_QUEUEING_TOS) {
- if (skb->nh.iph->tos &
- IP_TOS_NOTIMPORTANT) {
- return 3;
- }
- if (skb->nh.iph->tos & IP_TOS_LOWDELAY) {
- return 0;
- }
- if (skb->nh.iph->tos &
- IP_TOS_HIGHTHROUGHPUT) {
- return 1;
- }
- if (skb->nh.iph->tos &
- IP_TOS_HIGHRELIABILITY) {
- return 2;
- }
- return QETH_DEFAULT_QUEUE;
- }
- if (card->options.do_prio_queueing ==
- PRIO_QUEUEING_PREC) {
- return 3 - (skb->nh.iph->tos >> 6);
- }
- } else if (version == 6) {
- /********************
- ********************
- *TODO: IPv6!!!
- ********************/
- }
- return card->options.default_queue;
- } else
- return card->options.default_queue;
- default:
- return 0;
- }
-}
-
-static void
-qeth_wakeup(struct qeth_card *card)
-{
- QETH_DBF_CARD5(0, trace, "wkup", card);
-
- atomic_set(&card->data_has_arrived, 1);
- wake_up(&card->wait_q);
-}
-
-static int
-qeth_check_idx_response(unsigned char *buffer)
-{
- if (!buffer)
- return 0;
- if ((buffer[2] & 0xc0) == 0xc0) {
- return -EIO;
- }
- return 0;
-}
-
-static int
-qeth_get_cards_problem(struct ccw_device *cdev, unsigned char *buffer,
- int dstat, int cstat, int rqparam,
- char *irb, char *sense)
-{
- char dbf_text[15];
- int problem = 0;
- struct qeth_card *card;
-
- card = CARD_FROM_CDEV(cdev);
-
- if (atomic_read(&card->shutdown_phase))
- return 0;
- if (dstat & DEV_STAT_UNIT_CHECK) {
- if (sense[SENSE_RESETTING_EVENT_BYTE] &
- SENSE_RESETTING_EVENT_FLAG) {
- QETH_DBF_CARD1(0, trace, "REVN", card);
- problem = PROBLEM_RESETTING_EVENT_INDICATOR;
- goto out;
- }
- if (sense[SENSE_COMMAND_REJECT_BYTE] &
- SENSE_COMMAND_REJECT_FLAG) {
- QETH_DBF_CARD1(0, trace, "CREJ", card);
- problem = PROBLEM_COMMAND_REJECT;
- goto out;
- }
- if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) {
- QETH_DBF_CARD1(0, trace, "AFFE", card);
- problem = PROBLEM_AFFE;
- goto out;
- }
- if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) {
- QETH_DBF_CARD1(0, trace, "ZSNS", card);
- problem = PROBLEM_ZERO_SENSE_DATA;
- goto out;
- }
- QETH_DBF_CARD1(0, trace, "GCHK", card);
- problem = PROBLEM_GENERAL_CHECK;
- goto out;
- }
- if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
- SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
- SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
- QETH_DBF_TEXT1(0, trace, "GCHK");
- QETH_DBF_TEXT1(0, trace, cdev->dev.bus_id);
- QETH_DBF_HEX1(0, misc, irb, __max(QETH_DBF_MISC_LEN, 64));
- PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x, "
- "rqparam=x%x\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- HEXDUMP16(WARN, "irb: ", irb);
- HEXDUMP16(WARN, "irb: ", ((char *) irb) + 32);
- problem = PROBLEM_GENERAL_CHECK;
- goto out;
- }
- if (qeth_check_idx_response(buffer)) {
- PRINT_WARN("received an IDX TERMINATE on device %s "
- "with cause code 0x%02x%s\n",
- CARD_BUS_ID(card), buffer[4],
- (buffer[4] ==
- 0x22) ? " -- try another portname" : "");
- QETH_DBF_CARD1(0, trace, "RTRM", card);
- problem = PROBLEM_RECEIVED_IDX_TERMINATE;
- goto out;
- }
- if (IS_IPA(buffer) && !IS_IPA_REPLY(buffer)) {
- if (*(PDU_ENCAPSULATION(buffer)) == IPA_CMD_STOPLAN) {
- atomic_set(&card->is_startlaned, 0);
- /* we don't do a netif_stop_queue(card->dev);
- we better discard all packets --
- the outage could take longer */
- PRINT_WARN("Link failure on %s (CHPID 0x%X) -- "
- "there is a network problem or someone "
- "pulled the cable or disabled the port."
- "Discarding outgoing packets.\n",
- card->dev_name, card->chpid);
- QETH_DBF_CARD1(0, trace, "CBOT", card);
- qeth_set_dev_flag_norunning(card);
- problem = 0;
- goto out;
- }
- if (*(PDU_ENCAPSULATION(buffer)) == IPA_CMD_STARTLAN) {
- if (!atomic_read(&card->is_startlaned)) {
- atomic_set(&card->is_startlaned, 1);
- problem = PROBLEM_CARD_HAS_STARTLANED;
- }
- goto out;
- }
- if (*(PDU_ENCAPSULATION(buffer)) == IPA_CMD_REGISTER_LOCAL_ADDR)
- QETH_DBF_CARD3(0, trace, "irla", card);
- if (*(PDU_ENCAPSULATION(buffer)) ==
- IPA_CMD_UNREGISTER_LOCAL_ADDR)
- QETH_DBF_CARD3(0, trace, "irla", card);
- PRINT_WARN("probably a problem on %s: received data is IPA, "
- "but not a reply: command=0x%x\n", card->dev_name,
- *(PDU_ENCAPSULATION(buffer) + 1));
- QETH_DBF_CARD1(0, trace, "INRP", card);
- goto out;
- }
- /* no probs */
-out:
- if (problem) {
- QETH_DBF_CARD3(0, trace, "gcpr", card);
- sprintf(dbf_text, "%2x%2x%4x", dstat, cstat, problem);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "%8x", rqparam);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- if (buffer)
- QETH_DBF_HEX3(0, trace, &buffer, sizeof (void *));
- QETH_DBF_HEX3(0, trace, &irb, sizeof (void *));
- QETH_DBF_HEX3(0, trace, &sense, sizeof (void *));
- }
- atomic_set(&card->problem, problem);
- return problem;
-}
-
-static void
-qeth_issue_next_read(struct qeth_card *card)
-{
- int result, result2;
- char dbf_text[15];
-
- QETH_DBF_CARD5(0, trace, "isnr", card);
-
- /* set up next read ccw */
- memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1));
- card->dma_stuff->read_ccw.count = QETH_BUFSIZE;
- /* recbuf is not yet used by read channel program */
- card->dma_stuff->read_ccw.cda = QETH_GET_ADDR(card->dma_stuff->recbuf);
-
- /*
- * we don't spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)),flags), as
- * we are only called in the interrupt handler
- */
- result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw,
- MPC_SETUP_STATE, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 =
- ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw,
- MPC_SETUP_STATE, 0, 0);
- PRINT_WARN("read handler on device %s, read: ccw_device_start "
- "returned %i, next try returns %i\n",
- CARD_BUS_ID(card), result, result2);
- QETH_DBF_CARD1(0, trace, "IsNR", card);
- sprintf(dbf_text, "%04x%04x", (__s16) result, (__s16) result2);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- }
-}
-
-static int
-qeth_is_to_recover(struct qeth_card *card, int problem)
-{
- switch (problem) {
- case PROBLEM_CARD_HAS_STARTLANED:
- return 1;
- case PROBLEM_RECEIVED_IDX_TERMINATE:
- if (atomic_read(&card->in_recovery)) {
- return 1;
- } else {
- qeth_set_dev_flag_norunning(card);
- return 0;
- }
- case PROBLEM_ACTIVATE_CHECK_CONDITION:
- return 1;
- case PROBLEM_RESETTING_EVENT_INDICATOR:
- return 1;
- case PROBLEM_COMMAND_REJECT:
- return 0;
- case PROBLEM_ZERO_SENSE_DATA:
- return 0;
- case PROBLEM_GENERAL_CHECK:
- return 1;
- case PROBLEM_BAD_SIGA_RESULT:
- return 1;
- case PROBLEM_USER_TRIGGERED_RECOVERY:
- return 1;
- case PROBLEM_AFFE:
- return 1;
- case PROBLEM_MACHINE_CHECK:
- return 1;
- case PROBLEM_TX_TIMEOUT:
- return 1;
- }
- return 0;
-}
-
-static int
-qeth_get_spare_buf(void)
-{
- int i = 0;
- char dbf_text[15];
-
- while (i < sparebuffer_count) {
- if (!atomic_compare_and_swap(SPAREBUF_FREE, SPAREBUF_USED,
- &sparebufs[i].status)) {
- sprintf(dbf_text, "gtspb%3x", i);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- return i;
- }
- i++;
- }
- QETH_DBF_TEXT3(0, trace, "nospbuf");
-
- return -1;
-}
-
-static void
-qeth_put_spare_buf(int no)
-{
- char dbf_text[15];
-
- sprintf(dbf_text, "ptspb%3x", no);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- atomic_set(&sparebufs[no].status, SPAREBUF_FREE);
-}
-
-static inline void
-qeth_put_buffer_pool_entry(struct qeth_card *card, int entry_no)
-{
- if (entry_no & SPAREBUF_MASK)
- qeth_put_spare_buf(entry_no & (~SPAREBUF_MASK));
- else
- card->inbound_buffer_pool_entry_used[entry_no] = BUFFER_UNUSED;
-}
-
-static inline int
-qeth_get_empty_buffer_pool_entry(struct qeth_card *card)
-{
- int i;
- int max_buffers = card->options.inbound_buffer_count;
-
- for (i = 0; i < max_buffers; i++) {
- if (xchg((int *) &card->inbound_buffer_pool_entry_used[i],
- BUFFER_USED) == BUFFER_UNUSED)
- return i;
- }
- return -1;
-}
-
-static inline void
-qeth_clear_input_buffer(struct qeth_card *card, int bufno)
-{
- struct qdio_buffer *buffer;
- int i;
- int elements, el_m_1;
- char dbf_text[15];
-
- QETH_DBF_CARD6(0, trace, "clib", card);
- sprintf(dbf_text, "bufno%3x", bufno);
- QETH_DBF_TEXT6(0, trace, dbf_text);
-
- buffer = &card->inbound_qdio_buffers[bufno];
- elements = BUFFER_MAX_ELEMENTS;
- el_m_1 = elements - 1;
-
- for (i = 0; i < elements; i++) {
- if (i == el_m_1)
- buffer->element[i].flags = SBAL_FLAGS_LAST_ENTRY;
- else
- buffer->element[i].flags = 0;
-
- buffer->element[i].length = PAGE_SIZE;
- buffer->element[i].addr = INBOUND_BUFFER_POS(card, bufno, i);
- }
-}
-
-static void
-qeth_queue_input_buffer(struct qeth_card *card, int bufno,
- unsigned int under_int)
-{
- int count = 0, start = 0, stop = 0, pos;
- int result;
- int cnt1, cnt2 = 0;
- int wrapped = 0;
- int i;
- int requeue_counter;
- char dbf_text[15];
- int no;
-
- QETH_DBF_CARD5(0, trace, "qibf", card);
- sprintf(dbf_text, "%4x%4x", under_int, bufno);
- QETH_DBF_TEXT5(0, trace, dbf_text);
- atomic_inc(&card->requeue_counter);
- if (atomic_read(&card->requeue_counter) <= QETH_REQUEUE_THRESHOLD)
- return;
-
- if (!spin_trylock(&card->requeue_input_lock)) {
- QETH_DBF_CARD5(0, trace, "qibl", card);
- return;
- }
- requeue_counter = atomic_read(&card->requeue_counter);
- pos = atomic_read(&card->requeue_position);
-
- start = pos;
- /*
- * omit the situation with 128 simultaneously
- * enqueued buffers, as then we can't benefit from PCI
- * avoidance anymore -- therefore we let count not grow as
- * big as requeue_counter
- */
- while ((!atomic_read(&card->inbound_buffer_refcnt[pos])) &&
- (count < requeue_counter - 1)) {
- no = qeth_get_empty_buffer_pool_entry(card);
- if (no == -1) {
- if (count)
- break;
- no = qeth_get_spare_buf();
- if (no == -1) {
- PRINT_ERR("%s: no more input buffers "
- "available! Inbound traffic could "
- "be lost! Try increasing the bufcnt "
- "parameter\n",
- card->dev_name);
- QETH_DBF_CARD2(1, trace, "QINB", card);
- goto out;
- }
- card->inbound_buffer_entry_no[pos] =
- no | SPAREBUF_MASK;
- }
- card->inbound_buffer_entry_no[pos] = no;
- atomic_set(&card->inbound_buffer_refcnt[pos], 1);
- count++;
- if (pos >= QDIO_MAX_BUFFERS_PER_Q - 1) {
- pos = 0;
- wrapped = 1;
- } else
- pos++;
- }
- /* stop points to the position after the last element */
- stop = pos;
-
- QETH_DBF_CARD3(0, trace, "qibi", card);
- sprintf(dbf_text, "%4x", requeue_counter);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", start, stop);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- if (wrapped) {
- cnt1 = QDIO_MAX_BUFFERS_PER_Q - start;
- cnt2 = stop;
- } else {
- cnt1 = count;
- /* cnt2 is already set to 0 */
- }
-
- atomic_sub(count, &card->requeue_counter);
- /*
- * this is the only place where card->requeue_position is
- * written to, so that's ok (as it is in a lock)
- */
- atomic_set(&card->requeue_position,
- (atomic_read(&card->requeue_position) + count)
- & (QDIO_MAX_BUFFERS_PER_Q - 1));
-
- if (cnt1) {
- for (i = start; i < start + cnt1; i++) {
- qeth_clear_input_buffer(card, i);
- }
- result = do_QDIO(CARD_DDEV(card),
- QDIO_FLAG_SYNC_INPUT | under_int,
- 0, start, cnt1, NULL);
- if (result) {
- PRINT_WARN("qeth_queue_input_buffer's "
- "do_QDIO returnd %i (device %s)\n",
- result, CARD_DDEV_ID(card));
- QETH_DBF_CARD1(0, trace, "QIDQ", card);
- sprintf(dbf_text, "%4x%4x", result, requeue_counter);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", start, cnt1);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- }
- }
- if (cnt2) {
- for (i = 0; i < cnt2; i++) {
- qeth_clear_input_buffer(card, i);
- }
- result = do_QDIO(CARD_DDEV(card),
- QDIO_FLAG_SYNC_INPUT | under_int, 0,
- 0, cnt2, NULL);
- if (result) {
- PRINT_WARN("qeth_queue_input_buffer's "
- "do_QDIO returnd %i (device %s)\n",
- result, CARD_DDEV_ID(card));
- QETH_DBF_CARD1(0, trace, "QIDQ", card);
- sprintf(dbf_text, "%4x%4x", result, requeue_counter);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", 0, cnt2);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- }
- }
-out:
- spin_unlock(&card->requeue_input_lock);
-
-}
-
-static inline struct sk_buff *
-qeth_get_skb(unsigned int len)
-{
- struct sk_buff *skb;
-
-#ifdef QETH_VLAN
- skb = dev_alloc_skb(len + VLAN_HLEN);
- if (skb)
- skb_reserve(skb, VLAN_HLEN);
-#else /* QETH_VLAN */
- skb = dev_alloc_skb(len);
-#endif /* QETH_VLAN */
- return skb;
-}
-
-static inline struct sk_buff *
-qeth_get_next_skb(struct qeth_card *card,
- int *element_ptr, int *pos_in_el_ptr,
- void **hdr_ptr, struct qdio_buffer *buffer)
-{
- int length;
- char *data_ptr;
- int step, len_togo, element, pos_in_el;
- int curr_len;
- int max_elements;
- struct sk_buff *skb;
- char dbf_text[15];
-
- max_elements = BUFFER_MAX_ELEMENTS;
-
-#define SBALE_LEN(x) ((x>=max_elements)?0:(buffer->element[x].length))
-#define SBALE_ADDR(x) (buffer->element[x].addr)
-
- element = *element_ptr;
-
- if (element >= max_elements) {
- PRINT_WARN("device %s: error in interpreting buffer (data "
- "too long), %i elements.\n",
- CARD_BUS_ID(card), element);
- QETH_DBF_CARD0(0, trace, "IEDL", card);
- sprintf(dbf_text, "%4x%4x", *element_ptr, *pos_in_el_ptr);
- QETH_DBF_TEXT0(1, trace, dbf_text);
- QETH_DBF_HEX0(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX0(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- return NULL;
- }
-
- pos_in_el = *pos_in_el_ptr;
-
- curr_len = SBALE_LEN(element);
- if (curr_len > PAGE_SIZE) {
- PRINT_WARN("device %s: bad element length in element %i: "
- "0x%x\n", CARD_BUS_ID(card), element, curr_len);
- QETH_DBF_CARD0(0, trace, "BELN", card);
- sprintf(dbf_text, "%4x", curr_len);
- QETH_DBF_TEXT0(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", *element_ptr, *pos_in_el_ptr);
- QETH_DBF_TEXT0(1, trace, dbf_text);
- QETH_DBF_HEX0(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX0(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- return NULL;
- }
- /* header fits in current element? */
- if (curr_len < pos_in_el + QETH_HEADER_SIZE) {
- if (!pos_in_el) {
- QETH_DBF_CARD6(0, trace, "gnmh", card);
- return NULL; /* no more data in buffer */
- }
- /* set hdr to next element */
- element++;
- pos_in_el = 0;
- curr_len = SBALE_LEN(element);
- /* does it fit in there? */
- if (curr_len < QETH_HEADER_SIZE) {
- QETH_DBF_CARD6(0, trace, "gdnf", card);
- return NULL;
- }
- }
-
- *hdr_ptr = SBALE_ADDR(element) + pos_in_el;
-
- length = *(__u16 *) ((char *) (*hdr_ptr) + QETH_HEADER_LEN_POS);
-
- QETH_DBF_CARD6(0, trace, "gdHd", card);
- QETH_DBF_HEX6(0, trace, hdr_ptr, sizeof (void *));
-
- pos_in_el += QETH_HEADER_SIZE;
- if (curr_len <= pos_in_el) {
- /* switch to next element for data */
- pos_in_el = 0;
- element++;
- curr_len = SBALE_LEN(element);
- if (!curr_len) {
- PRINT_WARN("device %s: inb. buffer with more headers "
- "than data areas (%i elements).\n",
- CARD_BUS_ID(card), element);
- QETH_DBF_CARD0(0, trace, "IEMH", card);
- sprintf(dbf_text, "%2x%2x%4x", element, *element_ptr,
- *pos_in_el_ptr);
- QETH_DBF_TEXT0(1, trace, dbf_text);
- QETH_DBF_HEX0(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX0(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- return NULL;
- }
- }
-
- data_ptr = SBALE_ADDR(element) + pos_in_el;
-
- if (card->options.fake_ll == FAKE_LL) {
- skb = qeth_get_skb(length + QETH_FAKE_LL_LEN);
- if (!skb)
- goto nomem;
- skb_pull(skb, QETH_FAKE_LL_LEN);
- } else {
- skb = qeth_get_skb(length);
- if (!skb)
- goto nomem;
- }
-
- QETH_DBF_HEX6(0, trace, &data_ptr, sizeof (void *));
- QETH_DBF_HEX6(0, trace, &skb, sizeof (void *));
-
- len_togo = length;
- while (1) {
- step = qeth_min(len_togo, curr_len - pos_in_el);
- if (!step) {
- PRINT_WARN("device %s: unexpected end of buffer, "
- "length of element %i is 0. Discarding "
- "packet.\n",
- CARD_BUS_ID(card), element);
- QETH_DBF_CARD0(0, trace, "IEUE", card);
- sprintf(dbf_text, "%2x%2x%4x", element, *element_ptr,
- *pos_in_el_ptr);
- QETH_DBF_TEXT0(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", len_togo, step);
- QETH_DBF_TEXT0(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", curr_len, pos_in_el);
- QETH_DBF_TEXT0(1, trace, dbf_text);
- QETH_DBF_HEX0(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX0(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- dev_kfree_skb_irq(skb);
- return NULL;
- }
- memcpy(skb_put(skb, step), data_ptr, step);
- len_togo -= step;
- if (len_togo) {
- pos_in_el = 0;
- element++;
- curr_len = SBALE_LEN(element);
- data_ptr = SBALE_ADDR(element);
- } else {
-#ifdef QETH_INBOUND_PACKING_1_PACKET_PER_SBALE
- element++;
- /* we don't need to calculate curr_len */
- pos_in_el = 0;
-#else /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
- pos_in_el += step;
-#endif /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
- break;
- }
- }
-
- sprintf(dbf_text, "%4x%4x", element, pos_in_el);
- QETH_DBF_TEXT6(0, trace, dbf_text);
-
- *element_ptr = element;
- *pos_in_el_ptr = pos_in_el;
-
- return skb;
-
-nomem:
- if (net_ratelimit()) {
- PRINT_WARN("no memory for packet from %s\n", card->dev_name);
- }
- QETH_DBF_CARD0(0, trace, "NOMM", card);
- return NULL;
-}
-
-static inline void
-__qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
- void *hdr_ptr)
-{
- skb->mac.raw = skb->data - QETH_FAKE_LL_LEN;
- switch (skb->pkt_type) {
- case PACKET_MULTICAST:
- switch (skb->protocol) {
-#ifdef QETH_IPV6
- case __constant_htons(ETH_P_IPV6):
- ndisc_mc_map((struct in6_addr *)
- skb->data + QETH_FAKE_LL_V6_ADDR_POS,
- skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
- card->dev, 0);
- break;
-#endif /* QETH_IPV6 */
- case __constant_htons(ETH_P_IP):
- qeth_get_mac_for_ipm(*(__u32*)
- skb->data + QETH_FAKE_LL_V4_ADDR_POS,
- skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
- card->dev);
- break;
- default:
- memcpy(skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
- card->dev->dev_addr, QETH_FAKE_LL_ADDR_LEN);
- }
- break;
- case PACKET_BROADCAST:
- memset(skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
- 0xff, QETH_FAKE_LL_ADDR_LEN);
- break;
- default:
- memcpy(skb->mac.raw + QETH_FAKE_LL_DEST_MAC_POS,
- card->dev->dev_addr, QETH_FAKE_LL_ADDR_LEN);
- }
-
- if (*(__u8 *) (hdr_ptr + 11) & QETH_EXT_HEADER_SRC_MAC_ADDRESS) {
- memcpy(skb->mac.raw + QETH_FAKE_LL_SRC_MAC_POS,
- hdr_ptr + QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR,
- QETH_FAKE_LL_ADDR_LEN);
- } else {
- /* clear source MAC for security reasons */
- memset(skb->mac.raw + QETH_FAKE_LL_SRC_MAC_POS,
- 0, QETH_FAKE_LL_ADDR_LEN);
- }
- memcpy(skb->mac.raw + QETH_FAKE_LL_PROT_POS,
- &skb->protocol, QETH_FAKE_LL_PROT_LEN);
-
-}
-
-static inline void
-__qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
- void *hdr_ptr)
-{
-#ifdef QETH_VLAN
- __u16 *vlan_tag;
-
- if (*(__u8 *) (hdr_ptr + 11) & QETH_EXT_HEADER_VLAN_FRAME) {
-
- vlan_tag = (__u16 *) skb_push(skb, VLAN_HLEN);
- /*
- if (*(__u8*)(hdr_ptr+11) &
- QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
- *vlan_tag = *(__u16*)(hdr_ptr+28);
- *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
- } else {
- */
- *vlan_tag = *(__u16 *) (hdr_ptr + 12);
- *(vlan_tag + 1) = skb->protocol;
- /*
- }
- */
- skb->protocol = __constant_htons(ETH_P_8021Q);
- }
-#endif
-}
-
-static inline void
-__qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, void *hdr_ptr)
-{
- char dbf_text[15];
- int version;
- unsigned short cast_type;
-
- version = ((*(__u16 *) (hdr_ptr)) & (QETH_HEADER_IPV6)) ? 6 : 4;
- skb->protocol = htons((version == 4) ? ETH_P_IP :
- (version == 6) ? ETH_P_IPV6 : ETH_P_ALL);
- cast_type = (*(__u16 *) (hdr_ptr)) & (QETH_CAST_FLAGS);
- switch (cast_type) {
- case QETH_CAST_UNICAST:
- skb->pkt_type = PACKET_HOST;
- break;
- case QETH_CAST_MULTICAST:
- skb->pkt_type = PACKET_MULTICAST;
- break;
- case QETH_CAST_BROADCAST:
- skb->pkt_type = PACKET_BROADCAST;
- break;
- case QETH_CAST_ANYCAST:
- case QETH_CAST_NOCAST:
- QETH_DBF_CARD2(0, trace, "ribf", card);
- sprintf(dbf_text, "castan%2x", cast_type);
- QETH_DBF_TEXT2(1, trace, dbf_text);
- skb->pkt_type = PACKET_HOST;
- break;
- default:
- PRINT_WARN("adapter is using an unknown casting value "
- "of 0x%x. Using unicasting instead.\n",
- cast_type);
- skb->pkt_type = PACKET_HOST;
- QETH_DBF_CARD2(0, trace, "ribf", card);
- sprintf(dbf_text, "castun%2x", cast_type);
- QETH_DBF_TEXT2(1, trace, dbf_text);
- }
-
- if (card->options.fake_ll == FAKE_LL)
- __qeth_rebuild_skb_fake_ll(card, skb, hdr_ptr);
- else
- skb->mac.raw = skb->data;
-
- skb->ip_summed = card->options.checksum_type;
- if (card->options.checksum_type == HW_CHECKSUMMING) {
- /* do we have a checksummed packet? */
-
- /*
- * we only check for TCP/UDP checksums when the pseudo
- * header was also checked successfully -- for the
- * rest of the packets, it's not clear, whether the
- * upper layer csum is alright. And they shouldn't
- * occur too often anyway in real life
- */
-
- if ((*(__u8*)(hdr_ptr+11) & (QETH_EXT_HEADER_CSUM_HDR_REQ |
- QETH_EXT_HEADER_CSUM_TRANSP_REQ)) ==
- (QETH_EXT_HEADER_CSUM_HDR_REQ |
- QETH_EXT_HEADER_CSUM_TRANSP_REQ)) {
-#if 0
- /* csum does not need to be set inbound anyway */
-
- /*
- * vlan is not an issue here, it's still in
- * the QDIO header, not pushed in the skb yet
- */
- int ip_len = (skb->data[0] & 0x0f) << 2;
-
- if (*(__u8 *) (hdr_ptr + 11) &
- QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE) {
- /* get the UDP checksum */
- skb->csum = *(__u16 *)
- (&skb->data[ip_len +
- QETH_UDP_CSUM_OFFSET]);
- } else {
- /* get the TCP checksum */
- skb->csum = *(__u16 *)
- (&skb->data[ip_len +
- QETH_TCP_CSUM_OFFSET]);
- }
-#endif /* 0 */
- skb->ip_summed=CHECKSUM_UNNECESSARY;
- } else {
- /* make the stack check it */
- skb->ip_summed = SW_CHECKSUMMING;
- }
- } else
- skb->ip_summed=card->options.checksum_type;
-
- __qeth_rebuild_skb_vlan(card, skb, hdr_ptr);
-}
-
-static void
-qeth_read_in_buffer(struct qeth_card *card, int buffer_no)
-{
- struct sk_buff *skb;
- void *hdr_ptr;
- int element = 0, pos_in_el = 0;
- struct qdio_buffer *buffer;
- int i;
- int max_elements;
- char dbf_text[15];
- struct net_device *dev;
-
- dev = card->dev;
- max_elements = BUFFER_MAX_ELEMENTS;
-
- buffer = &card->inbound_qdio_buffers[buffer_no];
-
- /* inform about errors */
- if (buffer->element[15].flags & 0xff) {
- PRINT_WARN("on device %s: incoming SBALF 15 on buffer "
- "0x%x are 0x%x\n",
- CARD_BUS_ID(card), buffer_no,
- buffer->element[15].flags & 0xff);
- sprintf(dbf_text, "SF%s%2x%2x",
- CARD_BUS_ID(card), buffer_no,
- buffer->element[15].flags & 0xff);
- QETH_DBF_HEX1(1, trace, dbf_text, QETH_DBF_TRACE_LEN);
- }
-
- for (i = 0; i < max_elements - 1; i++) {
- if (buffer->element[i].flags & SBAL_FLAGS_LAST_ENTRY) {
- buffer->element[i + 1].length = 0;
- break;
- }
- }
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.bufs_rec++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- sprintf(dbf_text, "ribX%s", CARD_BUS_ID(card));
- dbf_text[3] = buffer_no;
- QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
-
- while ((skb = qeth_get_next_skb(card, &element, &pos_in_el,
- &hdr_ptr, buffer))) {
-
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.skbs_rec++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- if (skb) {
- skb->dev = dev;
-
-#ifdef QETH_IPV6
- if ((*(__u16 *) (hdr_ptr)) & (QETH_HEADER_PASSTHRU))
- skb->protocol = card->type_trans(skb, dev);
- else
-#endif /* QETH_IPV6 */
- __qeth_rebuild_skb(card, skb, hdr_ptr);
-
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.inbound_time +=
- NOW - card->perf_stats.inbound_start_time;
- card->perf_stats.inbound_cnt++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- QETH_DBF_CARD6(0, trace, "rxpk", card);
-
- netif_rx(skb);
- dev->last_rx = jiffies;
- card->stats->rx_packets++;
- card->stats->rx_bytes += skb->len;
- } else {
- PRINT_WARN("%s: dropped packet, no buffers "
- "available.\n", card->dev_name);
- QETH_DBF_CARD2(1, trace, "DROP", card);
- card->stats->rx_dropped++;
- }
- }
- atomic_set(&card->inbound_buffer_refcnt[buffer_no], 0);
- qeth_put_buffer_pool_entry(card,
- card->inbound_buffer_entry_no[buffer_no]);
-}
-
-static inline void
-__qeth_fill_header_add_vlan(struct qeth_hdr *hdr, struct sk_buff *skb,
- int version)
-{
-#ifdef QETH_VLAN
- struct qeth_card *card;
-
- /*
- * before we're going to overwrite this location with next hop ip.
- * v6 uses passthrough, v4 sets the tag in the QDIO header.
- */
- card = (struct qeth_card *) skb->dev->priv;
- if ((card->vlangrp != NULL) && vlan_tx_tag_present(skb)) {
- hdr->ext_flags = (version == 4) ? QETH_EXT_HEADER_VLAN_FRAME :
- QETH_EXT_HEADER_INCLUDE_VLAN_TAG;
- hdr->vlan_id = vlan_tx_tag_get(skb);
- }
-#endif
-}
-
-static inline __u8
-__qeth_get_flags_v4(int multicast)
-{
- if (multicast == RTN_MULTICAST)
- return QETH_CAST_MULTICAST;
- if (multicast == RTN_BROADCAST)
- return QETH_CAST_BROADCAST;
- return QETH_CAST_UNICAST;
-}
-
-static inline __u8
-__qeth_get_flags_v6(int multicast)
-{
- if (multicast == RTN_MULTICAST)
- return QETH_CAST_MULTICAST |
- QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6;
- if (multicast == RTN_ANYCAST)
- return QETH_CAST_ANYCAST |
- QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6;
- if (multicast == RTN_BROADCAST)
- return QETH_CAST_BROADCAST |
- QETH_HEADER_PASSTHRU | QETH_HEADER_IPV6;
- return QETH_CAST_UNICAST |
-#ifdef QETH_IPV6
- QETH_HEADER_PASSTHRU |
-#endif /* QETH_IPV6 */
- QETH_HEADER_IPV6;
-}
-
-static inline void
-qeth_fill_header(struct qeth_hdr *hdr, struct sk_buff *skb,
- int version, int multicast)
-{
- char dbf_text[15];
-
- hdr->id = 1;
- hdr->ext_flags = 0;
-
- __qeth_fill_header_add_vlan(hdr, skb, version);
-
- hdr->length = skb->len - QETH_HEADER_SIZE; /* as skb->len includes
- the header now */
-
- /* yes, I know this is doubled code, but a small little bit
- faster maybe */
- if (version == 4) { /* IPv4 */
- hdr->flags = __qeth_get_flags_v4(multicast);
- *((__u32 *) (&hdr->dest_addr[0])) = 0;
- *((__u32 *) (&hdr->dest_addr[4])) = 0;
- *((__u32 *) (&hdr->dest_addr[8])) = 0;
- if ((skb->dst) && (skb->dst->neighbour)) {
- *((__u32 *) (&hdr->dest_addr[12])) =
- *((__u32 *) skb->dst->neighbour->primary_key);
- } else {
- /* fill in destination address used in ip header */
- *((__u32 *) (&hdr->dest_addr[12])) = skb->nh.iph->daddr;
- }
- } else if (version == 6) { /* IPv6 or passthru */
- hdr->flags = __qeth_get_flags_v6(multicast);
- if ((skb->dst) && (skb->dst->neighbour)) {
- memcpy(hdr->dest_addr,
- skb->dst->neighbour->primary_key, 16);
- } else {
- /* fill in destination address used in ip header */
- memcpy(hdr->dest_addr, &skb->nh.ipv6h->daddr, 16);
- }
- } else { /* passthrough */
- if (!memcmp(skb->data + QETH_HEADER_SIZE,
- skb->dev->broadcast, 6)) { /* broadcast? */
- hdr->flags = QETH_CAST_BROADCAST | QETH_HEADER_PASSTHRU;
- } else {
- hdr->flags = (multicast == RTN_MULTICAST) ?
- QETH_CAST_MULTICAST | QETH_HEADER_PASSTHRU :
- QETH_CAST_UNICAST | QETH_HEADER_PASSTHRU;
- }
- }
- sprintf(dbf_text, "filhdr%2x", version);
- QETH_DBF_TEXT6(0, trace, dbf_text);
- sprintf(dbf_text, "%2x", multicast);
- QETH_DBF_TEXT6(0, trace, dbf_text);
- QETH_DBF_HEX6(0, trace, &skb, sizeof (void *));
- QETH_DBF_HEX6(0, trace, &skb->data, sizeof (void *));
- QETH_DBF_HEX6(0, misc, hdr, __max(QETH_HEADER_SIZE, QETH_DBF_MISC_LEN));
- QETH_DBF_HEX6(0, data, skb->data,
- __max(QETH_DBF_DATA_LEN, QETH_DBF_DATA_LEN));
-}
-
-static inline int
-qeth_fill_buffer(struct qdio_buffer *buffer, char *dataptr,
- int length, int element)
-{
- int length_here;
- int first_lap = 1;
- char dbf_text[15];
- int first_element = element;
-
- while (length > 0) {
- /* length_here is the remaining amount of data in this page */
- length_here =
- PAGE_SIZE - ((unsigned long) dataptr & (PAGE_SIZE - 1));
- if (length < length_here)
- length_here = length;
-
- buffer->element[element].addr = dataptr;
- buffer->element[element].length = length_here;
- length -= length_here;
- if (!length) {
- if (first_lap) {
- buffer->element[element].flags = 0;
- } else {
- buffer->element[element].flags =
- SBAL_FLAGS_LAST_FRAG;
- }
- } else {
- if (first_lap) {
- buffer->element[element].flags =
- SBAL_FLAGS_FIRST_FRAG;
- } else {
- buffer->element[element].flags =
- SBAL_FLAGS_MIDDLE_FRAG;
- }
- }
- dataptr = dataptr + length_here;
- element++;
- if (element > QDIO_MAX_ELEMENTS_PER_BUFFER) {
- PRINT_ERR("qeth_fill_buffer: IP packet too big!\n");
- QETH_DBF_TEXT1(0, trace, "IPpktobg");
- QETH_DBF_HEX1(1, trace, &dataptr, sizeof (void *));
- buffer->element[first_element].length = 0;
- break;
- }
- first_lap = 0;
- }
- sprintf(dbf_text, "filbuf%2x", element);
- QETH_DBF_TEXT6(0, trace, dbf_text);
- QETH_DBF_HEX3(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX3(0, misc, buffer + QETH_DBF_MISC_LEN, QETH_DBF_MISC_LEN);
-
- return element;
-}
-
-static inline void
-qeth_flush_packed_packets(struct qeth_card *card, int queue, int under_int)
-{
- struct qdio_buffer *buffer;
- int result;
- int position;
- int position_for_do_qdio;
- char dbf_text[15];
- int last_pci;
-
- position = card->outbound_first_free_buffer[queue];
- /* can happen, when in the time between deciding to pack and sending
- the next packet the lower mark was reached: */
- if (!card->outbound_ringbuffer[queue]->ringbuf_element[position].
- next_element_to_fill)
- return;
-
- buffer = &card->outbound_ringbuffer[queue]->buffer[position];
- buffer->element[card->outbound_ringbuffer[queue]->
- ringbuf_element[position].
- next_element_to_fill - 1].flags |=
- SBAL_FLAGS_LAST_ENTRY;
-
- card->dev->trans_start = jiffies;
-
-#ifdef QETH_PERFORMANCE_STATS
- if (card->outbound_buffer_send_state[queue][position] ==
- SEND_STATE_DONT_PACK) {
- card->perf_stats.bufs_sent_dont_pack++;
- } else if (card->outbound_buffer_send_state[queue][position] ==
- SEND_STATE_PACK) {
- card->perf_stats.bufs_sent_pack++;
- }
- card->perf_stats.bufs_sent++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- position_for_do_qdio = position;
-
- position = (position + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
- card->outbound_first_free_buffer[queue] = position;
-
- card->outbound_bytes_in_buffer[queue] = 0;
- /* we can override that, as we have at most 127 buffers enqueued */
- card->outbound_ringbuffer[queue]->ringbuf_element[position].
- next_element_to_fill = 0;
-
- atomic_inc(&card->outbound_used_buffers[queue]);
-
- QETH_DBF_CARD5(0, trace, "flsp", card);
- sprintf(dbf_text, "%4x%2x%2x", position_for_do_qdio, under_int, queue);
- QETH_DBF_TEXT5(0, trace, dbf_text);
- QETH_DBF_HEX5(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX5(0, misc, buffer + QETH_DBF_MISC_LEN, QETH_DBF_MISC_LEN);
-
- /*
- * we always set the outbound pci flag, don't care, whether the
- * adapter honors it or not
- */
- switch (card->send_state[queue]) {
- case SEND_STATE_DONT_PACK:
- if (atomic_read(&card->outbound_used_buffers[queue])
- < HIGH_WATERMARK_PACK - WATERMARK_FUZZ)
- break;
- /* set the PCI bit */
- card->outbound_ringbuffer[queue]->
- buffer[position_for_do_qdio].element[0].flags |= 0x40;
- atomic_set(&card->last_pci_pos[queue], position_for_do_qdio);
- break;
- case SEND_STATE_PACK:
- last_pci = atomic_read(&card->last_pci_pos[queue]);
- if (position_for_do_qdio < last_pci)
- last_pci -= QDIO_MAX_BUFFERS_PER_Q;
- /* so:
- * last_pci is the position of the last pci we've set
- * position_for_do_qdio is the position we will send out now
- * outbound_used_buffers is the number of buffers used (means
- * all buffers hydra has, inclusive position_for_do_qdio)
- *
- * we have to request a pci, if we have got the buffer of the
- * last_pci position back.
- *
- * position_for_do_qdio-outbound_used_buffers is the newest
- * buffer that we got back from hydra
- *
- * if this is greater or equal than the last_pci position,
- * we should request a pci, as no pci request is
- * outstanding anymore
- */
- if (position_for_do_qdio -
- atomic_read(&card->outbound_used_buffers[queue]) >=
- last_pci) {
- /* set the PCI bit */
- card->outbound_ringbuffer[queue]->
- buffer[position_for_do_qdio].
- element[0].flags |= 0x40;
- atomic_set(&card->last_pci_pos[queue],
- position_for_do_qdio);
- }
- }
-
- /*
- * this has to be at the end, otherwise a buffer could be flushed
- * twice (see comment in qeth_do_send_packet)
- */
- result = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_OUTPUT | under_int, queue,
- position_for_do_qdio, 1, NULL);
-
- if (result) {
- PRINT_WARN("Outbound do_QDIO returned %i "
- "(device %s)\n", result, CARD_DDEV_ID(card));
- QETH_DBF_CARD5(0, trace, "FLSP", card);
- sprintf(dbf_text, "odoQ%4x", result);
- QETH_DBF_TEXT5(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%2x%2x", position_for_do_qdio,
- under_int, queue);
- QETH_DBF_TEXT5(0, trace, dbf_text);
- QETH_DBF_HEX5(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX5(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- }
-}
-
-#define ERROR_NONE 0
-#define ERROR_RETRY 1
-#define ERROR_LINK_FAILURE 2
-#define ERROR_KICK_THAT_PUPPY 3
-static inline int
-qeth_determine_send_error(int cc, int qdio_error, int sbalf15)
-{
- char dbf_text[15];
-
- switch (cc & 3) {
- case 0:
- if (qdio_error)
- return ERROR_LINK_FAILURE;
- return ERROR_NONE;
- case 2:
- if (cc & QDIO_SIGA_ERROR_B_BIT_SET) {
- QETH_DBF_TEXT3(0, trace, "sigacc2b");
- return ERROR_KICK_THAT_PUPPY;
- }
- if (qeth_sbalf15_in_retrieable_range(sbalf15))
- return ERROR_RETRY;
- return ERROR_LINK_FAILURE;
- /* look at qdio_error and sbalf 15 */
- case 1:
- PRINT_WARN("siga returned cc 1! cc=0x%x, "
- "qdio_error=0x%x, sbalf15=0x%x\n",
- cc, qdio_error, sbalf15);
-
- QETH_DBF_TEXT3(0, trace, "siga-cc1");
- QETH_DBF_TEXT2(0, qerr, "siga-cc1");
- sprintf(dbf_text, "%1x%2x%2x", cc, qdio_error, sbalf15);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- QETH_DBF_TEXT2(0, qerr, dbf_text);
- return ERROR_LINK_FAILURE;
- case 3:
- QETH_DBF_TEXT3(0, trace, "siga-cc3");
- return ERROR_KICK_THAT_PUPPY;
- }
- return ERROR_LINK_FAILURE; /* should never happen */
-}
-
-static inline void
-qeth_free_buffer(struct qeth_card *card, int queue, int bufno,
- int qdio_error, int siga_error)
-{
- struct sk_buff *skb;
- int error;
- int retries;
- int sbalf15;
- char dbf_text[15];
- struct qdio_buffer *buffer;
-
- switch (card->outbound_buffer_send_state[queue][bufno]) {
- case SEND_STATE_DONT_PACK: /* fallthrough */
- case SEND_STATE_PACK:
- QETH_DBF_CARD5(0, trace, "frbf", card);
- sprintf(dbf_text, "%2x%2x%4x", queue, bufno,
- card->outbound_buffer_send_state[queue][bufno]);
- QETH_DBF_TEXT5(0, trace, dbf_text);
-
- buffer = &card->outbound_ringbuffer[queue]->buffer[bufno];
- sbalf15 = buffer->element[15].flags & 0xff;
- error =
- qeth_determine_send_error(siga_error, qdio_error, sbalf15);
- if (error == ERROR_KICK_THAT_PUPPY) {
- sprintf(dbf_text, "KP%s%2x",
- CARD_BUS_ID(card), queue);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- QETH_DBF_TEXT2(0, qerr, dbf_text);
- QETH_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "%2x%2x%2x%2x", bufno,
- siga_error, qdio_error, sbalf15);
- QETH_DBF_TEXT2(1, trace, dbf_text);
- QETH_DBF_TEXT2(1, qerr, dbf_text);
- PRINT_ERR("Outbound queue x%x on device %s (%s); "
- "errs: siga: x%x, qdio: x%x, flags15: "
- "x%x. The device will be taken down.\n",
- queue, CARD_BUS_ID(card), card->dev_name,
- siga_error, qdio_error, sbalf15);
- netif_stop_queue(card->dev);
- qeth_set_dev_flag_norunning(card);
- atomic_set(&card->problem, PROBLEM_BAD_SIGA_RESULT);
- qeth_schedule_recovery(card);
- } else if (error == ERROR_RETRY) {
- /* analyze, how many retries we did so far */
- retries = card->send_retries[queue][bufno];
-
- sprintf(dbf_text, "Rt%s%2x",
- CARD_BUS_ID(card), queue);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "b%2x:%2x%2x", bufno,
- sbalf15, retries);
- QETH_DBF_TEXT4(0, trace, dbf_text);
-
- if (++retries > SEND_RETRIES_ALLOWED) {
- error = ERROR_LINK_FAILURE;
- QETH_DBF_TEXT4(1, trace, "ndegelnd");
- }
- /* else error stays RETRY for the switch statemnet */
- } else if (error == ERROR_LINK_FAILURE) {
- /* we don't want to log failures resulting from
- * too many retries */
- QETH_DBF_CARD3(1, trace, "Fail", card);
- QETH_DBF_HEX3(0, misc, buffer, QETH_DBF_MISC_LEN);
- QETH_DBF_HEX3(0, misc, buffer + QETH_DBF_MISC_LEN,
- QETH_DBF_MISC_LEN);
- }
-
- while ((skb = skb_dequeue(&card->outbound_ringbuffer[queue]->
- ringbuf_element[bufno].skb_list))) {
- switch (error) {
- case ERROR_NONE:
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- break;
- case ERROR_RETRY:
- QETH_DBF_TEXT3(0, qerr, "RETRY!!!");
- QETH_DBF_TEXT4(0, trace, "RETRY!!!");
- atomic_dec(&skb->users);
- /* retry packet async (quickly) ... */
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- break;
- case ERROR_LINK_FAILURE:
- case ERROR_KICK_THAT_PUPPY:
- QETH_DBF_TEXT4(0, trace, "endeglnd");
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- break;
- }
- }
- break;
- default:
- PRINT_WARN("oops... wrong send_state on %s. "
- "shouldn't happen "
- "(line %i). q=%i, bufno=x%x, state=%i\n",
- card->dev_name, __LINE__, queue, bufno,
- card->outbound_buffer_send_state[queue][bufno]);
- QETH_DBF_CARD0(1, trace, "UPSf", card);
- QETH_DBF_CARD0(1, qerr, "UPSf", card);
- sprintf(dbf_text, "%2x%2x%4x", queue, bufno,
- card->outbound_buffer_send_state[queue][bufno]);
- QETH_DBF_TEXT0(1, trace, dbf_text);
- QETH_DBF_TEXT0(1, qerr, dbf_text);
- }
- card->outbound_buffer_send_state[queue][bufno] = SEND_STATE_INACTIVE;
- card->send_retries[queue][bufno] = 0;
-}
-
-static inline void
-qeth_free_all_skbs(struct qeth_card *card)
-{
- int q, b;
-
- for (q = 0; q < card->no_queues; q++)
- for (b = 0; b < QDIO_MAX_BUFFERS_PER_Q; b++)
- if (card->outbound_buffer_send_state[q][b] !=
- SEND_STATE_INACTIVE)
- qeth_free_buffer(card, q, b, 0, 0);
-}
-
-static inline void
-qeth_flush_buffer(struct qeth_card *card, int queue, int under_int)
-{
- char dbf_text[15];
- QETH_DBF_CARD5(0, trace, "flsb", card);
- sprintf(dbf_text, "%2x%2x%2x", queue, under_int,
- card->outbound_buffer_send_state[queue]
- [card->outbound_first_free_buffer[queue]]);
- QETH_DBF_TEXT5(0, trace, dbf_text);
-
- switch (card->outbound_buffer_send_state[queue]
- [card->outbound_first_free_buffer[queue]]) {
- case SEND_STATE_DONT_PACK:
- break;
- case SEND_STATE_PACK:
- qeth_flush_packed_packets(card, queue, under_int);
- break;
- default:
- break;
- }
-}
-
-#ifdef QETH_VLAN
-static inline void
-qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
-{
-
- /* Move the mac addresses to the beginning of the new header.
- * We are using three memcpys instead of one memmove to save cycles.
- */
-#define TMP_CPYSIZE 4
- __u16 *tag;
- tag = (__u16 *) skb_push(__skb, VLAN_HLEN);
- memcpy(__skb->data, __skb->data + TMP_CPYSIZE, TMP_CPYSIZE);
- memcpy(__skb->data + TMP_CPYSIZE,
- __skb->data + (2 * TMP_CPYSIZE), TMP_CPYSIZE);
- memcpy(__skb->data + (2 * TMP_CPYSIZE),
- __skb->data + (3 * TMP_CPYSIZE), TMP_CPYSIZE);
- tag = (__u16 *) (__skb->data + (3 * TMP_CPYSIZE));
-
- /*first two bytes = ETH_P_8021Q (0x8100)
- *second two bytes = VLANID
- */
-
- *tag = __constant_htons(ETH_P_8021Q);
- *(tag + 1) = vlan_tx_tag_get(__skb);
- *(tag + 1) = htons(*(tag + 1));
-#undef TMP_CPYSIZE
-}
-#endif
-
-static inline void
-__qeth_add_vlan_tag(struct qeth_card *card, struct sk_buff *skb, int version)
-{
-#ifdef QETH_VLAN
- if ((card->vlangrp != NULL) &&
- vlan_tx_tag_present(skb) && (version == 6)) {
- qeth_insert_ipv6_vlan_tag(skb);
- }
-#endif
-}
-
-static inline void
-qeth_send_packet_fast(struct qeth_card *card, struct sk_buff *skb,
- struct net_device *dev,
- int queue, int version, int multicast)
-{
- struct qeth_ringbuffer_element *mybuffer;
- int position;
- struct qeth_hdr *hdr;
- char *dataptr;
- char dbf_text[15];
- struct sk_buff *nskb;
-
- position = card->outbound_first_free_buffer[queue];
-
- card->outbound_buffer_send_state[queue][position] =
- SEND_STATE_DONT_PACK;
-
- mybuffer = &card->outbound_ringbuffer[queue]->ringbuf_element[position];
- if (skb_headroom(skb) < QETH_HEADER_SIZE) {
- if ((version) && (!card->realloc_message)) {
- card->realloc_message = 1;
- PRINT_WARN("%s: not enough headroom in skb. "
- "Increasing the "
- "add_hhlen parameter by %i may help.\n",
- card->dev_name,
- QETH_HEADER_SIZE - skb_headroom(skb));
- }
- PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
- card->dev_name,
- QETH_HEADER_SIZE - skb_headroom(skb));
- QETH_DBF_CARD3(0, trace, "NHRf", card);
- sprintf(dbf_text, "%2x%2x%2x%2x", skb_headroom(skb),
- version, multicast, queue);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- QETH_DBF_HEX3(0, trace, &skb->head, sizeof (void *));
- QETH_DBF_HEX3(0, trace, &skb->data, sizeof (void *));
- nskb = skb_realloc_headroom(skb, QETH_HEADER_SIZE);
- if (!nskb) {
- PRINT_WARN("%s: could not realloc headroom\n",
- card->dev_name);
- QETH_DBF_CARD2(0, trace, "CNRf", card);
- dev_kfree_skb_irq(skb);
- return;
- }
- dev_kfree_skb_irq(skb);
- skb = nskb;
- }
- __qeth_add_vlan_tag(card, skb, version);
- hdr = (struct qeth_hdr *) (skb_push(skb, QETH_HEADER_SIZE));
- /*
- * sanity check, the Linux memory allocation scheme should
- * never present us cases like this one (the 32bytes header plus
- * the first 40 bytes of the paket cross a 4k boundary)
- */
- dataptr = (char *) hdr;
- if ((((unsigned long) dataptr) & (~(PAGE_SIZE - 1))) !=
- (((unsigned long) dataptr + QETH_HEADER_SIZE +
- QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
- PRINT_ERR("%s: packet misaligned -- the first %i bytes "
- "are not in the same page. Discarding packet!\n",
- card->dev_name,
- QETH_HEADER_SIZE + QETH_IP_HEADER_SIZE);
- PRINT_ERR("head=%p, data=%p\n", skb->head, skb->data);
- QETH_DBF_CARD1(0, trace, "PMAf", card);
- sprintf(dbf_text, "%2x%2x%2x%2x", skb_headroom(skb),
- version, multicast, queue);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_HEX1(0, trace, &skb->head, sizeof (void *));
- QETH_DBF_HEX1(1, trace, &skb->data, sizeof (void *));
- dev_kfree_skb_irq(skb);
- return;
- }
-
- atomic_inc(&skb->users);
- skb_queue_tail(&mybuffer->skb_list, skb);
- qeth_fill_header(hdr, skb, version, multicast);
- /* we need to write to next_element_to_fill as
- qeth_flush_packed_packets checks it */
- card->outbound_ringbuffer[queue]->ringbuf_element[position].
- next_element_to_fill =
- qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
- buffer[position], (char *) hdr, skb->len, 0);
-
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.skbs_sent_dont_pack++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- qeth_flush_packed_packets(card, queue, 0);
-}
-
-/* no checks, if all elements are used, as then we would not be here (at most
- 127 buffers are enqueued) */
-static inline void
-qeth_send_packet_packed(struct qeth_card *card, struct sk_buff *skb,
- struct net_device *dev,
- int queue, int version, int multicast)
-{
- struct qeth_ringbuffer_element *mybuffer;
- int elements_needed;
- int element_to_fill;
- int buffer_no;
- int length;
- char *dataptr;
- struct qeth_hdr *hdr;
- char dbf_text[15];
- struct sk_buff *nskb;
-
- /* sanity check, dev->hard_header_len should prevent this */
- if (skb_headroom(skb) < QETH_HEADER_SIZE) {
- if ((version) && (!card->realloc_message)) {
- card->realloc_message = 1;
- PRINT_WARN("%s: not enough headroom in skb. "
- "Try increasing the "
- "add_hhlen parameter by %i.\n",
- card->dev_name,
- QETH_HEADER_SIZE - skb_headroom(skb));
- }
- PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
- card->dev_name,
- QETH_HEADER_SIZE - skb_headroom(skb));
- QETH_DBF_CARD3(0, trace, "NHRp", card);
- sprintf(dbf_text, "%2x%2x%2x%2x", skb_headroom(skb),
- version, multicast, queue);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- QETH_DBF_HEX3(0, trace, &skb->head, sizeof (void *));
- QETH_DBF_HEX3(0, trace, &skb->data, sizeof (void *));
- nskb = skb_realloc_headroom(skb, QETH_HEADER_SIZE);
- if (!nskb) {
- PRINT_WARN("%s: could not realloc headroom\n",
- card->dev_name);
- QETH_DBF_CARD2(0, trace, "CNRp", card);
- dev_kfree_skb_irq(skb);
- return;
- }
- dev_kfree_skb_irq(skb);
- skb = nskb;
- }
- __qeth_add_vlan_tag(card, skb, version);
- hdr = (struct qeth_hdr *) (skb_push(skb, QETH_HEADER_SIZE));
-
- length = skb->len;
-
- /*
- * sanity check, the Linux memory allocation scheme should
- * never present us cases like this one (the 32bytes header plus
- * the first 40 bytes of the paket cross a 4k boundary)
- */
- dataptr = (char *) hdr;
- if ((((unsigned long) dataptr) & (~(PAGE_SIZE - 1))) !=
- (((unsigned long) dataptr + QETH_HEADER_SIZE +
- QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
- PRINT_ERR("%s: packet misaligned -- the first %i bytes "
- "are not in the same page. Discarding packet!\n",
- card->dev_name,
- QETH_HEADER_SIZE + QETH_IP_HEADER_SIZE);
- QETH_DBF_CARD1(0, trace, "PMAp", card);
- sprintf(dbf_text, "%2x%2x%2x%2x", skb_headroom(skb),
- version, multicast, queue);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_HEX1(0, trace, &skb->head, sizeof (void *));
- QETH_DBF_HEX1(1, trace, &skb->data, sizeof (void *));
- dev_kfree_skb_irq(skb);
- return;
- }
-
- buffer_no = card->outbound_first_free_buffer[queue];
-
- element_to_fill = card->outbound_ringbuffer[queue]->
- ringbuf_element[buffer_no].next_element_to_fill;
-
- elements_needed = 1 + (((((unsigned long) dataptr) & (PAGE_SIZE - 1)) +
- length) >> PAGE_SHIFT);
- if ((elements_needed > (QDIO_MAX_ELEMENTS_PER_BUFFER - element_to_fill))
- ||
- ((elements_needed ==
- (QDIO_MAX_ELEMENTS_PER_BUFFER - element_to_fill))
- && ((element_to_fill >> PAGE_SHIFT) ==
- card->outbound_bytes_in_buffer[queue]))) {
- qeth_flush_packed_packets(card, queue, 0);
- element_to_fill = 0;
- card->outbound_bytes_in_buffer[queue] = 0;
- buffer_no = (buffer_no + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
- }
-
- if (!element_to_fill)
- card->outbound_buffer_send_state[queue][buffer_no]
- = SEND_STATE_PACK;
-
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.skbs_sent_pack++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- mybuffer =
- &card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no];
- atomic_inc(&skb->users);
- skb_queue_tail(&mybuffer->skb_list, skb);
- qeth_fill_header(hdr, skb, version, multicast);
- card->outbound_bytes_in_buffer[queue] += length + QETH_HEADER_SIZE;
- card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no].
- next_element_to_fill =
- qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
- buffer[buffer_no],
- dataptr, length, element_to_fill);
-}
-
-static void
-qeth_alloc_spare_bufs(void)
-{
- int i;
- int dont_alloc_more = 0;
- char dbf_text[15];
-
- sparebuffer_count = 0;
- for (i = 0; i < qeth_sparebufs; i++) {
- if (!dont_alloc_more) {
- sparebufs[i].buf = (char *)
- kmalloc(DEFAULT_BUFFER_SIZE, GFP_KERNEL);
- if (sparebufs[i].buf)
- sparebuffer_count++;
- else
- dont_alloc_more = 1;
- }
- atomic_set(&sparebufs[i].status, (dont_alloc_more) ?
- SPAREBUF_UNAVAIL : SPAREBUF_FREE);
- }
- sprintf(dbf_text, "alspb%3x", sparebuffer_count);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- PRINT_INFO("allocated %i spare buffers\n", sparebuffer_count);
-}
-
-static void
-qeth_free_all_spare_bufs(void)
-{
- int i;
-
- QETH_DBF_TEXT2(0, trace, "frealspb");
-
- for (i = 0; i < qeth_sparebufs; i++)
- if (atomic_read(&sparebufs[i].status) != SPAREBUF_UNAVAIL) {
- kfree(sparebufs[i].buf);
- atomic_set(&sparebufs[i].status, SPAREBUF_UNAVAIL);
- }
-}
-
-static inline void
-__qeth_dump_packet_info(struct qeth_card *card, int version, int multicast,
- int queue)
-{
- char dbf_text[15];
-
- QETH_DBF_CARD6(0, trace, "dsp:", card);
- sprintf(dbf_text, "%c %c%4x",
- (version == 4) ? '4' : ((version == 6) ? '6' : '0'),
- (multicast) ? 'm' : '_', queue);
- QETH_DBF_TEXT6(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x",
- card->outbound_first_free_buffer[queue],
- atomic_read(&card->outbound_used_buffers[queue]));
- QETH_DBF_TEXT6(0, trace, dbf_text);
- if (qeth_sbal_packing_on_card(card->type)) {
- switch (card->send_state[queue]) {
- case SEND_STATE_DONT_PACK:
- QETH_DBF_TEXT6(0, trace, "usngfast");
- break;
- case SEND_STATE_PACK:
- QETH_DBF_TEXT6(0, trace, "usngpack");
- break;
- }
- } else {
- QETH_DBF_TEXT6(0, trace, "usngfast");
- }
-}
-
-static inline void
-__qeth_switch_state_if_needed(struct qeth_card *card, int queue)
-{
- if (atomic_read(&card->outbound_used_buffers[queue])
- >= HIGH_WATERMARK_PACK) {
- card->send_state[queue] = SEND_STATE_PACK;
- QETH_DBF_CARD3(0, trace, "stchup", card);
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.sc_dp_p++;
-#endif /* QETH_PERFORMANCE_STATS */
- }
-}
-
-static inline int
-qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb,
- struct net_device *dev)
-{
- int queue, result = 0;
- int multicast, version;
-
- version = QETH_IP_VERSION(skb);
- multicast = qeth_is_multicast_skb_at_all(skb, version);
- queue = qeth_get_prioqueue(card, skb, multicast, version);
-
- __qeth_dump_packet_info(card, version, multicast, queue);
-
- if (atomic_read(&card->outbound_used_buffers[queue])
- >= QDIO_MAX_BUFFERS_PER_Q - 1) {
- QETH_DBF_CARD2(1, trace, "cdbs", card);
- netif_stop_queue(dev);
- return -EBUSY;
- }
-
- /*
- * we are not called under int, so we just spin
- * happens around once a second under heavy traffic. takes a little
- * bit less than 10usec in avg. on a z900
- */
- if (atomic_compare_and_swap(QETH_LOCK_UNLOCKED, QETH_LOCK_NORMAL,
- &card->outbound_ringbuffer_lock[queue])) {
- QETH_DBF_CARD2(0, trace, "SPIN", card);
- while (atomic_compare_and_swap
- (QETH_LOCK_UNLOCKED, QETH_LOCK_NORMAL,
- &card->outbound_ringbuffer_lock[queue])) ;
- QETH_DBF_CARD2(0, trace, "spin", card);
- }
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.skbs_sent++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- if (qeth_sbal_packing_on_card(card->type)) {
- switch (card->send_state[queue]) {
- case SEND_STATE_DONT_PACK:
- qeth_send_packet_fast(card, skb, dev, queue,
- version, multicast);
- __qeth_switch_state_if_needed(card, queue);
- break;
- case SEND_STATE_PACK:
- qeth_send_packet_packed(card, skb, dev, queue,
- version, multicast);
- break;
- default:
- result = -EBUSY;
- QETH_DBF_CARD0(1, trace, "UPSs", card);
- PRINT_ALL("oops... shouldn't happen (line %i:%i).\n",
- __LINE__, card->send_state[queue]);
- }
- } else {
- qeth_send_packet_fast(card, skb, dev, queue,
- version, multicast);
- }
-
- /* ATOMIC: (NORMAL->UNLOCKED, FLUSH->NORMAL) */
- while (atomic_dec_return(&card->outbound_ringbuffer_lock[queue])) {
- qeth_flush_buffer(card, queue, 0);
- card->send_state[queue] = SEND_STATE_DONT_PACK;
- }
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.outbound_time +=
- NOW - card->perf_stats.outbound_start_time;
- card->perf_stats.outbound_cnt++;
-#endif /* QETH_PERFORMANCE_STATS */
-
- card->stats->tx_packets++;
- card->stats->tx_bytes += skb->len;
-
- return result;
-}
-
-static int
-qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct qeth_card *card;
- int result;
-
- card = (struct qeth_card *) (dev->priv);
-
- if (skb == NULL)
- return 0;
-
- QETH_DBF_HEX4(0, data, skb->data, __max(QETH_DBF_DATA_LEN, skb->len));
-
- netif_stop_queue(dev);
-
- if (!card) {
- QETH_DBF_TEXT2(0, trace, "XMNSNOCD");
- dev_kfree_skb_irq(skb);
- return 0;
- }
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.outbound_start_time = NOW;
-#endif /* QETH_PERFORMANCE_STATS */
-
- if (!atomic_read(&card->is_startlaned)) {
- card->stats->tx_carrier_errors++;
- QETH_DBF_CARD2(0, trace, "XMNS", card);
- dev_kfree_skb_irq(skb);
- return 0;
- }
-
- result = qeth_do_send_packet(card, skb, dev);
-
- if (!result)
- netif_wake_queue(card->dev);
-
- return result;
-}
-
-static struct net_device_stats *
-qeth_get_stats(struct net_device *dev)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) (dev->priv);
-
- QETH_DBF_CARD3(0, trace, "gtst", card);
-
- return card->stats;
-}
-
-static int
-qeth_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct qeth_card *card;
- char dbf_text[15];
-
- card = (struct qeth_card *) (dev->priv);
-
- QETH_DBF_CARD2(0, trace, "mtu", card);
- sprintf(dbf_text, "%8x", new_mtu);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- if (new_mtu < 64)
- return -EINVAL;
- if (new_mtu > 65535)
- return -EINVAL;
- if ((!qeth_is_supported(IPA_IP_FRAGMENTATION)) &&
- (!qeth_mtu_is_valid(card, new_mtu)))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-static void
-qeth_start_softsetup_thread(struct qeth_card *card)
-{
- if (!atomic_read(&card->shutdown_phase)) {
- QETH_DBF_CARD2(0, trace, "stss", card);
- up(&card->softsetup_thread_sem);
- }
-}
-
-static int
-qeth_sleepon(struct qeth_card *card, int timeout)
-{
- char dbf_text[15];
-
- QETH_DBF_CARD5(0, trace, "slpn", card);
- sprintf(dbf_text, "%08x", timeout);
- QETH_DBF_TEXT5(0, trace, dbf_text);
-
- wait_event_interruptible_timeout(card->wait_q,
- atomic_read(&card->data_has_arrived),
- timeout * HZ);
- if (atomic_read(&card->data_has_arrived)) {
- atomic_set(&card->data_has_arrived, 0);
- return 0;
- }
- return -ETIME;
-}
-
-static void
-qeth_wakeup_ioctl(struct qeth_card *card)
-{
-
- QETH_DBF_CARD5(0, trace, "wkup", card);
-
- atomic_set(&card->ioctl_data_has_arrived, 1);
- wake_up(&card->ioctl_wait_q);
-}
-
-static int
-qeth_sleepon_ioctl(struct qeth_card *card, int timeout)
-{
- char dbf_text[15];
-
- QETH_DBF_CARD5(0, trace, "ioctlslpn", card);
- sprintf(dbf_text, "%08x", timeout);
- QETH_DBF_TEXT5(0, trace, dbf_text);
-
- wait_event_interruptible_timeout(card->ioctl_wait_q,
- atomic_read(&card->
- ioctl_data_has_arrived),
- timeout * HZ);
- if (atomic_read(&card->ioctl_data_has_arrived)) {
- atomic_set(&card->ioctl_data_has_arrived, 0);
- return 0;
- }
- return -ETIME;
-}
-
-/*SNMP IOCTL on Procfile */
-
-static void
-qeth_wakeup_procfile(void)
-{
- QETH_DBF_TEXT5(0, trace, "procwkup");
- /* is this if statement correct? */
- if (atomic_read(&qeth_procfile_ioctl_sem.count) <=
- PROCFILE_SLEEP_SEM_MAX_VALUE)
- up(&qeth_procfile_ioctl_sem);
-}
-
-static int
-qeth_sleepon_procfile(void)
-{
- QETH_DBF_TEXT5(0, trace, "procslp");
- if (down_interruptible(&qeth_procfile_ioctl_sem)) {
- return -ERESTARTSYS;
- }
- return 0;
-}
-
-/* SNMP END */
-
-static char *
-qeth_send_control_data(struct qeth_card *card, unsigned char *buffer,
- int len, unsigned long intparam)
-{
- unsigned long flags;
- int result, result2;
- char dbf_text[15];
- unsigned char *rec_buf;
- int setip = (intparam & IPA_SETIP_FLAG) ? 1 : 0;
-
-again:
- if (atomic_read(&card->shutdown_phase) == QETH_REMOVE_CARD_QUICK)
- return NULL;
- if (atomic_read(&card->escape_softsetup))
- return NULL;
-
- /* we lock very early to synchronize access to seqnos */
- if (atomic_swap(&card->write_busy, 1)) {
- qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
- QETH_DBF_CARD2(0, trace, "LSCD", card);
- goto again;
- }
- memcpy(card->dma_stuff->sendbuf, card->send_buf, QETH_BUFSIZE);
-
- memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(buffer),
- &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
- card->seqno.trans_hdr++;
-
- memcpy(QETH_PDU_HEADER_SEQ_NO(buffer),
- &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH);
- card->seqno.pdu_hdr++;
- memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(buffer),
- &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
-
- /* there is noone doing this except sleep and this function */
- atomic_set(&card->data_has_arrived, 0);
-
- memcpy(&card->dma_stuff->write_ccw, WRITE_CCW, sizeof (struct ccw1));
- card->dma_stuff->write_ccw.count = len;
- card->dma_stuff->write_ccw.cda =
- QETH_GET_ADDR(card->dma_stuff->sendbuf);
-
- QETH_DBF_CARD2(0, trace, "scdw", card);
- sprintf(dbf_text, "%8x", len);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- QETH_DBF_HEX4(0, trace, &intparam, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, control, buffer, QETH_DBF_CONTROL_LEN);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags);
- result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->write_ccw,
- intparam, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 = ccw_device_start(CARD_WDEV(card),
- &card->dma_stuff->write_ccw,
- intparam, 0, 0);
- if (result2 != -ENODEV)
- PRINT_WARN("qeth_send_control_data: do_IO "
- "returned %i, next try returns %i\n",
- result, result2);
- result = result2;
- }
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags);
-
- if (result) {
- QETH_DBF_TEXT2(0, trace, "scd:doio");
- sprintf(dbf_text, "%4x", (__s16) result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* re-enable qeth_send_control_data again */
- atomic_set(&card->write_busy,0);
- return NULL;
- }
-
- if (intparam == IPA_IOCTL_STATE) {
- if (qeth_sleepon_ioctl(card, QETH_IPA_TIMEOUT)) {
- QETH_DBF_TEXT2(0, trace, "scd:ioctime");
- /* re-enable qeth_send_control_data again */
- atomic_set(&card->write_busy, 0);
- return NULL;
- }
- rec_buf = card->dma_stuff->recbuf;
- QETH_DBF_CARD2(0, trace, "scro", card);
- } else {
- if (qeth_sleepon(card, (setip) ? QETH_IPA_TIMEOUT :
- QETH_MPC_TIMEOUT)) {
- QETH_DBF_TEXT2(0, trace, "scd:time");
- /* re-enable qeth_send_control_data again */
- atomic_set(&card->write_busy, 0);
- return NULL;
- }
- rec_buf = card->ipa_buf;
- QETH_DBF_CARD2(0, trace, "scri", card);
- }
- QETH_DBF_HEX2(0, control, rec_buf, QETH_DBF_CONTROL_LEN);
-
- memcpy(&card->seqno.pdu_hdr_ack,
- QETH_PDU_HEADER_SEQ_NO(rec_buf), QETH_SEQ_NO_LENGTH);
-
- return rec_buf;
-}
-
-static int
-qeth_send_ipa_cmd(struct qeth_card *card, struct ipa_cmd *cmd, int update_cmd,
- int ipatype)
-{
- unsigned char *buffer;
- struct ipa_cmd *reply;
- int ipa_cmd;
- int result;
-
- /* don't muck around with ipv6 if there's no use to do so */
- if ((cmd->prot_version == 6) && (!qeth_is_supported(IPA_IPv6)))
- return 0;
-
- ipa_cmd = cmd->command;
-
- memcpy(card->send_buf, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-
- memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
- &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-
- memcpy(card->send_buf + IPA_PDU_HEADER_SIZE,
- cmd, sizeof (struct ipa_cmd));
-
- buffer = qeth_send_control_data(card, card->send_buf,
- IPA_PDU_HEADER_SIZE +
- sizeof (struct ipa_cmd), ipatype);
-
- if (!buffer) {
- if (atomic_read(&card->escape_softsetup))
- return 0;
- else
- return -1;
- }
- reply = (struct ipa_cmd *) PDU_ENCAPSULATION(buffer);
- if ((update_cmd) && (reply))
- memcpy(cmd, reply, sizeof (struct ipa_cmd));
- result = reply->return_code;
-
- /* some special sausages: */
- if ((ipa_cmd == IPA_CMD_SETASSPARMS) && (result == 0)) {
- result = reply->data.setassparms.return_code;
- if ((reply->data.setassparms.assist_no==IPA_INBOUND_CHECKSUM) &&
- (reply->data.setassparms.command_code == IPA_CMD_ASS_START))
- card->csum_enable_mask =
- reply->data.setassparms.data.flags_32bit;
- }
- if ((ipa_cmd == IPA_CMD_SETADAPTERPARMS) && (result == 0)) {
- result = reply->data.setadapterparms.return_code;
- }
-
- return result;
-}
-
-static void
-qeth_fill_ipa_cmd(struct qeth_card *card, struct ipa_cmd *cmd,
- __u8 command, int ip_vers)
-{
- memset(cmd, 0, sizeof (struct ipa_cmd));
- cmd->command = command;
- cmd->initiator = INITIATOR_HOST;
- cmd->seq_no = card->seqno.ipa++;
- cmd->adapter_type = qeth_get_adapter_type_for_ipa(card->link_type);
- cmd->rel_adapter_no = (__u8) card->options.portno;
- cmd->prim_version_no = 1;
- cmd->param_count = 1;
- cmd->prot_version = ip_vers;
- cmd->ipa_supported = 0;
- cmd->ipa_enabled = 0;
-}
-
-static int
-qeth_send_startstoplan(struct qeth_card *card, __u8 ipacmd, __u16 ip_vers)
-{
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, ipacmd, 0);
- cmd.param_count = 0;
- cmd.prot_version = ip_vers;
- cmd.ipa_supported = 0;
- cmd.ipa_enabled = 0;
-
- result = qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE);
- return result;
-}
-
-static int
-qeth_send_startlan(struct qeth_card *card, __u16 ip_vers)
-{
- int result;
- char dbf_text[15];
-
- QETH_DBF_CARD4(0, trace, "stln", card);
-
- result = qeth_send_startstoplan(card, IPA_CMD_STARTLAN, ip_vers);
- if (!result)
- atomic_set(&card->is_startlaned, 1);
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "STRTLNFL", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-
- return result;
-}
-
-static int
-qeth_send_stoplan(struct qeth_card *card)
-{
-#ifdef QETH_SEND_STOPLAN_ON_SHUTDOWN
- int result;
- char dbf_text[15];
-
- atomic_set(&card->is_startlaned, 0);
-
- QETH_DBF_CARD4(0, trace, "spln", card);
-
- result = qeth_send_startstoplan(card, IPA_CMD_STOPLAN, 4);
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "STPLNFLD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-
- return result;
-#else /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
- return 0;
-#endif /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
-}
-
-static int
-qeth_send_qipassist(struct qeth_card *card, short ip_vers)
-{
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_QIPASSIST, ip_vers);
-
- result = qeth_send_ipa_cmd(card, &cmd, 1, IPA_CMD_STATE);
-
- if (!result) {
- if (ip_vers == 4) {
- card->ipa_supported = cmd.ipa_supported;
- card->ipa_enabled = cmd.ipa_enabled;
- } else {
- card->ipa6_supported = cmd.ipa_supported;
- card->ipa6_enabled = cmd.ipa_enabled;
- }
- }
-
- return result;
-}
-
-/* QUERY ARP FUNCTIONS */
-
-static int
-qeth_send_ipa_arpcmd(struct qeth_card *card, struct arp_cmd *cmd,
- int update_cmd, int ipatype, __u32 req_size)
-{
- unsigned char *buffer;
- int ipa_cmd;
- int result;
- __u16 s1, s2;
-
- /* don't muck around with ipv6 if there's no use to do so */
- if ((cmd->prot_version == 6) && (!qeth_is_supported(IPA_IPv6)))
- return 0;
- result = 0;
- ipa_cmd = cmd->command;
-
- memcpy(card->send_buf, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
- memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
- &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(card->send_buf + IPA_PDU_HEADER_SIZE,
- cmd, sizeof (struct arp_cmd));
-
- if (req_size) {
- /* adjust sizes for big requests */
- s1 = (__u32) IPA_PDU_HEADER_SIZE + SNMP_BASE_CMDLENGTH +
- req_size;
- s2 = (__u32) SNMP_BASE_CMDLENGTH + req_size;
- memcpy(QETH_IPA_PDU_LEN_TOTAL(card->send_buf), &s1, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU1(card->send_buf), &s2, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU2(card->send_buf), &s2, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU3(card->send_buf), &s2, 2);
- }
-
- buffer = qeth_send_control_data(card, card->send_buf,
- IPA_PDU_HEADER_SIZE +
- sizeof (struct arp_cmd), ipatype);
- if (!buffer)
- result = -ENODATA;
- else
- result = card->ioctl_returncode;
- return result;
-}
-
-static int
-qeth_ioctl_handle_snmp_data(struct qeth_card *card, struct arp_cmd *reply)
-{
- __u16 data_len;
-
-#define SNMP_HEADER_SIZE_WITH_TOKEN 36
-
- data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(card->dma_stuff->recbuf));
- if (reply->data.setadapterparms.frame_seq_no == 1)
- data_len = data_len -
- (__u16)((char*)reply->data.setadapterparms.data.
- snmp_subcommand.snmp_data - (char*)reply);
- else
- data_len = data_len -
- (__u16)((char*)&reply->data.setadapterparms.data.
- snmp_subcommand.snmp_request - (char*)reply);
-
- if (reply->data.setadapterparms.frame_seq_no == 1) {
-
- if (card->ioctl_buffersize <= (SNMP_HEADER_SIZE_WITH_TOKEN +
- reply->data.setadapterparms.
- frames_used_total *
- ARP_DATA_SIZE)) {
-
- card->ioctl_returncode = ARP_RETURNCODE_ERROR;
- reply->data.setadapterparms.data.snmp_subcommand.
- snmp_returncode = -ENOMEM;
- } else {
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
- card->number_of_entries = 0;
- memcpy(((char *)card->ioctl_data_buffer),
- reply->data.setadapterparms.snmp_token,
- SNMP_HEADER_SIZE_WITH_TOKEN);
- card->ioctl_buffer_pointer = card->ioctl_data_buffer+
- SNMP_HEADER_SIZE_WITH_TOKEN;
- }
- }
-
- if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
- reply->data.setadapterparms.frame_seq_no <=
- reply->data.setadapterparms.frames_used_total) {
-
- if (reply->data.setadapterparms.return_code ==
- IPA_REPLY_SUCCESS) {
-
- if (reply->data.setadapterparms.frame_seq_no == 1)
- memcpy(card->ioctl_buffer_pointer,
- reply->data.setadapterparms.data.
- snmp_subcommand.snmp_data, data_len);
- else
- memcpy(card->ioctl_buffer_pointer,
- (char*)&reply->data.setadapterparms.
- data.snmp_subcommand.snmp_request,
- data_len);
-
- card->ioctl_buffer_pointer =
- card->ioctl_buffer_pointer + data_len;
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
-
- if (reply->data.setadapterparms.frame_seq_no ==
- reply->data.setadapterparms.frames_used_total) {
- card->ioctl_returncode =
- ARP_RETURNCODE_LASTREPLY;
- }
- } else {
- card->ioctl_returncode = ARP_RETURNCODE_ERROR;
- memset(card->ioctl_data_buffer, 0,
- card->ioctl_buffersize);
- reply->data.setadapterparms.data.snmp_subcommand.
- snmp_returncode =
- reply->data.setadapterparms.return_code;
- }
- }
-#undef SNMP_HEADER_SIZE_WITH_TOKEN
-
- return card->ioctl_returncode;
-}
-
-static int
-qeth_ioctl_handle_arp_data(struct qeth_card *card, struct arp_cmd *reply)
-{
-
- if (reply->data.setassparms.seq_no == 1) {
- if (card->ioctl_buffersize <=
- (sizeof (__u16) + sizeof (int) +
- reply->data.setassparms.number_of_replies *
- ARP_DATA_SIZE)) {
-
- card->ioctl_returncode = ARP_RETURNCODE_ERROR;
-
- } else {
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
- card->number_of_entries = 0;
- card->ioctl_buffer_pointer =
- card->ioctl_data_buffer + sizeof (__u16) +
- sizeof (int);
- }
- }
-
- if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
- reply->data.setassparms.seq_no <=
- reply->data.setassparms.number_of_replies) {
-
- if (reply->data.setassparms.return_code == IPA_REPLY_SUCCESS) {
-
- card->number_of_entries = card->number_of_entries +
- reply->data.setassparms.
- data.queryarp_data.number_of_entries;
- memcpy(card->ioctl_buffer_pointer,
- reply->data.setassparms.data.queryarp_data.
- arp_data, ARP_DATA_SIZE);
- card->ioctl_buffer_pointer = card->
- ioctl_buffer_pointer + ARP_DATA_SIZE;
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
- if (reply->data.setassparms.seq_no ==
- reply->data.setassparms.number_of_replies) {
- memcpy(card->ioctl_data_buffer,
- &reply->data.setassparms.data.
- queryarp_data.osa_setbitmask,
- sizeof (__u16));
- card->ioctl_returncode =
- ARP_RETURNCODE_LASTREPLY;
- }
- } else {
-
- card->ioctl_returncode = ARP_RETURNCODE_ERROR;
- memset(card->ioctl_data_buffer, 0,
- card->ioctl_buffersize);
- }
- }
- return card->ioctl_returncode;
-}
-
-static int
-qeth_look_for_arp_data(struct qeth_card *card)
-{
- struct arp_cmd *reply;
- int result;
-
- reply = (struct arp_cmd *) PDU_ENCAPSULATION(card->dma_stuff->recbuf);
-
- if ((reply->command == IPA_CMD_SETASSPARMS) &&
- (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) &&
- (reply->data.setassparms.command_code ==
- IPA_CMD_ASS_ARP_FLUSH_CACHE)) {
- result = ARP_FLUSH;
- } else if ((reply->command == IPA_CMD_SETASSPARMS) &&
- (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) &&
- (reply->data.setassparms.command_code ==
- IPA_CMD_ASS_ARP_QUERY_INFO) &&
- (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) {
-
- result = qeth_ioctl_handle_arp_data(card, reply);
-
- } else if ((reply->command == IPA_CMD_SETADAPTERPARMS) &&
- (reply->data.setadapterparms.command_code ==
- IPA_SETADP_SET_SNMP_CONTROL) &&
- (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) {
-
- result = qeth_ioctl_handle_snmp_data(card, reply);
- } else
- result = ARP_RETURNCODE_NOARPDATA;
-
- return result;
-}
-
-static int
-qeth_queryarp(struct qeth_card *card, struct ifreq *req, int version,
- __u32 assist_no, __u16 command_code, char *c_data, __u16 len)
-{
- int data_size;
- struct arp_cmd *cmd;
- int result;
-
- cmd = (struct arp_cmd *) kmalloc(sizeof (struct arp_cmd), GFP_KERNEL);
- if (!cmd) {
- return IPA_REPLY_FAILED;
- }
-
- memcpy(&data_size, c_data, sizeof (int));
-
- qeth_fill_ipa_cmd(card, (struct ipa_cmd *) cmd, IPA_CMD_SETASSPARMS,
- version);
-
- cmd->data.setassparms.assist_no = assist_no;
- cmd->data.setassparms.length = 8 + len;
- cmd->data.setassparms.command_code = command_code;
- cmd->data.setassparms.return_code = 0;
- cmd->data.setassparms.seq_no = 0;
-
- card->ioctl_buffersize = data_size;
- card->ioctl_data_buffer = (char *) vmalloc(data_size);
- if (!card->ioctl_data_buffer) {
- kfree(cmd);
- return IPA_REPLY_FAILED;
- }
-
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
-
- result = qeth_send_ipa_arpcmd(card, cmd, 1, IPA_IOCTL_STATE, 0);
-
- if ((result == ARP_RETURNCODE_ERROR) || (result == -ENODATA)) {
- result = IPA_REPLY_FAILED;
- } else {
- result = IPA_REPLY_SUCCESS;
- memcpy(((char *) (card->ioctl_data_buffer)) + sizeof (__u16),
- &(card->number_of_entries), sizeof (int));
- if (copy_to_user(req->ifr_ifru.ifru_data,
- card->ioctl_data_buffer, data_size))
- result = -EFAULT;
- }
- card->ioctl_buffer_pointer = NULL;
- vfree(card->ioctl_data_buffer);
- kfree(cmd);
- card->number_of_entries = 0;
- card->ioctl_buffersize = 0;
-
- return result;
-}
-
-static int
-snmp_set_setadapterparms_command(struct qeth_card *card,
- struct arp_cmd *cmd, struct ifreq *req,
- char *data, __u16 len,
- __u16 command_code, int req_size)
-{
- __u32 data_size;
-
- memcpy(&data_size, data, sizeof (__u32));
-
- card->ioctl_buffersize = data_size;
- card->ioctl_data_buffer = (char *) vmalloc(data_size);
- if (!card->ioctl_data_buffer) {
- return -ENOMEM;
- }
- card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
-
- memcpy(cmd->data.setadapterparms.snmp_token,
- data + SNMP_REQUEST_DATA_OFFSET, req_size);
-
- cmd->data.setadapterparms.cmdlength = SNMP_SETADP_CMDLENGTH + req_size;
- cmd->data.setadapterparms.command_code = command_code;
- cmd->data.setadapterparms.frames_used_total = 1;
- cmd->data.setadapterparms.frame_seq_no = 1;
-
- return 0;
-}
-static int
-qeth_send_snmp_control(struct qeth_card *card, struct ifreq *req,
- __u32 command, __u16 command_code,
- char *c_data, __u16 len)
-{
- struct arp_cmd *cmd;
- __u32 result, req_size;
-
- cmd = (struct arp_cmd *) kmalloc(sizeof (struct arp_cmd), GFP_KERNEL);
- if (!cmd) {
- return IPA_REPLY_FAILED;
- }
-
- qeth_fill_ipa_cmd(card, (struct ipa_cmd *) cmd, command, 4);
-
- memcpy(&req_size, ((char *) c_data) + sizeof (__u32), sizeof (__u32));
-
- if (snmp_set_setadapterparms_command(card, cmd, req, c_data,
- len, command_code, req_size)) {
- kfree(cmd);
- return IPA_REPLY_FAILED;
- }
-
- result = qeth_send_ipa_arpcmd(card, cmd, 1, IPA_IOCTL_STATE, req_size);
-
- if (result == -ENODATA) {
- result = IPA_REPLY_FAILED;
- goto snmp_out;
- }
- if (result == ARP_RETURNCODE_ERROR) {
- result = IPA_REPLY_FAILED;
- if (copy_to_user(req->ifr_ifru.ifru_data +
- SNMP_REQUEST_DATA_OFFSET, card->ioctl_data_buffer,
- card->ioctl_buffersize))
- result = -EFAULT;
- } else {
- result = IPA_REPLY_SUCCESS;
- if (copy_to_user(req->ifr_ifru.ifru_data +
- SNMP_REQUEST_DATA_OFFSET, card->ioctl_data_buffer,
- card->ioctl_buffersize))
- result = -EFAULT;
- }
-snmp_out:
- card->number_of_entries = 0;
- card->ioctl_buffersize = 0;
- card->ioctl_buffer_pointer = NULL;
- vfree(card->ioctl_data_buffer);
- kfree(cmd);
-
- return result;
-}
-
-static int
-qeth_send_setassparms(struct qeth_card *card, int version, __u32 assist_no,
- __u16 command_code, long data, __u16 len)
-{
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_SETASSPARMS, version);
-
- cmd.data.setassparms.assist_no = assist_no;
- cmd.data.setassparms.length = 8 + len;
- cmd.data.setassparms.command_code = command_code;
- cmd.data.setassparms.return_code = 0;
- cmd.data.setassparms.seq_no = 0;
-
- if (len <= sizeof (__u32))
- cmd.data.setassparms.data.flags_32bit = (__u32) data;
- else if (len > sizeof (__u32))
- memcpy(&cmd.data.setassparms.data, (void *) data,
- qeth_min(len, PAGE_SIZE));
- if (command_code != IPA_CMD_ASS_START) {
- result = qeth_send_ipa_cmd(card, &cmd, 0,
- ((assist_no == IPA_ARP_PROCESSING) &&
- (command_code !=
- IPA_CMD_ASS_ARP_FLUSH_CACHE)) ?
- IPA_IOCTL_STATE : IPA_CMD_STATE);
-
- } else
- result = qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE);
-
- return result;
-}
-
-static int
-qeth_send_setadapterparms_query(struct qeth_card *card)
-{
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_SETADAPTERPARMS,
- IPA_SETADAPTERPARMS_IP_VERSION);
- cmd.data.setadapterparms.cmdlength = sizeof (struct ipa_setadp_cmd);
- cmd.data.setadapterparms.command_code =
- IPA_SETADP_QUERY_COMMANDS_SUPPORTED;
- cmd.data.setadapterparms.frames_used_total = 1;
- cmd.data.setadapterparms.frame_seq_no = 1;
- result = qeth_send_ipa_cmd(card, &cmd, 1, IPA_CMD_STATE);
-
- if (cmd.data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f)
- card->link_type = cmd.data.setadapterparms.data.
- query_cmds_supp.lan_type;
-
- card->adp_supported =
- cmd.data.setadapterparms.data.query_cmds_supp.supported_cmds;
-
- return result;
-}
-
-static int
-qeth_send_setadapterparms_mode(struct qeth_card *card, __u32 command,
- __u32 mode)
-{
-
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_SETADAPTERPARMS,
- IPA_SETADAPTERPARMS_IP_VERSION);
- cmd.data.setadapterparms.cmdlength = sizeof (struct ipa_setadp_cmd);
- cmd.data.setadapterparms.command_code = command;
- cmd.data.setadapterparms.frames_used_total = 1;
- cmd.data.setadapterparms.frame_seq_no = 1;
- cmd.data.setadapterparms.data.mode = mode;
- result = qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE);
-
- return result;
-}
-
-static int
-qeth_send_setadapterparms_change_addr(struct qeth_card *card,
- __u32 command,
- __u32 subcmd, __u8 * mac_addr,
- int addr_len)
-{
- struct ipa_cmd cmd;
- int result;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_SETADAPTERPARMS,
- IPA_SETADAPTERPARMS_IP_VERSION);
- cmd.data.setadapterparms.cmdlength = sizeof (struct ipa_setadp_cmd);
- cmd.data.setadapterparms.command_code = command;
- cmd.data.setadapterparms.frames_used_total = 1;
- cmd.data.setadapterparms.frame_seq_no = 1;
- cmd.data.setadapterparms.data.change_addr.cmd = subcmd;
- cmd.data.setadapterparms.data.change_addr.addr_size = addr_len;
- memcpy(&cmd.data.setadapterparms.data.change_addr.addr,
- mac_addr, addr_len);
-
- result = qeth_send_ipa_cmd(card, &cmd, 1, IPA_CMD_STATE);
-
- memcpy(mac_addr, &cmd.data.setadapterparms.data.change_addr.addr,
- addr_len);
-
- return result;
-}
-
-static int
-qeth_send_setassparms_simple_with_data(struct qeth_card *card,
- __u32 assist_no,
- __u16 command_code, long data)
-{
- return qeth_send_setassparms(card, 4, assist_no, command_code, data, 4);
-}
-
-static int
-qeth_send_setassparms_simple_without_data(struct qeth_card *card,
- __u32 assist_no, __u16 command_code)
-{
- return qeth_send_setassparms(card, 4, assist_no, command_code, 0, 0);
-}
-
-static int
-qeth_send_setassparms_simple_without_data6(struct qeth_card *card,
- __u32 assist_no, __u16 command_code)
-{
- return qeth_send_setassparms(card, 6, assist_no, command_code, 0, 0);
-}
-
-static int
-qeth_send_setdelip(struct qeth_card *card, __u8 * ip, __u8 * netmask,
- int ipacmd, short ip_vers, unsigned int flags)
-{
- struct ipa_cmd cmd;
- int ip_len = (ip_vers == 6) ? 16 : 4;
-
- qeth_fill_ipa_cmd(card, &cmd, ipacmd, ip_vers);
-
- if (ip_vers == 6) {
- memcpy(&cmd.data.setdelip6.ip, ip, ip_len);
- memcpy(&cmd.data.setdelip6.netmask, netmask, ip_len);
- cmd.data.setdelip6.flags = flags;
- } else {
- memcpy(&cmd.data.setdelip4.ip, ip, ip_len);
- memcpy(&cmd.data.setdelip4.netmask, netmask, ip_len);
- cmd.data.setdelip4.flags = flags;
- }
-
- return qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE |
- ((ipacmd ==
- IPA_CMD_SETIP) ? IPA_SETIP_FLAG : 0));
-}
-
-static int
-qeth_send_setdelipm(struct qeth_card *card, __u8 * ip, __u8 * mac,
- int ipacmd, short ip_vers)
-{
- struct ipa_cmd cmd;
- int ip_len = (ip_vers == 6) ? 16 : 4;
-
- qeth_fill_ipa_cmd(card, &cmd, ipacmd, ip_vers);
- memcpy(&cmd.data.setdelipm.mac, mac, 6);
- if (ip_vers == 6) {
- memcpy(&cmd.data.setdelipm.ip6, ip, ip_len);
- } else {
- memcpy(&cmd.data.setdelipm.ip4_6, ip, ip_len);
- }
-
- return qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE |
- ((ipacmd ==
- IPA_CMD_SETIPM) ? IPA_SETIP_FLAG : 0));
-}
-
-#define PRINT_SETIP_ERROR(x) \
- if (result) \
- PRINT_ERR("setip%c: return code 0x%x (%s)\n",x,result, \
- (result==0xe002)?"invalid mtu size": \
- (result==0xe005)?"duplicate ip address": \
- (result==0xe0a5)?"duplicate ip address": \
- (result==0xe006)?"ip table full": \
- (result==0xe008)?"startlan not received": \
- (result==0xe009)?"setip already received": \
- (result==0xe00a)?"dup network ip address": \
- (result==0xe00b)?"mblk no free main task entry": \
- (result==0xe00d)?"invalid ip version": \
- (result==0xe00e)?"unsupported arp assist cmd": \
- (result==0xe00f)?"arp assist not enabled": \
- (result==0xe080)?"startlan disabled": \
- (result==0xf012)?"unicast IP address invalid": \
- (result==0xf013)?"multicast router limit reached": \
- (result==0xf014)?"stop assist not supported": \
- (result==0xf015)?"multicast assist not set": \
- (result==0xf080)?"VM: startlan disabled": \
- (result==-1)?"IPA communication timeout": \
- "unknown return code")
-
-static inline int
-qeth_send_setip(struct qeth_card *card, __u8 * ip,
- __u8 * netmask, short ip_vers, int use_retries)
-{
- int result;
- int retries;
- char dbf_text[15];
- int takeover = 0;
-
- retries = (use_retries) ? QETH_SETIP_RETRIES : 1;
- if (qeth_is_ipa_covered_by_ipato_entries(ip_vers, ip, card)) {
- QETH_DBF_CARD2(0, trace, "ipto", card);
- if (ip_vers == 4) {
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) ip);
- *((__u32 *) (&dbf_text[4])) = *((__u32 *) netmask);
- QETH_DBF_HEX2(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- } else {
- QETH_DBF_HEX2(0, trace, ip, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, ip + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, netmask, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, netmask + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- }
- takeover = 1;
- } else {
- }
-retry:
- result = qeth_send_setdelip(card, ip, netmask, IPA_CMD_SETIP, ip_vers,
- (takeover) ? IPA_SETIP_TAKEOVER_FLAGS :
- IPA_SETIP_FLAGS);
- PRINT_SETIP_ERROR(' ');
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "SETIPFLD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-
- if (((result == -1) || (result == 0xe080) ||(result==0xf080)) &&
- (retries--)) {
- QETH_DBF_CARD2(0, trace, "sipr", card);
- if (ip_vers == 4) {
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) ip);
- *((__u32 *) (&dbf_text[4])) = *((__u32 *) netmask);
- QETH_DBF_HEX2(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- } else {
- QETH_DBF_HEX2(0, trace, ip, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, ip + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, netmask, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, netmask + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- }
- PRINT_WARN("trying again...\n");
- goto retry;
- }
-
- return result;
-}
-
-static inline int
-qeth_send_delip(struct qeth_card *card, __u8 * ip,
- __u8 * netmask, short ip_vers)
-{
- return qeth_send_setdelip(card, ip, netmask, IPA_CMD_DELIP, ip_vers,
- IPA_DELIP_FLAGS);
-}
-
-static inline int
-qeth_send_setipm(struct qeth_card *card, __u8 * ip,
- __u8 * mac, short ip_vers, int use_retries)
-{
- int result;
- int retries;
- char dbf_text[15];
-
- retries = (use_retries) ? QETH_SETIP_RETRIES : 1;
- if (qeth_is_ipa_covered_by_ipato_entries(ip_vers, ip, card)) {
- QETH_DBF_CARD2(0, trace, "imto", card);
- if (ip_vers == 4) {
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) ip);
- QETH_DBF_HEX2(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- } else {
- QETH_DBF_HEX2(0, trace, ip, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, ip + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- }
- }
-
-retry:
- result = qeth_send_setdelipm(card, ip, mac, IPA_CMD_SETIPM, ip_vers);
- PRINT_SETIP_ERROR('m');
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "SETIMFLD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-
- if ((result == -1) && (retries--)) {
- QETH_DBF_CARD2(0, trace, "simr", card);
- if (ip_vers == 4) {
- sprintf(dbf_text, "%08x", *((__u32 *) ip));
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else {
- QETH_DBF_HEX2(0, trace, ip, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, ip + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- }
- QETH_DBF_HEX2(0, trace, mac, OSA_ADDR_LEN);
- PRINT_WARN("trying again...\n");
- goto retry;
- }
-
- return result;
-}
-
-static inline int
-qeth_send_delipm(struct qeth_card *card, __u8 * ip, __u8 * mac, short ip_vers)
-{
- return qeth_send_setdelipm(card, ip, mac, IPA_CMD_DELIPM, ip_vers);
-}
-
-static int
-qeth_add_vipa_entry(struct qeth_card *card, int version, __u8 * addr, int flag)
-{
- struct qeth_vipa_entry *entry, *e;
- int result = 0;
-
- entry =
- (struct qeth_vipa_entry *) kmalloc(sizeof (struct qeth_vipa_entry),
- GFP_KERNEL);
- if (!entry) {
- PRINT_ERR("not enough memory for vipa handling\n");
- return -ENOMEM;
- }
- entry->version = version;
- entry->flag = flag;
- memcpy(entry->ip, addr, 16);
- entry->state = VIPA_2_B_ADDED;
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- if (e->version != version)
- goto next;
- if (memcmp(e->ip, addr, (version == 4) ? 4 : 16))
- goto next;
- if (flag == IPA_SETIP_VIPA_FLAGS) {
- PRINT_ERR("vipa already set\n");
- } else {
- PRINT_ERR("rxip already set\n");
- }
- kfree(entry);
- result = -EALREADY;
- goto out;
- next:
- e = e->next;
- }
- entry->next = card->vipa_list;
- card->vipa_list = entry;
-out:
- write_unlock(&card->vipa_list_lock);
- return result;
-}
-
-static int
-qeth_del_vipa_entry(struct qeth_card *card, int version, __u8 * addr, int flag)
-{
- struct qeth_vipa_entry *e;
- int result = 0;
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- if (e->version != version)
- goto next;
- if (e->flag != flag)
- goto next;
- if (memcmp(e->ip, addr, (version == 4) ? 4 : 16))
- goto next;
- e->state = VIPA_2_B_REMOVED;
- goto out;
- next:
- e = e->next;
- }
- if (flag == IPA_SETIP_VIPA_FLAGS) {
- PRINT_ERR("vipa not found\n");
- } else {
- PRINT_ERR("rxip not found\n");
- }
- result = -ENOENT;
-out:
- write_unlock(&card->vipa_list_lock);
- return result;
-}
-
-static void
-qeth_set_vipas(struct qeth_card *card, int set_only)
-{
- struct qeth_vipa_entry *e, *le = NULL, *ne; /* ne stands for new entry,
- le is last entry */
- char dbf_text[15];
- int result;
- __u8 netmask[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- struct qeth_vipa_entry *priv_add_list = NULL;
- struct qeth_vipa_entry *priv_del_list = NULL;
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- switch (e->state) {
- case VIPA_2_B_ADDED:
- if (!set_only)
- break;
- if (!atomic_read(&card->is_open))
- break;
- /* we don't want to hold the lock for a long time...
- * so we clone the entry */
- ne = (struct qeth_vipa_entry *)
- kmalloc(sizeof (struct qeth_vipa_entry),
- GFP_ATOMIC);
- if (ne) {
- ne->version = e->version;
- ne->flag = e->flag;
- memcpy(ne->ip, e->ip, 16);
- ne->next = priv_add_list;
- priv_add_list = ne;
-
- e->state = VIPA_ESTABLISHED;
- } else {
- PRINT_ERR("not enough for internal vipa "
- "handling... trying to set "
- "vipa next time.\n");
- qeth_start_softsetup_thread(card);
- }
- break;
- case VIPA_2_B_REMOVED:
- if (set_only)
- break;
- if (le)
- le->next = e->next;
- else
- card->vipa_list = e->next;
- ne = e->next;
- e->next = priv_del_list;
- priv_del_list = e;
- e = ne;
- continue;
- case VIPA_ESTABLISHED:
- if (atomic_read(&card->is_open))
- break;
- /* we don't want to hold the lock for a long time...
- * so we clone the entry */
- ne = (struct qeth_vipa_entry *)
- kmalloc(sizeof (struct qeth_vipa_entry),
- GFP_KERNEL);
- if (ne) {
- ne->version = e->version;
- ne->flag = e->flag;
- memcpy(ne->ip, e->ip, 16);
- ne->next = priv_del_list;
- priv_del_list = ne;
-
- e->state = VIPA_2_B_ADDED;
- } else {
- PRINT_ERR("not enough for internal vipa "
- "handling... VIPA/RXIP remains set "
- "although device is stopped.\n");
- qeth_start_softsetup_thread(card);
- }
- break;
- default:
- break;
- }
- le = e;
- e = e->next;
- }
- write_unlock(&card->vipa_list_lock);
-
- while (priv_add_list) {
- result = qeth_send_setdelip(card, priv_add_list->ip, netmask,
- IPA_CMD_SETIP,
- priv_add_list->version,
- priv_add_list->flag);
- PRINT_SETIP_ERROR('s');
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "SETSVFLD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- if (priv_add_list->version == 4) {
- PRINT_ERR("going to leave vipa/rxip x%08x"
- "unset...\n",
- *((__u32 *) & priv_add_list->ip[0]));
- sprintf(dbf_text, "%08x",
- *((__u32 *) & priv_add_list->ip[0]));
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else {
- PRINT_ERR("going to leave vipa/rxip "
- "%08x%08x%08x%08x unset...\n",
- *((__u32 *) & priv_add_list->ip[0]),
- *((__u32 *) & priv_add_list->ip[4]),
- *((__u32 *) & priv_add_list->ip[8]),
- *((__u32 *) & priv_add_list->ip[12]));
- QETH_DBF_HEX2(0, trace, &priv_add_list->ip[0],
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, &priv_add_list->ip[8],
- QETH_DBF_TRACE_LEN);
- }
- }
- e = priv_add_list;
- priv_add_list = priv_add_list->next;
- kfree(e);
- }
-
- while (priv_del_list) {
- result = qeth_send_setdelip(card, priv_del_list->ip, netmask,
- IPA_CMD_DELIP,
- priv_del_list->version,
- priv_del_list->flag);
- if (result) {
- QETH_DBF_CARD2(0, trace, "DELSVFLD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- if (priv_del_list->version == 4) {
- PRINT_ERR("could not delete vipa/rxip "
- "%08x...\n",
- *((__u32 *) & priv_del_list->ip[0]));
- sprintf(dbf_text, "%08x",
- *((__u32 *) & priv_del_list->ip[0]));
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else {
- PRINT_ERR("could not delete vipa/rxip "
- "%08x%08x%08x%08x...\n",
- *((__u32 *) & priv_del_list->ip[0]),
- *((__u32 *) & priv_del_list->ip[4]),
- *((__u32 *) & priv_del_list->ip[8]),
- *((__u32 *) & priv_del_list->ip[12]));
- QETH_DBF_HEX2(0, trace, &priv_del_list->ip[0],
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX2(0, trace, &priv_del_list->ip[8],
- QETH_DBF_TRACE_LEN);
- }
-/* in case of problems, it's better if we just display a message and
- * don't requeue the entry back...
- write_lock(&card->vipa_list_lock);
- e=card->vipa_list;
- card->vipa_list=priv_del_list;
- priv_del_list=priv_del_list->next;
- card->vipa_list->next=e;
- card->vipa_list->state=VIPA_ESTABLISHED;
- write_unlock(&card->vipa_list_lock);
- continue;
-*/
- }
- e = priv_del_list;
- priv_del_list = priv_del_list->next;
- kfree(e);
- }
-}
-
-static void
-qeth_refresh_vipa_states(struct qeth_card *card)
-{
- struct qeth_vipa_entry *e;
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- if (e->state == VIPA_ESTABLISHED)
- e->state = VIPA_2_B_ADDED;
- e = e->next;
- }
- write_unlock(&card->vipa_list_lock);
-}
-
-static inline int
-qeth_send_setrtg(struct qeth_card *card, int routing_type, short ip_vers)
-{
- struct ipa_cmd cmd;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_SETRTG, ip_vers);
- /* strip off RESET_ROUTING_FLAG */
- cmd.data.setrtg.type = (routing_type) & (ROUTER_MASK);
-
- return qeth_send_ipa_cmd(card, &cmd, 0, IPA_CMD_STATE);
-}
-
-static int
-qeth_is_ipa_in_list(struct in_ifaddr *ip, struct in_ifaddr *list)
-{
- while (list) {
- if (ip->ifa_address == list->ifa_address)
- return 1;
- list = list->ifa_next;
- }
- return 0;
-}
-
-#ifdef QETH_IPV6
-static int
-qeth_is_ipa_in_list6(struct inet6_ifaddr *ip, struct inet6_ifaddr *list)
-{
- while (list) {
- if (!memcmp(&ip->addr.s6_addr, &list->addr.s6_addr, 16))
- return 1;
- list = list->if_next;
- }
- return 0;
-}
-
-static int
-qeth_add_ifa6_to_list(struct inet6_ifaddr **list, struct inet6_ifaddr *ifa)
-{
- struct inet6_ifaddr *i;
-
- if (*list == NULL) {
- *list = ifa;
- } else {
- if (qeth_is_ipa_in_list6(ifa, *list))
- return -EALREADY;
- i = *list;
- while (i->if_next) {
- i = i->if_next;
- }
- i->if_next = ifa;
- }
- ifa->if_next = NULL;
- return 0;
-}
-#endif /* QETH_IPV6 */
-
-static int
-qeth_add_ifa_to_list(struct in_ifaddr **list, struct in_ifaddr *ifa)
-{
- struct in_ifaddr *i;
-
- if (*list == NULL) {
- *list = ifa;
- } else {
- if (qeth_is_ipa_in_list(ifa, *list))
- return -EALREADY;
- i = *list;
- while (i->ifa_next) {
- i = i->ifa_next;
- }
- i->ifa_next = ifa;
- }
- ifa->ifa_next = NULL;
- return 0;
-}
-
-static void
-__qeth_setips_ipv6(struct qeth_card *card, int use_setip_retries)
-{
-#ifdef QETH_IPV6
- int result;
- char dbf_text[15];
- struct inet6_ifaddr *addr6;
- __u8 netmask[16];
-
-#define FILL_NETMASK(len) { \
- int i,j; \
- for (i=0;i<16;i++) { \
- j=(len)-(i*8); \
- netmask[i]=(__u8)(0xFF00>>j); \
- } \
-}
- /* here we go with IPv6 */
- addr6 = card->ip_current_state.ip6_ifa;
- while (addr6) {
- if (qeth_is_ipa_in_list6(addr6, card->ip_new_state.ip6_ifa)) {
- addr6 = addr6->if_next;
- continue;
- }
- QETH_DBF_TEXT3(0, trace, "setipdl6");
- QETH_DBF_HEX3(0, trace, &addr6->addr.s6_addr,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace,
- ((char *) (&addr6->addr.s6_addr)) +
- QETH_DBF_TRACE_LEN, QETH_DBF_TRACE_LEN);
- sprintf(dbf_text, "nmsk%4u", addr6->prefix_len);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- FILL_NETMASK(addr6->prefix_len);
- result = qeth_send_delip(card,
- (__u8 *) & addr6->addr.s6_addr,
- (__u8 *) & netmask, 6);
- if (result) {
- PRINT_ERR("was not able to delete ip "
- "%04x:%04x:%04x:%04x:%04x:%04x:"
- "%04x:%04x/%u on device %s "
- "(result: 0x%x), "
- "trying to continue\n",
- addr6->addr.s6_addr16[0],
- addr6->addr.s6_addr16[1],
- addr6->addr.s6_addr16[2],
- addr6->addr.s6_addr16[3],
- addr6->addr.s6_addr16[4],
- addr6->addr.s6_addr16[5],
- addr6->addr.s6_addr16[6],
- addr6->addr.s6_addr16[7],
- addr6->prefix_len,
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "std6%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- }
- addr6 = addr6->if_next;
- }
-
- addr6 = card->ip_new_state.ip6_ifa;
- while (addr6) {
- if (qeth_is_ipa_in_list6(addr6,
- card->ip_current_state.ip6_ifa)) {
- addr6 = addr6->if_next;
- continue;
- }
- QETH_DBF_TEXT3(0, trace, "setipst6");
- QETH_DBF_HEX3(0, trace, &addr6->addr.s6_addr,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace,
- ((char *) (&addr6->addr.s6_addr)) +
- QETH_DBF_TRACE_LEN, QETH_DBF_TRACE_LEN);
- sprintf(dbf_text, "nmsk%4u", addr6->prefix_len);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- FILL_NETMASK(addr6->prefix_len);
- result = qeth_send_setip(card,
- (__u8 *) & addr6->addr.s6_addr,
- (__u8 *) & netmask, 6,
- use_setip_retries);
- if (!result) {
- addr6 = addr6->if_next;
- continue;
- }
- PRINT_ERR("was not able to set ip "
- "%04x:%04x:%04x:%04x:%04x:%04x:"
- "%04x:%04x/%u on device %s "
- "(result: 0x%x), trying to continue\n",
- addr6->addr.s6_addr16[0],
- addr6->addr.s6_addr16[1],
- addr6->addr.s6_addr16[2],
- addr6->addr.s6_addr16[3],
- addr6->addr.s6_addr16[4],
- addr6->addr.s6_addr16[5],
- addr6->addr.s6_addr16[6],
- addr6->addr.s6_addr16[7],
- addr6->prefix_len,
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "sts6%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- addr6 = addr6->if_next;
- }
-#endif /* QETH_IPV6 */
-}
-
-static int
-qeth_setips(struct qeth_card *card, int use_setip_retries)
-{
- struct in_ifaddr *addr;
- int result;
- char dbf_text[15];
-
- QETH_DBF_CARD3(0, trace, "stip", card);
-
- addr = card->ip_current_state.ip_ifa;
- while (addr) {
- if (!qeth_is_ipa_in_list(addr, card->ip_new_state.ip_ifa)) {
- QETH_DBF_TEXT3(0, trace, "setipdel");
- *((__u32 *) (&dbf_text[0])) =
- *((__u32 *) & addr->ifa_address);
- *((__u32 *) (&dbf_text[4])) =
- *((__u32 *) & addr->ifa_mask);
- QETH_DBF_HEX3(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- result =
- qeth_send_delip(card, (__u8 *) & addr->ifa_address,
- (__u8 *) & addr->ifa_mask, 4);
- if (result) {
- PRINT_ERR("was not able to delete ip "
- "%08x/%08x on device %s "
- "(result: 0x%x), "
- "trying to continue\n",
- addr->ifa_address, addr->ifa_mask,
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "stdl%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- }
- }
- addr = addr->ifa_next;
- }
-
- addr = card->ip_new_state.ip_ifa;
- while (addr) {
- if (qeth_is_ipa_in_list(addr, card->ip_current_state.ip_ifa)) {
- addr = addr->ifa_next;
- continue;
- }
- QETH_DBF_TEXT3(0, trace, "setipset");
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) & addr->ifa_address);
- *((__u32 *) (&dbf_text[4])) = *((__u32 *) & addr->ifa_mask);
- QETH_DBF_HEX3(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- result = qeth_send_setip(card, (__u8 *) & addr->ifa_address,
- (__u8 *) & addr->ifa_mask, 4,
- use_setip_retries);
- if (!result) {
- addr = addr->ifa_next;
- continue;
- }
- PRINT_ERR("was not able to set ip "
- "%08x/%08x on device %s, trying to continue\n",
- addr->ifa_address, addr->ifa_mask,
- CARD_BUS_ID(card));
- sprintf(dbf_text, "stst%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- addr = addr->ifa_next;
- }
-
- __qeth_setips_ipv6(card, use_setip_retries);
-
- return 0;
-}
-
-static int
-qeth_is_ipma_in_list(struct qeth_ipm_mac *ipma, struct qeth_ipm_mac *list)
-{
- while (list) {
- if ((!memcmp(ipma->ip, list->ip, 16)) &&
- (!memcmp(ipma->mac, list->mac, 6)))
- return 1;
- list = list->next;
- }
- return 0;
-}
-
-static void
-qeth_remove_mc_ifa_from_list(struct qeth_ipm_mac **list,
- struct qeth_ipm_mac *ipma)
-{
- struct qeth_ipm_mac *i, *li = NULL;
-
- if ((!(*list)) || (!ipma))
- return;
-
- if (*list == ipma) {
- *list = ipma->next;
- } else {
- i = *list;
- while (i) {
- if (i == ipma) {
- li->next = i->next;
- } else {
- li = i;
- }
- i = i->next;
- }
- }
-}
-
-static int
-qeth_add_mc_ifa_to_list(struct qeth_ipm_mac **list, struct qeth_ipm_mac *ipma)
-{
- struct qeth_ipm_mac *i;
-
- if (qeth_is_ipma_in_list(ipma, *list))
- return -EALREADY;
-
- if (*list == NULL) {
- *list = ipma;
- } else {
- i = *list;
- while (i->next) {
- i = i->next;
- }
- i->next = ipma;
- }
- ipma->next = NULL;
- return 0;
-}
-
-static void
-__qeth_setipms_ipv6(struct qeth_card *card, int use_setipm_retries)
-{
-#ifdef QETH_IPV6
- struct qeth_ipm_mac *addr;
- int result;
- char dbf_text[15];
-
- /* here we go with IPv6 */
- addr = card->ip_mc_current_state.ipm6_ifa;
- while (addr) {
- if (!qeth_is_ipma_in_list(addr,
- card->ip_mc_new_state.ipm6_ifa)) {
- QETH_DBF_TEXT3(0, trace, "setimdl6");
- QETH_DBF_HEX3(0, trace, &addr->ip[0],
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace,
- (&addr->ip[0]) + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace, &addr->mac,
- QETH_DBF_TRACE_LEN);
- result = qeth_send_delipm(card,
- (__u8 *) & addr->ip[0],
- (__u8 *) addr->mac, 6);
- if (result) {
- PRINT_ERR("was not able to delete "
- "multicast ip %04x:%04x:"
- "%04x:%04x:%04x:%04x:%04x:%04x/"
- "%02x%02x%02x%02x%02x%02x "
- "on device %s (result: 0x%x), "
- "trying to continue\n",
- *((__u16 *) & addr->ip[0]),
- *((__u16 *) & addr->ip[2]),
- *((__u16 *) & addr->ip[4]),
- *((__u16 *) & addr->ip[6]),
- *((__u16 *) & addr->ip[8]),
- *((__u16 *) & addr->ip[10]),
- *((__u16 *) & addr->ip[12]),
- *((__u16 *) & addr->ip[14]),
- addr->mac[0], addr->mac[1],
- addr->mac[2], addr->mac[3],
- addr->mac[4], addr->mac[5],
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "smd6%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- }
- }
- addr = addr->next;
- }
-
- addr = card->ip_mc_new_state.ipm6_ifa;
- while (addr) {
- if (qeth_is_ipma_in_list(addr,
- card->ip_mc_current_state.ipm6_ifa)) {
- qeth_remove_mc_ifa_from_list(
- &card->ip_mc_new_state.ipm6_ifa,
- addr);
- addr = addr->next;
- continue;
- }
- QETH_DBF_TEXT3(0, trace, "setimst6");
- QETH_DBF_HEX3(0, trace, &addr->ip[0], QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace, (&addr->ip[0]) + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace, &addr->mac, QETH_DBF_TRACE_LEN);
- result = qeth_send_setipm(card,
- (__u8 *) & addr->ip[0],
- (__u8 *) addr->mac, 6,
- use_setipm_retries);
- if (result) {
- PRINT_ERR("was not able to set "
- "multicast ip %04x:%04x:"
- "%04x:%04x:%04x:%04x:%04x:%04x/"
- "%02x%02x%02x%02x%02x%02x "
- "on device %s (result: 0x%x), "
- "trying to continue\n",
- *((__u16 *) & addr->ip[0]),
- *((__u16 *) & addr->ip[2]),
- *((__u16 *) & addr->ip[4]),
- *((__u16 *) & addr->ip[6]),
- *((__u16 *) & addr->ip[8]),
- *((__u16 *) & addr->ip[10]),
- *((__u16 *) & addr->ip[12]),
- *((__u16 *) & addr->ip[14]),
- addr->mac[0], addr->mac[1],
- addr->mac[2], addr->mac[3],
- addr->mac[4], addr->mac[5],
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "sms6%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- } else {
- qeth_remove_mc_ifa_from_list(
- &card->ip_mc_new_state.ipm6_ifa,
- addr);
- qeth_add_mc_ifa_to_list(
- &card->ip_mc_current_state.ipm6_ifa,
- addr);
- }
- addr = addr->next;
- }
-#endif /* QETH_IPV6 */
-}
-
-static int
-qeth_setipms(struct qeth_card *card, int use_setipm_retries)
-{
- struct qeth_ipm_mac *addr;
- int result;
- char dbf_text[15];
-
- QETH_DBF_CARD3(0, trace, "stim", card);
-
- if (!qeth_is_supported(IPA_MULTICASTING))
- return 0;
- addr = card->ip_mc_current_state.ipm_ifa;
- while (addr) {
- if (!qeth_is_ipma_in_list(addr,
- card->ip_mc_new_state.ipm_ifa)) {
- QETH_DBF_TEXT3(0, trace, "setimdel");
- sprintf(dbf_text, "%08x", *((__u32 *) & addr->ip[0]));
- QETH_DBF_TEXT3(0, trace, dbf_text);
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) & addr->mac);
- *((__u32 *) (&dbf_text[4])) =
- *(((__u32 *) & addr->mac) + 1);
- QETH_DBF_HEX3(0, trace, dbf_text,
- QETH_DBF_TRACE_LEN);
- result = qeth_send_delipm(card,
- (__u8 *) & addr->ip[0],
- (__u8 *) addr->mac, 4);
- if (result) {
- PRINT_ERR("was not able to delete "
- "multicast ip %08x/"
- "%02x%02x%02x%02x%02x%02x "
- "on device %s "
- "(result: 0x%x), "
- "trying to continue\n",
- *((__u32 *) & addr->ip[0]),
- addr->mac[0], addr->mac[1],
- addr->mac[2], addr->mac[3],
- addr->mac[4], addr->mac[5],
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "smdl%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- }
- }
- addr = addr->next;
- }
-
- addr = card->ip_mc_new_state.ipm_ifa;
- while (addr) {
- if (qeth_is_ipma_in_list(addr,
- card->ip_mc_current_state.ipm_ifa)) {
- addr = addr->next;
- continue;
- }
- QETH_DBF_TEXT3(0, trace, "setimset");
- sprintf(dbf_text, "%08x", *((__u32 *) & addr->ip[0]));
- QETH_DBF_TEXT3(0, trace, dbf_text);
- *((__u32 *) (&dbf_text[0])) = *((__u32 *) & addr->mac);
- *((__u32 *) (&dbf_text[4])) = *(((__u32 *) & addr->mac) + 1);
- QETH_DBF_HEX3(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
- result = qeth_send_setipm(card, (__u8 *) & addr->ip[0],
- (__u8 *) addr->mac, 4,
- use_setipm_retries);
- if (result) {
- PRINT_ERR("was not able to set multicast ip %08x/"
- "%02x%02x%02x%02x%02x%02x "
- "on device %s (result: 0x%x), "
- "trying to continue\n",
- *((__u32 *) & addr->ip[0]),
- addr->mac[0], addr->mac[1],
- addr->mac[2], addr->mac[3],
- addr->mac[4], addr->mac[5],
- CARD_BUS_ID(card), result);
- sprintf(dbf_text, "smst%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- qeth_remove_mc_ifa_from_list
- (&card->ip_mc_current_state.ipm_ifa, addr);
- }
- addr = addr->next;
- }
- __qeth_setipms_ipv6(card, use_setipm_retries);
- return 0;
-}
-
-static void
-qeth_clone_ifa(struct in_ifaddr *src, struct in_ifaddr *dest)
-{
- memcpy(dest, src, sizeof (struct in_ifaddr));
- dest->ifa_next = NULL;
-}
-
-#ifdef QETH_IPV6
-static void
-qeth_clone_ifa6(struct inet6_ifaddr *src, struct inet6_ifaddr *dest)
-{
- memcpy(dest, src, sizeof (struct inet6_ifaddr));
- dest->if_next = NULL;
-}
-#endif /* QETH_IPV6 */
-
-#define QETH_STANDARD_RETVALS \
- ret_val=-EIO; \
- if (result == -EFAULT) ret_val = -EFAULT; \
- if (result==IPA_REPLY_SUCCESS) ret_val=0; \
- if (result==IPA_REPLY_FAILED) ret_val=-EIO; \
- if (result==IPA_REPLY_OPNOTSUPP) ret_val=-EOPNOTSUPP
-
-static int
-qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- char *data;
- int result, i, ret_val;
- int version = 4;
- struct qeth_card *card;
- char dbf_text[15];
- char buff[100];
-
- card = (struct qeth_card *) dev->priv;
-
- PRINT_STUPID("CALL: qeth_do_ioctl called with cmd %i (=0x%x).\n", cmd,
- cmd);
- QETH_DBF_CARD2(0, trace, "ioct", card);
- sprintf(dbf_text, "cmd=%4x", cmd);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- QETH_DBF_HEX2(0, trace, &rq, sizeof (void *));
-
- if ((cmd < SIOCDEVPRIVATE) || (cmd > SIOCDEVPRIVATE + 5))
- return -EOPNOTSUPP;
- if (copy_from_user(buff, rq->ifr_ifru.ifru_data, sizeof (buff)))
- return -EFAULT;
- data = buff;
-
- if ((!atomic_read(&card->is_registered)) ||
- (!atomic_read(&card->is_hardsetup)))
- return -ENODEV;
-
- if (atomic_read(&card->shutdown_phase))
- return -ENODEV;
-
- spin_lock(&card->ioctl_lock);
-
- if (atomic_read(&card->shutdown_phase)) {
- ret_val = -ENODEV;
- goto out;
- }
- if ((!atomic_read(&card->is_registered)) ||
- (!atomic_read(&card->is_hardsetup))) {
- ret_val = -ENODEV;
- goto out;
- }
-
- switch (cmd) {
- case SIOCDEVPRIVATE + 0:
- if (!capable(CAP_NET_ADMIN)) {
- ret_val = -EPERM;
- break;
- }
- result =
- qeth_send_setassparms(card, version, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
- rq->ifr_ifru.ifru_ivalue, 4);
- QETH_STANDARD_RETVALS;
- if (result == 3)
- ret_val = -EINVAL;
- break;
- case SIOCDEVPRIVATE + 1:
- if (!capable(CAP_NET_ADMIN)) {
- ret_val = -EPERM;
- break;
- }
- result = qeth_queryarp(card, rq, version, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_QUERY_INFO, data, 4);
-
- QETH_STANDARD_RETVALS;
- break;
- case SIOCDEVPRIVATE + 2:
- if (!capable(CAP_NET_ADMIN)) {
- ret_val = -EPERM;
- break;
- }
- for (i = 12; i < 24; i++)
- if (data[i])
- version = 6;
- result =
- qeth_send_setassparms(card, version, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_ADD_ENTRY,
- (long) data, 56);
- QETH_STANDARD_RETVALS;
- break;
- case SIOCDEVPRIVATE + 3:
- if (!capable(CAP_NET_ADMIN)) {
- ret_val = -EPERM;
- break;
- }
- for (i = 4; i < 12; i++)
- if (data[i])
- version = 6;
- result =
- qeth_send_setassparms(card, version, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_REMOVE_ENTRY,
- (long) data, 16);
- QETH_STANDARD_RETVALS;
- break;
- case SIOCDEVPRIVATE + 4:
- if (!capable(CAP_NET_ADMIN)) {
- ret_val = -EPERM;
- break;
- }
- result =
- qeth_send_setassparms(card, version, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_FLUSH_CACHE, 0, 0);
- QETH_STANDARD_RETVALS;
- break;
- case SIOCDEVPRIVATE + 5:
-
- result =
- qeth_send_snmp_control(card, rq, IPA_CMD_SETADAPTERPARMS,
- IPA_SETADP_SET_SNMP_CONTROL, data,
- 4);
- QETH_STANDARD_RETVALS;
- break;
-
- default:
- ret_val = -EOPNOTSUPP;
- goto out;
- }
-out:
- spin_unlock(&card->ioctl_lock);
-
- sprintf(dbf_text, "ret=%4x", ret_val);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- return ret_val;
-}
-
-static void
-qeth_clear_ifamc_list(struct qeth_ipm_mac **ifa_list)
-{
- struct qeth_ipm_mac *ifa;
- while (*ifa_list) {
- ifa = *ifa_list;
- *ifa_list = ifa->next;
- kfree(ifa);
- }
-}
-
-#ifdef QETH_IPV6
-static void
-qeth_clear_ifa6_list(struct inet6_ifaddr **ifa_list)
-{
- struct inet6_ifaddr *ifa;
- while (*ifa_list) {
- ifa = *ifa_list;
- *ifa_list = ifa->if_next;
- kfree(ifa);
- }
-}
-
-static inline void
-__qeth_append_vlan_ipas_v6(struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- char dbf_text[15];
- struct vlan_group *card_group;
- int i;
- int remove;
- struct inet6_ifaddr *ifa, *ifanew;
-
- /*
- * append all known VLAN IP Addresses corresponding to the real device
- * card->dev->ifindex
- */
- QETH_DBF_TEXT4(0, trace, "to-vip6s");
- if ((!qeth_is_supported(IPA_FULL_VLAN)) || (!atomic_read(&card->is_open)))
- return;
-
- card_group = (struct vlan_group *) card->vlangrp;
-
- if (!card_group)
- return;
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- if (!card_group->vlan_devices[i] ||
- !(card_group->vlan_devices[i]->flags & IFF_UP) ||
- !(struct inet6_dev *) card_group->vlan_devices[i]->ip6_ptr)
- continue;
- ifa = ((struct inet6_dev *)
- card_group->vlan_devices[i]->ip6_ptr)->addr_list;
-
- while (ifa) {
- ifanew = kmalloc(sizeof(struct inet6_ifaddr),
- GFP_KERNEL);
- if (!ifanew) {
- PRINT_WARN("No memory for IP address "
- "handling. Some of the IPs "
- "will not be set on %s.\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPNMEM");
- } else {
- qeth_clone_ifa6(ifa, ifanew);
- remove = qeth_add_ifa6_to_list
- (&card->ip_new_state.ip6_ifa, ifanew);
- QETH_DBF_HEX4(0, trace,
- &ifanew->addr.s6_addr,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace,
- &ifanew->addr.s6_addr +
- QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- sprintf(dbf_text, "pref%4u", ifanew->prefix_len);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- if (remove) {
- kfree(ifanew);
- QETH_DBF_TEXT4(0, trace, "alrdv6rm");
- }
- }
- ifa = ifa->if_next;
- }
- }
-#endif
-}
-
-static inline void
-__qeth_append_vlan_ipas_v6_mc(struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- struct vlan_group *card_group;
- int i;
- int remove;
- struct inet6_dev *in6_vdev;
- char buf[MAX_ADDR_LEN];
- struct qeth_ipm_mac *ipmanew;
- struct ifmcaddr6 *im6;
-
- QETH_DBF_TEXT4(0, trace, "tovipm6s");
- if (!qeth_is_supported(IPA_FULL_VLAN) || !atomic_read(&card->is_open))
- return;
-
- card_group = (struct vlan_group *) card->vlangrp;
- if (!card_group)
- return;
-
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- if (!card_group->vlan_devices[i] ||
- !(card_group->vlan_devices[i]->flags & IFF_UP))
- continue;
-
- in6_vdev = in6_dev_get(card_group->vlan_devices[i]);
- if (!in6_vdev) {
- QETH_DBF_CARD2(0, trace, "id26", card);
- continue;
- }
-
- read_lock(&in6_vdev->lock);
- for (im6 = in6_vdev->mc_list; im6; im6 = im6->next) {
- ndisc_mc_map(&im6->mca_addr, buf,
- card_group->vlan_devices[i], 0);
- ipmanew = (struct qeth_ipm_mac *)
- kmalloc(sizeof(struct qeth_ipm_mac), GFP_KERNEL);
- if (!ipmanew) {
- PRINT_WARN("No memory for IPM address "
- "handling. Multicast IP "
- "%04x:%04x:%04x:%04x:%04x:"
- "%04x:%04x:%04x"
- "will not be set on %s.\n",
- im6->mca_addr.s6_addr16[0],
- im6->mca_addr.s6_addr16[1],
- im6->mca_addr.s6_addr16[2],
- im6->mca_addr.s6_addr16[3],
- im6->mca_addr.s6_addr16[4],
- im6->mca_addr.s6_addr16[5],
- im6->mca_addr.s6_addr16[6],
- im6->mca_addr.s6_addr16[7],
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPMNMM");
- } else {
- memset(ipmanew, 0, sizeof(struct qeth_ipm_mac));
- memcpy(ipmanew->mac, buf,OSA_ADDR_LEN);
- memcpy(ipmanew->ip, im6->mca_addr.s6_addr, 16);
- ipmanew->next = NULL;
- remove = qeth_add_mc_ifa_to_list
- (&card->ip_mc_new_state.ipm6_ifa,
- ipmanew);
- QETH_DBF_HEX4(0, trace, &ipmanew->ip,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace, &ipmanew->ip +
- QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace, &ipmanew->mac,
- QETH_DBF_TRACE_LEN);
-
- if (remove) {
- QETH_DBF_TEXT4(0, trace, "mlrdv6rm");
- kfree(ipmanew);
- }
- }
- }
- read_unlock(&in6_vdev->lock);
- in6_dev_put(in6_vdev);
- }
-#endif
-}
-
-static struct inet6_dev *
-__qeth_get_mc_lock_v6(struct qeth_card *card)
-{
- struct inet6_dev *in6_dev;
-
- in6_dev = in6_dev_get(card->dev);
-
- if (!in6_dev) {
- QETH_DBF_CARD2(0, trace, "id16", card);
- return ERR_PTR(-ENODEV);
- }
- read_lock(&in6_dev->lock);
- return in6_dev;
-}
-
-static void
-__qeth_takeover_ip_ipms6_mc(struct qeth_card *card, struct inet6_dev *in6_dev)
-{
- int remove;
- struct qeth_ipm_mac *ipmanew;
- struct ifmcaddr6 *im6;
- char buf[MAX_ADDR_LEN];
-
- QETH_DBF_TEXT4(0, trace, "to-ipm6s");
- if (atomic_read(&card->is_open))
- for (im6 = in6_dev->mc_list; im6; im6 = im6->next) {
- ndisc_mc_map(&im6->mca_addr, buf, card->dev, 0);
- ipmanew =
- (struct qeth_ipm_mac *)
- kmalloc(sizeof (struct qeth_ipm_mac), GFP_ATOMIC);
- if (!ipmanew) {
- PRINT_WARN("No memory for IPM address "
- "handling. Multicast IP "
- "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
- "will not be set on %s.\n",
- im6->mca_addr.s6_addr16[0],
- im6->mca_addr.s6_addr16[1],
- im6->mca_addr.s6_addr16[2],
- im6->mca_addr.s6_addr16[3],
- im6->mca_addr.s6_addr16[4],
- im6->mca_addr.s6_addr16[5],
- im6->mca_addr.s6_addr16[6],
- im6->mca_addr.s6_addr16[7],
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPMNMM");
- } else {
- memset(ipmanew, 0,
- sizeof (struct qeth_ipm_mac));
- memcpy(ipmanew->mac, buf, OSA_ADDR_LEN);
- memcpy(ipmanew->ip, im6->mca_addr.s6_addr, 16);
- ipmanew->next = NULL;
- remove =
- qeth_add_mc_ifa_to_list(&card->
- ip_mc_new_state.
- ipm6_ifa, ipmanew);
- QETH_DBF_HEX4(0, trace, &ipmanew->ip,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace,
- &ipmanew->ip + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace, &ipmanew->mac,
- QETH_DBF_TRACE_LEN);
- if (remove) {
- QETH_DBF_TEXT4(0, trace, "mlrdy6rm");
- kfree(ipmanew);
- }
- }
- }
- __qeth_append_vlan_ipas_v6_mc(card);
-
- read_unlock(&in6_dev->lock);
- in6_dev_put(in6_dev);
-}
-#endif /* QETH_IPV6 */
-
-static void
-qeth_takeover_ip_ipms6(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- struct inet6_ifaddr *ifa, *ifanew;
- char dbf_text[15];
- int remove;
- struct inet6_dev *in6_dev;
-
- QETH_DBF_CARD3(0, trace, "tip6", card);
- /* unicast */
- /* clear ip_current_state */
- qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
- /* take it over */
- card->ip_current_state.ip6_ifa = card->ip_new_state.ip6_ifa;
- card->ip_new_state.ip6_ifa = NULL;
-
- in6_dev = __qeth_get_mc_lock_v6(card);
- if (PTR_ERR(in6_dev) == -ENODEV)
- return;
- /* get new one, we try to have the same order as ifa_list in device
- structure, for what reason ever */
- QETH_DBF_TEXT4(0, trace, "to-ip6s");
- if ((atomic_read(&card->is_open)) && (card->dev->ip6_ptr) &&
- (((struct inet6_dev *) card->dev->ip6_ptr)->addr_list)) {
- ifa = ((struct inet6_dev *) card->dev->ip6_ptr)->addr_list;
-
- while (ifa) {
- ifanew =
- kmalloc(sizeof (struct inet6_ifaddr), GFP_ATOMIC);
- if (!ifanew) {
- PRINT_WARN("No memory for IP address "
- "handling. Some of the IPs "
- "will not be set on %s.\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPNMEM");
- } else {
- qeth_clone_ifa6(ifa, ifanew);
- remove =
- qeth_add_ifa6_to_list(&card->ip_new_state.
- ip6_ifa, ifanew);
- QETH_DBF_HEX4(0, trace, &ifanew->addr.s6_addr,
- QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX4(0, trace,
- &ifanew->addr.s6_addr +
- QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
- sprintf(dbf_text, "pref%4u",
- ifanew->prefix_len);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- if (remove) {
- kfree(ifanew);
- QETH_DBF_TEXT4(0, trace, "alrdy6rm");
- }
- }
- ifa = ifa->if_next;
- }
- }
-
- __qeth_append_vlan_ipas_v6(card);
-
- __qeth_takeover_ip_ipms6_mc(card, in6_dev);
-#endif /* QETH_IPV6 */
-}
-
-static void
-qeth_clear_ifa4_list(struct in_ifaddr **ifa_list)
-{
- struct in_ifaddr *ifa;
- while (*ifa_list) {
- ifa = *ifa_list;
- *ifa_list = ifa->ifa_next;
- kfree(ifa);
- }
-}
-
-static inline void
-__qeth_append_vlan_ipas_v4(struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- struct in_ifaddr *ifa, *ifanew;
- char dbf_text[15];
- struct vlan_group *card_group;
- int i;
- int remove;
- struct in_device *vin4_dev;
-
- /*
- * append all known VLAN IP Addresses corresponding to the real device
- * card->dev->ifindex
- */
- QETH_DBF_TEXT4(0, trace, "to-vips");
- if (!qeth_is_supported(IPA_FULL_VLAN) || !atomic_read(&card->is_open))
- return;
-
- card_group = (struct vlan_group *) card->vlangrp;
- if (!card_group)
- return;
-
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- vin4_dev = in_dev_get(card->dev);
- if (!vin4_dev) {
- QETH_DBF_TEXT2(0, trace, "nodvhol2");
- QETH_DBF_TEXT2(0, trace, card->dev_name);
- continue;
- }
- read_lock(&vin4_dev->lock);
-
- if ((card_group->vlan_devices[i]) &&
- (card_group->vlan_devices[i]->flags & IFF_UP)) {
- ifa = ((struct in_device *)
- card_group->vlan_devices[i]->ip_ptr)->ifa_list;
- while (ifa) {
- ifanew = kmalloc(sizeof(struct in_ifaddr),
- GFP_KERNEL);
- if (!ifanew) {
- PRINT_WARN("No memory for IP address "
- "handling. Some of the IPs "
- "will not be set on %s.\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPNMEM");
- } else {
- qeth_clone_ifa(ifa, ifanew);
- remove = qeth_add_ifa_to_list
- (&card->ip_new_state.ip_ifa,
- ifanew);
- *((__u32*) (&dbf_text[0])) =
- *((__u32*) &ifanew->ifa_address);
- *((__u32*) (&dbf_text[4])) =
- *((__u32*) &ifanew->ifa_mask);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- if (remove) {
- kfree(ifanew);
- QETH_DBF_TEXT4(0, trace,
- "alrdv4rm");
- }
- }
- ifa = ifa->ifa_next;
- }
- }
-
- read_unlock(&vin4_dev->lock);
- in_dev_put(vin4_dev);
- }
-#endif /* QETH_VLAN */
-
-}
-
-static inline void
-__qeth_append_vlan_ipas_v4_mc(struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- char dbf_text[15];
- int i;
- int remove;
- struct vlan_group *card_group;
- struct in_device *vin4_dev;
- struct qeth_ipm_mac *ipmanew;
- struct ip_mc_list *im4;
- char buf[MAX_ADDR_LEN];
- __u32 maddr;
-
- QETH_DBF_TEXT4(0, trace, "to-vipms");
- if (!qeth_is_supported(IPA_FULL_VLAN) || !atomic_read(&card->is_open))
- return;
-
- card_group = (struct vlan_group *) card->vlangrp;
- if (!card_group)
- return;
-
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- if (!card_group->vlan_devices[i] ||
- !(card_group->vlan_devices[i]->flags & IFF_UP))
- continue;
-
- vin4_dev = in_dev_get(card_group->vlan_devices[i]);
- if (!vin4_dev) {
- QETH_DBF_TEXT2(0, trace, "novdhol3");
- QETH_DBF_TEXT2(0, trace, card->dev_name);
- QETH_DBF_TEXT2(0, trace,
- card_group->vlan_devices[i]->name);
- continue;
- }
- read_lock(&vin4_dev->lock);
- for (im4 = vin4_dev->mc_list; im4; im4 = im4->next) {
- qeth_get_mac_for_ipm(im4->multiaddr, buf, vin4_dev->dev);
- ipmanew = (struct qeth_ipm_mac *)
- kmalloc(sizeof(struct qeth_ipm_mac), GFP_KERNEL);
- if (!ipmanew) {
- PRINT_WARN("No memory for IPM address "
- "handling. Multicast VLAN IP %08x"
- "will not be set on %s.\n",
- (__u32) im4->multiaddr,
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPMNMM");
- } else {
- memset(ipmanew, 0, sizeof(struct qeth_ipm_mac));
- memcpy(ipmanew->mac, buf, OSA_ADDR_LEN);
- maddr = im4->multiaddr;
- memcpy(&(ipmanew->ip[0]), &maddr, 4);
- memset(&(ipmanew->ip[4]), 0xff, 12);
- ipmanew->next = NULL;
- remove = qeth_add_mc_ifa_to_list
- (&card->ip_mc_new_state.ipm_ifa,
- ipmanew);
- sprintf(dbf_text, "%08x",
- *((__u32 *) &ipmanew->ip));
- QETH_DBF_TEXT4(0, trace, dbf_text);
- QETH_DBF_HEX4(0, trace, &ipmanew->mac,
- QETH_DBF_TRACE_LEN);
- if (remove) {
- QETH_DBF_TEXT4(0, trace, "mlrdv4rm");
- kfree(ipmanew);
- }
- }
- }
- read_unlock(&vin4_dev->lock);
- in_dev_put(vin4_dev);
- }
-#endif /* QETH_VLAN */
-
-}
-
-static struct in_device *
-__qeth_get_mc_lock(struct qeth_card *card)
-{
- struct in_device *in4_dev;
-
- /* multicast */
- /* clear ip_mc_current_state */
- qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
- /* take it over */
- card->ip_mc_current_state.ipm_ifa = card->ip_mc_new_state.ipm_ifa;
- /* get new one, we try to have the same order as ifa_list in device
- structure, for what reason ever */
- card->ip_mc_new_state.ipm_ifa = NULL;
-
- in4_dev = in_dev_get(card->dev);
- if (!in4_dev) {
- QETH_DBF_TEXT2(0, trace, "nodvhol1");
- QETH_DBF_TEXT2(0, trace, card->dev_name);
- return ERR_PTR(-ENODEV);
- }
- read_lock(&in4_dev->lock);
- return in4_dev;
-}
-
-static void
-__qeth_takeover_ip_ipms_mc(struct qeth_card *card, struct in_device *in4_dev)
-{
- char dbf_text[15];
- int remove;
- struct qeth_ipm_mac *ipmanew;
- struct ip_mc_list *im4;
- char buf[MAX_ADDR_LEN];
- __u32 maddr;
-
- QETH_DBF_TEXT4(0, trace, "to-ipms");
- if (atomic_read(&card->is_open))
- for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
- qeth_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev);
- ipmanew =
- (struct qeth_ipm_mac *)
- kmalloc(sizeof (struct qeth_ipm_mac), GFP_ATOMIC);
- if (!ipmanew) {
- PRINT_WARN("No memory for IPM address "
- "handling. Multicast IP %08x"
- "will not be set on %s.\n",
- (__u32) im4->multiaddr,
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPMNMM");
- } else {
- memset(ipmanew, 0,
- sizeof (struct qeth_ipm_mac));
- memcpy(ipmanew->mac, buf, OSA_ADDR_LEN);
- maddr = im4->multiaddr;
- memcpy(&(ipmanew->ip[0]), &maddr, 4);
- memset(&(ipmanew->ip[4]), 0xff, 12);
- ipmanew->next = NULL;
- remove =
- qeth_add_mc_ifa_to_list(&card->
- ip_mc_new_state.
- ipm_ifa, ipmanew);
- sprintf(dbf_text, "%08x",
- *((__u32 *) & ipmanew->ip));
- QETH_DBF_TEXT4(0, trace, dbf_text);
- QETH_DBF_HEX4(0, trace, &ipmanew->mac,
- QETH_DBF_TRACE_LEN);
- if (remove) {
- QETH_DBF_TEXT4(0, trace, "mlrdy4rm");
- kfree(ipmanew);
- }
- }
- }
- __qeth_append_vlan_ipas_v4(card);
-
- read_unlock(&in4_dev->lock);
- in_dev_put(in4_dev);
-
-}
-
-static void
-qeth_takeover_ip_ipms(struct qeth_card *card)
-{
- struct in_ifaddr *ifa, *ifanew;
- char dbf_text[15];
- int remove;
- struct in_device *in4_dev;
-
- QETH_DBF_CARD3(0, trace, "tips", card);
- /* unicast */
- /* clear ip_current_state */
- qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
- /* take it over */
- card->ip_current_state.ip_ifa = card->ip_new_state.ip_ifa;
- card->ip_new_state.ip_ifa = NULL;
-
- in4_dev = __qeth_get_mc_lock(card);
- if (PTR_ERR(in4_dev) == -ENODEV)
- return;
-
- /* get new one, we try to have the same order as ifa_list in device
- structure, for what reason ever */
- QETH_DBF_TEXT4(0, trace, "to-ips");
- if ((atomic_read(&card->is_open)) && (card->dev->ip_ptr) &&
- (((struct in_device *) card->dev->ip_ptr)->ifa_list)) {
- ifa = ((struct in_device *) card->dev->ip_ptr)->ifa_list;
-
- while (ifa) {
- ifanew = kmalloc(sizeof (struct in_ifaddr), GFP_ATOMIC);
- if (!ifanew) {
- PRINT_WARN("No memory for IP address "
- "handling. Some of the IPs "
- "will not be set on %s.\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "TOIPNMEM");
- } else {
- qeth_clone_ifa(ifa, ifanew);
- remove =
- qeth_add_ifa_to_list(&card->ip_new_state.
- ip_ifa, ifanew);
- *((__u32 *) (&dbf_text[0])) =
- *((__u32 *) & ifanew->ifa_address);
- *((__u32 *) (&dbf_text[4])) =
- *((__u32 *) & ifanew->ifa_mask);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- if (remove) {
- kfree(ifanew);
- QETH_DBF_TEXT4(0, trace, "alrdy4rm");
- }
- }
-
- ifa = ifa->ifa_next;
- }
- }
- __qeth_append_vlan_ipas_v4(card);
-
- __qeth_takeover_ip_ipms_mc(card, in4_dev);
-}
-
-static void
-qeth_get_unique_id(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- struct ipa_cmd cmd;
- int result;
- char dbf_text[15];
-
- if (!qeth_is_supported(IPA_IPv6)) {
- card->unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
- UNIQUE_ID_NOT_BY_CARD;
- return;
- }
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_CREATE_ADDR, 6);
-
- *((__u16 *) & cmd.data.create_destroy_addr.unique_id[6]) =
- card->unique_id;
-
- result = qeth_send_ipa_cmd(card, &cmd, 1, IPA_CMD_STATE);
-
- if (result) {
- card->unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
- UNIQUE_ID_NOT_BY_CARD;
- PRINT_WARN("couldn't get a unique id from the card on device "
- "%s (result=x%x), using default id. ipv6 "
- "autoconfig on other lpars may lead to duplicate "
- "ip addresses. please use manually "
- "configured ones.\n",
- CARD_BUS_ID(card), result);
- QETH_DBF_CARD2(0, trace, "unid fld", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else {
- card->unique_id =
- *((__u16 *) & cmd.data.create_destroy_addr.unique_id[6]);
- QETH_DBF_CARD2(0, setup, "uniqueid", card);
- sprintf(dbf_text, "%4x", card->unique_id);
- QETH_DBF_TEXT2(0, setup, dbf_text);
- }
-#else /* QETH_IPV6 */
- card->unique_id =
- UNIQUE_ID_IF_CREATE_ADDR_FAILED | UNIQUE_ID_NOT_BY_CARD;
-#endif /* QETH_IPV6 */
-}
-
-static void
-qeth_put_unique_id(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- struct ipa_cmd cmd;
- int result;
- char dbf_text[15];
-
- /* is also true, if ipv6 is not supported on the card */
- if ((card->unique_id & UNIQUE_ID_NOT_BY_CARD) == UNIQUE_ID_NOT_BY_CARD)
- return;
-
- qeth_fill_ipa_cmd(card, &cmd, IPA_CMD_DESTROY_ADDR, 6);
- *((__u16 *) & cmd.data.create_destroy_addr.unique_id[6]) =
- card->unique_id;
- memcpy(&cmd.data.create_destroy_addr.unique_id[0], card->dev->dev_addr,
- OSA_ADDR_LEN);
-
- result = qeth_send_ipa_cmd(card, &cmd, 1, IPA_CMD_STATE);
-
- if (result) {
- QETH_DBF_CARD2(0, trace, "unibkfld", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-#else /* QETH_IPV6 */
- card->unique_id =
- UNIQUE_ID_IF_CREATE_ADDR_FAILED | UNIQUE_ID_NOT_BY_CARD;
-#endif /* QETH_IPV6 */
-}
-
-static inline void
-__qeth_setparms_hstr(struct qeth_card *card)
-{
- char dbf_text[15];
- int result;
-
- if ((card->link_type != QETH_MPC_LINK_TYPE_HSTR) &&
- (card->link_type != QETH_MPC_LINK_TYPE_LANE_TR))
- return;
-
- QETH_DBF_CARD3(0, trace, "hstr", card);
-
- if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
- result = qeth_send_setadapterparms_mode
- (card, IPA_SETADP_SET_BROADCAST_MODE,
- card->options.broadcast_mode);
- if (result) {
- PRINT_WARN("couldn't set broadcast mode on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), result);
- QETH_DBF_CARD1(0, trace, "STBRDCST", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- }
- } else if (card->options.broadcast_mode) {
- PRINT_WARN("set adapter parameters not available "
- "to set broadcast mode, using ALLRINGS "
- "on device %s:\n", CARD_BUS_ID(card));
- QETH_DBF_CARD1(0, trace, "NOBC", card);
- }
-
- if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
- result = qeth_send_setadapterparms_mode
- (card, IPA_SETADP_ALTER_MAC_ADDRESS,
- card->options.macaddr_mode);
- if (result) {
- PRINT_WARN("couldn't set macaddr mode on "
- "device %s: x%x\n", CARD_BUS_ID(card),
- result);
- QETH_DBF_CARD1(0, trace, "STMACMOD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- }
- } else if (card->options.macaddr_mode) {
- PRINT_WARN("set adapter parameters not available "
- "to set macaddr mode, using NONCANONICAL "
- "on device %s:\n", CARD_BUS_ID(card));
- QETH_DBF_CARD1(0, trace, "NOMA", card);
- }
-}
-
-static void
-qeth_do_setadapterparms_stuff(struct qeth_card *card)
-{
- int result;
- char dbf_text[15];
-
- if (!qeth_is_supported(IPA_SETADAPTERPARMS)) {
- return;
- }
-
- QETH_DBF_CARD4(0, trace, "stap", card);
-
- result = qeth_send_setadapterparms_query(card);
-
- if (result) {
- PRINT_WARN("couldn't set adapter parameters on device %s: "
- "x%x\n", CARD_BUS_ID(card), result);
- QETH_DBF_CARD1(0, trace, "SETADPFL", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- return;
- }
-
- sprintf(dbf_text, "spap%4x", card->adp_supported);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- if (qeth_is_adp_supported(IPA_SETADP_ALTER_MAC_ADDRESS)) {
- QETH_DBF_CARD3(0, trace, "rdmc", card);
- QETH_DBF_CARD2(0, setup, "rdmc", card);
-
- result = qeth_send_setadapterparms_change_addr(card,
- IPA_SETADP_ALTER_MAC_ADDRESS,
- CHANGE_ADDR_READ_MAC,
- card->dev->
- dev_addr,
- OSA_ADDR_LEN);
- if (result) {
- PRINT_WARN("couldn't get MAC address on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), result);
- QETH_DBF_CARD1(0, trace, "NOMACADD", card);
- sprintf(dbf_text, "%4x", result);
- QETH_DBF_TEXT1(1, trace, dbf_text);
- } else {
- QETH_DBF_HEX2(0, setup, card->dev->dev_addr,
- __max(OSA_ADDR_LEN, QETH_DBF_SETUP_LEN));
- QETH_DBF_HEX3(0, trace, card->dev->dev_addr,
- __max(OSA_ADDR_LEN, QETH_DBF_TRACE_LEN));
- }
- }
- __qeth_setparms_hstr(card);
-}
-
-static inline void
-__qeth_start_vlan_assist(struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- char dbf_text[15];
- int result;
-
- if (!qeth_is_supported(IPA_FULL_VLAN)) {
- PRINT_WARN("VLAN not supported on %s\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "vlnotsup");
- return;
- }
- result = qeth_send_setassparms_simple_without_data(card,
- IPA_VLAN_PRIO,
- IPA_CMD_ASS_START);
- QETH_DBF_TEXT2(0, trace, "enavlan");
- if (result) {
- PRINT_WARN("Could not start vlan "
- "assist on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "VLAN%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- return;
- }
- card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#endif /* QETH_VLAN */
-}
-
-static inline void
-__qeth_start_mc_assist(struct qeth_card *card)
-{
- char dbf_text[15];
- int result;
-
- if (!qeth_is_supported(IPA_MULTICASTING)) {
- PRINT_WARN("multicasting not supported on %s\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "mcnotsup");
- return;
- }
- result = qeth_send_setassparms_simple_without_data(card,
- IPA_MULTICASTING,
- IPA_CMD_ASS_START);
- QETH_DBF_TEXT2(0, trace, "enamcass");
- if (result) {
- PRINT_WARN("Could not start multicast "
- "assist on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "MCAS%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- return;
- }
- card->dev->flags |= IFF_MULTICAST;
-}
-
-static int
-__qeth_softsetup_enable_ipv6(struct qeth_card *card, int do_a_startlan6)
-{
- int result;
- char dbf_text[15];
-
- if (do_a_startlan6) {
- QETH_DBF_TEXT2(0, trace, "startln6");
- netif_stop_queue(card->dev);
- result = qeth_send_startlan(card, 6);
- if (result) {
- sprintf(dbf_text, "stl6%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- /* do not return an error */
- if ((result == 0xe080) || (result == 0xf080))
- result = 0;
- return result;
- }
- }
- netif_wake_queue(card->dev);
-
- QETH_DBF_TEXT2(0, trace, "qipassi6");
- result = qeth_send_qipassist(card, 6);
- if (result) {
- PRINT_WARN("couldn't send QIPASSIST6 on %s: 0x%x\n",
- card->dev_name, result);
- sprintf(dbf_text, "QIP6%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- return result;
- }
-
- sprintf(dbf_text, "%4x%4x", card->ipa6_supported, card->ipa6_enabled);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- QETH_DBF_TEXT2(0, trace, "enaipv46");
- result = qeth_send_setassparms_simple_with_data(card, IPA_IPv6,
- IPA_CMD_ASS_START, 3);
- if (result) {
- PRINT_WARN("Could not enable IPv4&6 assist "
- "on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "I46A%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* go on */
- }
-
- QETH_DBF_TEXT2(0, trace, "enaipv6");
- result = qeth_send_setassparms_simple_without_data6(card, IPA_IPv6,
- IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not start IPv6 assist "
- "on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "I6AS%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* go on */
- }
-
- QETH_DBF_TEXT2(0, trace, "enapstr6");
- result = qeth_send_setassparms_simple_without_data6(card, IPA_PASSTHRU,
- IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not enable passthrough "
- "on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "PSTR%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* go on */
- }
- return 0;
-}
-
-static int
-__qeth_softsetup_start_assists(struct qeth_card *card)
-{
- int result;
- char dbf_text[15];
- int do_a_startlan6 = 0;
-
- if (atomic_read(&card->is_softsetup))
- return 0;
-
- atomic_set(&card->enable_routing_attempts4, QETH_ROUTING_ATTEMPTS);
-#ifdef QETH_IPV6
- atomic_set(&card->enable_routing_attempts6, QETH_ROUTING_ATTEMPTS);
-#endif /* QETH_IPV6 */
- if ((!atomic_read(&card->is_startlaned)) &&
- (atomic_read(&card->startlan_attempts))) {
- atomic_dec(&card->startlan_attempts);
- QETH_DBF_TEXT2(0, trace, "startlan");
- netif_stop_queue(card->dev);
- result = qeth_send_startlan(card, 4);
- if (result) {
- PRINT_WARN("couldn't send STARTLAN on %s "
- "(CHPID 0x%X): 0x%x (%s)\n",
- card->dev_name, card->chpid, result,
- (result == 0xe080) ?
- "startlan disabled (link "
- "failure -- please check the "
- "network, plug in the cable or "
- "enable the OSA port" :
- (result==0xf080) ?
- "startlan disabled (VM: LAN " \
- "is offline for functions " \
- "requiring LAN access.":
- "unknown return code");
- sprintf(dbf_text, "stln%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- atomic_set(&card->is_startlaned, 0);
- /* do not return an error */
- if ((result == 0xe080) || (result == 0xf080)) {
- result = 0;
- }
- return result;
- }
- do_a_startlan6 = 1;
- }
- netif_wake_queue(card->dev);
-
- qeth_do_setadapterparms_stuff(card);
-
- if (!qeth_is_supported(IPA_ARP_PROCESSING)) {
- PRINT_WARN("oops... ARP processing not supported "
- "on %s!\n", card->dev_name);
- QETH_DBF_TEXT1(0, trace, "NOarpPRC");
- } else {
- QETH_DBF_TEXT2(0, trace, "enaARPpr");
- result = qeth_send_setassparms_simple_without_data
- (card, IPA_ARP_PROCESSING, IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not start ARP processing "
- "assist on %s: 0x%x\n",
- card->dev_name, result);
- sprintf(dbf_text, "ARPp%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- return result;
- }
- }
-
- if (qeth_is_supported(IPA_IP_FRAGMENTATION)) {
- PRINT_INFO("IP fragmentation supported on "
- "%s... :-)\n", card->dev_name);
- /* start it */
- QETH_DBF_TEXT2(0, trace, "enaipfrg");
- result = qeth_send_setassparms_simple_without_data
- (card, IPA_IP_FRAGMENTATION, IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not start IP fragmenting "
- "assist on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "IFRG%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* go on */
- }
- }
- if (card->options.fake_ll == FAKE_LL) {
- if (qeth_is_supported(IPA_SOURCE_MAC_AVAIL)) {
- /* start it */
- QETH_DBF_TEXT2(0, trace, "enainsrc");
- result = qeth_send_setassparms_simple_without_data
- (card, IPA_SOURCE_MAC_AVAIL, IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN
- ("Could not start inbound source "
- "assist on %s: 0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "INSR%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- /* go on */
- }
- } else {
- PRINT_INFO("Inbound source addresses not "
- "supported on %s\n", card->dev_name);
- }
- }
- __qeth_start_vlan_assist(card);
- __qeth_start_mc_assist(card);
-
- if (!qeth_is_supported(IPA_IPv6)) {
- QETH_DBF_TEXT2(0, trace, "ipv6ntsp");
- PRINT_WARN("IPv6 not supported on %s\n", card->dev_name);
- } else {
- result = __qeth_softsetup_enable_ipv6(card, do_a_startlan6);
- if (result != 0)
- return result;
- }
-
- card->broadcast_capable = 0;
- if (!qeth_is_supported(IPA_FILTERING)) {
- QETH_DBF_TEXT2(0, trace, "filtntsp");
- PRINT_WARN("Broadcasting not supported on %s\n",
- card->dev_name);
- } else {
- QETH_DBF_TEXT2(0, trace, "enafiltr");
- result = qeth_send_setassparms_simple_without_data
- (card, IPA_FILTERING, IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not enable broadcast "
- "filtering on %s: "
- "0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "FLT1%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- goto go_on_filt;
- }
- result = qeth_send_setassparms_simple_with_data
- (card, IPA_FILTERING, IPA_CMD_ASS_CONFIGURE, 1);
- if (result) {
- PRINT_WARN("Could not set up broadcast "
- "filtering on %s: "
- "0x%x, continuing\n",
- card->dev_name, result);
- sprintf(dbf_text, "FLT2%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- goto go_on_filt;
- }
- card->dev->flags |= IFF_BROADCAST;
- card->broadcast_capable = 1;
- }
-go_on_filt:
- if (card->options.checksum_type == HW_CHECKSUMMING) {
- if (!qeth_is_supported(IPA_INBOUND_CHECKSUM)) {
- PRINT_WARN("Inbound HW checksumming not "
- "supported on %s, continuing "
- "using inbound sw checksumming\n",
- card->dev_name);
- QETH_DBF_TEXT2(0, trace, "ibckntsp");
- card->options.checksum_type = SW_CHECKSUMMING;
- } else {
- QETH_DBF_TEXT2(0, trace, "ibcksupp");
- result = qeth_send_setassparms_simple_without_data
- (card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_START);
- if (result) {
- PRINT_WARN("Could not start inbound "
- "checksumming on %s: 0x%x, "
- "continuing using "
- "inbound sw checksumming\n",
- card->dev_name, result);
- sprintf(dbf_text, "SIBC%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- card->options.checksum_type = SW_CHECKSUMMING;
- goto go_on_checksum;
- }
- result=qeth_send_setassparms_simple_with_data
- (card,IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE, card->csum_enable_mask);
- if (result) {
- PRINT_WARN("Could not enable inbound " \
- "checksumming on %s: 0x%x, " \
- "continuing using " \
- "inbound sw checksumming\n",
- card->dev_name,result);
- sprintf(dbf_text,"EIBC%4x",result);
- QETH_DBF_TEXT2(0,trace,dbf_text);
- card->options.checksum_type = SW_CHECKSUMMING;
- goto go_on_checksum;
-
- }
- }
- }
-go_on_checksum:
- atomic_set(&card->is_softsetup, 1);
- return 0;
-}
-
-static inline void
-__qeth_softsetup_routingv4(struct qeth_card *card)
-{
- int result;
- char dbf_text[15];
-
- if (!atomic_read(&card->enable_routing_attempts4))
- return;
-
- if (!card->options.routing_type4) {
- atomic_set(&card->enable_routing_attempts4, 0);
- atomic_set(&card->rt4fld, 0);
- return;
- }
-
- sprintf(dbf_text, "strtg4%2x", card->options.routing_type4);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- result = qeth_send_setrtg(card, card->options.routing_type4, 4);
- if (!result) { /* routing set correctly */
- atomic_set(&card->enable_routing_attempts4, 0);
- atomic_set(&card->rt4fld, 0);
- return;
- }
- if (atomic_dec_return(&card->enable_routing_attempts4)) {
- PRINT_WARN("couldn't set up v4 routing type "
- "on %s: 0x%x (%s).\nWill try "
- "next time again.\n",
- card->dev_name, result,
- ((result == 0xe010) || (result == 0xe008)) ?
- "primary already defined"
- : ((result == 0xe011) || (result == 0xe009)) ?
- "secondary already defined"
- : (result == 0xe012) ? "invalid indicator" :
- "unknown return code");
- sprintf(dbf_text, "sRT4%4x", result);
- atomic_set(&card->rt4fld, 1);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else {
- PRINT_WARN("couldn't set up v4 routing type "
- "on %s: 0x%x (%s).\nTrying to "
- "continue without routing.\n",
- card->dev_name, result,
- ((result == 0xe010) || (result == 0xe008)) ?
- "primary already defined"
- : ((result == 0xe011) || (result == 0xe009)) ?
- "secondary already defined"
- : (result == 0xe012) ? "invalid indicator" :
- "unknown return code");
- sprintf(dbf_text, "SRT4%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->rt4fld, 1);
- }
-}
-
-static void
-__qeth_softsetup_routingv6(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- int result;
- char dbf_text[15];
-
- if (!atomic_read(&card->enable_routing_attempts6))
- return;
-
- if (!card->options.routing_type6 ||
- ((card->type == QETH_CARD_TYPE_OSAE) &&
- ((card->options.routing_type6&ROUTER_MASK) == MULTICAST_ROUTER) &&
- !qeth_is_supported6(IPA_OSA_MC_ROUTER_AVAIL))) {
- atomic_set(&card->enable_routing_attempts6, 0);
- atomic_set(&card->rt6fld, 0);
- return;
- }
- sprintf(dbf_text, "strtg6%2x", card->options.routing_type6);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- result = qeth_send_setrtg(card, card->options.routing_type6, 6);
- if (!result) { /* routing set correctly */
- atomic_set(&card->enable_routing_attempts6, 0);
- atomic_set(&card->rt6fld, 0);
- return;
- }
- if (atomic_dec_return(&card->enable_routing_attempts6)) {
- PRINT_WARN("couldn't set up v6 routing type "
- "on %s: 0x%x (%s).\nWill try "
- "next time again.\n",
- card->dev_name, result,
- ((result == 0xe010) || (result == 0xe008)) ?
- "primary already defined"
- : ((result == 0xe011) || (result == 0xe009)) ?
- "secondary already defined"
- : (result == 0xe012) ? "invalid indicator" :
- "unknown return code");
- sprintf(dbf_text, "sRT6%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->rt6fld, 1);
- } else {
- PRINT_WARN("couldn't set up v6 routing type "
- "on %s: 0x%x (%s).\nTrying to "
- "continue without routing.\n",
- card->dev_name, result,
- ((result == 0xe010) || (result == 0xe008)) ?
- "primary already defined"
- : ((result == 0xe011) || (result == 0xe009)) ?
- "secondary already defined"
- : (result == 0xe012) ? "invalid indicator" :
- "unknown return code");
- sprintf(dbf_text, "SRT6%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->rt6fld, 1);
- }
-#endif /* QETH_IPV6 */
-}
-
-static int
-qeth_softsetup_card(struct qeth_card *card, int wait_for_lock)
-{
- int result;
- char dbf_text[15];
- int use_setip_retries = 1;
-
- if (wait_for_lock == QETH_WAIT_FOR_LOCK) {
- down(&card->softsetup_sema);
- } else if (wait_for_lock == QETH_DONT_WAIT_FOR_LOCK) {
- if (!down_trylock(&card->softsetup_sema)) {
- return -EAGAIN;
- }
- } else if (wait_for_lock == QETH_LOCK_ALREADY_HELD) {
- use_setip_retries = 0; /* we are in recovery and don't want
- to repeat setting ips on and on */
- } else {
- return -EINVAL;
- }
-
- qeth_save_dev_flag_state(card);
-
- QETH_DBF_CARD1(0, trace, wait_for_lock?"sscw":"sscn", card);
-
- result = __qeth_softsetup_start_assists(card);
- if (result)
- goto out;
-
- __qeth_softsetup_routingv4(card);
- __qeth_softsetup_routingv6(card);
-
- QETH_DBF_TEXT2(0, trace, "delvipa");
- qeth_set_vipas(card, 0);
- QETH_DBF_TEXT2(0, trace, "toip/ms");
- qeth_takeover_ip_ipms(card);
- qeth_takeover_ip_ipms6(card);
- QETH_DBF_TEXT2(0, trace, "setvipa");
- qeth_set_vipas(card, 1);
-
- result = qeth_setips(card, use_setip_retries);
- if (result) { /* by now, qeth_setips does not return errors */
- PRINT_WARN("couldn't set up IPs on %s: 0x%x\n",
- card->dev_name, result);
- sprintf(dbf_text, "SSIP%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- goto out;
- }
- result = qeth_setipms(card, use_setip_retries);
- if (result) { /* by now, qeth_setipms does not return errors */
- PRINT_WARN("couldn't set up multicast IPs on %s: 0x%x\n",
- card->dev_name, result);
- sprintf(dbf_text, "ssim%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- goto out;
- }
-out:
- if (!result) {
- netif_wake_queue(card->dev);
- }
- if (wait_for_lock != QETH_LOCK_ALREADY_HELD)
- up(&card->softsetup_sema);
- return result;
-}
-
-static int
-qeth_softsetup_thread(void *param)
-{
- char name[15];
- struct qeth_card *card = (struct qeth_card *) param;
-
- /* set a nice name ... */
- sprintf(name, "qethsoftd%s", CARD_BUS_ID(card));
- daemonize(name);
-
- QETH_DBF_CARD2(0, trace, "ssth", card);
-
- atomic_set(&card->softsetup_thread_is_running, 1);
- for (;;) {
- if (atomic_read(&card->shutdown_phase))
- goto out;
- down_interruptible(&card->softsetup_thread_sem);
- QETH_DBF_CARD2(0, trace, "ssst", card);
- if (atomic_read(&card->shutdown_phase))
- goto out;
- while (qeth_softsetup_card(card, QETH_DONT_WAIT_FOR_LOCK)
- == -EAGAIN) {
- if (atomic_read(&card->shutdown_phase))
- goto out;
- qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
- }
- QETH_DBF_CARD2(0, trace, "sssd", card);
- netif_wake_queue(card->dev);
- }
-out:
- atomic_set(&card->softsetup_thread_is_running, 0);
-
- QETH_DBF_CARD2(0, trace, "lsst", card);
-
- return 0;
-}
-
-static void
-qeth_softsetup_thread_starter(void *data)
-{
- struct qeth_card *card = (struct qeth_card *) data;
-
- QETH_DBF_CARD4(0, trace, "ssts", card);
- sema_init(&card->softsetup_thread_sem, 0);
- kernel_thread(qeth_softsetup_thread, card, SIGCHLD);
-}
-
-static void
-qeth_start_reinit_thread(struct qeth_card *card)
-{
- /* we allow max 2 reinit threads, one could be just about to
- * finish and the next would be waiting. another waiting
- * reinit_thread is not necessary. */
- if (atomic_read(&card->reinit_counter) < 2) {
- atomic_inc(&card->reinit_counter);
- if (atomic_read(&card->shutdown_phase)) {
- atomic_dec(&card->reinit_counter);
- return;
- }
- QETH_DBF_CARD2(0, trace, "stri", card);
- PRINT_STUPID("starting reinit-thread\n");
- kernel_thread(qeth_reinit_thread, card, SIGCHLD);
- }
-}
-
-static void
-qeth_recover(void *data)
-{
- struct qeth_card *card;
- int i;
- char dbf_text[15];
-
- card = (struct qeth_card *) data;
-
- QETH_DBF_CARD2(0, trace, "recv", card);
-
- if (atomic_compare_and_swap(0, 1, &card->in_recovery))
- return;
-
- i = atomic_read(&card->problem);
-
- sprintf(dbf_text, "PROB%4x", i);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- if (i != PROBLEM_TX_TIMEOUT)
- PRINT_WARN("recovery was scheduled on device %s (%s) with "
- "problem 0x%x\n",
- CARD_BUS_ID(card), card->dev_name, i);
- switch (i) {
- case PROBLEM_RECEIVED_IDX_TERMINATE:
- if (atomic_read(&card->in_recovery))
- atomic_set(&card->break_out, QETH_BREAKOUT_AGAIN);
- break;
- case PROBLEM_CARD_HAS_STARTLANED:
- PRINT_WARN("You are lucky! Somebody either fixed the "
- "network problem, plugged the cable back in "
- "or enabled the OSA port on %s (CHPID 0x%X). "
- "The link has come up.\n",
- card->dev_name, card->chpid);
- sprintf(dbf_text, "CBIN%4x", i);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- atomic_set(&card->is_softsetup, 0);
- qeth_set_dev_flag_running(card);
- atomic_set(&card->enable_routing_attempts4,
- QETH_ROUTING_ATTEMPTS);
- qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
-#ifdef QETH_IPV6
- atomic_set(&card->enable_routing_attempts6,
- QETH_ROUTING_ATTEMPTS);
- qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
-#endif /* QETH_IPV6 */
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
-#ifdef QETH_IPV6
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
-#endif /* QETH_IPV6 */
- qeth_refresh_vipa_states(card);
- qeth_start_softsetup_thread(card);
- atomic_set(&card->in_recovery, 0);
- break;
- case PROBLEM_RESETTING_EVENT_INDICATOR:
- /* we do nothing here */
- break;
- case PROBLEM_ACTIVATE_CHECK_CONDITION:
- case PROBLEM_GENERAL_CHECK:
- case PROBLEM_USER_TRIGGERED_RECOVERY:
- case PROBLEM_AFFE:
- case PROBLEM_MACHINE_CHECK:
- case PROBLEM_BAD_SIGA_RESULT:
- case PROBLEM_TX_TIMEOUT:
- qeth_start_reinit_thread(card);
- break;
- }
-}
-
-static inline void
-qeth_schedule_recovery(struct qeth_card *card)
-{
- if (card) {
- INIT_WORK(&card->tqueue, qeth_recover, card);
- schedule_work(&card->tqueue);
- } else {
- QETH_DBF_TEXT2(1, trace, "scdnocrd");
- PRINT_WARN("recovery requested to be scheduled "
- "with no card!\n");
- }
-}
-
-static void
-qeth_qdio_input_handler(struct ccw_device *cdev, unsigned int status,
- unsigned int qdio_error, unsigned int siga_error,
- unsigned int queue,
- int first_element, int count, unsigned long card_ptr)
-{
- struct net_device *dev;
- struct qeth_card *card;
- int problem;
- int sbalf15;
- char dbf_text[15];
-
- sprintf(dbf_text, "qibhn%s", cdev->dev.bus_id);
- QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
-
- card = (struct qeth_card *) card_ptr;
-
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.inbound_start_time = NOW;
-#endif /* QETH_PERFORMANCE_STATS */
- dev = card->dev;
-
- if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
- if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- problem = PROBLEM_ACTIVATE_CHECK_CONDITION;
- atomic_set(&card->problem, problem);
- QETH_DBF_TEXT1(0, trace, "IHACTQCK");
- sprintf(dbf_text, "%4x%4x", first_element, count);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", queue, status);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_CARD1(1, trace, "qscd", card);
- qeth_schedule_recovery(card);
- return;
- }
- sbalf15 = (card->inbound_qdio_buffers[(first_element + count - 1)
- & QDIO_MAX_BUFFERS_PER_Q].
- element[15].flags) && 0xff;
- PRINT_STUPID("inbound qdio transfer error on device %s. "
- "qdio_error=0x%x (more than one: %c), "
- "siga_error=0x%x (more than one: %c), "
- "sbalf15=x%x, bufno=x%x\n", cdev->dev.bus_id,
- qdio_error,
- (status & QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR) ?
- 'y' : 'n', siga_error,
- (status & QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR) ?
- 'y' : 'n', sbalf15, first_element);
- QETH_DBF_CARD1(0, trace, "IQTI", card);
- QETH_DBF_CARD1(0, qerr, "IQTI", card);
- sprintf(dbf_text, "%4x%4x", first_element, count);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- sprintf(dbf_text, "%2x%4x%2x", queue, status, sbalf15);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- sprintf(dbf_text, "%4x%4x", qdio_error, siga_error);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- /* we inform about error more detailed in
- * qeth_read_in_buffer() */
- }
-
- for (;;) {
- qeth_read_in_buffer(card, first_element);
- qeth_queue_input_buffer(card, first_element,
- QDIO_FLAG_UNDER_INTERRUPT);
- count--;
- if (count)
- first_element = (first_element + 1) &
- (QDIO_MAX_BUFFERS_PER_Q - 1);
- else
- break;
- }
-}
-
-static void
-__qeth_try_to_flush_packets(struct qeth_card *card, int last_pci_hit,
- unsigned int queue)
-{
- int switch_state;
-
- switch_state = (atomic_read(&card->outbound_used_buffers[queue]) <=
- LOW_WATERMARK_PACK);
- /* first_element is the last buffer that we got back from hydra */
- if (!switch_state && !last_pci_hit)
- return;
- QETH_DBF_CARD3(0, trace, "stchcw", card);
- if (atomic_swap(&card->outbound_ringbuffer_lock[queue], QETH_LOCK_FLUSH)
- == QETH_LOCK_UNLOCKED) {
- /*
- * we stop the queue as we try to not run onto the
- * outbound_ringbuffer_lock -- this will not prevent it totally,
- * but reduce it. in high traffic situations, it saves around
- * 20us per second, hopefully this is amortized by calling
- * netif_...
- */
- netif_stop_queue(card->dev);
- qeth_flush_packed_packets(card, queue,
- QDIO_FLAG_UNDER_INTERRUPT);
- /*
- * only switch state to non-packing, if the amount of used
- * buffers decreased
- */
- if (switch_state)
- card->send_state[queue] = SEND_STATE_DONT_PACK;
- netif_wake_queue(card->dev);
- atomic_set(&card->outbound_ringbuffer_lock[queue],
- QETH_LOCK_UNLOCKED);
- }
- /*
- * if the lock was UNLOCKED, we flush ourselves, otherwise this is done
- * in do_send_packet when the lock is released
- */
-#ifdef QETH_PERFORMANCE_STATS
- card->perf_stats.sc_p_dp++;
-#endif /* QETH_PERFORMANCE_STATS */
-}
-
-static void
-qeth_qdio_output_handler(struct ccw_device *cdev,
- unsigned int status,
- unsigned int qdio_error,
- unsigned int siga_error,
- unsigned int queue,
- int first_element, int count, unsigned long card_ptr)
-{
- struct qeth_card *card;
- int mycnt, problem, buffers_used;
- int sbalf15;
- char dbf_text[15];
- int last_pci_hit = 0;
- int last_pci;
-
- sprintf(dbf_text, "qouthn%s", cdev->dev.bus_id);
- QETH_DBF_HEX6(0, trace, dbf_text, QETH_DBF_TRACE_LEN);
-
- mycnt = count;
- card = (struct qeth_card *) card_ptr;
-
- if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
- if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- problem = PROBLEM_ACTIVATE_CHECK_CONDITION;
- atomic_set(&card->problem, problem);
- QETH_DBF_TEXT1(0, trace, "OHACTQCK");
- sprintf(dbf_text, "%4x%4x", first_element, count);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", queue, status);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_CARD1(1, trace, "qscd", card);
- qeth_schedule_recovery(card);
- goto out;
- }
- sbalf15 = (card->outbound_ringbuffer[queue]->
- buffer[(first_element + count - 1) & QDIO_MAX_BUFFERS_PER_Q].
- element[15].flags) & 0xff;
- PRINT_STUPID("outbound qdio transfer error on device %s, "
- "queue=%i. qdio_error=0x%x (more than one: %c),"
- " siga_error=0x%x (more than one: %c), "
- "sbalf15=x%x, bufno=x%x\n",
- cdev->dev.bus_id, queue, qdio_error, status &
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR ? 'y' : 'n',
- siga_error, status &
- QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR ? 'y' : 'n',
- sbalf15, first_element);
- QETH_DBF_CARD1(0, trace, "IQTO", card);
- QETH_DBF_CARD1(0, qerr, "IQTO", card);
- sprintf(dbf_text, "%4x%4x", first_element, count);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- sprintf(dbf_text, "%2x%4x%2x", queue, status, sbalf15);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- sprintf(dbf_text, "%4x%4x", qdio_error, siga_error);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_TEXT1(0, qerr, dbf_text);
- /* we maybe do recovery or dst_link_failures
- * in qeth_free_buffer */
- }
-
- if (mycnt) {
- last_pci = atomic_read(&card->last_pci_pos[queue]);
- for (;;) {
- qeth_free_buffer(card, queue, first_element,
- qdio_error, siga_error);
- if (first_element == last_pci)
- last_pci_hit = 1;
- mycnt--;
- if (mycnt > 0)
- first_element = (first_element + 1) &
- (QDIO_MAX_BUFFERS_PER_Q - 1);
- else
- break;
- }
- }
-
- buffers_used = atomic_add_return(-count,
- &card->outbound_used_buffers[queue])
- + count;
-
- switch (card->send_state[queue]) {
- case SEND_STATE_PACK:
- __qeth_try_to_flush_packets(card, last_pci_hit, queue);
- break;
- default:
- break;
- }
-
- /* we don't have to start the queue, if it was started already */
- if (buffers_used < QDIO_MAX_BUFFERS_PER_Q - 1)
- return;
-
-out:
- netif_wake_queue(card->dev);
-}
-
-static long
-__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb)
-{
- if (!IS_ERR(irb))
- return 0;
-
- switch (PTR_ERR(irb)) {
- case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
- break;
- case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
- break;
- default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
- }
- return PTR_ERR(irb);
-}
-
-static void
-qeth_interrupt_handler_read(struct ccw_device *cdev, unsigned long intparm,
- struct irb *irb)
-{
- int cstat, dstat;
- int problem;
- struct qeth_card *card;
- int rqparam;
- char dbf_text[15];
- int result;
-
- if (__qeth_check_irb_error(cdev, irb))
- return;
-
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- rqparam = intparm;
-
- sprintf(dbf_text, "rint%s", cdev->dev.bus_id);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", cstat, dstat);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x", rqparam);
- QETH_DBF_TEXT4(0, trace, dbf_text);
-
- card = CARD_FROM_CDEV(cdev);
- if (!card)
- return;
-
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
- atomic_set(&card->clear_succeeded0, 1);
- wake_up(&card->wait_q);
- return;
- }
-
- if (!rqparam) {
- PRINT_STUPID("got unsolicited interrupt in read handler "
- "for %s\n", cdev->dev.bus_id);
- return;
- }
-
- if ((dstat == 0) && (cstat == 0))
- return;
-
- if (irb->esw.esw0.erw.cons) {
- PRINT_WARN("sense data available on read channel.\n");
- HEXDUMP16(WARN, "irb: ", irb);
- HEXDUMP16(WARN, "sense data: ", irb->ecw);
- sprintf(dbf_text, "RSNS%s", cdev->dev.bus_id);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN);
- }
-
- if (cstat != 0) {
- PRINT_WARN("got nonzero-nonpci channel status in read_"
- "handler (device %s, devstat 0x%02x, schstat "
- "0x%02x, rqparam 0x%x)\n", cdev->dev.bus_id,
- dstat, cstat, rqparam);
- }
-
- problem = qeth_get_cards_problem(cdev, card->dma_stuff->recbuf,
- dstat, cstat, rqparam,
- (char *) irb, (char *) irb->ecw);
-
- /* detect errors in dstat here */
- if ((dstat & DEV_STAT_UNIT_EXCEP) || (dstat & DEV_STAT_UNIT_CHECK)) {
- PRINT_WARN("unit check/exception in read_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x, "
- "rqparam 0x%x)\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
-
- if (!atomic_read(&card->is_hardsetup)) {
- if ((problem) && (qeth_is_to_recover(card, problem)))
- atomic_set(&card->break_out,
- QETH_BREAKOUT_AGAIN);
- else
- atomic_set(&card->break_out,
- QETH_BREAKOUT_LEAVE);
- goto wakeup_out;
- } else
- goto recover;
- }
-
- if (!(dstat & DEV_STAT_CHN_END)) {
- PRINT_WARN("didn't get device end in read_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x, "
- "rqparam 0x%x)\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- goto wakeup_out;
- }
-
- if ((rqparam == IDX_ACTIVATE_WRITE_STATE) || (rqparam == NOP_STATE)) {
- goto wakeup_out;
- }
-
- /* at this point, (maybe channel end and) device end has appeared */
-
- /* we don't start the next read until we have examined the buffer. */
- if ((rqparam != IDX_ACTIVATE_READ_STATE) &&
- (rqparam != IDX_ACTIVATE_WRITE_STATE))
- qeth_issue_next_read(card);
-
-recover:
- if (qeth_is_to_recover(card, problem)) {
- QETH_DBF_CARD2(1, trace, "rscd", card);
- qeth_schedule_recovery(card);
- goto wakeup_out;
- }
-
- if (!IS_IPA(card->dma_stuff->recbuf) ||
- IS_IPA_REPLY(card->dma_stuff->recbuf)) {
- /* setup or unknown data */
- result = qeth_look_for_arp_data(card);
- switch (result) {
- case ARP_RETURNCODE_ERROR:
- case ARP_RETURNCODE_LASTREPLY:
- qeth_wakeup_ioctl(card);
- return;
- default:
- break;
- }
- }
-
-wakeup_out:
- memcpy(card->ipa_buf, card->dma_stuff->recbuf, QETH_BUFSIZE);
- qeth_wakeup(card);
-}
-
-static void
-qeth_interrupt_handler_write(struct ccw_device *cdev, unsigned long intparm,
- struct irb *irb)
-{
- int cstat, dstat, rqparam;
- struct qeth_card *card;
- int problem;
- char dbf_text[15];
-
- if (__qeth_check_irb_error(cdev, irb))
- return;
-
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- rqparam = intparm;
-
- sprintf(dbf_text, "wint%s", cdev->dev.bus_id);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", cstat, dstat);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x", rqparam);
- QETH_DBF_TEXT4(0, trace, dbf_text);
-
- card = CARD_FROM_CDEV(cdev);
- if (!card)
- return;
-
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
- atomic_set(&card->clear_succeeded1, 1);
- wake_up(&card->wait_q);
- goto out;
- }
-
- if (!rqparam) {
- PRINT_STUPID("got unsolicited interrupt in write handler "
- "for %s\n", cdev->dev.bus_id);
- return;
- }
-
- if ((dstat == 0) && (cstat == 0))
- goto out;
-
- if (irb->esw.esw0.erw.cons) {
- PRINT_WARN("sense data available on write channel.\n");
- HEXDUMP16(WARN, "irb: ", irb);
- HEXDUMP16(WARN, "sense data: ", irb->ecw);
- sprintf(dbf_text, "WSNS%s", cdev->dev.bus_id);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN);
- }
-
- if (cstat != 0) {
- PRINT_WARN("got nonzero channel status in write_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x, "
- "rqparam 0x%x)\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- }
-
- problem = qeth_get_cards_problem(cdev, NULL,
- dstat, cstat, rqparam,
- (char *) irb, (char *) irb->ecw);
-
- /* detect errors in dstat here */
- if ((dstat & DEV_STAT_UNIT_EXCEP) || (dstat & DEV_STAT_UNIT_CHECK)) {
- PRINT_WARN("unit check/exception in write_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x, "
- "rqparam 0x%x)\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- if (!atomic_read(&card->is_hardsetup)) {
- if (problem == PROBLEM_RESETTING_EVENT_INDICATOR) {
- atomic_set(&card->break_out,
- QETH_BREAKOUT_AGAIN);
- qeth_wakeup(card);
- goto out;
- }
- atomic_set(&card->break_out, QETH_BREAKOUT_LEAVE);
- goto out;
- } else
- goto recover;
- }
-
- if (dstat == DEV_STAT_DEV_END)
- goto out;
-
- if (!(dstat & DEV_STAT_CHN_END)) {
- PRINT_WARN("didn't get device end in write_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x, "
- "rqparam 0x%x)\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- goto out;
- }
-
-recover:
- if (qeth_is_to_recover(card, problem)) {
- QETH_DBF_CARD2(1, trace, "wscd", card);
- qeth_schedule_recovery(card);
- goto out;
- }
-
- /* at this point, (maybe channel end and) device end has appeared */
- if ((rqparam == IDX_ACTIVATE_READ_STATE) ||
- (rqparam == IDX_ACTIVATE_WRITE_STATE) || (rqparam == NOP_STATE)) {
- qeth_wakeup(card);
- goto out;
- }
-
- /* well, a write has been done successfully. */
-
-out:
- /* all statuses are final statuses on the write channel */
- atomic_set(&card->write_busy, 0);
-}
-
-static void
-qeth_interrupt_handler_qdio(struct ccw_device *cdev, unsigned long intparm,
- struct irb *irb)
-{
- int cstat, dstat, rqparam;
- char dbf_text[15];
- struct qeth_card *card;
-
- if (__qeth_check_irb_error(cdev, irb))
- return;
-
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- rqparam = intparm;
-
- sprintf(dbf_text, "qint%s", cdev->dev.bus_id);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", cstat, dstat);
- QETH_DBF_TEXT4(0, trace, dbf_text);
- sprintf(dbf_text, "%4x", rqparam);
- QETH_DBF_TEXT4(0, trace, dbf_text);
-
- card = CARD_FROM_CDEV(cdev);
- if (!card)
- return;
-
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
- atomic_set(&card->clear_succeeded2, 1);
- wake_up(&card->wait_q);
- return;
- }
-
- if (!rqparam) {
- PRINT_STUPID("got unsolicited interrupt in qdio handler, "
- "device%s\n", cdev->dev.bus_id);
- return;
- }
-
- if ((dstat == 0) && (cstat == 0))
- return;
-
- if (irb->esw.esw0.erw.cons) {
- PRINT_WARN("sense data available on qdio channel.\n");
- HEXDUMP16(WARN, "irb: ", irb);
- HEXDUMP16(WARN, "sense data: ", irb->ecw);
- sprintf(dbf_text, "QSNS%s", cdev->dev.bus_id);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- QETH_DBF_HEX0(0, sense, irb, QETH_DBF_SENSE_LEN);
- }
-
- if (rqparam == NOP_STATE) {
- qeth_wakeup(card);
- return;
- }
-
- if (cstat != 0) {
- sprintf(dbf_text, "qchk%s", cdev->dev.bus_id);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "%4x%4x", cstat, dstat);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "%4x", rqparam);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- PRINT_WARN("got nonzero channel status in qdio_handler "
- "(device %s, devstat 0x%02x, schstat 0x%02x)\n",
- cdev->dev.bus_id, dstat, cstat);
- }
-
- if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- PRINT_WARN("got the following dstat on the qdio channel: "
- "device %s, dstat 0x%02x, cstat 0x%02x, "
- "rqparam=%i\n",
- cdev->dev.bus_id, dstat, cstat, rqparam);
- }
-
-}
-
-static int
-qeth_register_netdev(struct qeth_card *card)
-{
- int result;
-
- QETH_DBF_CARD3(0, trace, "rgnd", card);
-
- /* sysfs magic */
- SET_NETDEV_DEV(card->dev, &card->gdev->dev);
- result = register_netdev(card->dev);
-
- return result;
-}
-
-static void
-qeth_unregister_netdev(struct qeth_card *card)
-{
- QETH_DBF_CARD3(0, trace, "nrgn", card);
-
- unregister_netdev(card->dev);
-}
-
-static int
-qeth_stop(struct net_device *dev)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_CARD2(0, trace, "stop", card);
- QETH_DBF_CARD2(0, setup, "stop", card);
-
- qeth_save_dev_flag_state(card);
-
- netif_stop_queue(dev);
- atomic_set(&card->is_open, 0);
-
- return 0;
-}
-
-static void
-qeth_softshutdown(struct qeth_card *card)
-{
- QETH_DBF_CARD3(0, trace, "ssht", card);
-
- qeth_send_stoplan(card);
-}
-
-static void
-__qeth_clear_card_halt_clear(struct qeth_card *card, int halt)
-{
- unsigned long flags0, flags1, flags2;
- int ret0, ret1, ret2;
-
- atomic_set(&card->clear_succeeded0, 0);
- atomic_set(&card->clear_succeeded1, 0);
- atomic_set(&card->clear_succeeded2, 0);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags0);
- if (halt)
- ret0 = ccw_device_halt(CARD_RDEV(card), CLEAR_STATE);
- else
- ret0 = ccw_device_clear(CARD_RDEV(card), CLEAR_STATE);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags0);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags1);
- if (halt)
- ret1 = ccw_device_halt(CARD_WDEV(card), CLEAR_STATE);
- else
- ret1 = ccw_device_clear(CARD_WDEV(card), CLEAR_STATE);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags1);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_DDEV(card)), flags2);
- if (halt)
- ret2 = ccw_device_halt(CARD_DDEV(card), CLEAR_STATE);
- else
- ret2 = ccw_device_clear(CARD_DDEV(card), CLEAR_STATE);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_DDEV(card)), flags2);
-
- /* The device owns us an interrupt. */
- if ((ret0 == 0) && (atomic_read(&card->clear_succeeded0) == 0))
- wait_event(card->wait_q,
- atomic_read(&card->clear_succeeded0) == 1);
- if ((ret1 == 0) && (atomic_read(&card->clear_succeeded1) == 0))
- wait_event(card->wait_q,
- atomic_read(&card->clear_succeeded1) == 1);
- if ((ret2 == 0) && (atomic_read(&card->clear_succeeded2) == 0))
- wait_event(card->wait_q,
- atomic_read(&card->clear_succeeded2) == 1);
-}
-
-static void
-qeth_clear_card(struct qeth_card *card, int qdio_clean, int use_halt)
-{
- QETH_DBF_CARD3(0, trace, qdio_clean?"clrq":"clr", card);
- QETH_DBF_CARD1(0, setup, qdio_clean?"clrq":"clr", card);
-
- atomic_set(&card->write_busy, 0);
- if (qdio_clean)
- qdio_cleanup(CARD_DDEV(card),
- (card->type == QETH_CARD_TYPE_IQD) ?
- QDIO_FLAG_CLEANUP_USING_HALT :
- QDIO_FLAG_CLEANUP_USING_CLEAR);
-
- if (use_halt)
- __qeth_clear_card_halt_clear(card, 1);
-
- __qeth_clear_card_halt_clear(card, 0);
-}
-
-static void
-qeth_free_card_stuff(struct qeth_card *card)
-{
- int i, j;
- struct qeth_vipa_entry *e, *e2;
-
- if (!card)
- return;
-
- QETH_DBF_CARD3(0, trace, "freest", card);
- QETH_DBF_CARD1(0, setup, "freest", card);
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- e2 = e->next;
- kfree(e);
- e = e2;
- }
- write_unlock(&card->vipa_list_lock);
-
- for (i = 0; i < card->options.inbound_buffer_count; i++) {
- for (j = 0; j < BUFFER_MAX_ELEMENTS; j++) {
- if (card->inbound_buffer_pool_entry[i][j]) {
- kfree(card->inbound_buffer_pool_entry[i][j]);
- card->inbound_buffer_pool_entry[i][j] = NULL;
- }
- }
- }
- for (i = 0; i < card->no_queues; i++)
- if (card->outbound_ringbuffer[i])
- vfree(card->outbound_ringbuffer[i]);
-
- if (card->stats)
- kfree(card->stats);
- if (card->dma_stuff)
- kfree(card->dma_stuff);
- if (card->dev)
- free_netdev(card->dev);
-
-}
-
-static void
-qeth_free_card(struct qeth_card *card)
-{
-
- if (!card)
- return;
-
- QETH_DBF_CARD3(0, trace, "free", card);
- QETH_DBF_CARD1(0, setup, "free", card);
-
- vfree(card); /* we checked against NULL already */
-}
-
-/* also locked from outside (setup_lock) */
-static void
-qeth_remove_card_from_list(struct qeth_card *card)
-{
- struct qeth_card *cn;
- unsigned long flags0, flags1, flags2;
-
- write_lock(&list_lock);
- if (!card) {
- QETH_DBF_TEXT2(0, trace, "RMCWNOCD");
- PRINT_WARN("qeth_remove_card_from_list call with no card!\n");
- write_unlock(&list_lock);
- return;
- }
-
- QETH_DBF_CARD3(0, trace, "rmcl", card);
-
- /* check first, if card is in list */
- if (!firstcard) {
- QETH_DBF_TEXT2(0, trace, "NOCRDINL");
- PRINT_WARN
- ("qeth_remove_card_from_list called on empty card list!!\n");
- write_unlock(&list_lock);
- return;
- }
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags0);
- spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags1);
- spin_lock_irqsave(get_ccwdev_lock(CARD_DDEV(card)), flags2);
-
- if (firstcard == card)
- firstcard = card->next;
- else {
- cn = firstcard;
- while (cn->next) {
- if (cn->next == card) {
- cn->next = card->next;
- card->next = NULL;
- break;
- }
- cn = cn->next;
- }
- }
-
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_DDEV(card)), flags2);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags1);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags0);
-
- write_unlock(&list_lock);
-
-}
-
-static void
-qeth_delete_all_ips(struct qeth_card *card)
-{
- struct qeth_vipa_entry *e;
-
- if (atomic_read(&card->is_softsetup)) {
- qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
-
-#ifdef QETH_IPV6
- qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
-#endif /* QETH_IPV6 */
-
- write_lock(&card->vipa_list_lock);
- e = card->vipa_list;
- while (e) {
- e->state = VIPA_2_B_REMOVED;
- e = e->next;
- }
- write_unlock(&card->vipa_list_lock);
- qeth_start_softsetup_thread(card);
- }
-}
-
-static void
-qeth_remove_card(struct qeth_card *card, int method)
-{
- if (!card)
- return;
-
- QETH_DBF_CARD2(0, trace, "rmcd", card);
- QETH_DBF_CARD1(0, setup, "rmcd", card);
-
- if (method == QETH_REMOVE_CARD_PROPER) {
- atomic_set(&card->shutdown_phase, QETH_REMOVE_CARD_PROPER);
- if (atomic_read(&card->is_open)) {
- qeth_stop(card->dev);
- qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
- }
- qeth_delete_all_ips(card);
- } else {
- atomic_set(&card->shutdown_phase, QETH_REMOVE_CARD_QUICK);
- }
- atomic_set(&card->write_busy, 0);
-
- QETH_DBF_TEXT4(0, trace, "freeskbs");
- qeth_free_all_skbs(card);
-
- QETH_DBF_TEXT2(0, trace, "upthrsem");
-
- up(&card->softsetup_thread_sem);
- up(&card->reinit_thread_sem);
- while ((atomic_read(&card->softsetup_thread_is_running)) ||
- (atomic_read(&card->reinit_counter))) {
- qeth_wait_nonbusy(QETH_WAIT_FOR_THREAD_TIME);
- }
-
- if (method == QETH_REMOVE_CARD_PROPER) {
- QETH_DBF_TEXT4(0, trace, "softshut");
- qeth_softshutdown(card);
- qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
- }
-
- atomic_set(&card->is_startlaned, 0); /* paranoia, qeth_stop
- should prevent
- further calls of
- hard_start_xmit */
-
- if (atomic_read(&card->is_registered)) {
- QETH_DBF_TEXT2(0, trace, "unregdev");
- qeth_unregister_netdev(card);
- qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
- atomic_set(&card->is_registered, 0);
- }
-
- qeth_put_unique_id(card);
-
- QETH_DBF_TEXT2(0, trace, "clrcard");
- if (atomic_read(&card->is_hardsetup)) {
- PRINT_STUPID("clearing card %s\n", card->dev_name);
- qeth_clear_card(card, 1, 0);
- }
-
- atomic_set(&card->is_hardsetup, 0);
- atomic_set(&card->is_softsetup, 0);
-
- QETH_DBF_TEXT2(0, trace, "cardrmvd");
-
-}
-
-static void
-qeth_set_multicast_list(struct net_device *dev)
-{
- struct qeth_card *card = dev->priv;
-
- QETH_DBF_CARD2(0, trace, "smcl", card);
-
- qeth_start_softsetup_thread(card);
-}
-
-static int
-qeth_set_mac_address(struct net_device *dev, void *addr)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_CARD2(0, trace, "stmc", card);
-
- return -EOPNOTSUPP;
-}
-
-static int
-qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_CARD2(0, trace, "ngst", card);
-
- return 0;
-}
-
-static void
-qeth_generate_tokens(struct qeth_card *card)
-{
- card->token.issuer_rm_w = 0x00010103UL;
- card->token.cm_filter_w = 0x00010108UL;
- card->token.cm_connection_w = 0x0001010aUL;
- card->token.ulp_filter_w = 0x0001010bUL;
- card->token.ulp_connection_w = 0x0001010dUL;
-}
-
-static int
-qeth_peer_func_level(int level)
-{
- if ((level & 0xff) == 8)
- return (level & 0xff) + 0x400;
- if (((level >> 8) & 3) == 1)
- return (level & 0xff) + 0x200;
- return level; /* hmmm... don't know what to do with that level. */
-}
-
-/* returns last four digits of bus_id */
-/* FIXME: device driver shouldn't be aware of bus_id format - but don't know
- what else to use... (CH) */
-static inline __u16
-__raw_devno_from_bus_id(char *id)
-{
- id += (strlen(id) - 4);
- return (__u16) simple_strtoul(id, &id, 16);
-}
-
-static int
-qeth_idx_activate_read(struct qeth_card *card)
-{
- int result, result2;
- __u16 temp;
- unsigned long flags;
- char dbf_text[15];
-
- result = result2 = 0;
-
- memcpy(&card->dma_stuff->write_ccw, WRITE_CCW, sizeof (struct ccw1));
- card->dma_stuff->write_ccw.count = IDX_ACTIVATE_SIZE;
- card->dma_stuff->write_ccw.cda =
- QETH_GET_ADDR(card->dma_stuff->sendbuf);
-
- memcpy(card->dma_stuff->sendbuf, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE);
- memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
- &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
-
- memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
- &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
- &card->func_level, 2);
-
- temp = __raw_devno_from_bus_id(CARD_DDEV_ID(card));
- memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf), &temp, 2);
- temp = (card->cula << 8) + card->unit_addr2;
- memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
- &temp, 2);
-
- QETH_DBF_TEXT2(0, trace, "iarw");
- QETH_DBF_TEXT2(0, trace, CARD_RDEV_ID(card));
- QETH_DBF_HEX2(0, control, card->dma_stuff->sendbuf,
- QETH_DBF_CONTROL_LEN);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags);
- result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->write_ccw,
- IDX_ACTIVATE_WRITE_STATE, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 = ccw_device_start(CARD_RDEV(card),
- &card->dma_stuff->write_ccw,
- IDX_ACTIVATE_WRITE_STATE, 0, 0);
- sprintf(dbf_text, "IRW1%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "IRW2%4x", result2);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- PRINT_WARN("qeth_idx_activate_read (write): do_IO returned "
- "%i, next try returns %i\n", result, result2);
- }
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags);
-
- if (atomic_read(&card->break_out)) {
- QETH_DBF_TEXT3(0, trace, "IARWBRKO");
- return -EIO;
- }
-
- if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) {
- QETH_DBF_TEXT1(0, trace, "IRWT");
- QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE(wr) on read channel device %s: "
- "timeout\n", CARD_RDEV_ID(card));
- return -EIO;
- }
-
-/* start reading on read channel, card->read_ccw is not yet used */
- memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1));
- card->dma_stuff->read_ccw.count = QETH_BUFSIZE;
- card->dma_stuff->read_ccw.cda = QETH_GET_ADDR(card->dma_stuff->recbuf);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags);
- result2 = 0;
- result = ccw_device_start(CARD_RDEV(card), &card->dma_stuff->read_ccw,
- IDX_ACTIVATE_READ_STATE, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 = ccw_device_start(CARD_RDEV(card),
- &card->dma_stuff->read_ccw,
- IDX_ACTIVATE_READ_STATE, 0, 0);
- sprintf(dbf_text, "IRR1%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "IRR2%4x", result2);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- PRINT_WARN("qeth_idx_activate_read (read): do_IO "
- "returned %i, next try returns %i\n",
- result, result2);
- }
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags);
-
- if (result2) {
- result = result2;
- if (result)
- return result;
- }
-
- if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) {
- QETH_DBF_TEXT1(0, trace, "IRRT");
- QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE(rd) on read channel device %s: "
- "timeout\n", CARD_RDEV_ID(card));
- return -EIO;
- }
- QETH_DBF_TEXT2(0, trace, "iarr");
- QETH_DBF_TEXT2(0, trace, CARD_RDEV_ID(card));
- QETH_DBF_HEX2(0, control, card->dma_stuff->recbuf,
- QETH_DBF_CONTROL_LEN);
-
- if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
- QETH_DBF_TEXT1(0, trace, "IRNR");
- QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: negative "
- "reply\n", CARD_RDEV_ID(card));
- return -EIO;
- }
-
- card->portname_required =
- ((!QETH_IDX_NO_PORTNAME_REQUIRED(card->dma_stuff->recbuf)) &&
- (card->type == QETH_CARD_TYPE_OSAE));
-
- /*
- * however, as the portname indication of OSA is wrong, we have to
- * do this:
- */
- card->portname_required = (card->type == QETH_CARD_TYPE_OSAE);
-
- memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf), 2);
- if (temp != qeth_peer_func_level(card->func_level)) {
- QETH_DBF_TEXT1(0, trace, "IRFL");
- QETH_DBF_TEXT1(0, trace, CARD_RDEV_ID(card));
- sprintf(dbf_text, "%4x%4x", card->func_level, temp);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- PRINT_WARN("IDX_ACTIVATE on read channel device %s: function "
- "level mismatch (sent: 0x%x, received: 0x%x)\n",
- CARD_RDEV_ID(card), card->func_level, temp);
- result = -EIO;
- }
-
- memcpy(&card->token.issuer_rm_r,
- QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->recbuf),
- QETH_MPC_TOKEN_LENGTH);
-
- memcpy(&card->level[0],
- QETH_IDX_REPLY_LEVEL(card->dma_stuff->recbuf), QETH_MCL_LENGTH);
-
- return result;
-}
-
-static int
-qeth_idx_activate_write(struct qeth_card *card)
-{
- int result, result2;
- __u16 temp;
- unsigned long flags;
- char dbf_text[15];
-
- result = result2 = 0;
-
- memcpy(&card->dma_stuff->write_ccw, WRITE_CCW, sizeof (struct ccw1));
- card->dma_stuff->write_ccw.count = IDX_ACTIVATE_SIZE;
- card->dma_stuff->write_ccw.cda =
- QETH_GET_ADDR(card->dma_stuff->sendbuf);
-
- memcpy(card->dma_stuff->sendbuf, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE);
- memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
- &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
- card->seqno.trans_hdr++;
-
- memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
- &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
- &card->func_level, 2);
-
- temp = __raw_devno_from_bus_id(CARD_DDEV_ID(card));
- memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf), &temp, 2);
- temp = (card->cula << 8) + card->unit_addr2;
- memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
- &temp, 2);
-
- QETH_DBF_TEXT2(0, trace, "iaww");
- QETH_DBF_TEXT2(0, trace, CARD_WDEV_ID(card));
- QETH_DBF_HEX2(0, control, card->dma_stuff->sendbuf,
- QETH_DBF_CONTROL_LEN);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags);
- result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->write_ccw,
- IDX_ACTIVATE_WRITE_STATE, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 = ccw_device_start(CARD_WDEV(card),
- &card->dma_stuff->write_ccw,
- IDX_ACTIVATE_WRITE_STATE, 0, 0);
- sprintf(dbf_text, "IWW1%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "IWW2%4x", result2);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- PRINT_WARN("qeth_idx_activate_write (write): do_IO "
- "returned %i, next try returns %i\n",
- result, result2);
- }
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags);
-
- if (atomic_read(&card->break_out)) {
- QETH_DBF_TEXT3(0, trace, "IAWWBRKO");
- return -EIO;
- }
-
- if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) {
- QETH_DBF_TEXT1(0, trace, "IWWT");
- QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE(wr) on write channel device %s: "
- "timeout\n", CARD_WDEV_ID(card));
- return -EIO;
- }
-
- QETH_DBF_TEXT3(0, trace, "idxawrrd");
- /* start one read on write channel */
- memcpy(&card->dma_stuff->read_ccw, READ_CCW, sizeof (struct ccw1));
- card->dma_stuff->read_ccw.count = QETH_BUFSIZE;
- /* recbuf and card->read_ccw is not yet used by any other
- read channel program */
- card->dma_stuff->read_ccw.cda = QETH_GET_ADDR(card->dma_stuff->recbuf);
-
- spin_lock_irqsave(get_ccwdev_lock(CARD_WDEV(card)), flags);
- result2 = 0;
- result = ccw_device_start(CARD_WDEV(card), &card->dma_stuff->read_ccw,
- IDX_ACTIVATE_READ_STATE, 0, 0);
- if (result) {
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
- result2 = ccw_device_start(CARD_WDEV(card),
- &card->dma_stuff->read_ccw,
- IDX_ACTIVATE_READ_STATE, 0, 0);
- sprintf(dbf_text, "IWR1%4x", result);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "IWR2%4x", result2);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- PRINT_WARN("qeth_idx_activate_write (read): do_IO returned "
- "%i, next try returns %i\n", result, result2);
- }
-
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_WDEV(card)), flags);
-
- if (result2) {
- result = result2;
- if (result)
- return result;
- }
-
- if (qeth_sleepon(card, QETH_MPC_TIMEOUT)) {
- QETH_DBF_TEXT1(0, trace, "IWRT");
- QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE(rd) on write channel device %s: "
- "timeout\n", CARD_WDEV_ID(card));
- return -EIO;
- }
- QETH_DBF_TEXT2(0, trace, "iawr");
- QETH_DBF_TEXT2(0, trace, CARD_WDEV_ID(card));
- QETH_DBF_HEX2(0, control, card->dma_stuff->recbuf,
- QETH_DBF_CONTROL_LEN);
-
- if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
- QETH_DBF_TEXT1(0, trace, "IWNR");
- QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card));
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: negative "
- "reply\n", CARD_WDEV_ID(card));
- return -EIO;
- }
-
- memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf), 2);
- if ((temp & ~0x0100) != qeth_peer_func_level(card->func_level)) {
- QETH_DBF_TEXT1(0, trace, "IWFM");
- QETH_DBF_TEXT1(0, trace, CARD_WDEV_ID(card));
- sprintf(dbf_text, "%4x%4x", card->func_level, temp);
- QETH_DBF_TEXT1(0, trace, dbf_text);
- PRINT_WARN("IDX_ACTIVATE on write channel device %s: function "
- "level mismatch (sent: 0x%x, received: 0x%x)\n",
- CARD_WDEV_ID(card), card->func_level, temp);
- result = -EIO;
- }
-
- return result;
-}
-
-static int
-qeth_cm_enable(struct qeth_card *card)
-{
- unsigned char *buffer;
- int result;
- char dbf_text[15];
-
- memcpy(card->send_buf, CM_ENABLE, CM_ENABLE_SIZE);
-
- memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(card->send_buf),
- &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_CM_ENABLE_FILTER_TOKEN(card->send_buf),
- &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH);
-
- buffer = qeth_send_control_data(card, card->send_buf,
- CM_ENABLE_SIZE, MPC_SETUP_STATE);
-
- if (!buffer) {
- QETH_DBF_TEXT2(0, trace, "CME:NOBF");
- return -EIO;
- }
-
- memcpy(&card->token.cm_filter_r,
- QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer), QETH_MPC_TOKEN_LENGTH);
-
- result = qeth_check_idx_response(buffer);
-
- sprintf(dbf_text, "cme=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_cm_setup(struct qeth_card *card)
-{
- unsigned char *buffer;
- int result;
- char dbf_text[15];
-
- memcpy(card->send_buf, CM_SETUP, CM_SETUP_SIZE);
-
- memcpy(QETH_CM_SETUP_DEST_ADDR(card->send_buf),
- &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(card->send_buf),
- &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_CM_SETUP_FILTER_TOKEN(card->send_buf),
- &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH);
-
- buffer = qeth_send_control_data(card, card->send_buf,
- CM_SETUP_SIZE, MPC_SETUP_STATE);
-
- if (!buffer) {
- QETH_DBF_TEXT2(0, trace, "CMS:NOBF");
- return -EIO;
- }
-
- memcpy(&card->token.cm_connection_r,
- QETH_CM_SETUP_RESP_DEST_ADDR(buffer), QETH_MPC_TOKEN_LENGTH);
-
- result = qeth_check_idx_response(buffer);
-
- sprintf(dbf_text, "cms=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_ulp_enable(struct qeth_card *card)
-{
- unsigned char *buffer;
- __u16 mtu, framesize;
- __u16 len;
- __u8 link_type;
- int result;
- char dbf_text[15];
-
- memcpy(card->send_buf, ULP_ENABLE, ULP_ENABLE_SIZE);
-
- *(QETH_ULP_ENABLE_LINKNUM(card->send_buf)) =
- (__u8) card->options.portno;
-
- memcpy(QETH_ULP_ENABLE_DEST_ADDR(card->send_buf),
- &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(card->send_buf),
- &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);
-
- memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(card->send_buf),
- card->options.portname, 9);
-
- buffer = qeth_send_control_data(card, card->send_buf,
- ULP_ENABLE_SIZE, MPC_SETUP_STATE);
-
- if (!buffer) {
- QETH_DBF_TEXT2(0, trace, "ULE:NOBF");
- return -EIO;
- }
-
- memcpy(&card->token.ulp_filter_r,
- QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer),
- QETH_MPC_TOKEN_LENGTH);
-
- /* to be done before qeth_init_ringbuffers and qeth_init_dev */
- if (qeth_get_mtu_out_of_mpc(card->type)) {
- memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(buffer), 2);
- mtu = qeth_get_mtu_outof_framesize(framesize);
-
- QETH_DBF_CARD2(0, trace, "ule", card);
- sprintf(dbf_text, "mtu=%4x", mtu);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- if (!mtu)
- return -EINVAL;
-
- card->max_mtu = mtu;
- card->initial_mtu = mtu;
- card->inbound_buffer_size = mtu + 2 * PAGE_SIZE;
- } else {
- card->initial_mtu = qeth_get_initial_mtu_for_card(card);
- card->max_mtu = qeth_get_max_mtu_for_card(card->type);
- card->inbound_buffer_size = DEFAULT_BUFFER_SIZE;
- }
-
- memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer), 2);
- if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) {
- memcpy(&link_type, QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer), 1);
- card->link_type = link_type;
- sprintf(dbf_text, "link=%2x", link_type);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- } else
- card->link_type = 0;
-
- result = qeth_check_idx_response(buffer);
-
- sprintf(dbf_text, "ule=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_ulp_setup(struct qeth_card *card)
-{
- unsigned char *buffer;
- __u16 temp;
- int result;
- char dbf_text[15];
-
- memcpy(card->send_buf, ULP_SETUP, ULP_SETUP_SIZE);
-
- memcpy(QETH_ULP_SETUP_DEST_ADDR(card->send_buf),
- &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(card->send_buf),
- &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_SETUP_FILTER_TOKEN(card->send_buf),
- &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH);
-
- temp = __raw_devno_from_bus_id(CARD_DDEV_ID(card));
- memcpy(QETH_ULP_SETUP_CUA(card->send_buf), &temp, 2);
- temp = (card->cula << 8) + card->unit_addr2;
- memcpy(QETH_ULP_SETUP_REAL_DEVADDR(card->send_buf), &temp, 2);
-
- buffer = qeth_send_control_data(card, card->send_buf,
- ULP_SETUP_SIZE, MPC_SETUP_STATE);
-
- if (!buffer) {
- QETH_DBF_TEXT2(0, trace, "ULS:NOBF");
- return -EIO;
- }
-
- memcpy(&card->token.ulp_connection_r,
- QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer),
- QETH_MPC_TOKEN_LENGTH);
-
- result = qeth_check_idx_response(buffer);
-
- sprintf(dbf_text, "uls=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_qdio_establish(struct qeth_card *card)
-{
- int result;
- char *adapter_area;
- char dbf_text[15];
- void **input_array, **output_array, **ptr;
- int i, j;
- struct qdio_initialize init_data;
-
- adapter_area = vmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
- if (!adapter_area)
- return -ENOMEM;
-
- memset(adapter_area, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
-
- adapter_area[0] = _ascebc['P'];
- adapter_area[1] = _ascebc['C'];
- adapter_area[2] = _ascebc['I'];
- adapter_area[3] = _ascebc['T'];
- *((unsigned int *) (&adapter_area[4])) = PCI_THRESHOLD_A;
- *((unsigned int *) (&adapter_area[8])) = PCI_THRESHOLD_B;
- *((unsigned int *) (&adapter_area[12])) = PCI_TIMER_VALUE;
-
- input_array = vmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof (void *));
- if (!input_array) {
- vfree(adapter_area);
- return -ENOMEM;
- }
- ptr = input_array;
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) {
- *ptr = (void *) virt_to_phys(&card->inbound_qdio_buffers[j]);
- ptr++;
- }
-
- output_array = vmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof (void *) *
- card->no_queues);
- if (!output_array) {
- vfree(input_array);
- vfree(adapter_area);
- return -ENOMEM;
- }
- ptr = output_array;
- for (i = 0; i < card->no_queues; i++)
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) {
- *ptr = (void *) virt_to_phys
- (&card->outbound_ringbuffer[i]->buffer[j]);
- ptr++;
- }
-
- init_data.cdev = CARD_DDEV(card);
- init_data.q_format = qeth_get_q_format(card->type);
- init_data.qib_param_field_format = 0;
- init_data.qib_param_field = adapter_area;
- init_data.input_slib_elements = NULL;
- init_data.output_slib_elements = NULL;
- init_data.min_input_threshold = card->options.polltime;
- init_data.max_input_threshold = card->options.polltime;
- init_data.min_output_threshold = QETH_MIN_OUTPUT_THRESHOLD;
- init_data.max_output_threshold = QETH_MAX_OUTPUT_THRESHOLD;
- init_data.no_input_qs = 1;
- init_data.no_output_qs = card->no_queues;
- init_data.input_handler = qeth_qdio_input_handler;
- init_data.output_handler = qeth_qdio_output_handler;
- init_data.int_parm = (unsigned long) card;
- init_data.flags = QDIO_INBOUND_0COPY_SBALS |
- QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
- init_data.input_sbal_addr_array = input_array;
- init_data.output_sbal_addr_array = output_array;
-
- result = qdio_initialize(&init_data);
-
- vfree(input_array);
- vfree(output_array);
- vfree(adapter_area);
-
- sprintf(dbf_text, "qde=%4i", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_qdio_activate(struct qeth_card *card)
-{
- int result;
- char dbf_text[15];
-
- result = qdio_activate(CARD_DDEV(card), 0);
-
- sprintf(dbf_text, "qda=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static int
-qeth_dm_act(struct qeth_card *card)
-{
- unsigned char *buffer;
- int result;
- char dbf_text[15];
-
- memcpy(card->send_buf, DM_ACT, DM_ACT_SIZE);
-
- memcpy(QETH_DM_ACT_DEST_ADDR(card->send_buf),
- &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_DM_ACT_CONNECTION_TOKEN(card->send_buf),
- &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-
- buffer = qeth_send_control_data(card, card->send_buf,
- DM_ACT_SIZE, MPC_SETUP_STATE);
-
- if (!buffer) {
- QETH_DBF_TEXT2(0, trace, "DMA:NOBF");
- return -EIO;
- }
-
- result = qeth_check_idx_response(buffer);
-
- sprintf(dbf_text, "dma=%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- return result;
-}
-
-static inline int
-__qeth_verify_dev_vlan(struct net_device *dev,struct qeth_card *card)
-{
-#ifdef QETH_VLAN
- struct vlan_group *vlan_grp;
- int i;
- int result = 0;
-
- /* check all vlan devices */
- vlan_grp = (struct vlan_group *) card->vlangrp;
- if (vlan_grp) {
- for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
- if (vlan_grp->vlan_devices[i] == dev) {
- result = QETH_VERIFY_IS_VLAN_DEV;
- }
- }
- }
- return result;
-#endif
- return 0;
-}
-
-#if defined(QETH_VLAN)||defined(QETH_IPV6)
-static int
-qeth_verify_dev(struct net_device *dev)
-{
- struct qeth_card *tmp;
- int result = 0;
-
- read_lock(&list_lock);
- tmp = firstcard;
- for (; tmp && (!result); tmp = tmp->next) {
- if (atomic_read(&tmp->shutdown_phase))
- continue;
- result = (dev == tmp->dev)?
- QETH_VERIFY_IS_REAL_DEV:__qeth_verify_dev_vlan(dev, tmp);
- }
- read_unlock(&list_lock);
- return result;
-}
-#endif /* defined(QETH_VLAN)||defined(QETH_IPV6) */
-
-static int
-qeth_verify_card(struct qeth_card *card)
-{
- struct qeth_card *tmp;
- int result = 0;
-
- read_lock(&list_lock);
- tmp = firstcard;
- while (tmp) {
- if ((card == tmp) && (!atomic_read(&card->shutdown_phase))) {
- result = 1;
- break;
- }
- tmp = tmp->next;
- }
- read_unlock(&list_lock);
- return result;
-}
-
-static inline struct qeth_card *
-__qeth_get_card_from_dev(struct net_device *dev)
-{
-#ifdef QETH_VLAN
- if (qeth_verify_dev(dev) == QETH_VERIFY_IS_VLAN_DEV)
- return (struct qeth_card *) VLAN_DEV_INFO(dev)->real_dev->priv;
- else
-#endif
- return (struct qeth_card *) dev->priv;
-}
-
-#ifdef QETH_IPV6
-/* FIXME: don't put extern declarations in a c file, use a header that's
- * shared with the definition for this! */
-extern struct neigh_table arp_tbl;
-static int (*qeth_old_arp_constructor) (struct neighbour *);
-static struct neigh_ops arp_direct_ops_template = {
- .family = AF_INET,
- .destructor = NULL,
- .solicit = NULL,
- .error_report = NULL,
- .output = dev_queue_xmit,
- .connected_output = dev_queue_xmit,
- .hh_output = dev_queue_xmit,
- .queue_xmit = dev_queue_xmit
-};
-
-/*
- * FIXME:
- * as we have neighbour structures point to this structure, even
- * after our life time, this will stay in memory as a leak
- */
-static struct neigh_ops *arp_direct_ops;
-
-
-static int
-qeth_arp_constructor(struct neighbour *neigh)
-{
- char dbf_text[15];
- struct net_device *dev = neigh->dev;
- struct in_device *in_dev = in_dev_get(dev);
-
- if (in_dev == NULL)
- return -EINVAL;
-
- QETH_DBF_TEXT4(0, trace, "arpconst");
- if (!qeth_verify_dev(dev)) {
-
- in_dev_put(in_dev);
- return qeth_old_arp_constructor(neigh);
- }
-
- neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
- if (in_dev->arp_parms)
- neigh->parms = in_dev->arp_parms;
-
- in_dev_put(in_dev);
-
- sprintf(dbf_text, "%08x", ntohl(*((__u32 *) (neigh->primary_key))));
- QETH_DBF_TEXT4(0, trace, dbf_text);
- QETH_DBF_HEX4(0, trace, &neigh, sizeof (void *));
-
- neigh->nud_state = NUD_NOARP;
- neigh->ops = arp_direct_ops;
- neigh->output = neigh->ops->queue_xmit;
- return 0;
-}
-
-static int
-qeth_hard_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- struct qeth_card *card;
-
- QETH_DBF_TEXT5(0, trace, "hardhdr");
-
- card = __qeth_get_card_from_dev(dev);
- return card->hard_header(skb, dev, type, daddr, saddr, len);
-}
-
-static void
-qeth_header_cache_update(struct hh_cache *hh,
- struct net_device *dev, unsigned char *haddr)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_TEXT5(0, trace, "hdrcheup");
- return card->header_cache_update(hh, dev, haddr);
-}
-
-static int
-qeth_rebuild_header(struct sk_buff *skb)
-{
- struct qeth_card *card;
- QETH_DBF_TEXT5(0, trace, "rebldhdr");
- if (skb->protocol == __constant_htons(ETH_P_IP))
- return 0;
-
- card = __qeth_get_card_from_dev(skb->dev);
-
- return card->rebuild_header(skb);
-}
-
-int
-qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
-{
- switch (dev->type) {
- case ARPHRD_ETHER:
- case ARPHRD_FDDI:
- case ARPHRD_IEEE802_TR:
- if (dev->addr_len != ETH_ALEN)
- return -1;
- memcpy(eui, dev->dev_addr, 3);
- memcpy(eui + 5, dev->dev_addr + 3, 3);
- eui[3] = (dev->dev_id >> 8) & 0xff;
- eui[4] = dev->dev_id & 0xff;
- return 0;
- }
- return -1;
-
-}
-#endif /* QETH_IPV6 */
-
-static void
-qeth_ipv6_init_card(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- card->hard_header = qeth_get_hard_header(card->link_type);
- card->rebuild_header = qeth_get_rebuild_header(card->link_type);
- card->hard_header_cache = qeth_get_hard_header_cache(card->link_type);
- card->header_cache_update =
- qeth_get_header_cache_update(card->link_type);
- card->type_trans = qeth_get_type_trans(card->link_type);
- card->dev->dev_id = card->unique_id & 0xffff;
- if (!(card->unique_id & UNIQUE_ID_NOT_BY_CARD))
- card->dev->generate_eui64 = qeth_ipv6_generate_eui64;
-#endif /* QETH_IPV6 */
-}
-
-#ifdef QETH_VLAN
-static void
-qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct qeth_card *card;
- card = (struct qeth_card *) dev->priv;
- spin_lock_irq(&card->vlan_lock);
- card->vlangrp = grp;
- spin_unlock_irq(&card->vlan_lock);
-}
-static void
-qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
-{
- struct qeth_card *card;
- card = (struct qeth_card *) dev->priv;
- spin_lock_irq(&card->vlan_lock);
- if (card->vlangrp)
- card->vlangrp->vlan_devices[vid] = NULL;
- spin_unlock_irq(&card->vlan_lock);
-}
-#endif
-
-static void
-qeth_tx_timeout(struct net_device *dev)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
- QETH_DBF_CARD2(1, trace, "XMTO", card);
- card->stats->tx_errors++;
- atomic_set(&card->problem, PROBLEM_TX_TIMEOUT);
- qeth_schedule_recovery(card);
-}
-
-static void*
-__qeth_rebuild_header_func(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ?
- (qeth_get_rebuild_header(card->link_type) ?
- qeth_rebuild_header : NULL) : NULL;
-#endif /* QETH_IPV6 */
- return NULL;
-}
-
-static void*
-__qeth_hard_header_func(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ?
- (qeth_get_hard_header(card->link_type) ?
- qeth_hard_header : NULL) : NULL;
-#endif /* QETH_IPV6 */
- return NULL;
-}
-
-static void*
-__qeth_header_cache_update_func(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ?
- (qeth_get_header_cache_update(card->link_type) ?
- qeth_header_cache_update : NULL) : NULL;
-#endif /* QETH_IPV6 */
- return NULL;
-}
-
-static void*
-__qeth_hard_header_cache_func(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- return (!(qeth_get_additional_dev_flags(card->type) & IFF_NOARP)) ?
- qeth_get_hard_header_cache(card->link_type) : NULL;
-#endif /* QETH_IPV6 */
- return NULL;
-}
-
-static int
-qeth_init_dev(struct net_device *dev)
-{
- struct qeth_card *card;
-
- card = (struct qeth_card *) dev->priv;
-
- QETH_DBF_CARD3(0, trace, "inid", card);
-
- dev->rebuild_header = __qeth_rebuild_header_func(card);
- dev->hard_header = __qeth_hard_header_func(card);
- dev->header_cache_update = __qeth_header_cache_update_func(card);
- dev->hard_header_cache = __qeth_hard_header_cache_func(card);
- dev->hard_header_parse = NULL;
-
- dev->flags |= qeth_get_additional_dev_flags(card->type);
-
- dev->flags |= ((card->options.fake_broadcast == FAKE_BROADCAST) ||
- (card->broadcast_capable)) ? IFF_BROADCAST : 0;
-
- /* is done in hardsetup_card... see comment below
- qeth_send_qipassist(card,4);*/
-
- /* that was the old place. one id. we need to make sure, that
- * hydra knows about us going to use the same id again, so we
- * do that in hardsetup_card every time
- qeth_get_unique_id(card);*/
-
- dev->tx_queue_len = qeth_get_device_tx_q_len(card->type);
- dev->hard_header_len =
- qeth_get_hlen(card->link_type) + card->options.add_hhlen;
- netif_start_queue(dev);
-
- dev->mtu = card->initial_mtu;
-
- qeth_ipv6_init_card(card);
-
- return 0;
-}
-
-static int
-qeth_get_unitaddr(struct qeth_card *card)
-{
- char *prcd;
- int result = 0;
- char dbf_text[15];
- int length;
-
- QETH_DBF_CARD3(0, trace, "gtua", card);
-
- result = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length);
- if (result) {
- sprintf(dbf_text, "rcd%4x", result);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- PRINT_ERR("read_conf_data for device %s returned %i\n",
- CARD_DDEV_ID(card), result);
- return result;
- }
-
- card->chpid = prcd[30];
- card->unit_addr2 = prcd[31];
- card->cula = prcd[63];
- card->is_guest_lan= ((prcd[0x10] == _ascebc['V']) &&
- (prcd[0x11] == _ascebc['M']));
-
- sprintf(dbf_text, "chpid:%02x", card->chpid);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "unad2:%02x", card->unit_addr2);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- sprintf(dbf_text, "cula:%02x", card->cula);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- return 0;
-}
-
-static int
-qeth_send_nops(struct qeth_card *card)
-{
- int result, result2;
- unsigned long saveflags;
-
- card->dma_stuff->write_ccw.cmd_code = CCW_NOP_CMD;
- card->dma_stuff->write_ccw.flags = CCW_FLAG_SLI;
- card->dma_stuff->write_ccw.count = CCW_NOP_COUNT;
- card->dma_stuff->write_ccw.cda = (unsigned long) NULL;
-
-#define DO_SEND_NOP(cdev) \
-do { \
- QETH_DBF_TEXT3(0, trace, "snnp"); \
- QETH_DBF_TEXT3(0, trace, cdev->dev.bus_id); \
-\
- spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); \
- ccw_device_set_options(cdev, 0); \
- result=ccw_device_start(cdev,&card->dma_stuff->write_ccw, \
- NOP_STATE,0,0); \
- if (result) { \
- qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); \
- result2=ccw_device_start(cdev,&card->dma_stuff->write_ccw, \
- NOP_STATE,0,0); \
- PRINT_WARN("qeth_send_nops on device %s: do_IO returned %i, " \
- "next try returns %i\n", \
- cdev->dev.bus_id,result,result2); \
- result=result2; \
- } \
- spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags); \
-\
- if (result) goto exit; \
-\
- if (qeth_sleepon(card,QETH_NOP_TIMEOUT)) { \
- QETH_DBF_TEXT2(0,trace,"snnp:tme"); \
- result=-EIO; \
- goto exit; \
- } \
-} while (0)
-
- DO_SEND_NOP(CARD_RDEV(card));
- DO_SEND_NOP(CARD_WDEV(card));
- DO_SEND_NOP(CARD_DDEV(card));
-
-exit:
- return result;
-}
-
-static void
-qeth_clear_card_structures(struct qeth_card *card)
-{
- int i, j;
-
- if (!card) {
- QETH_DBF_TEXT2(0, trace, "clrCRDnc");
- return;
- }
-
- QETH_DBF_CARD3(0, trace, "clcs", card);
-
- atomic_set(&card->is_startlaned, 0);
-
- for (i = 0; i < QETH_MAX_QUEUES; i++) {
- card->send_state[i] = SEND_STATE_DONT_PACK;
- card->outbound_first_free_buffer[i] = 0;
- atomic_set(&card->outbound_used_buffers[i], 0);
- atomic_set(&card->outbound_ringbuffer_lock[i], 0);
-
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) {
- card->outbound_buffer_send_state[i][j] =
- SEND_STATE_DONT_PACK;
- card->send_retries[i][j] = 0;
-
- if (i < card->no_queues) {
- card->outbound_ringbuffer[i]->
- ringbuf_element[j].next_element_to_fill = 0;
- card->outbound_bytes_in_buffer[i] = 0;
- skb_queue_head_init(&card->
- outbound_ringbuffer[i]->
- ringbuf_element[j].
- skb_list);
- }
- }
- }
-
- for (i = 0; i < card->options.inbound_buffer_count; i++) {
- xchg((int *) &card->inbound_buffer_pool_entry_used[i],
- BUFFER_UNUSED);
- }
-
- spin_lock_init(&card->requeue_input_lock);
- atomic_set(&card->requeue_position, 0);
- atomic_set(&card->requeue_counter, 0);
-
- card->seqno.trans_hdr = 0;
- card->seqno.pdu_hdr = 0;
- card->seqno.pdu_hdr_ack = 0;
- card->seqno.ipa = 0;
-
- qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
- qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
-
-#ifdef QETH_IPV6
- qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
- qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
-#endif /* QETH_IPV6 */
-}
-
-static void
-qeth_init_input_buffers(struct qeth_card *card)
-{
- int i;
-
- /* slowly, slowly (we don't want to enqueue all buffers
- * at one time) */
- for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- atomic_set(&card->inbound_buffer_refcnt[i], 1);
- }
- for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- atomic_set(&card->inbound_buffer_refcnt[i], 0);
- /* only try to queue as many buffers as we have at all */
- if (i < card->options.inbound_buffer_count)
- qeth_queue_input_buffer(card,i,0);
- }
- qdio_synchronize(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0);
-}
-
-/* initializes all the structures for a card */
-static int
-qeth_hardsetup_card(struct qeth_card *card, int in_recovery)
-{
- int result, q, breakout;
- unsigned long flags;
- int laps = QETH_HARDSETUP_LAPS;
- int clear_laps;
- int cleanup_qdio;
- char dbf_text[15];
- int i, r;
-
- /* setup name and so on */
- atomic_set(&card->shutdown_phase, 0);
-
- if (atomic_read(&card->is_hardsetup)) {
- QETH_DBF_CARD2(1, trace, "hscd", card);
- PRINT_ALL("card is already hardsetup.\n");
- return 0;
- }
-
- cleanup_qdio = in_recovery; /* if we are in recovery, we clean
- the qdio stuff up */
-
- down(&card->hardsetup_sema);
- atomic_set(&card->write_busy, 0);
-
- do {
- if (in_recovery) {
- PRINT_STUPID("qeth: recovery: quiescing %s...\n",
- card->dev_name);
- QETH_DBF_CARD2(0, trace, "Rqsc", card);
- qeth_wait_nonbusy(QETH_QUIESCE_WAIT_BEFORE_CLEAR);
- }
- clear_laps = QETH_HARDSETUP_CLEAR_LAPS;
- do {
- if (in_recovery)
- PRINT_STUPID("clearing card %s\n",
- card->dev_name);
- qeth_clear_card(card, cleanup_qdio,
- (card->type == QETH_CARD_TYPE_OSAE));
- result = qeth_send_nops(card);
- breakout = atomic_read(&card->break_out);
- } while ((--clear_laps) && (result));
- if (result) {
- goto exit;
- }
-
- if (in_recovery) {
- PRINT_STUPID("qeth: recovery: still quiescing %s...\n",
- card->dev_name);
- QETH_DBF_CARD2(0, trace, "RQsc", card);
- qeth_wait_nonbusy(QETH_QUIESCE_WAIT_AFTER_CLEAR);
- } else {
- atomic_set(&card->shutdown_phase, 0);
- }
-
- cleanup_qdio = 0; /* qdio was cleaned now, if necessary */
-
- result = qeth_get_unitaddr(card);
- if (result)
- goto exit;
-
- qeth_generate_tokens(card);
-
-#define PRINT_TOKENS do { \
- sprintf(dbf_text,"stra "); \
- memcpy(&dbf_text[4],&card->seqno.trans_hdr,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"spdu "); \
- memcpy(&dbf_text[4],&card->seqno.pdu_hdr,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"spda "); \
- memcpy(&dbf_text[4],&card->seqno.pdu_hdr_ack,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"sipa "); \
- memcpy(&dbf_text[4],&card->seqno.ipa,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tisw "); \
- memcpy(&dbf_text[4],&card->token.issuer_rm_w,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tisr "); \
- memcpy(&dbf_text[4],&card->token.issuer_rm_r,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tcfw "); \
- memcpy(&dbf_text[4],&card->token.cm_filter_w,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tcfr "); \
- memcpy(&dbf_text[4],&card->token.cm_filter_r,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tccw "); \
- memcpy(&dbf_text[4],&card->token.cm_connection_w,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tccr "); \
- memcpy(&dbf_text[4],&card->token.cm_connection_r,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tufw "); \
- memcpy(&dbf_text[4],&card->token.ulp_filter_w,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tufr "); \
- memcpy(&dbf_text[4],&card->token.ulp_filter_r,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tucw "); \
- memcpy(&dbf_text[4],&card->token.ulp_connection_w,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- sprintf(dbf_text,"tucr "); \
- memcpy(&dbf_text[4],&card->token.ulp_connection_r,4); \
- QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
- } while (0)
-
- PRINT_TOKENS;
-
- /* card->break_out and problem will be set here to 0
- * (in each lap) (there can't be a problem at this
- * early time) */
- atomic_set(&card->problem, 0);
- atomic_set(&card->break_out, 0);
-
-#define CHECK_ERRORS \
- breakout=atomic_read(&card->break_out); \
- if (breakout==QETH_BREAKOUT_AGAIN) \
- continue; \
- else if (breakout==QETH_BREAKOUT_LEAVE) { \
- result=-EIO; \
- goto exit; \
- } \
- if (result) goto exit
-
- QETH_DBF_TEXT2(0, trace, "hsidxard");
- result = qeth_idx_activate_read(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hsidxawr");
- result = qeth_idx_activate_write(card);
- CHECK_ERRORS;
-
- QETH_DBF_TEXT2(0, trace, "hsissurd");
- /* from here, there will always be an outstanding read */
- spin_lock_irqsave(get_ccwdev_lock(CARD_RDEV(card)), flags);
- qeth_issue_next_read(card);
- spin_unlock_irqrestore(get_ccwdev_lock(CARD_RDEV(card)), flags);
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hscmenab");
- result = qeth_cm_enable(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hscmsetu");
- result = qeth_cm_setup(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hsulpena");
- result = qeth_ulp_enable(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hsulpset");
- result = qeth_ulp_setup(card);
- CHECK_ERRORS;
-
- cleanup_qdio = 1;
-
- QETH_DBF_TEXT2(0, trace, "hsqdioes");
- result = qeth_qdio_establish(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hsqdioac");
- result = qeth_qdio_activate(card);
- CHECK_ERRORS;
-
- PRINT_TOKENS;
- QETH_DBF_TEXT2(0, trace, "hsdmact");
- result = qeth_dm_act(card);
- CHECK_ERRORS;
- } while ((laps--) && (breakout == QETH_BREAKOUT_AGAIN));
- if (breakout == QETH_BREAKOUT_AGAIN) {
- QETH_DBF_CARD2(0, trace, "hsnr", card);
- PRINT_ERR("qeth: recovery not successful on device "
- "%s/%s/%s; giving up.\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card), CARD_DDEV_ID(card));
- result = -EIO;
- goto exit;
- }
-
- qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
- qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
-
-#ifdef QETH_IPV6
- qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
- qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
-#endif /* QETH_IPV6 */
-
- if (!atomic_read(&card->is_registered)) {
- card->dev->dev_addr[0] = 0; /* we don't know the mac addr yet */
- card->dev->dev_addr[1] = 0;
- card->dev->dev_addr[2] = 0;
- card->dev->dev_addr[3] = 0;
- card->dev->dev_addr[4] = 0;
- card->dev->dev_addr[5] = 0;
- card->dev->broadcast[0] = card->dev->broadcast[1] = 0xff;
- card->dev->broadcast[2] = card->dev->broadcast[3] = 0xff;
- card->dev->broadcast[4] = card->dev->broadcast[5] = 0xff;
-
- card->dev->type = qeth_get_arphrd_type(card->type,
- card->link_type);
-
- card->dev->init = qeth_init_dev;
-
- card->ipa_timeout = qeth_get_ipa_timeout(card->type);
- }
-
- atomic_set(&card->is_hardsetup, 1);
- atomic_set(&card->is_softsetup, 0);
- atomic_set(&card->startlan_attempts, 1);
-
- for (q = 0; q < card->no_queues; q++)
- card->send_state[q] = SEND_STATE_DONT_PACK;
-
- /* we need to know first, whether we should include a value
- * into eui-64 address generation */
- QETH_DBF_TEXT2(0, trace, "qipassi4");
- r = qeth_send_qipassist(card, 4);
- if (r) {
- PRINT_WARN("couldn't send QIPASSIST4 on %s: "
- "0x%x\n", card->dev_name, r);
- sprintf(dbf_text, "QIP4%4x", r);
- QETH_DBF_TEXT2(0, trace, dbf_text);
- }
-
- sprintf(dbf_text, "%4x%4x", card->ipa_supported, card->ipa_enabled);
- QETH_DBF_TEXT2(0, trace, dbf_text);
-
- qeth_get_unique_id(card);
-
- /* print out status */
- if (in_recovery) {
- qeth_clear_card_structures(card);
- qeth_init_input_buffers(card);
- QETH_DBF_TEXT1(0, trace, "RECOVSUC");
- PRINT_INFO("qeth: recovered device %s/%s/%s (%s) "
- "successfully.\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card), card->dev_name);
- } else {
- QETH_DBF_TEXT2(0, trace, "hrdsetok");
-
- switch (card->type) {
- case QETH_CARD_TYPE_OSAE:
- /*
- * VM will use a non-zero first character to indicate
- * a HiperSockets like reporting of the level
- * OSA sets the first character to zero
- */
- if (!card->level[0]) {
- sprintf(card->level, "%02x%02x", card->level[2],
- card->level[3]);
- card->level[QETH_MCL_LENGTH] = 0;
- break;
- }
- /* fallthrough */
- case QETH_CARD_TYPE_IQD:
- card->level[0] = (char) _ebcasc[(__u8) card->level[0]];
- card->level[1] = (char) _ebcasc[(__u8) card->level[1]];
- card->level[2] = (char) _ebcasc[(__u8) card->level[2]];
- card->level[3] = (char) _ebcasc[(__u8) card->level[3]];
- card->level[QETH_MCL_LENGTH] = 0;
- break;
- default:
- memset(&card->level[0], 0, QETH_MCL_LENGTH + 1);
- }
-
- sprintf(dbf_text, "lvl:%s", card->level);
- QETH_DBF_TEXT2(0, setup, dbf_text);
-
- if (card->portname_required) {
- sprintf(dbf_text, "%s", card->options.portname + 1);
- for (i = 0; i < 8; i++)
- dbf_text[i] =
- (char) _ebcasc[(__u8) dbf_text[i]];
- dbf_text[8] = 0;
- printk("qeth: Device %s/%s/%s is a%s card%s%s%s\n"
- "with link type %s (portname: %s)\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
- qeth_get_cardname(card->type,
- card->is_guest_lan),
- (card->level[0]) ? " (level: " : "",
- (card->level[0]) ? card->level : "",
- (card->level[0]) ? ")" : "",
- qeth_get_link_type_name(card->type,
- card->link_type),
- dbf_text);
- } else {
- if (card->options.portname[0])
- printk("qeth: Device %s/%s/%s is a%s "
- "card%s%s%s\nwith link type %s "
- "(no portname needed by interface).\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
- qeth_get_cardname(card->type,
- card->is_guest_lan),
- (card->level[0]) ? " (level: " : "",
- (card->level[0]) ? card->level : "",
- (card->level[0]) ? ")" : "",
- qeth_get_link_type_name(card->type,
- card->link_type));
- else
- printk("qeth: Device %s/%s/%s is a%s "
- "card%s%s%s\nwith link type %s.\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
- qeth_get_cardname(card->type,
- card->is_guest_lan),
- (card->level[0]) ? " (level: " : "",
- (card->level[0]) ? card->level : "",
- (card->level[0]) ? ")" : "",
- qeth_get_link_type_name(card->type,
- card->link_type));
- }
- }
-
-exit:
- up(&card->hardsetup_sema);
- return result;
-}
-
-static int
-qeth_reinit_thread(void *param)
-{
- struct qeth_card *card = (struct qeth_card *) param;
- int already_registered;
- int already_hardsetup;
- int retry = QETH_RECOVERY_HARDSETUP_RETRY;
- int result;
- char name[15];
-
- QETH_DBF_CARD1(0, trace, "RINI", card);
-
- /* set a nice name ... */
- sprintf(name, "qethrinid%s", CARD_BUS_ID(card));
- daemonize(name);
-
- if (atomic_read(&card->shutdown_phase))
- goto out_wakeup;
- down_interruptible(&card->reinit_thread_sem);
- if (atomic_read(&card->shutdown_phase))
- goto out_wakeup;
-
- QETH_DBF_TEXT1(0, trace, "ri-gotin");
- PRINT_STUPID("entering recovery (reinit) thread for device %s\n",
- card->dev_name);
-
- atomic_set(&card->is_startlaned, 0);
- atomic_set(&card->is_softsetup, 0);
-
- read_lock(&list_lock);
- if (!qeth_verify_card(card))
- goto out;
- QETH_DBF_TEXT1(0, trace, "ri-vrfd");
-
- atomic_set(&card->write_busy, 0);
- qeth_set_dev_flag_norunning(card);
- already_hardsetup = atomic_read(&card->is_hardsetup);
- already_registered = atomic_read(&card->is_registered);
- if (already_hardsetup) {
- atomic_set(&card->is_hardsetup, 0);
-
- if (-1 == my_spin_lock_nonbusy(card, &setup_lock))
- goto out;
- if (atomic_read(&card->shutdown_phase))
- goto out_wakeup;
-
- atomic_set(&card->escape_softsetup, 1);
-
- if (-1 == my_down_trylock_nonbusy(card, &card->softsetup_sema)) {
- atomic_set(&card->escape_softsetup, 0);
- goto out;
- }
- atomic_set(&card->escape_softsetup, 0);
- if (atomic_read(&card->shutdown_phase)) {
- up(&card->softsetup_sema);
- goto out_wakeup;
- }
- if (!qeth_verify_card(card))
- goto out;
-
- if (already_registered)
- netif_stop_queue(card->dev);
-
- qeth_wait_nonbusy(QETH_QUIESCE_NETDEV_TIME);
-
- atomic_set(&card->is_startlaned, 0);
-
- QETH_DBF_TEXT1(0, trace, "ri-frskb");
- qeth_free_all_skbs(card);
- do {
- QETH_DBF_TEXT1(0, trace, "ri-hrdst");
- result = qeth_hardsetup_card(card, 1);
- } while (result && (retry--));
-
- /* tries to remove old ips, that's paranoid, but ok */
- qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
-
-#ifdef QETH_IPV6
- qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
- qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
-#endif /* QETH_IPV6 */
-
- if (result) {
- QETH_DBF_TEXT1(0, trace, "ri-nosuc");
- PRINT_ERR("qeth: RECOVERY WAS NOT SUCCESSFUL ON %s "
- "(%s/%s/%s), GIVING UP, "
- "OUTGOING PACKETS WILL BE DISCARDED!\n",
- card->dev_name,
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card));
- /* early leave hard_start_xmit! */
- atomic_set(&card->is_startlaned, 0);
- qeth_wakeup_procfile();
- } else {
- QETH_DBF_TEXT1(0, trace, "ri-sftst");
- qeth_softsetup_card(card, QETH_LOCK_ALREADY_HELD);
- up(&card->softsetup_sema);
-
- if (!already_registered) {
- QETH_DBF_TEXT1(0, trace, "ri-regcd");
- qeth_register_netdev(card);
- }
- qeth_restore_dev_flag_state(card);
- netif_wake_queue(card->dev);
- qeth_wakeup_procfile();
- }
- spin_unlock(&setup_lock);
- }
-out:
- atomic_set(&card->in_recovery, 0);
- read_unlock(&list_lock);
- QETH_DBF_TEXT1(0, trace, "ri-leave");
-out_wakeup:
- up(&card->reinit_thread_sem);
- atomic_dec(&card->reinit_counter);
-
- return 0;
-}
-
-static void
-qeth_fill_qeth_card_options(struct qeth_card *card)
-{
- int i;
-
- card->options.portname[0] = 0;
- for (i = 1; i < 9; i++)
- card->options.portname[i] = _ascebc[' '];
- strcpy(card->options.devname, " ");
- card->options.routing_type4 = NO_ROUTER;
-#ifdef QETH_IPV6
- card->options.routing_type6 = NO_ROUTER;
-#endif /* QETH_IPV6 */
- card->options.portno = 0;
- card->options.checksum_type = QETH_CHECKSUM_DEFAULT;
- card->options.do_prio_queueing = QETH_PRIOQ_DEFAULT;
- card->options.default_queue = QETH_DEFAULT_QUEUE;
- card->options.inbound_buffer_count = DEFAULT_BUFFER_COUNT;
- card->options.polltime = QETH_MAX_INPUT_THRESHOLD;
- card->options.macaddr_mode = MACADDR_NONCANONICAL;
- card->options.broadcast_mode = BROADCAST_ALLRINGS;
- card->options.fake_broadcast = DONT_FAKE_BROADCAST;
- card->options.ena_ipat = ENABLE_TAKEOVER;
- card->options.add_hhlen = DEFAULT_ADD_HHLEN;
- card->options.fake_ll = DONT_FAKE_LL;
-}
-
-static void qeth_setup(struct net_device *dev)
-{
- dev->tx_timeout = &qeth_tx_timeout;
- dev->watchdog_timeo = QETH_TX_TIMEOUT;
- dev->open = qeth_open;
- dev->stop = qeth_stop;
- dev->set_config = qeth_set_config;
- dev->hard_start_xmit = qeth_hard_start_xmit;
- dev->do_ioctl = qeth_do_ioctl;
- dev->get_stats = qeth_get_stats;
- dev->change_mtu = qeth_change_mtu;
-#ifdef QETH_VLAN
- dev->vlan_rx_register = qeth_vlan_rx_register;
- dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
-#endif
- dev->set_multicast_list = qeth_set_multicast_list;
- dev->set_mac_address = qeth_set_mac_address;
- dev->neigh_setup = qeth_neigh_setup;
- dev->addr_len = OSA_ADDR_LEN; /* is ok for eth, tr, atm lane */
- SET_MODULE_OWNER(dev);
-}
-
-static int
-qeth_alloc_card_stuff(struct qeth_card *card)
-{
- if (!card)
- return -EINVAL;
-
- QETH_DBF_TEXT3(0, trace, "alccrdst");
-
- card->dma_stuff =
- (struct qeth_dma_stuff *) kmalloc(sizeof (struct qeth_dma_stuff),
- GFP_KERNEL | GFP_DMA);
- if (!card->dma_stuff)
- goto exit_dma;
- memset(card->dma_stuff, 0, sizeof (struct qeth_dma_stuff));
-
- card->dma_stuff->recbuf = (char *) kmalloc(QETH_BUFSIZE,
- GFP_KERNEL | GFP_DMA);
- if (!card->dma_stuff->recbuf)
- goto exit_dma1;
- memset(card->dma_stuff->recbuf, 0, QETH_BUFSIZE);
-
- card->dma_stuff->sendbuf = (char *) kmalloc(QETH_BUFSIZE,
- GFP_KERNEL | GFP_DMA);
- if (!card->dma_stuff->sendbuf)
- goto exit_dma2;
- memset(card->dma_stuff->sendbuf, 0, QETH_BUFSIZE);
-
- card->dev = alloc_netdev(0, "", qeth_setup);
- if (!card->dev)
- goto exit_dev;
-
- card->stats =
- (struct net_device_stats *)
- kmalloc(sizeof (struct net_device_stats), GFP_KERNEL);
- if (!card->stats)
- goto exit_stats;
- memset(card->stats, 0, sizeof (struct net_device_stats));
-
- /* setup net_device stuff */
- card->dev->priv = card;
-
- /* setup net_device_stats stuff */
- /* =nothing yet */
-
- return 0;
-
- /* these are quick exits in case of failures of the kmallocs */
-exit_stats:
- free_netdev(card->dev);
-exit_dev:
- kfree(card->dma_stuff->sendbuf);
-exit_dma2:
- kfree(card->dma_stuff->recbuf);
-exit_dma1:
- kfree(card->dma_stuff);
-exit_dma:
- return -ENOMEM;
-}
-
-static struct qeth_card *
-qeth_alloc_card(void)
-{
- struct qeth_card *card;
-
- QETH_DBF_TEXT3(0, trace, "alloccrd");
- card = (struct qeth_card *) vmalloc(sizeof (struct qeth_card));
- if (!card)
- return NULL;
- memset(card, 0, sizeof (struct qeth_card));
- init_waitqueue_head(&card->wait_q);
- init_waitqueue_head(&card->ioctl_wait_q);
-
- qeth_fill_qeth_card_options(card);
-
- init_MUTEX(&card->softsetup_sema);
- init_MUTEX(&card->hardsetup_sema);
- spin_lock_init(&card->ioctl_lock);
-#ifdef QETH_VLAN
- spin_lock_init(&card->vlan_lock);
- card->vlangrp = NULL;
-#endif
- card->unique_id = 0;
- sema_init(&card->reinit_thread_sem, 0);
- up(&card->reinit_thread_sem);
-
- /* setup card stuff */
- card->ip_current_state.ip_ifa = NULL;
- card->ip_new_state.ip_ifa = NULL;
- card->ip_mc_current_state.ipm_ifa = NULL;
- card->ip_mc_new_state.ipm_ifa = NULL;
-
-#ifdef QETH_IPV6
- card->ip_current_state.ip6_ifa = NULL;
- card->ip_new_state.ip6_ifa = NULL;
- card->ip_mc_current_state.ipm6_ifa = NULL;
- card->ip_mc_new_state.ipm6_ifa = NULL;
-#endif /* QETH_IPV6 */
-
- card->csum_enable_mask = IPA_CHECKSUM_DEFAULT_ENABLE_MASK;
-
- /* and return to the sender */
- return card;
-
-}
-
-static int
-qeth_init_ringbuffers1(struct qeth_card *card)
-{
- int i, j;
-
- QETH_DBF_CARD3(0, trace, "irb1", card);
-
- for (i = 0; i < card->no_queues; i++) {
- card->outbound_ringbuffer[i] =
- vmalloc(sizeof (struct qeth_ringbuffer));
- if (!card->outbound_ringbuffer[i]) {
- for (j = i - 1; j >= 0; j--) {
- vfree(card->outbound_ringbuffer[j]);
- card->outbound_ringbuffer[j] = NULL;
- }
- return -ENOMEM;
- }
- memset(card->outbound_ringbuffer[i], 0,
- sizeof (struct qeth_ringbuffer));
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
- skb_queue_head_init(&card->outbound_ringbuffer[i]->
- ringbuf_element[j].skb_list);
- }
-
- return 0;
-}
-
-static int
-qeth_init_ringbuffers2(struct qeth_card *card)
-{
- int i, j;
-
- QETH_DBF_CARD3(0, trace, "irb2", card);
-
- for (i = 0; i < card->options.inbound_buffer_count; i++) {
- for (j = 0; j < BUFFER_MAX_ELEMENTS; j++) {
- card->inbound_buffer_pool_entry[i][j] =
- kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!card->inbound_buffer_pool_entry[i][j]) {
- goto out;
- }
- }
- card->inbound_buffer_pool_entry_used[i] = BUFFER_UNUSED;
- }
-
- spin_lock_init(&card->requeue_input_lock);
-
- return 0;
-out:
- for (i = 0; i < card->options.inbound_buffer_count; i++) {
- for (j = 0; j < QDIO_MAX_ELEMENTS_PER_BUFFER; j++) {
- if (card->inbound_buffer_pool_entry[i][j]) {
- if (j < BUFFER_MAX_ELEMENTS)
- kfree(card->
- inbound_buffer_pool_entry[i][j]);
- card->inbound_buffer_pool_entry[i][j] = NULL;
- }
- }
- }
- for (i = 0; i < card->no_queues; i++) {
- vfree(card->outbound_ringbuffer[i]);
- card->outbound_ringbuffer[i] = NULL;
- }
- return -ENOMEM;
-
-}
-
-/* also locked from outside (setup_lock) */
-static void
-qeth_insert_card_into_list(struct qeth_card *card)
-{
- QETH_DBF_CARD3(0, trace, "icil", card);
-
- write_lock(&list_lock);
- card->next = firstcard;
- firstcard = card;
- write_unlock(&list_lock);
-}
-
-static int
-qeth_determine_card_type(struct qeth_card *card)
-{
- int i = 0;
- char dbf_text[15];
-
- while (known_devices[i][4]) {
- if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
- (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
- card->type = known_devices[i][4];
- if (card->options.ena_ipat == ENABLE_TAKEOVER)
- card->func_level = known_devices[i][6];
- else
- card->func_level = known_devices[i][7];
- card->no_queues = known_devices[i][8];
- card->is_multicast_different = known_devices[i][9];
- QETH_DBF_TEXT2(0, setup, CARD_BUS_ID(card));
- sprintf(dbf_text, "ctyp%4x", card->type);
- QETH_DBF_TEXT2(0, setup, dbf_text);
- return 0;
- }
- i++;
- }
- card->type = QETH_CARD_TYPE_UNKNOWN;
- QETH_DBF_TEXT2(0, setup, CARD_BUS_ID(card));
- sprintf(dbf_text, "ctypUNKN");
- QETH_DBF_TEXT2(0, setup, dbf_text);
- PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card));
- return -ENOENT;
-}
-
-static int
-qeth_getint(char *s, int longint)
-{
- int cnt;
- int hex;
- int result;
- char c;
-
- if (!s)
- return -1;
- hex = ((s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) ? 1 : 0;
- cnt = (hex) ? 2 : 0; /* start from the first real digit */
- if (!(s[cnt]))
- return -1;
- result = 0;
- while ((c = s[cnt++])) {
- if (hex) {
- if (isxdigit(c))
- result = result * 16 + qeth_getxdigit(c);
- else
- return -1;
- } else {
- if (isdigit(c))
- result = result * 10 + c - '0';
- else
- return -1;
- }
- /* prevent overflow, 0xffff is enough for us */
- if (longint) {
- if (result > 0xfffffff)
- return -1;
- } else {
- if (result > 0xffff)
- return -1;
- }
- }
- return result;
-}
-
-static void
-__qeth_correct_routing_status_v4(struct qeth_card *card)
-{
- if (card->options.routing_type4 == NO_ROUTER)
- return;
-
- if (card->type == QETH_CARD_TYPE_IQD) {
- /* if it's not a mc router, it's no router */
- if ((card->options.routing_type4 == PRIMARY_ROUTER) ||
- (card->options.routing_type4 == SECONDARY_ROUTER)) {
- PRINT_WARN("routing not applicable, reset "
- "routing status for ipv4. \n");
- card->options.routing_type4 = NO_ROUTER;
- }
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- } else {
- /* if it's a mc router, it's no router */
- if ((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL) &&
- (card->options.routing_type4 == MULTICAST_ROUTER)) ||
- (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
- (card->options.routing_type4 == SECONDARY_CONNECTOR)) {
- PRINT_WARN("routing not applicable, reset "
- "routing status for ipv4. (Did you mean "
- "primary_router or secondary_router?)\n");
- card->options.routing_type4 = NO_ROUTER;
- }
- }
-}
-
-static void
-__qeth_correct_routing_status_v6(struct qeth_card *card)
-{
-#ifdef QETH_IPV6
- if (card->options.routing_type6 == NO_ROUTER)
- return;
- if (card->type == QETH_CARD_TYPE_IQD) {
- /* if it's not a mc router, it's no router */
- if ((card->options.routing_type6 == PRIMARY_ROUTER) ||
- (card->options.routing_type6 == SECONDARY_ROUTER)) {
- PRINT_WARN("routing not applicable, reset "
- "routing status for ipv6. \n");
- card->options.routing_type6 = NO_ROUTER;
- }
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- } else {
- /* if it's a mc router, it's no router */
- if ((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL) &&
- (card->options.routing_type6 == MULTICAST_ROUTER)) ||
- (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
- (card->options.routing_type6 == SECONDARY_CONNECTOR)) {
- PRINT_WARN("routing not applicable, reset "
- "routing status for ipv6. (Did you mean "
- "primary_router or secondary_router?)\n");
- card->options.routing_type6 = NO_ROUTER;
- }
- }
-#endif /* QETH_IPV6 */
-}
-
-static void
-qeth_correct_routing_status(struct qeth_card *card)
-{
- __qeth_correct_routing_status_v4(card);
- __qeth_correct_routing_status_v6(card);
-}
-
-static int
-qeth_init_netdev(struct qeth_card *card)
-{
-
- int result;
- char dbf_text[15];
-
- result = qeth_register_netdev(card);
- if (result) {
- PRINT_ALL(" register_netdev %s -- rc=%i\n",
- card->dev_name, result);
- sprintf(dbf_text, "rgnd%4x", (__u16) result);
- QETH_DBF_TEXT2(1, trace, dbf_text);
- atomic_set(&card->is_registered, 0);
- goto out;
- }
- strcpy(card->dev_name, card->dev->name);
- atomic_set(&card->write_busy, 0);
- atomic_set(&card->is_registered, 1);
-
- result = qeth_softsetup_card(card, QETH_WAIT_FOR_LOCK);
-
- if (!result) {
- qeth_init_input_buffers(card);
- } else {
- QETH_DBF_TEXT2(0, trace, "SSFAILED");
- PRINT_WARN("soft-setup of card failed!\n");
- }
-
- INIT_WORK(&card->tqueue, qeth_softsetup_thread_starter, card);
- schedule_work(&card->tqueue);
-out:
- qeth_wakeup_procfile();
- return result;
-
-}
-
-static int
-qeth_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct qeth_card *card;
- struct net_device *dev = (struct net_device *) ptr;
-
- QETH_DBF_TEXT3(0, trace, "devevent");
- QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long));
- QETH_DBF_HEX3(0, trace, &dev, sizeof (void *));
-
- card = __qeth_get_card_from_dev(dev);
- if (qeth_does_card_exist(card)) {
- qeth_save_dev_flag_state(card);
- switch (event) {
- default:
- qeth_start_softsetup_thread(card);
- break;
- }
- }
-
- return NOTIFY_DONE;
-}
-
-static int
-qeth_ip_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct qeth_card *card;
- struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
- struct net_device *dev = ifa->ifa_dev->dev;
- char dbf_text[15];
-
- QETH_DBF_TEXT3(0, trace, "ipevent");
- QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long));
- QETH_DBF_HEX3(0, trace, &dev, sizeof (void *));
- sprintf(dbf_text, "%08x", ifa->ifa_address);
- QETH_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "%08x", ifa->ifa_mask);
- QETH_DBF_TEXT3(0, trace, dbf_text);
-
- card = __qeth_get_card_from_dev(dev);
- if (qeth_does_card_exist(card)) {
- QETH_DBF_HEX3(0, trace, &card, sizeof (void *));
- qeth_save_dev_flag_state(card);
- qeth_start_softsetup_thread(card);
- }
-
- return NOTIFY_DONE;
-}
-
-#ifdef QETH_IPV6
-static int
-qeth_ip6_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct qeth_card *card;
- struct inet6_ifaddr *ifa = (struct inet6_ifaddr *) ptr;
- struct net_device *dev = ifa->idev->dev;
-
- QETH_DBF_TEXT3(0, trace, "ip6event");
- QETH_DBF_HEX3(0, trace, &event, sizeof (unsigned long));
- QETH_DBF_HEX3(0, trace, &dev, sizeof (void *));
- QETH_DBF_HEX3(0, trace, ifa->addr.s6_addr, QETH_DBF_TRACE_LEN);
- QETH_DBF_HEX3(0, trace, ifa->addr.s6_addr + QETH_DBF_TRACE_LEN,
- QETH_DBF_TRACE_LEN);
-
- card = __qeth_get_card_from_dev(dev);
- if (qeth_does_card_exist(card)) {
- QETH_DBF_HEX3(0, trace, &card, sizeof (void *));
- qeth_save_dev_flag_state(card);
- qeth_start_softsetup_thread(card);
- }
-
- return NOTIFY_DONE;
-}
-#endif /* QETH_IPV6 */
-
-static int
-qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct qeth_card *card;
-
- read_lock(&list_lock);
- if (firstcard) {
- card = firstcard;
- clear_another_one:
- if (card->type == QETH_CARD_TYPE_IQD) {
- ccw_device_halt(CARD_DDEV(card), 0);
- ccw_device_clear(CARD_RDEV(card), 0);
- ccw_device_clear(CARD_WDEV(card), 0);
- ccw_device_clear(CARD_DDEV(card), 0);
- } else {
- ccw_device_clear(CARD_DDEV(card), 0);
- ccw_device_clear(CARD_RDEV(card), 0);
- ccw_device_clear(CARD_WDEV(card), 0);
- }
- if (card->next) {
- card = card->next;
- goto clear_another_one;
- }
- }
- read_unlock(&list_lock);
-
- return 0;
-}
-
-static struct notifier_block qeth_dev_notifier = {
- qeth_dev_event,
- 0
-};
-
-static struct notifier_block qeth_ip_notifier = {
- qeth_ip_event,
- 0
-};
-
-#ifdef QETH_IPV6
-static struct notifier_block qeth_ip6_notifier = {
- qeth_ip6_event,
- 0
-};
-#endif /* QETH_IPV6 */
-
-static struct notifier_block qeth_reboot_notifier = {
- qeth_reboot_event,
- 0
-};
-
-static void
-qeth_register_notifiers(void)
-{
- int r;
-
- QETH_DBF_TEXT5(0, trace, "regnotif");
- /* register to be notified on events */
- r = register_netdevice_notifier(&qeth_dev_notifier);
-
- r = register_inetaddr_notifier(&qeth_ip_notifier);
-#ifdef QETH_IPV6
- r = register_inet6addr_notifier(&qeth_ip6_notifier);
-#endif /* QETH_IPV6 */
- r = register_reboot_notifier(&qeth_reboot_notifier);
-}
-
-static void __exit
-qeth_unregister_notifiers(void)
-{
- int r;
-
- QETH_DBF_TEXT5(0, trace, "unregnot");
- r = unregister_netdevice_notifier(&qeth_dev_notifier);
- r = unregister_inetaddr_notifier(&qeth_ip_notifier);
-#ifdef QETH_IPV6
- r = unregister_inet6addr_notifier(&qeth_ip6_notifier);
-#endif /* QETH_IPV6 */
- r = unregister_reboot_notifier(&qeth_reboot_notifier);
-
-}
-
-static int
-qeth_procfile_open(struct inode *inode, struct file *file)
-{
- int length = 0;
- struct qeth_card *card;
- char checksum_str[5], queueing_str[14], router_str[8], bufsize_str[4];
- char *buffer;
- int rc = 0;
- int size;
- struct tempinfo *info;
-
- info = (struct tempinfo *) vmalloc(sizeof (struct tempinfo));
- if (info == NULL) {
- PRINT_WARN("No memory available for data\n");
- return -ENOMEM;
- } else {
- file->private_data = (void *) info;
- }
-
- /* lock all the stuff */
- read_lock(&list_lock);
- card = firstcard;
- size = 200; /* 2 lines plus some sanity space */
- while (card) {
- size += 90; /* if device name is > 10 chars, (should never
- happen...), we'll need that */
- card = card->next;
- }
-
- buffer = info->data = (char *) vmalloc(size);
- if (info->data == NULL) {
- PRINT_WARN("No memory available for data\n");
- vfree(info);
- rc = -ENOMEM;
- goto out;
- }
-
- QETH_DBF_TEXT2(0, trace, "procread");
- length += sprintf(buffer + length,
- "devices CHPID "
- "device cardtype port chksum prio-q'ing "
- "rtr fsz cnt\n");
- length += sprintf(buffer + length,
- "-------------------------- --- ----"
- "------ -------------- -- -- ---------- "
- "--- --- ---\n");
- card = firstcard;
- while (card) {
- strcpy(checksum_str,
- (card->options.checksum_type == SW_CHECKSUMMING) ? "SW" :
- (card->options.checksum_type == HW_CHECKSUMMING) ? "HW" :
- "no");
- if (card->options.do_prio_queueing == NO_PRIO_QUEUEING) {
- sprintf(queueing_str, "always_q_%i",
- card->options.default_queue);
- } else {
- strcpy(queueing_str, (card->options.do_prio_queueing
- ==
- PRIO_QUEUEING_PREC) ? "by_prec." :
- "by_ToS");
- }
-
- /* FIXME: this is really a mess... */
-
-#ifdef QETH_IPV6
- if (atomic_read(&card->rt4fld) || atomic_read(&card->rt6fld))
- strcpy(router_str, "FLD");
-#else/* QETH_IPV6 */
- if (atomic_read(&card->rt4fld))
- strcpy(router_str, "FLD");
-#endif /* QETH_IPV6 */
- else if (((card->options.routing_type4 & ROUTER_MASK) ==
- PRIMARY_ROUTER)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- PRIMARY_ROUTER) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "pri");
- } else
- if (((card->options.routing_type4 & ROUTER_MASK) ==
- SECONDARY_ROUTER)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- SECONDARY_ROUTER) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "sec");
- } else
- if (((card->options.routing_type4 & ROUTER_MASK) ==
- MULTICAST_ROUTER)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- MULTICAST_ROUTER) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "mc");
- } else
- if (((card->options.routing_type4 & ROUTER_MASK) ==
- PRIMARY_CONNECTOR)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- PRIMARY_CONNECTOR) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "p.c");
- } else
- if (((card->options.routing_type4 & ROUTER_MASK) ==
- SECONDARY_CONNECTOR)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- SECONDARY_CONNECTOR) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "s.c");
- } else
- if (((card->options.routing_type4 & ROUTER_MASK) ==
- NO_ROUTER)
-#ifdef QETH_IPV6
- &&
- (((card->options.routing_type6 & ROUTER_MASK) ==
- NO_ROUTER) ||
- (!qeth_is_supported(IPA_IPv6)))
-#endif /* QETH_IPV6 */
- ) {
- strcpy(router_str, "no");
- } else {
- strcpy(router_str, "mix");
- }
- strcpy(bufsize_str,
- (BUFFER_SIZE == 16384) ? "16k" :
- (BUFFER_SIZE == 24576) ? "24k" :
- (BUFFER_SIZE == 32768) ? "32k" :
- (BUFFER_SIZE == 40960) ? "40k" : "64k");
-
- if (!atomic_read(&card->is_startlaned)) {
- length += sprintf(buffer + length,
- "%s/%s/%s x%02X %10s %14s %2i"
- " +++ CABLE PULLED +++\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
- card->chpid,
- card->dev_name,
- qeth_get_cardname_short
- (card->type, card->link_type,
- card->is_guest_lan),
- card->options.portno);
- } else {
- length += sprintf(buffer + length,
- "%s/%s/%s x%02X %10s %14s %2i"
- " %2s %10s %3s %3s %3i\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
- card->chpid, card->dev_name,
- qeth_get_cardname_short
- (card->type, card->link_type,
- card->is_guest_lan),
- card->options.portno, checksum_str,
- queueing_str, router_str, bufsize_str,
- card->options.inbound_buffer_count);
- }
- card = card->next;
- }
-
-out:
- info->len = length;
- /* unlock all the stuff */
- read_unlock(&list_lock);
- return rc;
-}
-
-#define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
-
-#ifdef QETH_PERFORMANCE_STATS
-static int
-qeth_perf_procfile_read(char *buffer, char **buffer_location,
- off_t offset, int buffer_length, int *eof, void *data)
-{
- int c = 0;
- struct qeth_card *card;
- /* we are always called with buffer_length=4k, so we all
- deliver on the first read */
- if (offset > 0)
- return 0;
-
- QETH_DBF_TEXT2(0, trace, "perfpfrd");
-
- card = firstcard;
-
- while (card) {
- _OUTP_IT("For card with devnos %s/%s/%s (%s):\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card), card->dev_name);
- _OUTP_IT(" Skb's/buffers received : %i/%i\n",
- card->perf_stats.skbs_rec, card->perf_stats.bufs_rec);
- _OUTP_IT(" Skb's/buffers sent : %i/%i\n",
- card->perf_stats.skbs_sent,
- card->perf_stats.bufs_sent);
- _OUTP_IT("\n");
- _OUTP_IT(" Skb's/buffers sent without packing : %i/%i\n",
- card->perf_stats.skbs_sent_dont_pack,
- card->perf_stats.bufs_sent_dont_pack);
- _OUTP_IT(" Skb's/buffers sent with packing : %i/%i\n",
- card->perf_stats.skbs_sent_pack,
- card->perf_stats.bufs_sent_pack);
- _OUTP_IT("\n");
- _OUTP_IT(" Packing state changes no pkg.->packing : %i/%i\n",
- card->perf_stats.sc_dp_p, card->perf_stats.sc_p_dp);
- _OUTP_IT(" Current buffer usage (outbound q's) : "
- "%i/%i/%i/%i\n",
- atomic_read(&card->outbound_used_buffers[0]),
- atomic_read(&card->outbound_used_buffers[1]),
- atomic_read(&card->outbound_used_buffers[2]),
- atomic_read(&card->outbound_used_buffers[3]));
- _OUTP_IT("\n");
- _OUTP_IT(" Inbound time (in us) : %i\n",
- card->perf_stats.inbound_time);
- _OUTP_IT(" Inbound cnt : %i\n",
- card->perf_stats.inbound_cnt);
- _OUTP_IT(" Outbound time (in us, incl QDIO) : %i\n",
- card->perf_stats.outbound_time);
- _OUTP_IT(" Outbound cnt : %i\n",
- card->perf_stats.outbound_cnt);
- _OUTP_IT(" Watermarks: L/H=%i/%i\n",
- LOW_WATERMARK_PACK, HIGH_WATERMARK_PACK);
- _OUTP_IT("\n");
-
- card = card->next;
- }
-
- return c;
-}
-
-static struct proc_dir_entry *qeth_perf_proc_file;
-
-#endif /* QETH_PERFORMANCE_STATS */
-
-static int
-qeth_ipato_procfile_open(struct inode *inode, struct file *file)
-{
- char text[33];
- struct ipato_entry *ipato_entry;
- struct qeth_card *card;
- struct qeth_vipa_entry *vipa_entry;
- int rc = 0;
- struct tempinfo *info;
- int size;
- char entry_type[5];
-
- info = (struct tempinfo *) vmalloc(sizeof (struct tempinfo));
- if (info == NULL) {
- PRINT_WARN("No memory available for data\n");
- return -ENOMEM;
- } else {
- file->private_data = (void *) info;
- }
- info->len = 0;
-
- QETH_DBF_TEXT2(0, trace, "ipatorea");
- /* lock all the stuff */
- spin_lock(&ipato_list_lock);
- read_lock(&list_lock);
-
- size = 64; /* for inv4/6 etc. */
-
- ipato_entry = ipato_entries;
- while (ipato_entry) {
- ipato_entry = ipato_entry->next;
- size += 64;
- }
- card = firstcard;
- while (card) {
- read_lock(&card->vipa_list_lock);
- vipa_entry = card->vipa_list;
- while (vipa_entry) {
- vipa_entry = vipa_entry->next;
- size += 64;
- }
- /*read_unlock(&card->vipa_list_lock); don't unlock it here */
- card = card->next;
- }
- info->data = (char *) vmalloc(size);
- if (info->data == NULL) {
- PRINT_WARN("No memory available for data\n");
- vfree(info);
- rc = -ENOMEM;
- goto out;
- }
-#define _IOUTP_IT(x...) info->len+=sprintf(info->data+info->len,x)
- if (ipato_inv4)
- _IOUTP_IT("inv4\n");
- ipato_entry = ipato_entries;
- text[8] = 0;
- while (ipato_entry) {
- if (ipato_entry->version == 4) {
- qeth_convert_addr_to_text(4, ipato_entry->addr, text);
- _IOUTP_IT("add4 %s/%i%s%s\n", text,
- ipato_entry->mask_bits,
- ipato_entry->dev_name[0] ? ":" : "",
- ipato_entry->dev_name[0] ?
- ipato_entry->dev_name : "");
- }
- ipato_entry = ipato_entry->next;
- }
-
- if (ipato_inv6)
- _IOUTP_IT("inv6\n");
- ipato_entry = ipato_entries;
- text[32] = 0;
- while (ipato_entry) {
- if (ipato_entry->version == 6) {
- qeth_convert_addr_to_text(6, ipato_entry->addr, text);
- _IOUTP_IT("add6 %s/%i%s%s\n", text,
- ipato_entry->mask_bits,
- ipato_entry->dev_name[0] ? ":" : "",
- ipato_entry->dev_name[0] ?
- ipato_entry->dev_name : "");
- }
- ipato_entry = ipato_entry->next;
- }
- card = firstcard;
- while (card) {
- vipa_entry = card->vipa_list;
- while (vipa_entry) {
- strcpy(entry_type, (vipa_entry->flag ==
- IPA_SETIP_VIPA_FLAGS) ?
- "vipa" : "rxip");
- if (vipa_entry->version == 4) {
- _IOUTP_IT("add_%s4 %02x%02x%02x%02x:%s\n",
- entry_type,
- vipa_entry->ip[0],
- vipa_entry->ip[1],
- vipa_entry->ip[2],
- vipa_entry->ip[3], card->dev_name);
- } else {
- _IOUTP_IT("add_%s6 %02x%02x%02x%02x"
- "%02x%02x%02x%02x"
- "%02x%02x%02x%02x"
- "%02x%02x%02x%02x:%s\n",
- entry_type,
- vipa_entry->ip[0],
- vipa_entry->ip[1],
- vipa_entry->ip[2],
- vipa_entry->ip[3],
- vipa_entry->ip[4],
- vipa_entry->ip[5],
- vipa_entry->ip[6],
- vipa_entry->ip[7],
- vipa_entry->ip[8],
- vipa_entry->ip[9],
- vipa_entry->ip[10],
- vipa_entry->ip[11],
- vipa_entry->ip[12],
- vipa_entry->ip[13],
- vipa_entry->ip[14],
- vipa_entry->ip[15], card->dev_name);
- }
- vipa_entry = vipa_entry->next;
- }
- card = card->next;
- }
-out:
- /* unlock all the stuff */
- card = firstcard;
- while (card) {
- /*read_lock(&card->vipa_list_lock); don't lock it here */
- read_unlock(&card->vipa_list_lock);
- card = card->next;
- }
- read_unlock(&list_lock);
- spin_unlock(&ipato_list_lock);
-
- return rc;
-}
-
-static ssize_t
-qeth_procfile_read(struct file *file, char *user_buf,
- size_t user_len, loff_t * offset)
-{
- loff_t len;
- struct tempinfo *p_info = (struct tempinfo *) file->private_data;
-
- if (*offset >= p_info->len) {
- return 0;
- } else {
- len = __min(user_len, (p_info->len - *offset));
- if (copy_to_user(user_buf, &(p_info->data[*offset]), len))
- return -EFAULT;
- (*offset) += len;
- return len;
- }
-}
-
-/* ATT: this is also the procfile release function for the ipato
- * procfs entry */
-static int
-qeth_procfile_release(struct inode *inode, struct file *file)
-{
- struct tempinfo *p_info = (struct tempinfo *) file->private_data;
-
- if (p_info) {
- if (p_info->data)
- vfree(p_info->data);
- vfree(p_info);
- }
-
- return 0;
-}
-
-static ssize_t
-qeth_ipato_procfile_write(struct file *file,
- const char *user_buffer,
- size_t user_len, loff_t * offset)
-{
- int add, version;
- char text[33];
- __u8 addr[16];
- int len, i, flag;
- int mask_bits;
- char *buffer;
- int dev_name_there;
- char *dev_name_ptr;
- struct qeth_card *card;
-#define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1)
-
- if (*offset > 0)
- return user_len;
- buffer =
- vmalloc(__max(__max(user_len + 1, BUFFER_LEN), QETH_DBF_MISC_LEN));
-
- if (buffer == NULL)
- return -ENOMEM;
- /* BUFFER_LEN=command incl. blank+addr+slash+mask_bits+
- * colon+DEV_NAME_LEN+zero */
- memset(buffer, 0, BUFFER_LEN);
-
- if (copy_from_user(buffer, user_buffer, user_len)) {
- vfree(buffer);
- return -EFAULT;
- }
-
- QETH_DBF_TEXT2(0, trace, "ipatowri");
- QETH_DBF_TEXT2(0, misc, buffer);
- if (!strncmp(buffer, "inv4", 4)) {
- ipato_inv4 = 1 - ipato_inv4;
- goto out;
- }
- if (!strncmp(buffer, "inv6", 4)) {
- ipato_inv6 = 1 - ipato_inv6;
- goto out;
- }
- if ((!strncmp(buffer, "add4 ", 5)) ||
- (!strncmp(buffer, "add6 ", 5)) ||
- (!strncmp(buffer, "del4 ", 5)) || (!strncmp(buffer, "del6 ", 5))) {
- text[8] = 0;
- text[32] = 0;
- add = !strncmp(buffer, "add", 3);
- version = (buffer[3] == '4') ? 4 : 6;
- len = (version == 4) ? 8 : 32;
- strncpy(text, buffer + 5, len);
- if (qeth_convert_text_to_addr(version, text, addr)) {
- PRINT_ERR("error in parsing ipato information "
- "(addr)\n");
- goto out;
- }
- strncpy(text, buffer + 5 + len + 1, 10);
- /* we prepare mask_bits for qeth_getints */
- dev_name_there = 0;
- for (i = 5 + len + 1; i < BUFFER_LEN; i++) {
- if (*(buffer + i) == '\n') {
- *(buffer + i) = 0;
- break;
- }
- if (*(buffer + i) == ':') {
- *(buffer + i) = 0; /* so that qeth_getint works */
- dev_name_there = i;
- break;
- }
- if (*(buffer + i) == 0)
- break;
- }
- mask_bits = qeth_getint(buffer + 5 + len + 1, 0);
- if ((mask_bits < 0)
- || (mask_bits > ((version == 4) ? 32 : 128))) {
- PRINT_ERR("error in parsing ipato information "
- "(mask bits)\n");
- goto out;
- }
- if (dev_name_there) {
- dev_name_ptr = buffer + dev_name_there + 1;
- /* wipe out the linefeed */
- for (i = dev_name_there + 1;
- i < dev_name_there + 1 + DEV_NAME_LEN + 1; i++)
- if (*(buffer + i) == '\n')
- *(buffer + i) = 0;
- } else
- dev_name_ptr = NULL;
-
- if (add)
- qeth_add_ipato_entry(version, addr, mask_bits,
- dev_name_ptr);
- else
- qeth_del_ipato_entry(version, addr, mask_bits,
- dev_name_ptr);
- goto out;
- }
- if ((!strncmp(buffer, "add_vipa4 ", 10)) ||
- (!strncmp(buffer, "add_rxip4 ", 10)) ||
- (!strncmp(buffer, "add_vipa6 ", 10)) ||
- (!strncmp(buffer, "add_rxip6 ", 10)) ||
- (!strncmp(buffer, "del_vipa4 ", 10)) ||
- (!strncmp(buffer, "del_rxip4 ", 10)) ||
- (!strncmp(buffer, "del_vipa6 ", 10)) ||
- (!strncmp(buffer, "del_rxip6 ", 10))) {
- text[8] = 0;
- text[32] = 0;
- add = !strncmp(buffer, "add", 3);
- flag =
- (!strncmp(buffer + 4, "vipa", 4)) ? IPA_SETIP_VIPA_FLAGS :
- IPA_SETIP_TAKEOVER_FLAGS;
- version = (buffer[8] == '4') ? 4 : 6;
- len = (version == 4) ? 8 : 32;
- strncpy(text, buffer + 10, len);
- if (qeth_convert_text_to_addr(version, text, addr)) {
- PRINT_ERR("error in parsing vipa/rxip information "
- "(addr)\n");
- goto out;
- }
- if (*(buffer + 10 + len) != ':') {
- PRINT_ERR("error in parsing vipa/rxip information "
- "(no interface)\n");
- goto out;
- }
- /* interface name is at buffer+10+len+1 */
- /* wipe out the \n */
- for (i = 10 + len + 1; i < 10 + len + 1 + DEV_NAME_LEN + 1; i++)
- if (*(buffer + i) == '\n')
- *(buffer + i) = 0;
- card = qeth_get_card_by_name(buffer + 10 + len + 1);
- if (!card) {
- PRINT_ERR("error in parsing vipa/rxip information "
- "(unknown interface)\n");
- goto out;
- }
- if (add)
- i = qeth_add_vipa_entry(card, version, addr, flag);
- else
- i = qeth_del_vipa_entry(card, version, addr, flag);
- if (!i)
- qeth_start_softsetup_thread(card);
- goto out;
- }
- PRINT_ERR("unknown ipato information command\n");
-out:
- vfree(buffer);
- *offset = *offset + user_len;
-#undef BUFFER_LEN
- return user_len;
-}
-
-static int
-qeth_procfile_getinterfaces(unsigned long arg)
-{
- struct qeth_card *card;
-
- char parms[16];
- char *buffer;
- char *buffer_pointer;
- __u32 version, valid_fields, qeth_version, number_of_devices, if_index;
- __u32 data_size, data_len;
- unsigned long ioctl_flags;
- int result = 0;
-
- /* the struct of version 0 is:
- typedef struct dev_list
- {
- char device_name[IFNAME_MAXLEN]; // OSA-Exp device name (e.g. eth0)
- __u32 if_index; // interface index from kernel
- __u32 flags; // device charateristics
- } __attribute__((packed)) DEV_LIST;
-
- typedef struct osaexp_dev_ver0
- {
- __u32 version; // structure version
- __u32 valid_fields; // bitmask of fields that are really filled
- __u32 qeth_version; // qeth driver version
- __u32 number_of_devices; // number of OSA Express devices
- struct dev_list devices[0]; // list of OSA Express devices
- } __attribute__((packed)) OSAEXP_DEV_VER0;
- */
-
- version = 0;
- valid_fields = 0;
- qeth_version = 0;
- number_of_devices = 0;
-
- if (copy_from_user((void *) parms, (void *) arg, sizeof (parms)))
- return -EFAULT;
- memcpy(&data_size, parms, sizeof (__u32));
-
- if (!(data_size > 0))
- return -EFAULT;
- if (data_size > IOCTL_MAX_TRANSFER_SIZE)
- return -EFAULT;
- if (!access_ok(VERIFY_WRITE, (void *) arg, data_size))
- return -EFAULT;
-
- read_lock(&list_lock);
- card = firstcard;
-#define IOCTL_USER_STRUCT_SIZE (DEV_NAME_LEN*sizeof(char)) + \
- sizeof(__u32) + sizeof(__u32)
- while (card) {
- if (card->type == QETH_CARD_TYPE_OSAE)
- number_of_devices =
- number_of_devices + IOCTL_USER_STRUCT_SIZE;
- card = card->next;
- }
-#undef IOCTL_USER_STRUCT_SIZE
- if ((number_of_devices + 4 * sizeof (__u32)) >= data_size) {
- result = -ENOMEM;
- goto out;
- }
-
- number_of_devices = 0;
- card = firstcard;
- buffer = (char *) vmalloc(data_size);
- if (!buffer) {
- result = -EFAULT;
- goto out;
- }
- buffer_pointer = ((char *) (buffer)) + (4 * sizeof (__u32));
- while (card) {
- if ((card->type == QETH_CARD_TYPE_OSAE) &&
- (atomic_read(&card->is_hardsetup)) &&
- (atomic_read(&card->is_registered))) {
-
- memcpy(buffer_pointer, card->dev_name, DEV_NAME_LEN);
- buffer_pointer = buffer_pointer + DEV_NAME_LEN;
- if_index = card->dev->ifindex;
- memcpy(buffer_pointer, &if_index, sizeof (__u32));
- buffer_pointer = buffer_pointer + sizeof (__u32);
- memcpy(buffer_pointer, &ioctl_flags, sizeof (__u32));
- buffer_pointer = buffer_pointer + sizeof (__u32);
- number_of_devices = number_of_devices + 1;
- }
- card = card->next;
- }
-
- /* we copy the real size */
- data_len = buffer_pointer - buffer;
-
- buffer_pointer = buffer;
- /* copy the header information at the beginning of the buffer */
- memcpy(buffer_pointer, &version, sizeof (__u32));
- memcpy(((char *) buffer_pointer) + sizeof (__u32), &valid_fields,
- sizeof (__u32));
- memcpy(((char *) buffer_pointer) + (2 * sizeof (__u32)), &qeth_version,
- sizeof (__u32));
- memcpy(((char *) buffer_pointer) + (3 * sizeof (__u32)),
- &number_of_devices, sizeof (__u32));
- if (copy_to_user((char *) arg, buffer, data_len))
- result = -EFAULT;
- vfree(buffer);
-out:
- read_unlock(&list_lock);
- return result;
-
-#undef PARMS_BUFFERLENGTH
-
-};
-
-static int
-qeth_procfile_interfacechanges(unsigned long arg)
-{
- return qeth_sleepon_procfile();
-
-}
-
-static int
-qeth_procfile_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
-
- int result;
- if (!down_interruptible(&qeth_procfile_ioctl_lock)) {
- switch (cmd) {
- case QETH_IOCPROC_OSAEINTERFACES:
- result = qeth_procfile_getinterfaces(arg);
- break;
- case QETH_IOCPROC_INTERFACECHANGES:
- result = qeth_procfile_interfacechanges(arg);
- break;
- default:
- result = -EOPNOTSUPP;
- }
- up(&qeth_procfile_ioctl_lock);
- } else
- result = -ERESTARTSYS;
- return result;
-};
-
-static struct file_operations qeth_procfile_fops = {
- .owner = THIS_MODULE,
- .ioctl = qeth_procfile_ioctl,
- .read = qeth_procfile_read,
- .open = qeth_procfile_open,
- .release = qeth_procfile_release,
-};
-
-static struct proc_dir_entry *qeth_proc_file;
-
-static struct file_operations qeth_ipato_procfile_fops = {
- .owner = THIS_MODULE,
- .read = qeth_procfile_read, /* same as above! */
- .write = qeth_ipato_procfile_write,
- .open = qeth_ipato_procfile_open,
- .release = qeth_procfile_release /* same as above! */
-};
-
-static struct proc_dir_entry *qeth_ipato_proc_file;
-
-static inline void
-__qeth_add_procfs_perf(void)
-{
-#ifdef QETH_PERFORMANCE_STATS
- proc_perf_file_registration = 0;
- qeth_perf_proc_file = create_proc_entry(QETH_PERF_PROCFILE_NAME,
- S_IFREG | 0444, &proc_root);
- if (qeth_perf_proc_file) {
- qeth_perf_proc_file->read_proc = &qeth_perf_procfile_read;
- } else
- proc_perf_file_registration = -1;
-
- if (proc_perf_file_registration)
- PRINT_WARN("was not able to register perf. proc-file (%i).\n",
- proc_perf_file_registration);
-#endif /* QETH_PERFORMANCE_STATS */
-}
-
-static void
-qeth_add_procfs_entries(void)
-{
- proc_file_registration = 0;
- qeth_proc_file = create_proc_entry(QETH_PROCFILE_NAME,
- S_IFREG | 0444, &proc_root);
- if (qeth_proc_file) {
- qeth_proc_file->proc_fops = &qeth_procfile_fops;
- sema_init(&qeth_procfile_ioctl_sem,
- PROCFILE_SLEEP_SEM_MAX_VALUE);
- sema_init(&qeth_procfile_ioctl_lock,
- PROCFILE_IOCTL_SEM_MAX_VALUE);
- } else
- proc_file_registration = -1;
-
- if (proc_file_registration)
- PRINT_WARN("was not able to register proc-file (%i).\n",
- proc_file_registration);
- proc_ipato_file_registration = 0;
- qeth_ipato_proc_file = create_proc_entry(QETH_IPA_PROCFILE_NAME,
- S_IFREG | 0644, &proc_root);
- if (qeth_ipato_proc_file) {
- qeth_ipato_proc_file->proc_fops = &qeth_ipato_procfile_fops;
- } else
- proc_ipato_file_registration = -1;
-
- if (proc_ipato_file_registration)
- PRINT_WARN("was not able to register ipato-proc-file (%i).\n",
- proc_ipato_file_registration);
- __qeth_add_procfs_perf();
-}
-
-static void __exit
-qeth_remove_procfs_entries(void)
-{
- if (!proc_file_registration) /* means if it went ok earlier */
- remove_proc_entry(QETH_PROCFILE_NAME, &proc_root);
-
- if (!proc_ipato_file_registration) /* means if it went ok earlier */
- remove_proc_entry(QETH_IPA_PROCFILE_NAME, &proc_root);
-
-#ifdef QETH_PERFORMANCE_STATS
- if (!proc_perf_file_registration) /* means if it went ok earlier */
- remove_proc_entry(QETH_PERF_PROCFILE_NAME, &proc_root);
-#endif /* QETH_PERFORMANCE_STATS */
-}
-
-static int
-qeth_register_dbf_views(void)
-{
- qeth_dbf_setup = debug_register(QETH_DBF_SETUP_NAME,
- QETH_DBF_SETUP_INDEX,
- QETH_DBF_SETUP_NR_AREAS,
- QETH_DBF_SETUP_LEN);
- if (!qeth_dbf_setup)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_setup, QETH_DBF_SETUP_LEVEL);
-
- qeth_dbf_misc = debug_register(QETH_DBF_MISC_NAME,
- QETH_DBF_MISC_INDEX,
- QETH_DBF_MISC_NR_AREAS,
- QETH_DBF_MISC_LEN);
- if (!qeth_dbf_misc)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_misc, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_misc, QETH_DBF_MISC_LEVEL);
-
- qeth_dbf_data = debug_register(QETH_DBF_DATA_NAME,
- QETH_DBF_DATA_INDEX,
- QETH_DBF_DATA_NR_AREAS,
- QETH_DBF_DATA_LEN);
- if (!qeth_dbf_data)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_data, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_data, QETH_DBF_DATA_LEVEL);
-
- qeth_dbf_control = debug_register(QETH_DBF_CONTROL_NAME,
- QETH_DBF_CONTROL_INDEX,
- QETH_DBF_CONTROL_NR_AREAS,
- QETH_DBF_CONTROL_LEN);
- if (!qeth_dbf_control)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_control, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_control, QETH_DBF_CONTROL_LEVEL);
-
- qeth_dbf_sense = debug_register(QETH_DBF_SENSE_NAME,
- QETH_DBF_SENSE_INDEX,
- QETH_DBF_SENSE_NR_AREAS,
- QETH_DBF_SENSE_LEN);
- if (!qeth_dbf_sense)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_sense, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_sense, QETH_DBF_SENSE_LEVEL);
-
- qeth_dbf_qerr = debug_register(QETH_DBF_QERR_NAME,
- QETH_DBF_QERR_INDEX,
- QETH_DBF_QERR_NR_AREAS,
- QETH_DBF_QERR_LEN);
- if (!qeth_dbf_qerr)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_qerr, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_qerr, QETH_DBF_QERR_LEVEL);
-
- qeth_dbf_trace = debug_register(QETH_DBF_TRACE_NAME,
- QETH_DBF_TRACE_INDEX,
- QETH_DBF_TRACE_NR_AREAS,
- QETH_DBF_TRACE_LEN);
- if (!qeth_dbf_trace)
- return -ENOMEM;
-
- debug_register_view(qeth_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(qeth_dbf_trace, QETH_DBF_TRACE_LEVEL);
-
- return 0;
-}
-
-static void
-qeth_unregister_dbf_views(void)
-{
- if (qeth_dbf_setup)
- debug_unregister(qeth_dbf_setup);
- if (qeth_dbf_qerr)
- debug_unregister(qeth_dbf_qerr);
- if (qeth_dbf_sense)
- debug_unregister(qeth_dbf_sense);
- if (qeth_dbf_misc)
- debug_unregister(qeth_dbf_misc);
- if (qeth_dbf_data)
- debug_unregister(qeth_dbf_data);
- if (qeth_dbf_control)
- debug_unregister(qeth_dbf_control);
- if (qeth_dbf_trace)
- debug_unregister(qeth_dbf_trace);
-}
-
-#ifdef QETH_IPV6
-static int
-qeth_ipv6_init(void)
-{
- qeth_old_arp_constructor = arp_tbl.constructor;
- write_lock(&arp_tbl.lock);
- arp_tbl.constructor = qeth_arp_constructor;
- write_unlock(&arp_tbl.lock);
-
- /* generate the memory leak here - FIXME*/
- arp_direct_ops = (struct neigh_ops*)
- kmalloc(sizeof(struct neigh_ops), GFP_KERNEL);
- if (!arp_direct_ops)
- return -ENOMEM;
-
- memcpy(arp_direct_ops, &arp_direct_ops_template,
- sizeof(struct neigh_ops));
- return 0;
-
-}
-
-static void
-qeth_ipv6_uninit(void)
-{
- write_lock(&arp_tbl.lock);
- arp_tbl.constructor = qeth_old_arp_constructor;
- write_unlock(&arp_tbl.lock);
-}
-#endif /* QETH_IPV6 */
-
-static int
-qeth_get_internal_functions(void)
-{
- struct net_device *dev;
-#ifdef CONFIG_NET_ETHERNET
- dev = alloc_etherdev(0);
- if (!dev) {
- PRINT_ERR("Not enough memory for internal functions.\n");
- return -ENOMEM;
- }
- qeth_my_eth_header = dev->hard_header;
- qeth_my_eth_rebuild_header = dev->rebuild_header;
- qeth_my_eth_header_cache = dev->hard_header_cache;
- qeth_my_eth_header_cache_update = dev->header_cache_update;
- free_netdev(dev);
-#endif
-#ifdef CONFIG_TR
- dev = alloc_trdev(0);
- if (!dev) {
- PRINT_ERR("Not enough memory for internal functions.\n");
- return -ENOMEM;
- }
- qeth_my_tr_header = dev->hard_header;
- qeth_my_tr_rebuild_header = dev->rebuild_header;
- free_netdev(dev);
-#endif
- return 0;
-}
-
-static struct ccw_device_id qeth_ids[] = {
- {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
- {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
- {},
-};
-
-MODULE_DEVICE_TABLE(ccw, qeth_ids);
-
-static struct ccw_driver qeth_ccw_driver = {
- .name = "qeth",
- .ids = qeth_ids,
- .probe = ccwgroup_probe_ccwdev,
- .remove = ccwgroup_remove_ccwdev,
-};
-
-static struct device *qeth_root_dev;
-
-static struct ccwgroup_driver qeth_ccwgroup_driver;
-static ssize_t
-qeth_group_store(struct device_driver *drv, const char *buf, size_t count)
-{
- const char *start, *end;
- char bus_ids[3][BUS_ID_SIZE], *argv[3];
- int i;
-
- pr_debug("group_store %s\n", buf);
- start = buf;
- for (i = 0; i < 3; i++) {
- static const char delim[] = { ',', ',', '\n' };
- int len;
-
- if (!(end = strchr(start, delim[i])))
- return count;
- len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start);
- strncpy(bus_ids[i], start, len);
- bus_ids[i][len] = '\0';
- start = end + 1;
- argv[i] = bus_ids[i];
- }
- pr_debug("creating qeth group device from '%s', '%s' and '%s'\n",
- bus_ids[0], bus_ids[1], bus_ids[2]);
- ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id,
- &qeth_ccw_driver, 3, argv);
- return count;
-}
-
-static DRIVER_ATTR(group, 0200, 0, qeth_group_store);
-
-static ssize_t
-qeth_bufcnt_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.inbound_buffer_count);
-}
-
-static ssize_t
-qeth_bufcnt_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- unsigned long cnt;
- char *tmp;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_softsetup))
- return -EPERM;
-
- cnt = simple_strtoul(buf, &tmp, 16);
- cnt = (cnt < BUFCNT_MIN) ? BUFCNT_MIN :
- ((cnt > BUFCNT_MAX) ? BUFCNT_MAX : cnt);
- card->options.inbound_buffer_count = cnt;
-
- return count;
-}
-
-static DEVICE_ATTR(bufcnt, 0644, qeth_bufcnt_show, qeth_bufcnt_store);
-
-static ssize_t
-qeth_portname_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
- char tmp[9];
- int i;
-
- if (!card)
- return -EINVAL;
-
- if (card->portname_required) {
- sprintf(tmp, "%s", card->options.portname + 1);
- for (i = 0; i < 8; i++)
- tmp[i] = (char) _ebcasc[(__u8) tmp[i]];
- tmp[8] = 0;
- return sprintf(buf, "%s\n", tmp);
- } else
- return sprintf(buf, "%s\n", "no portname required");
-}
-
-static ssize_t
-qeth_portname_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- if ((strlen(tmp) > 8) || (strlen(tmp) < 2))
- return -EINVAL;
-
- card->options.portname[0] = strlen(tmp);
- /* for beauty reasons: */
- for (i = 1; i < 9; i++)
- card->options.portname[i] = ' ';
- strcpy(card->options.portname + 1, tmp);
- for (i = 1; i < 9; i++)
- card->options.portname[i] =
- _ascebc[(unsigned char)card->options.portname[i]];
-
- return count;
-}
-
-static DEVICE_ATTR(portname, 0644, qeth_portname_show, qeth_portname_store);
-
-static ssize_t
-qeth_route4_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (atomic_read(&card->rt4fld))
- return sprintf(buf, "%s\n", "FLD");
-
- switch (card->options.routing_type4 & ROUTER_MASK) {
- case PRIMARY_ROUTER:
- return sprintf(buf, "%s\n", "primary router");
- case SECONDARY_ROUTER:
- return sprintf(buf, "%s\n", "secondary router");
- case MULTICAST_ROUTER:
- return sprintf(buf, "%s\n", "multicast router");
- case PRIMARY_CONNECTOR:
- return sprintf(buf, "%s\n", "primary connector");
- case SECONDARY_CONNECTOR:
- return sprintf(buf, "%s\n", "secondary connector");
- default:
- return sprintf(buf, "%s\n", "no");
- }
-}
-
-static ssize_t
-qeth_route4_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int cnt;
- char *tmp;
-
- if (!card)
- return count;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- cnt = strlen(tmp);
- if (!strncmp(tmp, "primary_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "pri4", card);
- card->options.routing_type4 =
- PRIMARY_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "secondary_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "sec4", card);
- card->options.routing_type4 =
- SECONDARY_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "multicast_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "mcr4", card);
- card->options.routing_type4 =
- MULTICAST_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "primary_connector", cnt)) {
- QETH_DBF_CARD2(0, trace, "prc4", card);
- card->options.routing_type4 =
- PRIMARY_CONNECTOR | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "secondary_connector", cnt)) {
- QETH_DBF_CARD2(0, trace, "scc4", card);
- card->options.routing_type4 =
- SECONDARY_CONNECTOR | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "no_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "nor4", card);
- card->options.routing_type4 = NO_ROUTER | RESET_ROUTING_FLAG;
- } else {
- PRINT_WARN("unknown command input in route4 attribute\n");
- return -EINVAL;
- }
- __qeth_correct_routing_status_v4(card);
- atomic_set(&card->enable_routing_attempts4, QETH_ROUTING_ATTEMPTS);
- if (atomic_read(&card->is_softsetup))
- qeth_start_softsetup_thread(card);
- return count;
-}
-
-static DEVICE_ATTR(route4, 0644, qeth_route4_show, qeth_route4_store);
-
-static ssize_t
-qeth_route6_show(struct device *dev, char *buf)
-{
-#ifdef QETH_IPV6
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (atomic_read(&card->rt6fld))
- return sprintf(buf, "%s\n", "FLD");
-
- if (!qeth_is_supported(IPA_IPv6))
- return sprintf(buf, "%s\n", "n/a");
-
- switch (card->options.routing_type6 & ROUTER_MASK) {
- case PRIMARY_ROUTER:
- return sprintf(buf, "%s\n", "primary router");
- case SECONDARY_ROUTER:
- return sprintf(buf, "%s\n", "secondary router");
- case MULTICAST_ROUTER:
- return sprintf(buf, "%s\n", "multicast router");
- case PRIMARY_CONNECTOR:
- return sprintf(buf, "%s\n", "primary connector");
- case SECONDARY_CONNECTOR:
- return sprintf(buf, "%s\n", "secondary connector");
- default:
- return sprintf(buf, "%s\n", "no");
- }
-#endif /* QETH_IPV6 */
- return sprintf(buf, "%s\n", "n/a");
-}
-
-static ssize_t
-qeth_route6_store(struct device *dev, const char *buf, size_t count)
-{
-#ifdef QETH_IPV6
- struct qeth_card *card = dev->driver_data;
- int cnt;
- char *tmp;
-
- if (!card)
- return count;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- cnt = strlen(tmp);
- if (!strncmp(tmp, "primary_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "pri6", card);
- card->options.routing_type6 =
- PRIMARY_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "secondary_router", cnt)) {
- QETH_DBF_TEXT2(0, trace, "sec6");
- QETH_DBF_CARD2(0, trace, "sec6", card);
- card->options.routing_type6 =
- SECONDARY_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "multicast_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "mcr6", card);
- card->options.routing_type6 =
- MULTICAST_ROUTER | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "primary_connector", cnt)) {
- QETH_DBF_CARD2(0, trace, "prc6", card);
- card->options.routing_type6 =
- PRIMARY_CONNECTOR | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "secondary_connector", cnt)) {
- QETH_DBF_CARD2(0, trace, "scc6", card);
- card->options.routing_type6 =
- SECONDARY_CONNECTOR | RESET_ROUTING_FLAG;
- } else if (!strncmp(tmp, "no_router", cnt)) {
- QETH_DBF_CARD2(0, trace, "nor6", card);
- card->options.routing_type6 = NO_ROUTER | RESET_ROUTING_FLAG;
- } else {
- PRINT_WARN("unknown command input in route6 attribute\n");
- return -EINVAL;
- }
- __qeth_correct_routing_status_v6(card);
- atomic_set(&card->enable_routing_attempts6, QETH_ROUTING_ATTEMPTS);
- if (atomic_read(&card->is_softsetup))
- qeth_start_softsetup_thread(card);
- return count;
-#endif /* QETH_IPV6 */
- return -EINVAL;
-}
-
-static DEVICE_ATTR(route6, 0644, qeth_route6_show, qeth_route6_store);
-
-
-static ssize_t
-qeth_checksum_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- switch (card->options.checksum_type) {
- case SW_CHECKSUMMING:
- return sprintf(buf, "%s\n", "sw");
- case HW_CHECKSUMMING:
- return sprintf(buf, "%s\n", "hw");
- default:
- return sprintf(buf, "%s\n", "no");
- }
-}
-
-static ssize_t
-qeth_checksum_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int cnt;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- cnt = strlen(tmp);
- if (!strncmp(tmp, "sw_checksumming", cnt))
- card->options.checksum_type = SW_CHECKSUMMING;
- else if (!strncmp(tmp, "hw_checksumming", cnt))
- card->options.checksum_type = HW_CHECKSUMMING;
- else if (!strncmp(tmp, "no_checksumming", cnt))
- card->options.checksum_type = NO_CHECKSUMMING;
- else
- PRINT_WARN("unknown checksumming type '%s'\n", tmp);
-
- return count;
-}
-
-static DEVICE_ATTR(checksumming, 0644, qeth_checksum_show, qeth_checksum_store);
-
-static ssize_t
-qeth_prioq_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- switch (card->options.do_prio_queueing) {
- case PRIO_QUEUEING_PREC:
- return sprintf(buf, "%s\n", "by precedence");
- case PRIO_QUEUEING_TOS:
- return sprintf(buf, "%s\n", "by type of service");
- default:
- return sprintf(buf, "always queue %i\n",
- card->options.default_queue);
- }
-}
-
-static ssize_t
-qeth_prioq_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int cnt;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- cnt = strlen(tmp);
- if (!strncmp(tmp, "prio_queueing_prec", cnt))
- card->options.do_prio_queueing = PRIO_QUEUEING_PREC;
- else if (!strncmp(tmp, "prio_queueing_tos", cnt))
- card->options.do_prio_queueing = PRIO_QUEUEING_TOS;
- else if (!strncmp(tmp, "no_prio_queueing:0", cnt)) {
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- card->options.default_queue = 0;
- } else if (!strncmp(tmp, "no_prio_queueing:1", cnt)) {
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- card->options.default_queue = 1;
- } else if (!strncmp(tmp, "no_prio_queueing:2", cnt)) {
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- card->options.default_queue = 2;
- } else if (!strncmp(tmp, "no_prio_queueing:3", cnt)) {
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- card->options.default_queue = 3;
- } else if (!strncmp(tmp, "no_prio_queueing", cnt)) {
- card->options.do_prio_queueing = NO_PRIO_QUEUEING;
- card->options.default_queue = QETH_DEFAULT_QUEUE;
- } else
- PRINT_WARN("unknown queueing type '%s'\n", tmp);
-
- return count;
-}
-
-static DEVICE_ATTR(priority_queueing, 0644, qeth_prioq_show, qeth_prioq_store);
-
-static ssize_t
-qeth_portno_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.portno);
-}
-
-static ssize_t
-qeth_portno_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if ((i < 0) || (i > MAX_PORTNO)) {
- PRINT_ERR("portno %i out of range\n", i);
- return -EINVAL;
- }
- card->options.portno = i;
-
- return count;
-}
-
-static DEVICE_ATTR(portno, 0644, qeth_portno_show, qeth_portno_store);
-
-static ssize_t
-qeth_polltime_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.polltime);
-}
-
-static ssize_t
-qeth_polltime_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i < 0) {
- PRINT_ERR("polltime %i invalid\n", i);
- return -EINVAL;
- }
- card->options.polltime = i;
-
- return count;
-}
-
-static DEVICE_ATTR(polltime, 0644, qeth_polltime_show, qeth_polltime_store);
-
-static ssize_t
-qeth_hhlen_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%i\n", card->options.add_hhlen);
-}
-
-static ssize_t
-qeth_hhlen_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int i;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if ((i < 0) || (i > MAX_ADD_HHLEN)) {
- PRINT_ERR("add_hhlen out of range\n");
- return -EINVAL;
- }
- card->options.add_hhlen = i;
-
- return count;
-}
-
-static DEVICE_ATTR(add_hhlen, 0644, qeth_hhlen_show, qeth_hhlen_store);
-
-static ssize_t
-qeth_takeover_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%s\n",
- (card->options.ena_ipat == ENABLE_TAKEOVER)?"1":"0");
-}
-
-static ssize_t
-qeth_takeover_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int i;
- char *tmp;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i == 1)
- card->options.ena_ipat = ENABLE_TAKEOVER;
- else if (i == 0)
- card->options.ena_ipat = DISABLE_TAKEOVER;
- else
- return -EINVAL;
-
- return count;
-}
-
-static DEVICE_ATTR(enable_takeover, 0644, qeth_takeover_show, qeth_takeover_store);
-
-static ssize_t
-qeth_macaddr_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%s\n",
- (card->options.macaddr_mode == MACADDR_CANONICAL)?"1":"0");
-}
-
-static ssize_t
-qeth_macaddr_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int i;
- char *tmp;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i == 0)
- card->options.macaddr_mode = MACADDR_NONCANONICAL;
- else if (i == 1)
- card->options.macaddr_mode = MACADDR_CANONICAL;
- else
- return -EINVAL;
-
- return count;
-}
-
-static DEVICE_ATTR(canonical_macaddr, 0644, qeth_macaddr_show, qeth_macaddr_store);
-
-static ssize_t
-qeth_fakebr_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%s\n",
- (card->options.fake_broadcast == FAKE_BROADCAST)?"1":"0");
-}
-
-static ssize_t
-qeth_fakebr_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int i;
- char *tmp;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i == 0)
- card->options.fake_broadcast = DONT_FAKE_BROADCAST;
- else if (i == 1)
- card->options.fake_broadcast = FAKE_BROADCAST;
- else
- return -EINVAL;
-
- return count;
-}
-
-static DEVICE_ATTR(fake_broadcast, 0644, qeth_fakebr_show, qeth_fakebr_store);
-
-static ssize_t
-qeth_fakell_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%s\n",
- (card->options.fake_ll == FAKE_LL)?"1":"0");
-}
-
-static ssize_t
-qeth_fakell_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int i;
- char *tmp;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i == 0)
- card->options.fake_ll = DONT_FAKE_LL;
- else if (i == 1)
- card->options.fake_ll = FAKE_LL;
- else
- return -EINVAL;
-
- return count;
-}
-
-static DEVICE_ATTR(fake_ll, 0644, qeth_fakell_show, qeth_fakell_store);
-
-static ssize_t
-qeth_broadcast_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- return sprintf(buf, "%s\n",
- (card->options.broadcast_mode == BROADCAST_ALLRINGS)
- ?"allrings":"local");
-}
-
-static ssize_t
-qeth_broadcast_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- char *tmp;
- int cnt;
-
- if (!card)
- return count;
-
- if (atomic_read(&card->is_hardsetup))
- return -EPERM;
-
- /* Remove trailing '\n'. */
- tmp = strsep((char **) &buf, "\n");
- cnt = strlen(tmp);
- if (!strncmp(tmp, "broadcast_allrings", cnt))
- card->options.broadcast_mode = BROADCAST_ALLRINGS;
- else if (!strncmp(tmp, "broadcast_local", cnt))
- card->options.broadcast_mode = BROADCAST_LOCAL;
- else
- PRINT_WARN("unknown broadcast type '%s'\n", tmp);
-
- return count;
-}
-
-static DEVICE_ATTR(broadcast_mode, 0644, qeth_broadcast_show, qeth_broadcast_store);
-
-static ssize_t
-qeth_recover_store(struct device *dev, const char *buf, size_t count)
-{
- struct qeth_card *card = dev->driver_data;
- int i;
- char *tmp;
-
- if (!card)
- return count;
-
- i = simple_strtoul(buf, &tmp, 16);
- if (i == 1) {
- QETH_DBF_CARD2(0, trace, "UTRC", card);
- atomic_set(&card->problem, PROBLEM_USER_TRIGGERED_RECOVERY);
- qeth_schedule_recovery(card);
- return count;
- } else
- return -EINVAL;
-}
-
-static DEVICE_ATTR(recover, 0200, 0, qeth_recover_store);
-
-static ssize_t
-qeth_card_type_show(struct device *dev, char *buf)
-{
- struct qeth_card *card = dev->driver_data;
-
- if (!card)
- return -EINVAL;
-
- if (!atomic_read(&card->is_softsetup))
- return sprintf(buf, "n/a\n");
-
- return sprintf(buf, "%s\n",
- qeth_get_cardname_short(card->type, card->link_type,
- card->is_guest_lan));
-}
-
-static DEVICE_ATTR(card_type, 0444, qeth_card_type_show, NULL);
-
-static struct attribute * qeth_attrs[] = {
- &dev_attr_bufcnt.attr,
- &dev_attr_portname.attr,
- &dev_attr_route4.attr,
- &dev_attr_route6.attr,
- &dev_attr_checksumming.attr,
- &dev_attr_priority_queueing.attr,
- &dev_attr_portno.attr,
- &dev_attr_polltime.attr,
- &dev_attr_add_hhlen.attr,
- &dev_attr_enable_takeover.attr,
- &dev_attr_canonical_macaddr.attr,
- &dev_attr_fake_broadcast.attr,
- &dev_attr_fake_ll.attr,
- &dev_attr_broadcast_mode.attr,
- &dev_attr_recover.attr,
- &dev_attr_card_type.attr,
- NULL,
-};
-
-static struct attribute_group qeth_attr_group = {
- .attrs = qeth_attrs,
-};
-
-static inline int
-__qeth_create_attributes(struct device *dev)
-{
- return sysfs_create_group(&dev->kobj, &qeth_attr_group);
-}
-
-static inline void
-__qeth_remove_attributes(struct device *dev)
-{
- sysfs_remove_group(&dev->kobj, &qeth_attr_group);
-}
-
-static int
-qeth_probe_device(struct ccwgroup_device *gdev)
-{
- struct qeth_card *card;
- int ret;
-
- if (!get_device(&gdev->dev))
- return -ENODEV;
-
- card = qeth_alloc_card();
- if (!card) {
- put_device(&gdev->dev);
- return -ENOMEM;
- }
-
- gdev->dev.driver_data = card;
- card->gdev = gdev;
-
- gdev->cdev[0]->handler = qeth_interrupt_handler_read;
-
- gdev->cdev[1]->handler = qeth_interrupt_handler_write;
-
- gdev->cdev[2]->handler = qeth_interrupt_handler_qdio;
-
- ret = __qeth_create_attributes(&gdev->dev);
- if (ret != 0)
- goto out;
-
- return 0;
-out:
- put_device(&gdev->dev);
- qeth_free_card(card);
- return ret;
-}
-
-/*
- * Replaces qeth_probe and qeth_attach_handler.
- * This is called after piping to the 'online' attribute,
- * when all parameters are ready.
- */
-static int
-qeth_activate(struct qeth_card *card)
-{
- int result;
-
- ccw_device_set_online(CARD_RDEV(card));
- ccw_device_set_online(CARD_WDEV(card));
- ccw_device_set_online(CARD_DDEV(card));
-
- QETH_DBF_CARD1(0, setup, "activ", card);
- QETH_DBF_HEX1(0, setup, &card, sizeof (void *));
- QETH_DBF_HEX1(0, setup, &card->dev, sizeof (void *));
- QETH_DBF_HEX1(0, setup, &card->stats, sizeof (void *));
-
- QETH_DBF_HEX2(0, misc, &card->options, QETH_DBF_MISC_LEN);
-
- if (qeth_determine_card_type(card)) {
- PRINT_WARN("%s: not a valid card type\n", __func__);
- goto out;
- }
-
- qeth_insert_card_into_list(card);
-
- qeth_correct_routing_status(card);
-
- result = qeth_init_ringbuffers1(card);
- if (result) {
- PRINT_WARN("%s: could not init ringbuffers1\n", __func__);
- goto out_remove;
- }
-
- result = qeth_hardsetup_card(card, 0);
- if (result) {
- goto out_remove;
- }
-
- result = qeth_init_ringbuffers2(card);
- if (result) {
- PRINT_WARN("%s: could not init ringbuffers2\n", __func__);
- goto out_remove;
- }
-
- /* this was previously done in chandev_initnetdevice */
- snprintf(card->dev->name, 8, "%s%%d",
- qeth_get_dev_basename(card->type, card->link_type));
- if (qeth_init_netdev(card))
- goto out_remove;
-
- return 0; /* success */
-
-out_remove:
- qeth_remove_card(card, QETH_REMOVE_CARD_QUICK);
- qeth_remove_card_from_list(card);
-
-out:
- QETH_DBF_TEXT4(0, trace, "freecard");
-
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
-
- return -ENODEV;
-}
-
-static int
-qeth_set_online(struct ccwgroup_device *gdev)
-{
- int rc;
- struct qeth_card *card = gdev->dev.driver_data;
-
- BUG_ON(!card);
-
- rc = qeth_alloc_card_stuff(card);
-
- return rc ? rc : qeth_activate(card);
-
-}
-
-static int
-qeth_set_offline(struct ccwgroup_device *gdev)
-{
- struct qeth_card *card = gdev->dev.driver_data;
-
- if (!card)
- return -ENODEV;
-
- qeth_remove_card(card, QETH_REMOVE_CARD_PROPER);
- qeth_remove_card_from_list(card);
-
- QETH_DBF_TEXT4(0, trace, "freecard");
-
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
-
- qeth_free_card_stuff(card);
-
- return 0;
-}
-
-static void
-qeth_remove_device(struct ccwgroup_device *gdev)
-{
- struct qeth_card *card = gdev->dev.driver_data;
-
- if (card && qeth_does_card_exist(card))
- /* Means that card is already in list. */
- qeth_set_offline(gdev);
- __qeth_remove_attributes(&gdev->dev);
- gdev->dev.driver_data = NULL;
- if (card)
- qeth_free_card(card);
- put_device(&gdev->dev);
-}
-
-static struct ccwgroup_driver qeth_ccwgroup_driver = {
- .owner = THIS_MODULE,
- .name = "qeth",
- .driver_id = 0xD8C5E3C8,
- .probe = qeth_probe_device,
- .remove = qeth_remove_device,
- .set_online = qeth_set_online,
- .set_offline = qeth_set_offline,
-};
-
-static int __init
-qeth_init(void)
-{
- int result;
-
- qeth_eyecatcher();
-
- printk(KERN_INFO "qeth: loading %s\n", version);
-
- result = qeth_get_internal_functions();
- if (result)
- goto out;
-
- qeth_alloc_spare_bufs();
-
-#ifdef QETH_IPV6
- if (qeth_ipv6_init()) {
- PRINT_ERR("Out of memory during ipv6 init.\n");
- goto out_sparebufs;
- }
-#endif /* QETH_IPV6 */
-
- result = qeth_register_dbf_views();
- if (result) {
- PRINT_ERR("not enough memory for dbf. Will not load module.\n");
- goto out_ipv6;
- }
-
- result = ccwgroup_driver_register(&qeth_ccwgroup_driver);
- if (result)
- goto out_dbf;
-
- result = ccw_driver_register(&qeth_ccw_driver);
- if (result)
- goto out_gdrv;
-
- result = driver_create_file(&qeth_ccwgroup_driver.driver,
- &driver_attr_group);
- if (result)
- goto out_cdrv;
-
- qeth_root_dev = s390_root_dev_register("qeth");
- if (IS_ERR(qeth_root_dev)) {
- result = PTR_ERR(qeth_root_dev);
- goto out_file;
- }
- qeth_register_notifiers();
- qeth_add_procfs_entries();
-
- return 0;
-
-out_file:
- driver_remove_file(&qeth_ccwgroup_driver.driver, &driver_attr_group);
-out_cdrv:
- ccw_driver_unregister(&qeth_ccw_driver);
-out_gdrv:
- ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
-out_dbf:
- qeth_unregister_dbf_views();
-out_ipv6:
-#ifdef QETH_IPV6
- qeth_ipv6_uninit();
-out_sparebufs:
-#endif /* QETH_IPV6 */
- qeth_free_all_spare_bufs();
-out:
- return result;
-}
-
-static void __exit
-qeth_exit(void)
-{
-#ifdef QETH_IPV6
- qeth_ipv6_uninit();
-#endif /* QETH_IPV6 */
- qeth_unregister_notifiers();
-
- qeth_remove_procfs_entries();
-
- QETH_DBF_TEXT1(0, trace, "cleanup.");
-
- driver_remove_file(&qeth_ccwgroup_driver.driver, &driver_attr_group);
- ccw_driver_unregister(&qeth_ccw_driver);
- ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
- s390_root_dev_unregister(qeth_root_dev);
-
- while (firstcard) {
- struct qeth_card *card = firstcard;
- qeth_remove_card(card, QETH_REMOVE_CARD_QUICK);
- qeth_remove_card_from_list(card);
- qeth_free_card(card);
- }
-
- qeth_free_all_spare_bufs();
-
- qeth_unregister_dbf_views();
-
- printk("qeth: %s: module removed\n", version);
-}
-
-EXPORT_SYMBOL(qeth_eyecatcher);
-
-module_init(qeth_init);
-module_exit(qeth_exit);
-/*
- * linux/drivers/s390/net/qeth.h
- *
- * Linux on zSeries OSA Express and HiperSockets support
- *
- * Copyright 2000,2003 IBM Corporation
- * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
- *
- */
#ifndef __QETH_H__
#define __QETH_H__
-#include <asm/qdio.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+
+#include <linux/if_tr.h>
+#include <linux/trdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
-#define QETH_NAME " qeth"
+#include <net/ipv6.h>
+#include <linux/in6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
-#define VERSION_QETH_H "$Revision: 1.60 $"
-/******************** CONFIG STUFF ***********************/
-//#define QETH_DBF_LIKE_HELL
+#include <asm/bitops.h>
+#include <asm/debug.h>
+#include <asm/qdio.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+#include "qeth_mpc.h"
+
+#define VERSION_QETH_H "$Revision: 1.98 $"
#ifdef CONFIG_QETH_IPV6
-#define QETH_IPV6
-#define QETH_VERSION_IPV6 ":IPv6"
+#define QETH_VERSION_IPV6 ":IPv6"
#else
-#define QETH_VERSION_IPV6 ""
-#endif /* CONFIG_QETH_IPV6 */
-
+#define QETH_VERSION_IPV6 ""
+#endif
#ifdef CONFIG_QETH_VLAN
-#define QETH_VLAN
-#define QETH_VERSION_VLAN ":VLAN"
+#define QETH_VERSION_VLAN ":VLAN"
#else
-#define QETH_VERSION_VLAN ""
-#endif /* CONFIG_QETH_VLAN */
-
-/* these values match CHECKSUM_* in include/linux/skbuff.h */
-#define SW_CHECKSUMMING 0
-#define HW_CHECKSUMMING 1
-#define NO_CHECKSUMMING 2
-
-#define QETH_CHECKSUM_DEFAULT NO_CHECKSUMMING
-
-#define QETH_PRIOQ_DEFAULT NO_PRIO_QUEUEING
-#define QETH_DEFAULT_QUEUE 2
-
-/******************** CONFIG STUFF END ***********************/
-/********************* TUNING STUFF **************************/
-#define HIGH_WATERMARK_PACK 5
-#define LOW_WATERMARK_PACK 2
-#define WATERMARK_FUZZ 2
-
-#define QETH_MAX_INPUT_THRESHOLD 500
-#define QETH_MAX_OUTPUT_THRESHOLD 300 /* ? */
-
-/* only the MAX values are used */
-#define QETH_MIN_INPUT_THRESHOLD 1
-#define QETH_MIN_OUTPUT_THRESHOLD 1
-
-#define QETH_REQUEUE_THRESHOLD (card->options.inbound_buffer_count/4)
-
-#ifdef CONFIG_QETH_PERF_STATS
-#define QETH_PERFORMANCE_STATS
-#endif /* CONFIG_QETH_PERF_STATS */
-
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_VERBOSE_LEVEL 8
-#else /* QETH_DBF_LIKE_HELL */
-#define QETH_VERBOSE_LEVEL 5
-#endif /* QETH_DBF_LIKE_HELL */
-
-#define PCI_THRESHOLD_A (card->options.inbound_buffer_count+1)
-/* buffers we have to be behind before we get a PCI */
-#define PCI_THRESHOLD_B 0 /* enqueued free buffers left before we get a PCI */
-#define PCI_TIMER_VALUE 3 /* not used, unless the microcode gets patched */
-
-#define DEFAULT_SPARE_BUFFERS 0
-#define MAX_SPARE_BUFFERS 1024
-#define SPAREBUF_MASK 65536
-#define MAX_PORTNO 15
-
-#define QETH_PROCFILE_NAME "qeth"
-#define QETH_PERF_PROCFILE_NAME "qeth_perf"
-#define QETH_IPA_PROCFILE_NAME "qeth_ipa_takeover"
-
-#define SEND_RETRIES_ALLOWED 5
-#define QETH_ROUTING_ATTEMPTS 2
-
-#define QETH_HARDSETUP_LAPS 5
-#define QETH_HARDSETUP_CLEAR_LAPS 3
-#define QETH_RECOVERY_HARDSETUP_RETRY 2
-
-/************************* DEBUG FACILITY STUFF *********************/
-
-#define QETH_DBF_HEX(ex,name,level,addr,len) \
- do { \
- if (ex) \
- debug_exception(qeth_dbf_##name,level,(void*)addr,len); \
- else \
- debug_event(qeth_dbf_##name,level,(void*)addr,len); \
- } while (0)
-#define QETH_DBF_TEXT(ex,name,level,text) \
- do { \
- if (ex) \
- debug_text_exception(qeth_dbf_##name,level,text); \
- else \
- debug_text_event(qeth_dbf_##name,level,text); \
- } while (0)
-
-#define QETH_DBF_CARD(ex,name,level,text,card) \
- do { \
- QETH_DBF_TEXT(ex,name,level,text); \
- QETH_DBF_TEXT(ex,name,level,card->gdev->dev.bus_id); \
- } while (0)
-
-#define QETH_DBF_HEX0(ex,name,addr,len) QETH_DBF_HEX(ex,name,0,addr,len)
-#define QETH_DBF_HEX1(ex,name,addr,len) QETH_DBF_HEX(ex,name,1,addr,len)
-#define QETH_DBF_HEX2(ex,name,addr,len) QETH_DBF_HEX(ex,name,2,addr,len)
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_HEX3(ex,name,addr,len) QETH_DBF_HEX(ex,name,3,addr,len)
-#define QETH_DBF_HEX4(ex,name,addr,len) QETH_DBF_HEX(ex,name,4,addr,len)
-#define QETH_DBF_HEX5(ex,name,addr,len) QETH_DBF_HEX(ex,name,5,addr,len)
-#define QETH_DBF_HEX6(ex,name,addr,len) QETH_DBF_HEX(ex,name,6,addr,len)
-#else /* QETH_DBF_LIKE_HELL */
-#define QETH_DBF_HEX3(ex,name,addr,len) do {} while (0)
-#define QETH_DBF_HEX4(ex,name,addr,len) do {} while (0)
-#define QETH_DBF_HEX5(ex,name,addr,len) do {} while (0)
-#define QETH_DBF_HEX6(ex,name,addr,len) do {} while (0)
-#endif /* QETH_DBF_LIKE_HELL */
-
-#define QETH_DBF_TEXT0(ex,name,text) QETH_DBF_TEXT(ex,name,0,text)
-#define QETH_DBF_TEXT1(ex,name,text) QETH_DBF_TEXT(ex,name,1,text)
-#define QETH_DBF_TEXT2(ex,name,text) QETH_DBF_TEXT(ex,name,2,text)
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_TEXT3(ex,name,text) QETH_DBF_TEXT(ex,name,3,text)
-#define QETH_DBF_TEXT4(ex,name,text) QETH_DBF_TEXT(ex,name,4,text)
-#define QETH_DBF_TEXT5(ex,name,text) QETH_DBF_TEXT(ex,name,5,text)
-#define QETH_DBF_TEXT6(ex,name,text) QETH_DBF_TEXT(ex,name,6,text)
-#else /* QETH_DBF_LIKE_HELL */
-#define QETH_DBF_TEXT3(ex,name,text) do {} while (0)
-#define QETH_DBF_TEXT4(ex,name,text) do {} while (0)
-#define QETH_DBF_TEXT5(ex,name,text) do {} while (0)
-#define QETH_DBF_TEXT6(ex,name,text) do {} while (0)
-#endif /* QETH_DBF_LIKE_HELL */
-
-#define QETH_DBF_CARD0(ex,name,text,card) QETH_DBF_CARD(ex,name,0,text,card)
-#define QETH_DBF_CARD1(ex,name,text,card) QETH_DBF_CARD(ex,name,1,text,card)
-#define QETH_DBF_CARD2(ex,name,text,card) QETH_DBF_CARD(ex,name,2,text,card)
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_CARD3(ex,name,text,card) QETH_DBF_CARD(ex,name,3,text,card)
-#define QETH_DBF_CARD4(ex,name,text,card) QETH_DBF_CARD(ex,name,4,text,card)
-#define QETH_DBF_CARD5(ex,name,text,card) QETH_DBF_CARD(ex,name,5,text,card)
-#define QETH_DBF_CARD6(ex,name,text,card) QETH_DBF_CARD(ex,name,6,text,card)
-#else /* QETH_DBF_LIKE_HELL */
-#define QETH_DBF_CARD3(ex,name,text,card) do {} while (0)
-#define QETH_DBF_CARD4(ex,name,text,card) do {} while (0)
-#define QETH_DBF_CARD5(ex,name,text,card) do {} while (0)
-#define QETH_DBF_CARD6(ex,name,text,card) do {} while (0)
-#endif /* QETH_DBF_LIKE_HELL */
+#define QETH_VERSION_VLAN ""
+#endif
+/**
+ * Debug Facility stuff
+ */
#define QETH_DBF_SETUP_NAME "qeth_setup"
#define QETH_DBF_SETUP_LEN 8
#define QETH_DBF_SETUP_INDEX 3
#define QETH_DBF_SETUP_NR_AREAS 1
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_SETUP_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_SETUP_LEVEL 3
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_MISC_NAME "qeth_misc"
#define QETH_DBF_MISC_LEN 128
#define QETH_DBF_MISC_INDEX 1
#define QETH_DBF_MISC_NR_AREAS 1
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_MISC_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_MISC_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_DATA_NAME "qeth_data"
#define QETH_DBF_DATA_LEN 96
#define QETH_DBF_DATA_INDEX 3
#define QETH_DBF_DATA_NR_AREAS 1
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_DATA_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_DATA_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_CONTROL_NAME "qeth_control"
-/* buffers are 255 bytes long, but no prob */
#define QETH_DBF_CONTROL_LEN 256
#define QETH_DBF_CONTROL_INDEX 3
#define QETH_DBF_CONTROL_NR_AREAS 2
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_CONTROL_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_CONTROL_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_TRACE_NAME "qeth_trace"
#define QETH_DBF_TRACE_LEN 8
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_TRACE_INDEX 3
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_TRACE_INDEX 2
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_TRACE_NR_AREAS 2
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_TRACE_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
-#define QETH_DBF_TRACE_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_TRACE_LEVEL 3
#define QETH_DBF_SENSE_NAME "qeth_sense"
#define QETH_DBF_SENSE_LEN 64
#define QETH_DBF_SENSE_INDEX 1
#define QETH_DBF_SENSE_NR_AREAS 1
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_SENSE_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_SENSE_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_QERR_NAME "qeth_qerr"
#define QETH_DBF_QERR_LEN 8
#define QETH_DBF_QERR_INDEX 1
#define QETH_DBF_QERR_NR_AREAS 2
-#ifdef QETH_DBF_LIKE_HELL
-#define QETH_DBF_QERR_LEVEL 6
-#else /* QETH_DBF_LIKE_HELL */
#define QETH_DBF_QERR_LEVEL 2
-#endif /* QETH_DBF_LIKE_HELL */
-/****************** END OF DEBUG FACILITY STUFF *********************/
-
-/********************* CARD DATA STUFF **************************/
-
-#define QETH_MAX_PARAMS 150
-#define QETH_CARD_TYPE_UNKNOWN 0
-#define QETH_CARD_TYPE_OSAE 10
-#define QETH_CARD_TYPE_IQD 1234
+#define QETH_DBF_TEXT(name,level,text) \
+ do { \
+ debug_text_event(qeth_dbf_##name,level,text); \
+ } while (0)
-#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x0101
-#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x0101
-#define QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT 0x4108
-#define QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT 0x5108
+#define QETH_DBF_HEX(name,level,addr,len) \
+ do { \
+ debug_event(qeth_dbf_##name,level,(void*)(addr),len); \
+ } while (0)
-#define QETH_MAX_QUEUES 4
+#define QETH_DBF_TEXT_(name,level,text...) \
+ do { \
+ sprintf(qeth_dbf_text_buf, text); \
+ debug_text_event(qeth_dbf_##name,level,qeth_dbf_text_buf);\
+ } while (0)
-#define UNIQUE_ID_IF_CREATE_ADDR_FAILED 0xfffe
-#define UNIQUE_ID_NOT_BY_CARD 0x10000
+#define QETH_DBF_SPRINTF(name,level,text...) \
+ do { \
+ debug_sprintf_event(qeth_dbf_trace, level, ##text ); \
+ debug_sprintf_event(qeth_dbf_trace, level, text ); \
+ } while (0)
-/*
- * CU type & model, Dev type & model, card_type, odd_even_restriction,
- * func level, no of queues, multicast is different (multicast-queue_no + 0x100)
+/**
+ * some more debug stuff
*/
-#define QETH_MODELLIST_ARRAY \
- {{0x1731,0x01,0x1732,0x01,QETH_CARD_TYPE_OSAE,1, \
- QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \
- QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \
- QETH_MAX_QUEUES,0}, \
- {0x1731,0x05,0x1732,0x05,QETH_CARD_TYPE_IQD,0, \
- QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT, \
- QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT, \
- QETH_MAX_QUEUES,0x103}, \
- {0,0,0,0,0,0,0,0,0}}
-
-#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
- /* only the first two bytes are looked at in qeth_get_cardname_short */
-#define QETH_MPC_LINK_TYPE_FAST_ETHERNET 0x01
-#define QETH_MPC_LINK_TYPE_HSTR 0x02
-#define QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET 0x03
-#define QETH_MPC_LINK_TYPE_10GIGABIT_ETHERNET 0x10
-#define QETH_MPC_LINK_TYPE_LANE_ETH100 0x81
-#define QETH_MPC_LINK_TYPE_LANE_TR 0x82
-#define QETH_MPC_LINK_TYPE_LANE_ETH1000 0x83
-#define QETH_MPC_LINK_TYPE_LANE 0x88
-#define QETH_MPC_LINK_TYPE_ATM_NATIVE 0x90
-
-#define DEFAULT_ADD_HHLEN 0
-#define MAX_ADD_HHLEN 1024
-
-#define QETH_HEADER_SIZE 32
-#define QETH_IP_HEADER_SIZE 40
-#define QETH_HEADER_LEN_POS 8
-/* flags for the header: */
-#define QETH_HEADER_PASSTHRU 0x10
-#define QETH_HEADER_IPV6 0x80
-
-#define QETH_ETH_MAC_V4 0x0100 /* like v4 */
-#define QETH_ETH_MAC_V6 0x3333 /* like v6 */
-/* tr mc mac is longer, but that will be enough to detect mc frames */
-#define QETH_TR_MAC_NC 0xc000 /* non-canonical */
-#define QETH_TR_MAC_C 0x0300 /* canonical */
-
-#define QETH_CAST_FLAGS 0x07
-#define QETH_CAST_UNICAST 6
-#define QETH_CAST_MULTICAST 4
-#define QETH_CAST_BROADCAST 5
-#define QETH_CAST_ANYCAST 7
-#define QETH_CAST_NOCAST 0
-
-/* VLAN defines */
-#define QETH_EXT_HEADER_VLAN_FRAME 0x01
-#define QETH_EXT_HEADER_TOKEN_ID 0x02
-#define QETH_EXT_HEADER_INCLUDE_VLAN_TAG 0x04
+#define PRINTK_HEADER "qeth: "
-#define QETH_EXT_HEADER_SRC_MAC_ADDRESS 0x08
-#define QETH_EXT_HEADER_CSUM_HDR_REQ 0x10
-#define QETH_EXT_HEADER_CSUM_TRANSP_REQ 0x20
-#define QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE 0x40
-
-#define QETH_UDP_CSUM_OFFSET 6
-#define QETH_TCP_CSUM_OFFSET 16
-
-#define QETH_VERIFY_IS_REAL_DEV 1
-#define QETH_VERIFY_IS_VLAN_DEV 2
+#define HEXDUMP16(importance,header,ptr) \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
+ *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
+ *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
+ *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
+ *(((char*)ptr)+12),*(((char*)ptr)+13), \
+ *(((char*)ptr)+14),*(((char*)ptr)+15)); \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)+16),*(((char*)ptr)+17), \
+ *(((char*)ptr)+18),*(((char*)ptr)+19), \
+ *(((char*)ptr)+20),*(((char*)ptr)+21), \
+ *(((char*)ptr)+22),*(((char*)ptr)+23), \
+ *(((char*)ptr)+24),*(((char*)ptr)+25), \
+ *(((char*)ptr)+26),*(((char*)ptr)+27), \
+ *(((char*)ptr)+28),*(((char*)ptr)+29), \
+ *(((char*)ptr)+30),*(((char*)ptr)+31));
-inline static unsigned int
-qeth_get_ipa_timeout(int cardtype)
+static inline void
+qeth_hex_dump(unsigned char *buf, size_t len)
{
- switch (cardtype) {
- case QETH_CARD_TYPE_IQD:
- return 2000;
- default:
- return 20000;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (i && !(i % 16))
+ printk("\n");
+ printk("%02x ", *(buf + i));
}
+ printk("\n");
}
-inline static unsigned short
-qeth_get_additional_dev_flags(int cardtype)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_IQD:
- return IFF_NOARP;
-#ifdef QETH_IPV6
- default:
- return 0;
-#else /* QETH_IPV6 */
- default:
- return IFF_NOARP;
-#endif /* QETH_IPV6 */
- }
-}
-
-inline static int
-qeth_get_hlen(__u8 link_type)
-{
-#ifdef QETH_IPV6
- switch (link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return QETH_HEADER_SIZE + TR_HLEN;
- default:
-#ifdef QETH_VLAN
- return QETH_HEADER_SIZE + VLAN_ETH_HLEN;
-#else
- return QETH_HEADER_SIZE + ETH_HLEN;
-#endif
- }
-#else /* QETH_IPV6 */
-#ifdef QETH_VLAN
- return QETH_HEADER_SIZE + VLAN_HLEN;
-#else
- return QETH_HEADER_SIZE;
-#endif
-
-#endif /* QETH_IPV6 */
-}
+#define SENSE_COMMAND_REJECT_BYTE 0
+#define SENSE_COMMAND_REJECT_FLAG 0x80
+#define SENSE_RESETTING_EVENT_BYTE 1
+#define SENSE_RESETTING_EVENT_FLAG 0x80
-static int (*qeth_my_eth_header) (struct sk_buff *, struct net_device *,
- unsigned short, void *, void *, unsigned);
-#ifdef CONFIG_TR
-static int (*qeth_my_tr_header) (struct sk_buff *, struct net_device *,
- unsigned short, void *, void *, unsigned);
-#endif /* CONFIG_TR */
-static int (*qeth_my_eth_rebuild_header) (struct sk_buff *);
-#ifdef CONFIG_TR
-static int (*qeth_my_tr_rebuild_header) (struct sk_buff *);
-#endif /* CONFIG_TR */
-static int (*qeth_my_eth_header_cache) (struct neighbour *, struct hh_cache *);
-static void (*qeth_my_eth_header_cache_update) (struct hh_cache *,
- struct net_device *,
- unsigned char *);
-
-#ifdef QETH_IPV6
-typedef int (*__qeth_temp1) (struct sk_buff *, struct net_device *,
- unsigned short, void *, void *, unsigned);
-inline static __qeth_temp1
-qeth_get_hard_header(__u8 link_type)
-{
- switch (link_type) {
-#ifdef CONFIG_TR
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return qeth_my_tr_header;
-#endif /* CONFIG_TR */
- default:
- return qeth_my_eth_header;
- }
-}
+/*
+ * Common IO related definitions
+ */
+extern struct device *qeth_root_dev;
+extern struct ccw_driver qeth_ccw_driver;
+extern struct ccwgroup_driver qeth_ccwgroup_driver;
-typedef int (*__qeth_temp2) (struct sk_buff *);
-inline static __qeth_temp2
-qeth_get_rebuild_header(__u8 link_type)
-{
- switch (link_type) {
-#ifdef CONFIG_TR
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return qeth_my_tr_rebuild_header;
-#endif /* CONFIG_TR */
- default:
- return qeth_my_eth_rebuild_header;
- }
-}
+#define CARD_RDEV(card) card->read.ccwdev
+#define CARD_WDEV(card) card->write.ccwdev
+#define CARD_DDEV(card) card->data.ccwdev
+#define CARD_BUS_ID(card) card->gdev->dev.bus_id
+#define CARD_RDEV_ID(card) card->read.ccwdev->dev.bus_id
+#define CARD_WDEV_ID(card) card->write.ccwdev->dev.bus_id
+#define CARD_DDEV_ID(card) card->data.ccwdev->dev.bus_id
+#define CHANNEL_ID(channel) channel->ccwdev->dev.bus_id
-typedef int (*__qeth_temp3) (struct neighbour *, struct hh_cache *);
-inline static __qeth_temp3
-qeth_get_hard_header_cache(__u8 link_type)
-{
- switch (link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return NULL;
- default:
- return qeth_my_eth_header_cache;
- }
-}
+#define CARD_FROM_CDEV(cdev) (struct qeth_card *) \
+ ((struct ccwgroup_device *)cdev->dev.driver_data)\
+ ->dev.driver_data;
-typedef void (*__qeth_temp4) (struct hh_cache *, struct net_device *,
- unsigned char *);
-inline static __qeth_temp4
-qeth_get_header_cache_update(__u8 link_type)
-{
- switch (link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return NULL;
- default:
- return qeth_my_eth_header_cache_update;
- }
-}
+/**
+ * card stuff
+ */
+#ifdef CONFIG_QETH_PERF_STATS
+struct qeth_perf_stats {
+ unsigned int bufs_rec;
+ unsigned int bufs_sent;
-static unsigned short
-qeth_eth_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
- struct ethhdr *eth;
-
- skb->mac.raw = skb->data;
- skb_pull(skb, ETH_ALEN * 2 + sizeof (short));
- eth = skb->mac.ethernet;
-
- if (*eth->h_dest & 1) {
- if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
- skb->pkt_type = PACKET_BROADCAST;
- else
- skb->pkt_type = PACKET_MULTICAST;
- } else {
- skb->pkt_type = PACKET_OTHERHOST;
- }
- if (ntohs(eth->h_proto) >= 1536)
- return eth->h_proto;
- if (*(unsigned short *) (skb->data) == 0xFFFF)
- return htons(ETH_P_802_3);
- return htons(ETH_P_802_2);
-}
+ unsigned int skbs_sent_pack;
+ unsigned int bufs_sent_pack;
-typedef unsigned short (*__qeth_temp5) (struct sk_buff *, struct net_device *);
-inline static __qeth_temp5
-qeth_get_type_trans(__u8 link_type)
-{
- switch (link_type) {
-#ifdef CONFIG_TR
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return tr_type_trans;
-#endif
- default:
- return qeth_eth_type_trans;
- }
-}
-#endif /* QETH_IPV6 */
+ unsigned int sc_dp_p;
+ unsigned int sc_p_dp;
-inline static const char *
-qeth_get_link_type_name(int cardtype, __u8 linktype)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return "unknown";
- case QETH_CARD_TYPE_OSAE:
- switch (linktype) {
- case QETH_MPC_LINK_TYPE_FAST_ETHERNET:
- return "Fast Eth";
- case QETH_MPC_LINK_TYPE_HSTR:
- return "HSTR";
- case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET:
- return "Gigabit Eth";
- case QETH_MPC_LINK_TYPE_LANE_ETH100:
- return "LANE Eth100";
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return "LANE TR";
- case QETH_MPC_LINK_TYPE_LANE_ETH1000:
- return "LANE Eth1000";
- default:
- return "unknown";
- }
- case QETH_CARD_TYPE_IQD:
- return "magic";
- default:
- return "unknown";
- }
-}
+ __u64 inbound_start_time;
+ unsigned int inbound_cnt;
+ unsigned int inbound_time;
+ __u64 outbound_start_time;
+ unsigned int outbound_cnt;
+ unsigned int outbound_time;
+};
+#endif /* CONFIG_QETH_PERF_STATS */
-inline static const char *
-qeth_get_dev_basename(int cardtype, __u8 link_type)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return "eth";
- case QETH_CARD_TYPE_OSAE:
- switch (link_type) {
- case QETH_MPC_LINK_TYPE_LANE_TR:
- /* fallthrough */
- case QETH_MPC_LINK_TYPE_HSTR:
- return "tr";
- default:
- return "eth";
- }
- case QETH_CARD_TYPE_IQD:
- return "hsi";
- default:
- return "eth";
- }
-}
+/* Routing stuff */
+struct qeth_routing_info {
+ enum qeth_routing_types type;
+};
-/* inbound: */
-#define DEFAULT_BUFFER_SIZE 65536
-#define DEFAULT_BUFFER_COUNT 128
-#define BUFCNT_MIN 8
-#define BUFCNT_MAX 128
-#define BUFFER_SIZE (card->inbound_buffer_size)
-#define BUFFER_MAX_ELEMENTS (BUFFER_SIZE>>12)
- /* 8k for each pair header-buffer: */
+/* IPA stuff */
+struct qeth_ipa_info {
+ __u32 supported_funcs;
+ __u32 enabled_funcs;
+};
-inline static int
-qeth_sbal_packing_on_card(int cardtype)
+static inline int
+qeth_is_ipa_supported(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func)
{
- switch (cardtype) {
- case QETH_CARD_TYPE_IQD:
- return 0;
- default:
- return 1;
- }
+ return (ipa->supported_funcs & func);
}
-/*
- * do it this way round -> __MODULE_STRING needs with
- * QETH_PRIO_NICE_LEVELS a single number
- */
-#define QETH_MAX_PRIO_QUEUES QETH_PRIO_NICE_LEVELS+1
-
static inline int
-qeth_sbalf15_in_retrieable_range(int sbalf15)
+qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func)
{
- return ((sbalf15 >= 15) && (sbalf15 <= 31));
+ return (ipa->supported_funcs & ipa->enabled_funcs & func);
}
-#define INBOUND_BUFFER_POS(card,bufno,sbale) \
- ( (bufno&SPAREBUF_MASK)? \
- ( \
- (sparebufs[bufno&(~SPAREBUF_MASK)].buf+ \
- PAGE_SIZE*sbale) \
- ):( \
- (card->inbound_buffer_pool_entry[card-> \
- inbound_buffer_entry_no[bufno]][sbale]) \
- ) )
-
-#define SPAREBUF_UNAVAIL 0
-#define SPAREBUF_FREE 1
-#define SPAREBUF_USED 2
-
-struct sparebufs {
- char *buf;
- atomic_t status;
-};
+#define qeth_adp_supported(c,f) \
+ qeth_is_ipa_supported(&c->options.adp, f)
+#define qeth_adp_enabled(c,f) \
+ qeth_is_ipa_enabled(&c->options.adp, f)
+#define qeth_is_supported(c,f) \
+ qeth_is_ipa_supported(&c->options.ipa4, f)
+#define qeth_is_enabled(c,f) \
+ qeth_is_ipa_enabled(&c->options.ipa4, f)
+#ifdef CONFIG_QETH_IPV6
+#define qeth_is_supported6(c,f) \
+ qeth_is_ipa_supported(&c->options.ipa6, f)
+#define qeth_is_enabled6(c,f) \
+ qeth_is_ipa_enabled(&c->options.ipa6, f)
+#else /* CONFIG_QETH_IPV6 */
+#define qeth_is_supported6(c,f) 0
+#define qeth_is_enabled6(c,f) 0
+#endif /* CONFIG_QETH_IPV6 */
+#define qeth_is_ipafunc_supported(c,prot,f) \
+ (prot==QETH_PROT_IPV6)? qeth_is_supported6(c,f):qeth_is_supported(c,f)
+#define qeth_is_ipafunc_enabled(c,prot,f) \
+ (prot==QETH_PROT_IPV6)? qeth_is_enabled6(c,f):qeth_is_enabled(c,f)
+
-#define SEND_STATE_INACTIVE 0
-#define SEND_STATE_DONT_PACK 1
-#define SEND_STATE_PACK 2
-
-#define QETH_LOCK_UNLOCKED 0
-#define QETH_LOCK_NORMAL 1
-#define QETH_LOCK_FLUSH 2
-
-#define QETH_TX_TIMEOUT 100*HZ /* 100 seconds */
-
-#define QETH_REMOVE_WAIT_TIME 200
-#define QETH_WAIT_FOR_THREAD_TIME 20
-#define QETH_IDLE_WAIT_TIME 10
-#define QETH_WAIT_BEFORE_2ND_DOIO 1000
-
-#define QETH_FAKE_LL_LEN ETH_HLEN /* 14 */
-#define QETH_FAKE_LL_PROT_LEN 2
-#define QETH_FAKE_LL_ADDR_LEN ETH_ALEN /* 6 */
-#define QETH_FAKE_LL_DEST_MAC_POS 0
-#define QETH_FAKE_LL_SRC_MAC_POS 6
-#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 6
-#define QETH_FAKE_LL_PROT_POS 12
-#define QETH_FAKE_LL_V4_ADDR_POS 16
+#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x0101
+#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x0101
+#define QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT 0x4108
+#define QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT 0x5108
+
+#define QETH_MODELLIST_ARRAY \
+ {{0x1731,0x01,0x1732,0x01,QETH_CARD_TYPE_OSAE,1, \
+ QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \
+ QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \
+ QETH_MAX_QUEUES,0}, \
+ {0x1731,0x05,0x1732,0x05,QETH_CARD_TYPE_IQD,0, \
+ QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT, \
+ QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT, \
+ QETH_MAX_QUEUES,0x103}, \
+ {0,0,0,0,0,0,0,0,0}}
+
+#define QETH_REAL_CARD 1
+#define QETH_VLAN_CARD 2
+#define QETH_BUFSIZE 4096
+
+/**
+ * some more defs
+ */
+#define IF_NAME_LEN 16
+#define QETH_TX_TIMEOUT 100 * HZ
+#define QETH_HEADER_SIZE 32
+#define MAX_PORTNO 15
+#define QETH_FAKE_LL_LEN ETH_HLEN
#define QETH_FAKE_LL_V6_ADDR_POS 24
+
+/*IPv6 address autoconfiguration stuff*/
+#define UNIQUE_ID_IF_CREATE_ADDR_FAILED 0xfffe
+#define UNIQUE_ID_NOT_BY_CARD 0x10000
-#define DEV_NAME_LEN 16
-#define IOCTL_MAX_TRANSFER_SIZE 65535
+/*****************************************************************************/
+/* QDIO queue and buffer handling */
+/*****************************************************************************/
+#define QETH_MAX_QUEUES 4
+#define QETH_IN_BUF_SIZE_DEFAULT 65536
+#define QETH_IN_BUF_COUNT_DEFAULT 16
+#define QETH_IN_BUF_COUNT_MIN 8
+#define QETH_IN_BUF_COUNT_MAX 128
+#define QETH_MAX_BUFFER_ELEMENTS(card) ((card)->qdio.in_buf_size >> 12)
+#define QETH_IN_BUF_REQUEUE_THRESHOLD(card) \
+ ((card)->qdio.in_buf_pool.buf_count / 4)
+
+/* buffers we have to be behind before we get a PCI */
+#define QETH_PCI_THRESHOLD_A(card) ((card)->qdio.in_buf_pool.buf_count+1)
+/*enqueued free buffers left before we get a PCI*/
+#define QETH_PCI_THRESHOLD_B(card) 0
+/*not used unless the microcode gets patched*/
+#define QETH_PCI_TIMER_VALUE(card) 3
+#define QETH_MIN_INPUT_THRESHOLD 1
+#define QETH_MAX_INPUT_THRESHOLD 500
+#define QETH_MIN_OUTPUT_THRESHOLD 1
+#define QETH_MAX_OUTPUT_THRESHOLD 300
+
+/* priority queing */
+#define QETH_PRIOQ_DEFAULT QETH_NO_PRIO_QUEUEING
+#define QETH_DEFAULT_QUEUE 2
+#define QETH_NO_PRIO_QUEUEING 0
+#define QETH_PRIO_Q_ING_PREC 1
+#define QETH_PRIO_Q_ING_TOS 2
#define IP_TOS_LOWDELAY 0x10
#define IP_TOS_HIGHTHROUGHPUT 0x08
#define IP_TOS_HIGHRELIABILITY 0x04
#define IP_TOS_NOTIMPORTANT 0x02
-#define QETH_RCD_LENGTH 128
-
-#define __max(a,b) ( ((a)>(b))?(a):(b) )
-#define __min(a,b) ( ((a)<(b))?(a):(b) )
-#define QETH_BUFSIZE __max(__max(IPA_PDU_HEADER_SIZE+sizeof(struct arp_cmd), \
- IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd)), \
- QETH_RCD_LENGTH)
-
-#define QETH_NOP_TIMEOUT 1500
-#define QETH_QUIESCE_NETDEV_TIME 300
-#define QETH_QUIESCE_WAIT_BEFORE_CLEAR 4000
-#define QETH_QUIESCE_WAIT_AFTER_CLEAR 4000
-
-#define NOP_STATE 0x1001
-#define IDX_ACTIVATE_READ_STATE 0x1003
-#define IDX_ACTIVATE_WRITE_STATE 0x1004
-#define MPC_SETUP_STATE 0x1005
-#define CLEAR_STATE 0x1006
-#define IPA_CMD_STATE 0x1007
-#define IPA_IOCTL_STATE 0x1009
-#define IPA_SETIP_FLAG 0x100000
-
-#define QETH_REMOVE_CARD_PROPER 1
-#define QETH_REMOVE_CARD_QUICK 2
-
-#define NO_PRIO_QUEUEING 0
-#define PRIO_QUEUEING_PREC 1
-#define PRIO_QUEUEING_TOS 2
-#define NO_ROUTER 0
-#define PRIMARY_ROUTER 1
-#define SECONDARY_ROUTER 2
-#define MULTICAST_ROUTER 3
-#define PRIMARY_CONNECTOR 4
-#define SECONDARY_CONNECTOR 5
-#define ROUTER_MASK 0xf /* used to remove SET_ROUTING_FLAG
- from routing_type */
-#define RESET_ROUTING_FLAG 0x10 /* used to indicate, that setting
- the routing type is desired */
-#define BROADCAST_ALLRINGS 0
-#define BROADCAST_LOCAL 1
-#define MACADDR_NONCANONICAL 0
-#define MACADDR_CANONICAL 1
-#define ENABLE_TAKEOVER 0
-#define DISABLE_TAKEOVER 1
-#define FAKE_BROADCAST 0
-#define DONT_FAKE_BROADCAST 1
-
-#define FAKE_LL 0
-#define DONT_FAKE_LL 1
-
-#define QETH_BREAKOUT_LEAVE 1
-#define QETH_BREAKOUT_AGAIN 2
-
-#define QETH_WAIT_FOR_LOCK 0
-#define QETH_DONT_WAIT_FOR_LOCK 1
-#define QETH_LOCK_ALREADY_HELD 2
-
-#define PROBLEM_CARD_HAS_STARTLANED 1
-#define PROBLEM_RECEIVED_IDX_TERMINATE 2
-#define PROBLEM_ACTIVATE_CHECK_CONDITION 3
-#define PROBLEM_RESETTING_EVENT_INDICATOR 4
-#define PROBLEM_COMMAND_REJECT 5
-#define PROBLEM_ZERO_SENSE_DATA 6
-#define PROBLEM_GENERAL_CHECK 7
-#define PROBLEM_BAD_SIGA_RESULT 8
-#define PROBLEM_USER_TRIGGERED_RECOVERY 9
-#define PROBLEM_AFFE 10
-#define PROBLEM_MACHINE_CHECK 11
-#define PROBLEM_TX_TIMEOUT 12
-
-#define CARD_RDEV(card) card->gdev->cdev[0]
-#define CARD_WDEV(card) card->gdev->cdev[1]
-#define CARD_DDEV(card) card->gdev->cdev[2]
-#define CARD_BUS_ID(card) card->gdev->dev.bus_id
-#define CARD_RDEV_ID(card) card->gdev->cdev[0]->dev.bus_id
-#define CARD_WDEV_ID(card) card->gdev->cdev[1]->dev.bus_id
-#define CARD_DDEV_ID(card) card->gdev->cdev[2]->dev.bus_id
-#define CARD_FROM_CDEV(cdev) (struct qeth_card *) \
- ((struct ccwgroup_device *) cdev->dev.driver_data)->dev.driver_data
+/* Packing */
+#define QETH_LOW_WATERMARK_PACK 2
+#define QETH_HIGH_WATERMARK_PACK 5
+#define QETH_WATERMARK_PACK_FUZZ 1
-#define SENSE_COMMAND_REJECT_BYTE 0
-#define SENSE_COMMAND_REJECT_FLAG 0x80
-#define SENSE_RESETTING_EVENT_BYTE 1
-#define SENSE_RESETTING_EVENT_FLAG 0x80
-
-#define BUFFER_USED 1
-#define BUFFER_UNUSED -1
+#define QETH_IP_HEADER_SIZE 40
+/* VLAN defines */
+#define QETH_EXT_HDR_VLAN_FRAME 0x01
+#define QETH_EXT_HDR_TOKEN_ID 0x02
+#define QETH_EXT_HDR_INCLUDE_VLAN_TAG 0x04
-typedef int (*reg_notifier_t) (struct notifier_block *);
+struct qeth_hdr {
+ __u8 id;
+ __u8 flags;
+ __u16 inbound_checksum;
+ __u32 token;
+ __u16 length;
+ __u8 vlan_prio;
+ __u8 ext_flags;
+ __u16 vlan_id;
+ __u16 frame_offset;
+ __u8 dest_addr[16];
+} __attribute__ ((packed));
-struct ipato_entry {
- int version;
- __u8 addr[16];
- int mask_bits;
- char dev_name[DEV_NAME_LEN];
- struct ipato_entry *next;
+/* flags for qeth_hdr.flags */
+#define QETH_HDR_PASSTHRU 0x10
+#define QETH_HDR_IPV6 0x80
+#define QETH_HDR_CAST_MASK 0x07
+enum qeth_cast_flags {
+ QETH_CAST_UNICAST = 0x06,
+ QETH_CAST_MULTICAST = 0x04,
+ QETH_CAST_BROADCAST = 0x05,
+ QETH_CAST_ANYCAST = 0x07,
+ QETH_CAST_NOCAST = 0x00,
};
-struct qeth_vipa_entry {
- int version;
- __u8 ip[16];
- int flag;
- volatile int state;
- struct qeth_vipa_entry *next;
-};
+/* flags for qeth_hdr.ext_flags */
+#define QETH_HDR_EXT_VLAN_FRAME 0x01
+#define QETH_HDR_EXT_CSUM_HDR_REQ 0x10
+#define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20
+#define QETH_HDR_EXT_SRC_MAC_ADDR 0x08
-struct ip_state {
- struct in_ifaddr *ip_ifa; /* pointer to IPv4 adresses */
- struct inet6_ifaddr *ip6_ifa;
-};
+static inline int
+qeth_is_last_sbale(struct qdio_buffer_element *sbale)
+{
+ return (sbale->flags & SBAL_FLAGS_LAST_ENTRY);
+}
-struct qeth_ipm_mac {
- __u8 mac[ETH_ALEN];
- __u8 ip[16];
- struct qeth_ipm_mac *next;
+enum qeth_qdio_buffer_states {
+ /*
+ * inbound: read out by driver; owned by hardware in order to be filled
+ * outbound: owned by driver in order to be filled
+ */
+ QETH_QDIO_BUF_EMPTY,
+ /*
+ * inbound: filled by hardware; owned by driver in order to be read out
+ * outbound: filled by driver; owned by hardware in order to be sent
+ */
+ QETH_QDIO_BUF_PRIMED,
+ /*
+ * inbound only: an error condition has been detected for a buffer
+ * the buffer will be discarded (not read out)
+ */
+ QETH_QDIO_BUF_ERROR,
};
-struct ip_mc_state {
- struct qeth_ipm_mac *ipm_ifa;
- struct qeth_ipm_mac *ipm6_ifa;
+enum qeth_qdio_info_states {
+ QETH_QDIO_UNINITIALIZED,
+ QETH_QDIO_ALLOCATED,
+ QETH_QDIO_ESTABLISHED,
};
-struct addr_request {
- struct addr_request *next;
- int request_type;
- __u8 mac[ETH_ALEN];
- __u8 ip[16];
-};
+struct qeth_buffer_pool_entry {
+ struct list_head list;
+ struct list_head init_list;
+ void *elements[QDIO_MAX_ELEMENTS_PER_BUFFER];
+};
-struct qeth_card_options {
- char devname[DEV_NAME_LEN];
- volatile int routing_type4;
-#ifdef QETH_IPV6
- volatile int routing_type6;
-#endif /* QETH_IPV6 */
- int checksum_type;
- int do_prio_queueing;
- int default_queue;
- int inbound_buffer_count;
- int polltime;
- char portname[9];
- int portno;
- int broadcast_mode;
- int macaddr_mode;
- int ena_ipat;
- int fake_broadcast;
- int add_hhlen;
- int fake_ll;
+struct qeth_qdio_buffer_pool {
+ struct list_head entry_list;
+ int buf_count;
};
-struct qeth_hdr {
- __u8 id;
- __u8 flags;
- __u16 inbound_checksum;
- __u32 token;
- __u16 length;
- __u8 vlan_prio;
- __u8 ext_flags;
- __u16 vlan_id;
- __u16 frame_offset;
- __u8 dest_addr[16];
+struct qeth_qdio_buffer {
+ struct qdio_buffer *buffer;
+ volatile enum qeth_qdio_buffer_states state;
+ /* the buffer pool entry currently associated to this buffer */
+ struct qeth_buffer_pool_entry *pool_entry;
};
-struct qeth_ringbuffer_element {
+struct qeth_qdio_q {
+ struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
+ struct qeth_qdio_buffer bufs[QDIO_MAX_BUFFERS_PER_Q];
+ /*
+ * buf_to_process means "buffer primed by hardware,
+ * has to be read in by driver"; current state PRIMED
+ */
+ volatile int next_buf_to_process;
+ /*
+ * buf_to_init means "buffer must be initialized by driver and must
+ * be made available for hardware" -> state is set to EMPTY
+ */
+ volatile int next_buf_to_init;
+} __attribute__ ((aligned(256)));
+
+struct qeth_qdio_out_buffer {
+ struct qdio_buffer *buffer;
+ volatile enum qeth_qdio_buffer_states state;
+ volatile int next_element_to_fill;
struct sk_buff_head skb_list;
- int next_element_to_fill;
-} __attribute__ ((packed));
-
-struct qeth_ringbuffer {
- struct qdio_buffer buffer[QDIO_MAX_BUFFERS_PER_Q];
- struct qeth_ringbuffer_element ringbuf_element[QDIO_MAX_BUFFERS_PER_Q];
-}__attribute__ ((packed, aligned(PAGE_SIZE)));
-
-struct qeth_dma_stuff {
- unsigned char *sendbuf;
- unsigned char *recbuf;
- struct ccw1 read_ccw;
- struct ccw1 write_ccw;
-}__attribute__ ((packed, aligned(PAGE_SIZE)));
-
-struct qeth_perf_stats {
- unsigned int skbs_rec;
- unsigned int bufs_rec;
-
- unsigned int skbs_sent;
- unsigned int bufs_sent;
-
- unsigned int skbs_sent_dont_pack;
- unsigned int bufs_sent_dont_pack;
- unsigned int skbs_sent_pack;
- unsigned int bufs_sent_pack;
- unsigned int skbs_sent_pack_better;
- unsigned int bufs_sent_pack_better;
+};
- unsigned int sc_dp_p;
- unsigned int sc_p_dp;
+struct qeth_card;
+
+struct qeth_qdio_out_q {
+ struct qdio_buffer qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
+ struct qeth_qdio_out_buffer bufs[QDIO_MAX_BUFFERS_PER_Q];
+ int queue_no;
+ struct qeth_card *card;
+ struct tasklet_struct tasklet;
+ spinlock_t lock;
+ volatile int do_pack;
+ /*
+ * index of buffer to be filled by driver; state EMPTY or PACKING
+ */
+ volatile int next_buf_to_fill;
+ volatile int next_buf_to_flush;
+ /*
+ * number of buffers that are currently filled (PRIMED)
+ * -> these buffers are hardware-owned
+ */
+ atomic_t used_buffers;
+ /* indicates whether PCI flag must be set (or if one is outstanding) */
+ atomic_t set_pci_flags_count;
+} __attribute__ ((aligned(256)));
+
+struct qeth_qdio_info {
+ volatile enum qeth_qdio_info_states state;
+ /* input */
+ struct qeth_qdio_q *in_q;
+ struct qeth_qdio_buffer_pool in_buf_pool;
+ struct qeth_qdio_buffer_pool init_pool;
+ int in_buf_size;
+ struct tasklet_struct in_tasklet;
+
+ /* output */
+ int no_out_queues;
+ struct qeth_qdio_out_q **out_qs;
+
+ /* priority queueing */
+ int do_prio_queueing;
+ int default_out_queue;
+};
- __u64 inbound_start_time;
- unsigned int inbound_cnt;
- unsigned int inbound_time;
- __u64 outbound_start_time;
- unsigned int outbound_cnt;
- unsigned int outbound_time;
+enum qeth_send_errors {
+ QETH_SEND_ERROR_NONE,
+ QETH_SEND_ERROR_LINK_FAILURE,
+ QETH_SEND_ERROR_RETRY,
+ QETH_SEND_ERROR_KICK_IT,
};
-/* ugly. I know. */
-struct qeth_card { /* pointed to by dev->priv */
+#define QETH_ETH_MAC_V4 0x0100 /* like v4 */
+#define QETH_ETH_MAC_V6 0x3333 /* like v6 */
+/* tr mc mac is longer, but that will be enough to detect mc frames */
+#define QETH_TR_MAC_NC 0xc000 /* non-canonical */
+#define QETH_TR_MAC_C 0x0300 /* canonical */
- /* pointer to options (defaults + parameters) */
- struct qeth_card_options options;
+#define DEFAULT_ADD_HHLEN 0
+#define MAX_ADD_HHLEN 1024
- atomic_t is_startlaned; /* card did not get a stoplan */
- /* also 0 when card is gone after a
- * machine check */
-
- __u8 link_type;
-
- int is_guest_lan;
-
- /* inbound buffer management */
- atomic_t inbound_buffer_refcnt[QDIO_MAX_BUFFERS_PER_Q];
- struct qdio_buffer inbound_qdio_buffers[QDIO_MAX_BUFFERS_PER_Q];
- /* inbound data area */
- void *inbound_buffer_pool_entry[QDIO_MAX_BUFFERS_PER_Q]
- [QDIO_MAX_ELEMENTS_PER_BUFFER];
- volatile int inbound_buffer_pool_entry_used[QDIO_MAX_BUFFERS_PER_Q];
- int inbound_buffer_entry_no[QDIO_MAX_BUFFERS_PER_Q];
-
- /* for requeueing of buffers */
- spinlock_t requeue_input_lock;
- atomic_t requeue_position;
- atomic_t requeue_counter;
-
- /* outbound QDIO stuff */
- volatile int send_state[QETH_MAX_QUEUES];
- volatile int outbound_first_free_buffer[QETH_MAX_QUEUES];
- atomic_t outbound_used_buffers[QETH_MAX_QUEUES];
- int outbound_buffer_send_state[QETH_MAX_QUEUES]
- [QDIO_MAX_BUFFERS_PER_Q];
- int send_retries[QETH_MAX_QUEUES][QDIO_MAX_BUFFERS_PER_Q];
- volatile int outbound_bytes_in_buffer[QETH_MAX_QUEUES];
- struct qeth_ringbuffer *outbound_ringbuffer[QETH_MAX_QUEUES];
- atomic_t outbound_ringbuffer_lock[QETH_MAX_QUEUES];
- atomic_t last_pci_pos[QETH_MAX_QUEUES];
-
-#ifdef QETH_IPV6
- int (*hard_header) (struct sk_buff *, struct net_device *,
- unsigned short, void *, void *, unsigned);
- int (*rebuild_header) (struct sk_buff *);
- int (*hard_header_cache) (struct neighbour *, struct hh_cache *);
- void (*header_cache_update) (struct hh_cache *, struct net_device *,
- unsigned char *);
- unsigned short (*type_trans) (struct sk_buff *, struct net_device *);
-#endif /* QETH_IPV6 */
+/**
+ * buffer stuff for read channel
+ */
+#define QETH_CMD_BUFFER_NO 8
-#ifdef QETH_VLAN
- struct vlan_group *vlangrp;
- spinlock_t vlan_lock;
-#endif
+/**
+ * channel state machine
+ */
+enum qeth_channel_states {
+ CH_STATE_UP,
+ CH_STATE_DOWN,
+ CH_STATE_ACTIVATING,
+ CH_STATE_HALTED,
+ CH_STATE_STOPPED,
+};
+/**
+ * card state machine
+ */
+enum qeth_card_states {
+ CARD_STATE_DOWN,
+ CARD_STATE_HARDSETUP,
+ CARD_STATE_SOFTSETUP,
+ CARD_STATE_UP_LAN_OFFLINE,
+ CARD_STATE_UP_LAN_ONLINE,
+ CARD_STATE_RECOVER,
+};
- char dev_name[DEV_NAME_LEN]; /* pointed to by dev->name */
- struct net_device *dev;
- struct net_device_stats *stats;
+/**
+ * Protocol versions
+ */
+enum qeth_prot_versions {
+ QETH_PROT_SNA = 0x0001,
+ QETH_PROT_IPV4 = 0x0004,
+ QETH_PROT_IPV6 = 0x0006,
+};
- int no_queues;
+enum qeth_ip_types {
+ QETH_IP_TYPE_NORMAL,
+ QETH_IP_TYPE_VIPA,
+ QETH_IP_TYPE_RXIP,
+};
-#ifdef QETH_PERFORMANCE_STATS
- struct qeth_perf_stats perf_stats;
-#endif /* QETH_PERFORMANCE_STATS */
-
- /* our state */
- atomic_t is_registered; /* card registered as netdev? */
- atomic_t is_hardsetup; /* card has gone through hardsetup */
- atomic_t is_softsetup; /* card is setup by softsetup */
- atomic_t is_open; /* card is in use */
-
- /* prevents deadlocks :-O */
- struct semaphore softsetup_sema;
- struct semaphore hardsetup_sema;
- spinlock_t ioctl_lock;
- atomic_t softsetup_thread_is_running;
- struct semaphore softsetup_thread_sem;
- struct work_struct tqueue_sst;
-
- atomic_t escape_softsetup; /* active, when recovery has to
- wait for softsetup */
- struct semaphore reinit_thread_sem;
- atomic_t in_recovery;
- atomic_t reinit_counter;
-
- /* problem management */
- atomic_t break_out;
- atomic_t problem;
- struct work_struct tqueue;
-
- struct {
- __u32 trans_hdr;
- __u32 pdu_hdr;
- __u32 pdu_hdr_ack;
- __u32 ipa;
- } seqno;
-
- struct {
- __u32 issuer_rm_w;
- __u32 issuer_rm_r;
- __u32 cm_filter_w;
- __u32 cm_filter_r;
- __u32 cm_connection_w;
- __u32 cm_connection_r;
- __u32 ulp_filter_w;
- __u32 ulp_filter_r;
- __u32 ulp_connection_w;
- __u32 ulp_connection_r;
- } token;
-
- /* this is card-related */
- int type;
- __u16 func_level;
- int initial_mtu;
- int max_mtu;
- int inbound_buffer_size;
-
- int is_multicast_different; /* if multicast traffic is to be sent
- on a different queue, this is the
- queue+no_queues */
- __u32 ipa_supported;
- __u32 ipa_enabled;
- __u32 ipa6_supported;
- __u32 ipa6_enabled;
- __u32 adp_supported;
-
- __u32 csum_enable_mask;
-
- atomic_t startlan_attempts;
- atomic_t enable_routing_attempts4;
- atomic_t rt4fld;
-#ifdef QETH_IPV6
- atomic_t enable_routing_attempts6;
- atomic_t rt6fld;
-#endif /* QETH_IPV6 */
- int unique_id;
+enum qeth_cmd_buffer_state {
+ BUF_STATE_FREE,
+ BUF_STATE_LOCKED,
+ BUF_STATE_PROCESSED,
+};
+/**
+ * IP address and multicast list
+ */
+struct qeth_ipaddr {
+ struct list_head entry;
+ enum qeth_ip_types type;
+ enum qeth_ipa_setdelip_flags set_flags;
+ enum qeth_ipa_setdelip_flags del_flags;
+ int is_multicast;
+ volatile int users;
+ enum qeth_prot_versions proto;
+ unsigned char mac[OSA_ADDR_LEN];
+ union {
+ struct {
+ unsigned int addr;
+ unsigned int mask;
+ } a4;
+ struct {
+ struct in6_addr addr;
+ unsigned int pfxlen;
+ } a6;
+ } u;
+};
- /* device and I/O data */
- struct ccwgroup_device *gdev;
- unsigned short unit_addr2;
- unsigned short cula;
- unsigned short chpid;
+struct qeth_ipato_entry {
+ struct list_head entry;
+ enum qeth_prot_versions proto;
+ char addr[16];
+ int mask_bits;
+};
- unsigned char ipa_buf[QETH_BUFSIZE];
- unsigned char send_buf[QETH_BUFSIZE];
+struct qeth_ipato {
+ int enabled;
+ int invert4;
+ int invert6;
+ struct list_head entries;
+};
-/* IOCTL Stuff */
- unsigned char *ioctl_data_buffer;
- unsigned char *ioctl_buffer_pointer;
- int ioctl_returncode;
- int ioctl_buffersize;
- int number_of_entries;
+struct qeth_channel;
- atomic_t ioctl_data_has_arrived;
- wait_queue_head_t ioctl_wait_q;
+struct qeth_cmd_buffer {
+ enum qeth_cmd_buffer_state state;
+ struct qeth_channel *channel;
+ unsigned char *data;
+ int rc;
+ void (*callback) (struct qeth_channel *, struct qeth_cmd_buffer *);
+};
-/* stuff under 2 gb */
- struct qeth_dma_stuff *dma_stuff;
- unsigned int ipa_timeout;
+/**
+ * definition of a qeth channel, used for read and write
+ */
+struct qeth_channel {
+ enum qeth_channel_states state;
+ struct ccw1 ccw;
+ spinlock_t iob_lock;
+ wait_queue_head_t wait_q;
+ struct tasklet_struct irq_tasklet;
+ struct ccw_device *ccwdev;
+/*command buffer for control data*/
+ struct qeth_cmd_buffer iob[QETH_CMD_BUFFER_NO];
+ atomic_t irq_pending;
+ volatile int io_buf_no;
+ volatile int buf_no;
+};
- atomic_t write_busy;
+/**
+ * OSA card related definitions
+ */
+struct qeth_token {
+ __u32 issuer_rm_w;
+ __u32 issuer_rm_r;
+ __u32 cm_filter_w;
+ __u32 cm_filter_r;
+ __u32 cm_connection_w;
+ __u32 cm_connection_r;
+ __u32 ulp_filter_w;
+ __u32 ulp_filter_r;
+ __u32 ulp_connection_w;
+ __u32 ulp_connection_r;
+};
- /* vipa stuff */
- rwlock_t vipa_list_lock;
- struct qeth_vipa_entry *vipa_list;
+struct qeth_seqno {
+ __u32 trans_hdr;
+ __u32 pdu_hdr;
+ __u32 pdu_hdr_ack;
+ __u32 ipa;
+};
- /* state information when doing I/O */
- atomic_t shutdown_phase;
- atomic_t data_has_arrived;
+struct qeth_reply {
+ struct list_head list;
wait_queue_head_t wait_q;
+ int (*callback)(struct qeth_card *,struct qeth_reply *,unsigned long);
+ int seqno;
+ int received;
+ int rc;
+ void *param;
+ struct qeth_card *card;
+ atomic_t refcnt;
+};
- atomic_t clear_succeeded0;
- atomic_t clear_succeeded1;
- atomic_t clear_succeeded2;
-
- /* bookkeeping of IP and multicast addresses */
- struct ip_state ip_current_state;
- struct ip_state ip_new_state;
-
- struct ip_mc_state ip_mc_current_state;
- struct ip_mc_state ip_mc_new_state;
+struct qeth_card_info {
- int broadcast_capable;
+ char if_name[IF_NAME_LEN];
+ unsigned short unit_addr2;
+ unsigned short cula;
+ unsigned short chpid;
+ __u16 func_level;
+ char mcl_level[QETH_MCL_LENGTH + 1];
+ int guestlan;
int portname_required;
+ int portno;
+ char portname[9];
+ enum qeth_card_types type;
+ enum qeth_link_types link_type;
+ int is_multicast_different;
+ int initial_mtu;
+ int max_mtu;
+ int broadcast_capable;
+ int unique_id;
+ __u32 csum_mask;
+};
- int realloc_message;
+struct qeth_card_options {
+ struct qeth_routing_info route4;
+ struct qeth_ipa_info ipa4;
+ struct qeth_ipa_info adp; /*Adapter parameters*/
+#ifdef CONFIG_QETH_IPV6
+ struct qeth_routing_info route6;
+ struct qeth_ipa_info ipa6;
+#endif /* QETH_IPV6 */
+ enum qeth_checksum_types checksum_type;
+ int broadcast_mode;
+ int macaddr_mode;
+ int enable_takeover;
+ int fake_broadcast;
+ int add_hhlen;
+ int fake_ll;
+};
- char level[QETH_MCL_LENGTH + 1];
+/*
+ * thread bits for qeth_card thread masks
+ */
+enum qeth_threads {
+ QETH_SET_IP_THREAD = 1,
+ QETH_SET_MC_THREAD = 2,
+ QETH_RECOVER_THREAD = 4,
+};
- volatile int saved_dev_flags;
+struct qeth_card {
+ struct list_head list;
+ enum qeth_card_states state;
+ int lan_online;
+ spinlock_t lock;
+/*hardware and sysfs stuff*/
+ struct ccwgroup_device *gdev;
+ struct qeth_channel read;
+ struct qeth_channel write;
+ struct qeth_channel data;
+
+ struct net_device *dev;
+ struct net_device_stats stats;
+
+ struct qeth_card_info info;
+ struct qeth_token token;
+ struct qeth_seqno seqno;
+ struct qeth_card_options options;
+
+ wait_queue_head_t wait_q;
+#ifdef CONFIG_QETH_VLAN
+ spinlock_t vlanlock;
+ struct vlan_group *vlangrp;
+#endif
+ struct work_struct kernel_thread_starter;
+ spinlock_t thread_mask_lock;
+ volatile unsigned long thread_start_mask;
+ volatile unsigned long thread_allowed_mask;
+ volatile unsigned long thread_running_mask;
+ spinlock_t ip_lock;
+ struct list_head ip_list;
+ struct list_head ip_tbd_list;
+ struct qeth_ipato ipato;
+ struct list_head cmd_waiter_list;
+ /* QDIO buffer handling */
+ struct qeth_qdio_info qdio;
+#ifdef CONFIG_QETH_PERF_STATS
+ struct qeth_perf_stats perf_stats;
+#endif /* CONFIG_QETH_PERF_STATS */
+ int use_hard_stop;
+};
- /* for our linked list */
- struct qeth_card *next;
+struct qeth_card_list_struct {
+ struct list_head list;
+ rwlock_t rwlock;
};
-inline static int
-qeth_get_arphrd_type(int cardtype, int linktype)
-{
- switch (cardtype) {
- case QETH_CARD_TYPE_OSAE:
- switch (linktype) {
- case QETH_MPC_LINK_TYPE_LANE_TR:
- /* fallthrough */
- case QETH_MPC_LINK_TYPE_HSTR:
- return ARPHRD_IEEE802_TR;
- default:
- return ARPHRD_ETHER;
- }
- case QETH_CARD_TYPE_IQD:
- return ARPHRD_ETHER;
- default:
- return ARPHRD_ETHER;
- }
-}
+extern struct qeth_card_list_struct qeth_card_list;
+
+/*some helper functions*/
inline static __u8
-qeth_get_adapter_type_for_ipa(int link_type)
+qeth_get_ipa_adp_type(enum qeth_link_types link_type)
{
switch (link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_HSTR:
return 2;
default:
return 1;
}
}
-inline static const char *
-qeth_get_cardname(int cardtype, int is_guest_lan)
+inline static int
+qeth_get_hlen(__u8 link_type)
{
-
- if (is_guest_lan) {
- switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return "n unknown";
- case QETH_CARD_TYPE_OSAE:
- return " Guest LAN QDIO";
- case QETH_CARD_TYPE_IQD:
- return " Guest LAN Hiper";
- default: return
- " strange";
- }
- } else {
- switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return "n unknown";
- case QETH_CARD_TYPE_OSAE:
- return "n OSD Express";
- case QETH_CARD_TYPE_IQD:
- return " HiperSockets";
- default:
- return " strange";
- }
+#ifdef CONFIG_QETH_IPV6
+ switch (link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
+ return sizeof(struct qeth_hdr) + TR_HLEN;
+ default:
+#ifdef CONFIG_QETH_VLAN
+ return sizeof(struct qeth_hdr) + VLAN_ETH_HLEN;
+#else
+ return sizeof(struct qeth_hdr) + ETH_HLEN;
+#endif
}
+#else /* CONFIG_QETH_IPV6 */
+#ifdef CONFIG_QETH_VLAN
+ return sizeof(struct qeth_hdr) + VLAN_HLEN;
+#else
+ return sizeof(struct qeth_hdr);
+#endif
+#endif /* CONFIG_QETH_IPV6 */
}
-/* max length to be returned: 14 */
-inline static const char *
-qeth_get_cardname_short(int cardtype, __u8 link_type, int is_guest_lan)
+inline static unsigned short
+qeth_get_netdev_flags(int cardtype)
{
switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return "unknown";
- case QETH_CARD_TYPE_OSAE:
- if (is_guest_lan)
- return "GuestLAN QDIO";
- switch (link_type) {
- case QETH_MPC_LINK_TYPE_FAST_ETHERNET:
- return "OSD_100";
- case QETH_MPC_LINK_TYPE_HSTR:
- return "HSTR";
- case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET:
- return "OSD_1000";
- case QETH_MPC_LINK_TYPE_LANE_ETH100:
- return "OSD_FE_LANE";
- case QETH_MPC_LINK_TYPE_LANE_TR:
- return "OSD_TR_LANE";
- case QETH_MPC_LINK_TYPE_LANE_ETH1000:
- return "OSD_GbE_LANE";
- case QETH_MPC_LINK_TYPE_LANE:
- return "OSD_ATM_LANE";
- default:
- return "OSD_Express";
- }
case QETH_CARD_TYPE_IQD:
- return is_guest_lan ? "GuestLAN Hiper" : "HiperSockets";
+ return IFF_NOARP;
+#ifdef CONFIG_QETH_IPV6
default:
- return " strange";
- }
-}
-
-inline static int
-qeth_mtu_is_valid(struct qeth_card * card, int mtu)
-{
- switch (card->type) {
- case QETH_CARD_TYPE_UNKNOWN:
- return 1;
- case QETH_CARD_TYPE_OSAE:
- return ((mtu >= 576) && (mtu <= 61440));
- case QETH_CARD_TYPE_IQD:
- return ((mtu >= 576) && (mtu <= card->max_mtu + 4096 - 32));
+ return 0;
+#else
default:
- return 1;
+ return IFF_NOARP;
+#endif
}
}
inline static int
qeth_get_initial_mtu_for_card(struct qeth_card * card)
{
- switch (card->type) {
+ switch (card->info.type) {
case QETH_CARD_TYPE_UNKNOWN:
return 1500;
case QETH_CARD_TYPE_IQD:
- return card->max_mtu;
+ return card->info.max_mtu;
case QETH_CARD_TYPE_OSAE:
- switch (card->link_type) {
- case QETH_MPC_LINK_TYPE_HSTR:
- case QETH_MPC_LINK_TYPE_LANE_TR:
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
return 2000;
default:
return 1492;
}
inline static int
-qeth_get_buffersize_for_card(int cardtype)
+qeth_mtu_is_valid(struct qeth_card * card, int mtu)
{
- switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return 65536;
+ switch (card->info.type) {
case QETH_CARD_TYPE_OSAE:
- return 65536;
+ return ((mtu >= 576) && (mtu <= 61440));
case QETH_CARD_TYPE_IQD:
- return 16384;
+ return ((mtu >= 576) &&
+ (mtu <= card->info.max_mtu + 4096 - 32));
+ case QETH_CARD_TYPE_UNKNOWN:
default:
- return 65536;
+ return 1;
}
}
inline static int
-qeth_get_min_number_of_buffers(int cardtype)
+qeth_get_arphdr_type(int cardtype, int linktype)
{
switch (cardtype) {
- case QETH_CARD_TYPE_UNKNOWN:
- return 32;
case QETH_CARD_TYPE_OSAE:
- return 32;
+ switch (linktype) {
+ case QETH_LINK_TYPE_LANE_TR:
+ case QETH_LINK_TYPE_HSTR:
+ return ARPHRD_IEEE802_TR;
+ default:
+ return ARPHRD_ETHER;
+ }
case QETH_CARD_TYPE_IQD:
- return 64;
default:
- return 64;
+ return ARPHRD_ETHER;
}
}
+#ifdef CONFIG_QETH_PERF_STATS
inline static int
-qeth_get_q_format(int cardtype)
+qeth_get_micros(void)
{
- switch (cardtype) {
+ return (int) (get_clock() >> 12);
+}
+#endif
+
+static inline int
+qeth_get_qdio_q_format(struct qeth_card *card)
+{
+ switch (card->info.type) {
case QETH_CARD_TYPE_IQD:
return 2;
default:
}
}
-inline static int
-qeth_get_device_tx_q_len(int cardtype)
+static inline void
+qeth_ipaddr4_to_string(const __u8 *addr, char *buf)
{
- return 100;
+ sprintf(buf, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
}
-inline static int
-qeth_get_max_number_of_buffers(int cardtype)
+static inline int
+qeth_string_to_ipaddr4(const char *buf, __u8 *addr)
{
- return 127;
+ const char *start, *end;
+ char abuf[4];
+ char *tmp;
+ int len;
+ int i;
+
+ start = buf;
+ for (i = 0; i < 3; i++) {
+ if (!(end = strchr(start, '.')))
+ return -EINVAL;
+ len = end - start;
+ memset(abuf, 0, 4);
+ strncpy(abuf, start, len);
+ addr[i] = simple_strtoul(abuf, &tmp, 10);
+ start = end + 1;
+ }
+ memset(abuf, 0, 4);
+ strcpy(abuf, start);
+ addr[3] = simple_strtoul(abuf, &tmp, 10);
+ return 0;
}
-/******************** OUTPUT FACILITIES **************************/
-
-#ifdef PRINT_INFO
-#undef PRINTK_HEADER
-#undef PRINT_STUPID
-#undef PRINT_ALL
-#undef PRINT_INFO
-#undef PRINT_WARN
-#undef PRINT_ERR
-#undef PRINT_CRIT
-#undef PRINT_ALERT
-#undef PRINT_EMERG
-#endif /* PRINT_INFO */
+static inline void
+qeth_ipaddr6_to_string(const __u8 *addr, char *buf)
+{
+ sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x"
+ ":%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ addr[0], addr[1], addr[2], addr[3],
+ addr[4], addr[5], addr[6], addr[7],
+ addr[8], addr[9], addr[10], addr[11],
+ addr[12], addr[13], addr[14], addr[15]);
+}
-#define PRINTK_HEADER QETH_NAME ": "
+static inline int
+qeth_string_to_ipaddr6(const char *buf, __u8 *addr)
+{
+ const char *start, *end;
+ u16 *tmp_addr;
+ char abuf[5];
+ char *tmp;
+ int len;
+ int i;
+
+ tmp_addr = (u16 *)addr;
+ start = buf;
+ for (i = 0; i < 7; i++) {
+ if (!(end = strchr(start, ':')))
+ return -EINVAL;
+ len = end - start;
+ memset(abuf, 0, 5);
+ strncpy(abuf, start, len);
+ tmp_addr[i] = simple_strtoul(abuf, &tmp, 16);
+ start = end + 1;
+ }
+ memset(abuf, 0, 5);
+ strcpy(abuf, start);
+ tmp_addr[7] = simple_strtoul(abuf, &tmp, 16);
+ return 0;
+}
-#if QETH_VERBOSE_LEVEL>8
-#define PRINT_STUPID(x...) printk( KERN_DEBUG PRINTK_HEADER x)
-#else
-#define PRINT_STUPID(x...)
-#endif
+static inline void
+qeth_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
+ char *buf)
+{
+ if (proto == QETH_PROT_IPV4)
+ return qeth_ipaddr4_to_string(addr, buf);
+ else if (proto == QETH_PROT_IPV6)
+ return qeth_ipaddr6_to_string(addr, buf);
+}
-#if QETH_VERBOSE_LEVEL>7
-#define PRINT_ALL(x...) printk( KERN_DEBUG PRINTK_HEADER x)
-#else
-#define PRINT_ALL(x...)
-#endif
+static inline int
+qeth_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto,
+ __u8 *addr)
+{
+ if (proto == QETH_PROT_IPV4)
+ return qeth_string_to_ipaddr4(buf, addr);
+ else if (proto == QETH_PROT_IPV6)
+ return qeth_string_to_ipaddr6(buf, addr);
+ else
+ return -EINVAL;
+}
-#if QETH_VERBOSE_LEVEL>6
-#define PRINT_INFO(x...) printk( KERN_INFO PRINTK_HEADER x)
-#else
-#define PRINT_INFO(x...)
-#endif
+extern int
+qeth_setrouting_v4(struct qeth_card *);
+extern int
+qeth_setrouting_v6(struct qeth_card *);
-#if QETH_VERBOSE_LEVEL>5
-#define PRINT_WARN(x...) printk( KERN_WARNING PRINTK_HEADER x)
-#else
-#define PRINT_WARN(x...)
-#endif
+int
+qeth_add_ipato_entry(struct qeth_card *, struct qeth_ipato_entry *);
-#if QETH_VERBOSE_LEVEL>4
-#define PRINT_ERR(x...) printk( KERN_ERR PRINTK_HEADER x)
-#else
-#define PRINT_ERR(x...)
-#endif
+void
+qeth_del_ipato_entry(struct qeth_card *, enum qeth_prot_versions, u8 *, int);
-#if QETH_VERBOSE_LEVEL>3
-#define PRINT_CRIT(x...) printk( KERN_CRIT PRINTK_HEADER x)
-#else
-#define PRINT_CRIT(x...)
-#endif
+int
+qeth_add_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-#if QETH_VERBOSE_LEVEL>2
-#define PRINT_ALERT(x...) printk( KERN_ALERT PRINTK_HEADER x)
-#else
-#define PRINT_ALERT(x...)
-#endif
+void
+qeth_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-#if QETH_VERBOSE_LEVEL>1
-#define PRINT_EMERG(x...) printk( KERN_EMERG PRINTK_HEADER x)
-#else
-#define PRINT_EMERG(x...)
-#endif
+int
+qeth_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-#define HEXDUMP16(importance,header,ptr) \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
- *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
- *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
- *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
- *(((char*)ptr)+12),*(((char*)ptr)+13), \
- *(((char*)ptr)+14),*(((char*)ptr)+15)); \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)+16),*(((char*)ptr)+17), \
- *(((char*)ptr)+18),*(((char*)ptr)+19), \
- *(((char*)ptr)+20),*(((char*)ptr)+21), \
- *(((char*)ptr)+22),*(((char*)ptr)+23), \
- *(((char*)ptr)+24),*(((char*)ptr)+25), \
- *(((char*)ptr)+26),*(((char*)ptr)+27), \
- *(((char*)ptr)+28),*(((char*)ptr)+29), \
- *(((char*)ptr)+30),*(((char*)ptr)+31));
+void
+qeth_del_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
+void
+qeth_schedule_recovery(struct qeth_card *);
#endif /* __QETH_H__ */
--- /dev/null
+/*
+ * linux/drivers/s390/net/qeth_fs.h
+ *
+ * Linux on zSeries OSA Express and HiperSockets support.
+ *
+ * This header file contains definitions related to sysfs and procfs.
+ *
+ * Copyright 2000,2003 IBM Corporation
+ * Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ *
+ */
+#ifndef __QETH_FS_H__
+#define __QETH_FS_H__
+
+#ifdef CONFIG_PROC_FS
+extern int
+qeth_create_procfs_entries(void);
+
+extern void
+qeth_remove_procfs_entries(void);
+#else
+static inline int
+qeth_create_procfs_entries(void)
+{
+ return 0;
+}
+
+static inline void
+qeth_remove_procfs_entries(void)
+{
+}
+#endif /* CONFIG_PROC_FS */
+
+extern int
+qeth_create_device_attributes(struct device *dev);
+
+extern void
+qeth_remove_device_attributes(struct device *dev);
+
+extern int
+qeth_create_driver_attributes(void);
+
+extern void
+qeth_remove_driver_attributes(void);
+
+/*
+ * utility functions used in qeth_proc.c and qeth_sys.c
+ */
+
+static inline const char *
+qeth_get_checksum_str(struct qeth_card *card)
+{
+ if (card->options.checksum_type == SW_CHECKSUMMING)
+ return "sw";
+ else if (card->options.checksum_type == HW_CHECKSUMMING)
+ return "hw";
+ else
+ return "no";
+}
+
+static inline const char *
+qeth_get_prioq_str(struct qeth_card *card, char *buf)
+{
+ if (card->qdio.do_prio_queueing == QETH_NO_PRIO_QUEUEING)
+ sprintf(buf, "always_q_%i", card->qdio.default_out_queue);
+ else
+ strcpy(buf, (card->qdio.do_prio_queueing ==
+ QETH_PRIO_Q_ING_PREC)?
+ "by_prec." : "by_ToS");
+ return buf;
+}
+
+static inline const char *
+qeth_get_bufsize_str(struct qeth_card *card)
+{
+ if (card->qdio.in_buf_size == 16384)
+ return "16k";
+ else if (card->qdio.in_buf_size == 24576)
+ return "24k";
+ else if (card->qdio.in_buf_size == 32768)
+ return "32k";
+ else if (card->qdio.in_buf_size == 40960)
+ return "40k";
+ else
+ return "64k";
+}
+
+static inline const char *
+qeth_get_cardname(struct qeth_card *card)
+{
+ if (card->info.guestlan) {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSAE:
+ return " Guest LAN QDIO";
+ case QETH_CARD_TYPE_IQD:
+ return " Guest LAN Hiper";
+ default:
+ return " unknown";
+ }
+ } else {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSAE:
+ return " OSD Express";
+ case QETH_CARD_TYPE_IQD:
+ return " HiperSockets";
+ default:
+ return " unknown";
+ }
+ }
+ return " n/a";
+}
+
+/* max length to be returned: 14 */
+static inline const char *
+qeth_get_cardname_short(struct qeth_card *card)
+{
+ if (card->info.guestlan){
+ switch (card->info.type){
+ case QETH_CARD_TYPE_OSAE:
+ return "GuestLAN QDIO";
+ case QETH_CARD_TYPE_IQD:
+ return "GuestLAN Hiper";
+ default:
+ return "unknown";
+ }
+ } else {
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSAE:
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_FAST_ETH:
+ return "OSD_100";
+ case QETH_LINK_TYPE_HSTR:
+ return "HSTR";
+ case QETH_LINK_TYPE_GBIT_ETH:
+ return "OSD_1000";
+ case QETH_LINK_TYPE_LANE_ETH100:
+ return "OSD_FE_LANE";
+ case QETH_LINK_TYPE_LANE_TR:
+ return "OSD_TR_LANE";
+ case QETH_LINK_TYPE_LANE_ETH1000:
+ return "OSD_GbE_LANE";
+ case QETH_LINK_TYPE_LANE:
+ return "OSD_ATM_LANE";
+ default:
+ return "OSD_Express";
+ }
+ case QETH_CARD_TYPE_IQD:
+ return "HiperSockets";
+ default:
+ return "unknown";
+ }
+ }
+ return "n/a";
+}
+
+#endif /* __QETH_FS_H__ */
--- /dev/null
+/*
+ *
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.77 $)
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ *
+ * Copyright 2000,2003 IBM Corporation
+ *
+ * Author(s): Original Code written by
+ * Utz Bacher (utz.bacher@de.ibm.com)
+ * Rewritten by
+ * Frank Pavlic (pavlic@de.ibm.com) and
+ * Thomas Spatzier <tspat@de.ibm.com>
+ *
+ * $Revision: 1.77 $ $Date: 2004/04/06 14:38:19 $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/***
+ * eye catcher; just for debugging purposes
+ */
+void volatile
+qeth_eyecatcher(void)
+{
+ return;
+}
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/ebcdic.h>
+#include <linux/ctype.h>
+#include <asm/semaphore.h>
+#include <asm/timex.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/skbuff.h>
+#include <net/route.h>
+#include <net/arp.h>
+#include <linux/in.h>
+#include <linux/igmp.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <asm/qeth.h>
+#include <linux/mii.h>
+
+#include "qeth.h"
+#include "qeth_mpc.h"
+#include "qeth_fs.h"
+
+#define VERSION_QETH_C "$Revision: 1.77 $"
+static const char *version = "qeth S/390 OSA-Express driver ("
+ VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
+ QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
+/**
+ * Debug Facility Stuff
+ */
+static debug_info_t *qeth_dbf_setup = NULL;
+static debug_info_t *qeth_dbf_data = NULL;
+static debug_info_t *qeth_dbf_misc = NULL;
+static debug_info_t *qeth_dbf_control = NULL;
+static debug_info_t *qeth_dbf_trace = NULL;
+static debug_info_t *qeth_dbf_sense = NULL;
+static debug_info_t *qeth_dbf_qerr = NULL;
+static char qeth_dbf_text_buf[255];
+
+/**
+ * some more definitions and declarations
+ */
+static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
+
+/* list of our cards */
+struct qeth_card_list_struct qeth_card_list;
+
+static void qeth_send_control_data_cb(struct qeth_channel *,
+ struct qeth_cmd_buffer *);
+
+static atomic_t qeth_hsi_count;
+
+/**
+ * here we go with function implementation
+ */
+static void
+qeth_init_qdio_info(struct qeth_card *card);
+
+static int
+qeth_init_qdio_queues(struct qeth_card *card);
+
+static int
+qeth_alloc_qdio_buffers(struct qeth_card *card);
+
+static void
+qeth_free_qdio_buffers(struct qeth_card *);
+
+static void
+qeth_clear_qdio_buffers(struct qeth_card *);
+
+static void
+qeth_clear_ip_list(struct qeth_card *, int, int);
+
+static void
+qeth_clear_ipacmd_list(struct qeth_card *);
+
+static int
+qeth_qdio_clear_card(struct qeth_card *, int);
+
+static void
+qeth_clear_working_pool_list(struct qeth_card *);
+
+static void
+qeth_clear_cmd_buffers(struct qeth_channel *);
+
+static int
+qeth_stop(struct net_device *);
+
+static void
+qeth_clear_ipato_list(struct qeth_card *);
+
+static int
+qeth_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
+
+static void
+qeth_irq_tasklet(unsigned long);
+
+static int
+qeth_set_online(struct ccwgroup_device *);
+/**
+ * free channel command buffers
+ */
+static void
+qeth_clean_channel(struct qeth_channel *channel)
+{
+ int cnt;
+
+ QETH_DBF_TEXT(setup, 2, "freech");
+ for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++)
+ kfree(channel->iob[cnt].data);
+}
+
+/**
+ * free card
+ */
+static void
+qeth_free_card(struct qeth_card *card)
+{
+
+ QETH_DBF_TEXT(setup, 2, "freecrd");
+ QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+ qeth_clean_channel(&card->read);
+ qeth_clean_channel(&card->write);
+ if (card->dev)
+ free_netdev(card->dev);
+ qeth_clear_ip_list(card, 0, 0);
+ qeth_clear_ipato_list(card);
+ qeth_free_qdio_buffers(card);
+ kfree(card);
+}
+
+/**
+ * alloc memory for command buffer per channel
+ */
+static int
+qeth_setup_channel(struct qeth_channel *channel)
+{
+ int cnt;
+
+ QETH_DBF_TEXT(setup, 2, "setupch");
+ for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++) {
+ channel->iob[cnt].data = (char *)
+ kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL);
+ if (channel->iob[cnt].data == NULL)
+ break;
+ channel->iob[cnt].state = BUF_STATE_FREE;
+ channel->iob[cnt].channel = channel;
+ channel->iob[cnt].callback = qeth_send_control_data_cb;
+ channel->iob[cnt].rc = 0;
+ }
+ if (cnt < QETH_CMD_BUFFER_NO) {
+ while (cnt-- > 0)
+ kfree(channel->iob[cnt].data);
+ return -ENOMEM;
+ }
+ channel->buf_no = 0;
+ channel->io_buf_no = 0;
+ atomic_set(&channel->irq_pending, 0);
+ spin_lock_init(&channel->iob_lock);
+
+ init_waitqueue_head(&channel->wait_q);
+ channel->irq_tasklet.data = (unsigned long) channel;
+ channel->irq_tasklet.func = qeth_irq_tasklet;
+ return 0;
+}
+
+/**
+ * alloc memory for card structure
+ */
+static struct qeth_card *
+qeth_alloc_card(void)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(setup, 2, "alloccrd");
+ card = (struct qeth_card *) kmalloc(sizeof(struct qeth_card),
+ GFP_DMA|GFP_KERNEL);
+ if (!card)
+ return NULL;
+ QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+ memset(card, 0, sizeof(struct qeth_card));
+ if (qeth_setup_channel(&card->read)) {
+ kfree(card);
+ return NULL;
+ }
+ if (qeth_setup_channel(&card->write)) {
+ qeth_clean_channel(&card->read);
+ kfree(card);
+ return NULL;
+ }
+ return card;
+}
+
+static long
+__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb)
+{
+ if (!IS_ERR(irb))
+ return 0;
+
+ switch (PTR_ERR(irb)) {
+ case -EIO:
+ PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ QETH_DBF_TEXT(trace, 2, "ckirberr");
+ QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO);
+ break;
+ case -ETIMEDOUT:
+ PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ QETH_DBF_TEXT(trace, 2, "ckirberr");
+ QETH_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT);
+ break;
+ default:
+ PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
+ cdev->dev.bus_id);
+ QETH_DBF_TEXT(trace, 2, "ckirberr");
+ QETH_DBF_TEXT(trace, 2, " rc???");
+ }
+ return PTR_ERR(irb);
+}
+
+static int
+qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
+{
+ int dstat,cstat;
+ char *sense;
+
+ sense = (char *) irb->ecw;
+ cstat = irb->scsw.cstat;
+ dstat = irb->scsw.dstat;
+
+ if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
+ SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
+ SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
+ QETH_DBF_TEXT(trace,2, "CGENCHK");
+ PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
+ cdev->dev.bus_id, dstat, cstat);
+ HEXDUMP16(WARN, "irb: ", irb);
+ HEXDUMP16(WARN, "irb: ", ((char *) irb) + 32);
+ return 1;
+ }
+
+ if (dstat & DEV_STAT_UNIT_CHECK) {
+ if (sense[SENSE_RESETTING_EVENT_BYTE] &
+ SENSE_RESETTING_EVENT_FLAG) {
+ QETH_DBF_TEXT(trace,2,"REVIND");
+ return 1;
+ }
+ if (sense[SENSE_COMMAND_REJECT_BYTE] &
+ SENSE_COMMAND_REJECT_FLAG) {
+ QETH_DBF_TEXT(trace,2,"CMDREJi");
+ return 0;
+ }
+ if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) {
+ QETH_DBF_TEXT(trace,2,"AFFE");
+ return 1;
+ }
+ if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) {
+ QETH_DBF_TEXT(trace,2,"ZEROSEN");
+ return 0;
+ }
+ QETH_DBF_TEXT(trace,2,"DGENCHK");
+ return 1;
+ }
+ return 0;
+}
+static int qeth_issue_next_read(struct qeth_card *);
+
+/**
+ * interrupt handler
+ */
+static void
+qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
+{
+ int rc;
+ int cstat,dstat;
+ struct qeth_cmd_buffer *buffer;
+ struct qeth_channel *channel;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,5,"irq");
+
+ if (__qeth_check_irb_error(cdev, irb))
+ return;
+ cstat = irb->scsw.cstat;
+ dstat = irb->scsw.dstat;
+
+ card = CARD_FROM_CDEV(cdev);
+ if (!card)
+ return;
+
+ if (card->read.ccwdev == cdev){
+ channel = &card->read;
+ QETH_DBF_TEXT(trace,5,"read");
+ } else if (card->write.ccwdev == cdev) {
+ channel = &card->write;
+ QETH_DBF_TEXT(trace,5,"write");
+ } else {
+ channel = &card->data;
+ QETH_DBF_TEXT(trace,5,"data");
+ }
+ atomic_set(&channel->irq_pending, 0);
+
+ if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC))
+ channel->state = CH_STATE_STOPPED;
+
+ if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC))
+ channel->state = CH_STATE_HALTED;
+
+ /*let's wake up immediately on data channel*/
+ if ((channel == &card->data) && (intparm != 0))
+ goto out;
+
+ if (intparm == QETH_CLEAR_CHANNEL_PARM) {
+ QETH_DBF_TEXT(trace, 6, "clrchpar");
+ /* we don't have to handle this further */
+ intparm = 0;
+ }
+ if (intparm == QETH_HALT_CHANNEL_PARM) {
+ QETH_DBF_TEXT(trace, 6, "hltchpar");
+ /* we don't have to handle this further */
+ intparm = 0;
+ }
+ if ((dstat & DEV_STAT_UNIT_EXCEP) ||
+ (dstat & DEV_STAT_UNIT_CHECK) ||
+ (cstat)) {
+ if (irb->esw.esw0.erw.cons) {
+ /* TODO: we should make this s390dbf */
+ PRINT_WARN("sense data available on channel %s.\n",
+ CHANNEL_ID(channel));
+ PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat);
+ HEXDUMP16(WARN,"irb: ",irb);
+ HEXDUMP16(WARN,"sense data: ",irb->ecw);
+ }
+ rc = qeth_get_problem(cdev,irb);
+ if (rc) {
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+ }
+
+ if (intparm) {
+ buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
+ buffer->state = BUF_STATE_PROCESSED;
+ }
+ if (channel == &card->data)
+ return;
+
+ if (channel == &card->read &&
+ channel->state == CH_STATE_UP)
+ qeth_issue_next_read(card);
+
+ tasklet_schedule(&channel->irq_tasklet);
+ return;
+out:
+ wake_up(&card->wait_q);
+}
+
+/**
+ * tasklet function scheduled from irq handler
+ */
+static void
+qeth_irq_tasklet(unsigned long data)
+{
+ struct qeth_card *card;
+ struct qeth_channel *channel;
+ struct qeth_cmd_buffer *iob;
+ __u8 index;
+
+ QETH_DBF_TEXT(trace,5,"irqtlet");
+ channel = (struct qeth_channel *) data;
+ iob = channel->iob;
+ index = channel->buf_no;
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ while (iob[index].state == BUF_STATE_PROCESSED) {
+ if (iob[index].callback !=NULL) {
+ iob[index].callback(channel,iob + index);
+ }
+ index = (index + 1) % QETH_CMD_BUFFER_NO;
+ }
+ channel->buf_no = index;
+ wake_up(&card->wait_q);
+}
+
+static int qeth_stop_card(struct qeth_card *);
+
+static int
+qeth_set_offline(struct ccwgroup_device *cgdev)
+{
+ struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
+ enum qeth_card_states recover_flag;
+
+ QETH_DBF_TEXT(setup, 3, "setoffl");
+ QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
+
+ recover_flag = card->state;
+ if (qeth_stop_card(card) == -ERESTARTSYS){
+ PRINT_WARN("Stopping card %s interrupted by user!\n",
+ CARD_BUS_ID(card));
+ return -ERESTARTSYS;
+ }
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ if ((recover_flag == CARD_STATE_UP_LAN_ONLINE) ||
+ (recover_flag == CARD_STATE_UP_LAN_OFFLINE))
+ card->state = CARD_STATE_RECOVER;
+ return 0;
+}
+
+static void
+qeth_remove_device(struct ccwgroup_device *cgdev)
+{
+ struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(setup, 3, "rmdev");
+ QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
+
+ if (!card)
+ return;
+
+ if (cgdev->state == CCWGROUP_ONLINE){
+ card->use_hard_stop = 1;
+ qeth_set_offline(cgdev);
+ }
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ atomic_dec(&qeth_hsi_count);
+ /* remove form our internal list */
+ write_lock_irqsave(&qeth_card_list.rwlock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+ unregister_netdev(card->dev);
+ qeth_free_card(card);
+ cgdev->dev.driver_data = NULL;
+ put_device(&cgdev->dev);
+}
+
+static int
+qeth_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *);
+static int
+qeth_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *);
+
+/**
+ * Add/remove address to/from card's ip list, i.e. try to add or remove
+ * reference to/from an IP address that is already registered on the card.
+ * Returns:
+ * 0 address was on card and its reference count has been adjusted,
+ * but is still > 0, so nothing has to be done
+ * also returns 0 if card was not on card and the todo was to delete
+ * the address -> there is also nothing to be done
+ * 1 address was not on card and the todo is to add it to the card's ip
+ * list
+ * -1 address was on card and its reference count has been decremented
+ * to <= 0 by the todo -> address must be removed from card
+ */
+static int
+__qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
+ struct qeth_ipaddr **__addr)
+{
+ struct qeth_ipaddr *addr;
+ int found = 0;
+
+ list_for_each_entry(addr, &card->ip_list, entry) {
+ if ((addr->proto == QETH_PROT_IPV4) &&
+ (todo->proto == QETH_PROT_IPV4) &&
+ (addr->type == todo->type) &&
+ (addr->u.a4.addr == todo->u.a4.addr) &&
+ (addr->u.a4.mask == todo->u.a4.mask) ){
+ found = 1;
+ break;
+ }
+ if ((addr->proto == QETH_PROT_IPV6) &&
+ (todo->proto == QETH_PROT_IPV6) &&
+ (addr->type == todo->type) &&
+ (addr->u.a6.pfxlen == todo->u.a6.pfxlen) &&
+ (memcmp(&addr->u.a6.addr, &todo->u.a6.addr,
+ sizeof(struct in6_addr)) == 0)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found){
+ addr->users += todo->users;
+ if (addr->users <= 0){
+ *__addr = addr;
+ return -1;
+ } else {
+ /* for VIPA and RXIP limit refcount to 1 */
+ if (addr->type != QETH_IP_TYPE_NORMAL)
+ addr->users = 1;
+ return 0;
+ }
+ }
+ if (todo->users > 0){
+ /* for VIPA and RXIP limit refcount to 1 */
+ if (todo->type != QETH_IP_TYPE_NORMAL)
+ addr->users = 1;
+ return 1;
+ } else
+ return 0;
+}
+
+static inline int
+__qeth_address_exists_in_list(struct list_head *list, struct qeth_ipaddr *addr,
+ int same_type)
+{
+ struct qeth_ipaddr *tmp;
+
+ list_for_each_entry(tmp, list, entry) {
+ if ((tmp->proto == QETH_PROT_IPV4) &&
+ (addr->proto == QETH_PROT_IPV4) &&
+ ((same_type && (tmp->type == addr->type)) ||
+ (!same_type && (tmp->type != addr->type)) ) &&
+ (tmp->u.a4.addr == addr->u.a4.addr) ){
+ return 1;
+ }
+ if ((tmp->proto == QETH_PROT_IPV6) &&
+ (addr->proto == QETH_PROT_IPV6) &&
+ ((same_type && (tmp->type == addr->type)) ||
+ (!same_type && (tmp->type != addr->type)) ) &&
+ (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
+ sizeof(struct in6_addr)) == 0) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Add IP to be added to todo list. If there is already an "add todo"
+ * in this list we just incremenent the reference count.
+ * Returns 0 if we just incremented reference count.
+ */
+static int
+__qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
+{
+ struct qeth_ipaddr *tmp, *t;
+ int found = 0;
+
+ list_for_each_entry_safe(tmp, t, &card->ip_tbd_list, entry) {
+ if ((tmp->proto == QETH_PROT_IPV4) &&
+ (addr->proto == QETH_PROT_IPV4) &&
+ (tmp->type == addr->type) &&
+ (tmp->is_multicast == addr->is_multicast) &&
+ (tmp->u.a4.addr == addr->u.a4.addr) &&
+ (tmp->u.a4.mask == addr->u.a4.mask) ){
+ found = 1;
+ break;
+ }
+ if ((tmp->proto == QETH_PROT_IPV6) &&
+ (addr->proto == QETH_PROT_IPV6) &&
+ (tmp->type == addr->type) &&
+ (tmp->is_multicast == addr->is_multicast) &&
+ (tmp->u.a6.pfxlen == addr->u.a6.pfxlen) &&
+ (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
+ sizeof(struct in6_addr)) == 0) ){
+ found = 1;
+ break;
+ }
+ }
+ if (found){
+ if (addr->users != 0)
+ tmp->users += addr->users;
+ else
+ tmp->users += add? 1:-1;
+ if (tmp->users == 0){
+ list_del(&tmp->entry);
+ kfree(tmp);
+ }
+ return 0;
+ } else {
+ if (addr->users == 0)
+ addr->users += add? 1:-1;
+ if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
+ qeth_is_addr_covered_by_ipato(card, addr)){
+ QETH_DBF_TEXT(trace, 2, "tkovaddr");
+ addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+ list_add_tail(&addr->entry, &card->ip_tbd_list);
+ return 1;
+ }
+}
+
+/**
+ * Remove IP address from list
+ */
+static int
+qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,4,"delip");
+ if (addr->proto == QETH_PROT_IPV4)
+ QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
+ else {
+ QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
+ QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+ }
+ spin_lock_irqsave(&card->ip_lock, flags);
+ rc = __qeth_insert_ip_todo(card, addr, 0);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+static int
+qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,4,"addip");
+ if (addr->proto == QETH_PROT_IPV4)
+ QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
+ else {
+ QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
+ QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+ }
+ spin_lock_irqsave(&card->ip_lock, flags);
+ rc = __qeth_insert_ip_todo(card, addr, 1);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+static void
+qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
+{
+ struct qeth_ipaddr *todo, *tmp;
+
+ list_for_each_entry_safe(todo, tmp, todos, entry){
+ list_del_init(&todo->entry);
+ if (todo->users < 0) {
+ if (!qeth_delete_ip(card, todo))
+ kfree(todo);
+ } else {
+ if (!qeth_add_ip(card, todo))
+ kfree(todo);
+ }
+ }
+}
+
+static void
+qeth_set_ip_addr_list(struct qeth_card *card)
+{
+ struct list_head failed_todos;
+ struct qeth_ipaddr *todo, *addr, *tmp;
+ unsigned long flags;
+ int rc;
+
+ QETH_DBF_TEXT(trace, 2, "sdiplist");
+ QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
+
+ INIT_LIST_HEAD(&failed_todos);
+
+process_todos:
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry_safe(todo, tmp, &card->ip_tbd_list, entry) {
+ list_del_init(&todo->entry);
+ rc = __qeth_ref_ip_on_card(card, todo, &addr);
+ if (rc == 0) {
+ /* nothing to be done; only adjusted refcount */
+ kfree(todo);
+ } else if (rc == 1) {
+ /* new entry to be added to on-card list */
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ rc = qeth_register_addr_entry(card, todo);
+ if (!rc){
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_add_tail(&todo->entry, &card->ip_list);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ } else
+ list_add_tail(&todo->entry, &failed_todos);
+ goto process_todos;
+ } else if (rc == -1) {
+ /* on-card entry to be removed */
+ list_del_init(&addr->entry);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ rc = qeth_deregister_addr_entry(card, addr);
+ if (!rc) {
+ kfree(addr);
+ kfree(todo);
+ } else {
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_add_tail(&addr->entry, &card->ip_list);
+ list_add_tail(&todo->entry, &failed_todos);
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ }
+ goto process_todos;
+ }
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ qeth_reinsert_todos(card, &failed_todos);
+}
+
+static void qeth_delete_mc_addresses(struct qeth_card *);
+static void qeth_add_multicast_ipv4(struct qeth_card *);
+#ifdef CONFIG_QETH_IPV6
+static void qeth_add_multicast_ipv6(struct qeth_card *);
+#endif
+
+static void
+qeth_set_thread_start_bit(struct qeth_card *card, unsigned long thread)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ card->thread_start_mask |= thread;
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+}
+
+static void
+qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ card->thread_start_mask &= ~thread;
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ wake_up(&card->wait_q);
+}
+
+static void
+qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ card->thread_running_mask &= ~thread;
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ wake_up(&card->wait_q);
+}
+
+static inline int
+__qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ if (card->thread_start_mask & thread){
+ if ((card->thread_allowed_mask & thread) &&
+ !(card->thread_running_mask & thread)){
+ rc = 1;
+ card->thread_start_mask &= ~thread;
+ card->thread_running_mask |= thread;
+ } else
+ rc = -EPERM;
+ }
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ return rc;
+}
+
+static int
+qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
+{
+ int rc = 0;
+
+ wait_event(card->wait_q,
+ (rc = __qeth_do_run_thread(card, thread)) >= 0);
+ return rc;
+}
+
+static int
+qeth_register_mc_addresses(void *ptr)
+{
+ struct qeth_card *card;
+
+ card = (struct qeth_card *) ptr;
+ daemonize("getmcaddr");
+ QETH_DBF_TEXT(trace,4,"regmcth1");
+ if (!qeth_do_run_thread(card, QETH_SET_MC_THREAD))
+ return 0;
+ QETH_DBF_TEXT(trace,4,"regmcth2");
+ qeth_delete_mc_addresses(card);
+ qeth_add_multicast_ipv4(card);
+#ifdef CONFIG_QETH_IPV6
+ qeth_add_multicast_ipv6(card);
+#endif
+ qeth_set_ip_addr_list(card);
+ qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD);
+ return 0;
+}
+
+static int
+qeth_register_ip_address(void *ptr)
+{
+ struct qeth_card *card;
+
+ card = (struct qeth_card *) ptr;
+ daemonize("regip");
+ QETH_DBF_TEXT(trace,4,"regipth1");
+ if (!qeth_do_run_thread(card, QETH_SET_IP_THREAD))
+ return 0;
+ QETH_DBF_TEXT(trace,4,"regipth2");
+ qeth_set_ip_addr_list(card);
+ qeth_clear_thread_running_bit(card, QETH_SET_IP_THREAD);
+ return 0;
+}
+
+static int
+qeth_recover(void *ptr)
+{
+ struct qeth_card *card;
+ int rc = 0;
+
+ card = (struct qeth_card *) ptr;
+ daemonize("recover");
+ QETH_DBF_TEXT(trace,2,"recover1");
+ QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
+ if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
+ return 0;
+ QETH_DBF_TEXT(trace,2,"recover2");
+ PRINT_WARN("Recovery of device %s started ...\n",
+ CARD_BUS_ID(card));
+ card->use_hard_stop = 1;
+ qeth_set_offline(card->gdev);
+ rc = qeth_set_online(card->gdev);
+ if (!rc)
+ PRINT_INFO("Device %s successfully recovered!\n",
+ CARD_BUS_ID(card));
+ else
+ PRINT_INFO("Device %s could not be recovered!\n",
+ CARD_BUS_ID(card));
+ /* don't run another scheduled recovery */
+ qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
+ qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
+ return 0;
+}
+
+void
+qeth_schedule_recovery(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(trace,2,"startrec");
+
+ qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+
+static int
+qeth_do_start_thread(struct qeth_card *card, unsigned long thread)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ QETH_DBF_TEXT_(trace, 4, " %02x%02x%02x",
+ (u8) card->thread_start_mask,
+ (u8) card->thread_allowed_mask,
+ (u8) card->thread_running_mask);
+ rc = (card->thread_start_mask & thread);
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ return rc;
+}
+
+static void
+qeth_start_kernel_thread(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(trace , 2, "strthrd");
+
+ if (card->read.state != CH_STATE_UP &&
+ card->write.state != CH_STATE_UP)
+ return;
+
+ if (qeth_do_start_thread(card, QETH_SET_IP_THREAD))
+ kernel_thread(qeth_register_ip_address, (void *) card, SIGCHLD);
+ if (qeth_do_start_thread(card, QETH_SET_MC_THREAD))
+ kernel_thread(qeth_register_mc_addresses, (void *)card,SIGCHLD);
+ if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
+ kernel_thread(qeth_recover, (void *) card, SIGCHLD);
+}
+
+
+static void
+qeth_set_intial_options(struct qeth_card *card)
+{
+ card->options.route4.type = NO_ROUTER;
+#ifdef CONFIG_QETH_IPV6
+ card->options.route6.type = NO_ROUTER;
+#endif /* QETH_IPV6 */
+ card->options.checksum_type = QETH_CHECKSUM_DEFAULT;
+ card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
+ card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL;
+ card->options.enable_takeover = 1;
+ card->options.fake_broadcast = 0;
+ card->options.add_hhlen = DEFAULT_ADD_HHLEN;
+ card->options.fake_ll = 0;
+}
+
+/**
+ * initialize channels ,card and all state machines
+ */
+static int
+qeth_setup_card(struct qeth_card *card)
+{
+
+ QETH_DBF_TEXT(setup, 2, "setupcrd");
+ QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+
+ card->read.state = CH_STATE_DOWN;
+ card->write.state = CH_STATE_DOWN;
+ card->data.state = CH_STATE_DOWN;
+ card->state = CARD_STATE_DOWN;
+ card->lan_online = 0;
+ card->use_hard_stop = 0;
+ card->dev = NULL;
+#ifdef CONFIG_QETH_VLAN
+ spin_lock_init(&card->vlanlock);
+ card->vlangrp = NULL;
+#endif
+ spin_lock_init(&card->ip_lock);
+ spin_lock_init(&card->thread_mask_lock);
+ card->thread_start_mask = 0;
+ card->thread_allowed_mask = 0;
+ card->thread_running_mask = 0;
+ INIT_WORK(&card->kernel_thread_starter,
+ (void *)qeth_start_kernel_thread,card);
+ INIT_LIST_HEAD(&card->ip_list);
+ INIT_LIST_HEAD(&card->ip_tbd_list);
+ INIT_LIST_HEAD(&card->cmd_waiter_list);
+ init_waitqueue_head(&card->wait_q);
+ /* intial options */
+ qeth_set_intial_options(card);
+ /* IP address takeover */
+ INIT_LIST_HEAD(&card->ipato.entries);
+ card->ipato.enabled = 0;
+ card->ipato.invert4 = 0;
+ card->ipato.invert6 = 0;
+ /* init QDIO stuff */
+ qeth_init_qdio_info(card);
+ return 0;
+}
+
+static int
+qeth_determine_card_type(struct qeth_card *card)
+{
+ int i = 0;
+
+ QETH_DBF_TEXT(setup, 2, "detcdtyp");
+
+ while (known_devices[i][4]) {
+ if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
+ (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
+ card->info.type = known_devices[i][4];
+ if (card->options.enable_takeover)
+ card->info.func_level = known_devices[i][6];
+ else
+ card->info.func_level = known_devices[i][7];
+ card->qdio.no_out_queues = known_devices[i][8];
+ card->info.is_multicast_different = known_devices[i][9];
+ return 0;
+ }
+ i++;
+ }
+ card->info.type = QETH_CARD_TYPE_UNKNOWN;
+ PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card));
+ return -ENOENT;
+}
+
+static int
+qeth_probe_device(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card;
+ struct device *dev;
+ unsigned long flags;
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "probedev");
+
+ dev = &gdev->dev;
+ if (!get_device(dev))
+ return -ENODEV;
+
+ card = qeth_alloc_card();
+ if (!card) {
+ put_device(dev);
+ QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM);
+ return -ENOMEM;
+ }
+ if ((rc = qeth_setup_card(card))){
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ put_device(dev);
+ qeth_free_card(card);
+ return rc;
+ }
+ gdev->dev.driver_data = card;
+ card->gdev = gdev;
+ gdev->cdev[0]->handler = qeth_irq;
+ gdev->cdev[1]->handler = qeth_irq;
+ gdev->cdev[2]->handler = qeth_irq;
+
+ rc = qeth_create_device_attributes(dev);
+ if (rc) {
+ put_device(dev);
+ qeth_free_card(card);
+ return rc;
+ }
+ card->read.ccwdev = gdev->cdev[0];
+ card->write.ccwdev = gdev->cdev[1];
+ card->data.ccwdev = gdev->cdev[2];
+ if ((rc = qeth_determine_card_type(card))){
+ PRINT_WARN("%s: not a valid card type\n", __func__);
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ put_device(dev);
+ qeth_free_card(card);
+ return rc;
+ }
+ /* insert into our internal list */
+ write_lock_irqsave(&qeth_card_list.rwlock, flags);
+ list_add_tail(&card->list, &qeth_card_list.list);
+ write_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+ return rc;
+}
+
+
+static int
+qeth_get_unitaddr(struct qeth_card *card)
+{
+ int length;
+ char *prcd;
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "getunit");
+ rc = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length);
+ if (rc) {
+ PRINT_ERR("read_conf_data for device %s returned %i\n",
+ CARD_DDEV_ID(card), rc);
+ return rc;
+ }
+ card->info.chpid = prcd[30];
+ card->info.unit_addr2 = prcd[31];
+ card->info.cula = prcd[63];
+ card->info.guestlan = ((prcd[0x10] == _ascebc['V']) &&
+ (prcd[0x11] == _ascebc['M']));
+ return 0;
+}
+
+static void
+qeth_init_tokens(struct qeth_card *card)
+{
+ card->token.issuer_rm_w = 0x00010103UL;
+ card->token.cm_filter_w = 0x00010108UL;
+ card->token.cm_connection_w = 0x0001010aUL;
+ card->token.ulp_filter_w = 0x0001010bUL;
+ card->token.ulp_connection_w = 0x0001010dUL;
+}
+
+static inline __u16
+raw_devno_from_bus_id(char *id)
+{
+ id += (strlen(id) - 4);
+ return (__u16) simple_strtoul(id, &id, 16);
+}
+/**
+ * setup channel
+ */
+static void
+qeth_setup_ccw(struct qeth_channel *channel,unsigned char *iob, __u32 len)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace, 4, "setupccw");
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ if (channel == &card->read)
+ memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1));
+ else
+ memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1));
+ channel->ccw.count = len;
+ channel->ccw.cda = (__u32) __pa(iob);
+}
+
+/**
+ * get free buffer for ccws (IDX activation, lancmds,ipassists...)
+ */
+static struct qeth_cmd_buffer *
+__qeth_get_buffer(struct qeth_channel *channel)
+{
+ __u8 index;
+
+ QETH_DBF_TEXT(trace, 6, "getbuff");
+ index = channel->io_buf_no;
+ do {
+ if (channel->iob[index].state == BUF_STATE_FREE) {
+ channel->iob[index].state = BUF_STATE_LOCKED;
+ channel->io_buf_no = (channel->io_buf_no + 1) %
+ QETH_CMD_BUFFER_NO;
+ memset(channel->iob[index].data, 0, QETH_BUFSIZE);
+ return channel->iob + index;
+ }
+ index = (index + 1) % QETH_CMD_BUFFER_NO;
+ } while(index != channel->io_buf_no);
+
+ return NULL;
+}
+
+/**
+ * release command buffer
+ */
+static void
+qeth_release_buffer(struct qeth_channel *channel, struct qeth_cmd_buffer *iob)
+{
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace, 6, "relbuff");
+ spin_lock_irqsave(&channel->iob_lock, flags);
+ memset(iob->data, 0, QETH_BUFSIZE);
+ iob->state = BUF_STATE_FREE;
+ iob->callback = qeth_send_control_data_cb;
+ iob->rc = 0;
+ spin_unlock_irqrestore(&channel->iob_lock, flags);
+}
+
+static struct qeth_cmd_buffer *
+qeth_get_buffer(struct qeth_channel *channel)
+{
+ struct qeth_cmd_buffer *buffer = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&channel->iob_lock, flags);
+ buffer = __qeth_get_buffer(channel);
+ spin_unlock_irqrestore(&channel->iob_lock, flags);
+ return buffer;
+}
+
+static struct qeth_cmd_buffer *
+qeth_wait_for_buffer(struct qeth_channel *channel)
+{
+ struct qeth_cmd_buffer *buffer;
+ wait_event(channel->wait_q,
+ ((buffer = qeth_get_buffer(channel)) != NULL));
+ return buffer;
+}
+
+static void
+qeth_clear_cmd_buffers(struct qeth_channel *channel)
+{
+ int cnt = 0;
+
+ for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++)
+ qeth_release_buffer(channel,&channel->iob[cnt]);
+ channel->buf_no = 0;
+ channel->io_buf_no = 0;
+}
+
+/**
+ * start IDX for read and write channel
+ */
+static int
+qeth_idx_activate_get_answer(struct qeth_channel *channel,
+ void (*idx_reply_cb)(struct qeth_channel *,
+ struct qeth_cmd_buffer *))
+{
+ struct qeth_cmd_buffer *iob;
+ unsigned long flags;
+ int rc;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(setup, 2, "idxanswr");
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ iob = qeth_get_buffer(channel);
+ iob->callback = idx_reply_cb;
+ memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1));
+ channel->ccw.count = QETH_BUFSIZE;
+ channel->ccw.cda = (__u32) __pa(iob->data);
+
+ wait_event(card->wait_q,
+ atomic_compare_and_swap(0,1,&channel->irq_pending) == 0);
+ QETH_DBF_TEXT(setup, 6, "noirqpnd");
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_start(channel->ccwdev,
+ &channel->ccw,(addr_t) iob, 0, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+
+ if (rc) {
+ PRINT_ERR("qeth: Error2 in activating channel rc=%d\n",rc);
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ atomic_set(&channel->irq_pending, 0);
+ wake_up(&card->wait_q);
+ return rc;
+ }
+ rc = wait_event_interruptible_timeout(card->wait_q,
+ channel->state == CH_STATE_UP, QETH_TIMEOUT);
+ if (rc == -ERESTARTSYS)
+ return rc;
+ if (channel->state != CH_STATE_UP){
+ rc = -ETIME;
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ } else
+ rc = 0;
+ return rc;
+}
+
+static int
+qeth_idx_activate_channel(struct qeth_channel *channel,
+ void (*idx_reply_cb)(struct qeth_channel *,
+ struct qeth_cmd_buffer *))
+{
+ struct qeth_card *card;
+ struct qeth_cmd_buffer *iob;
+ unsigned long flags;
+ __u16 temp;
+ int rc;
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+
+ QETH_DBF_TEXT(setup, 2, "idxactch");
+
+ iob = qeth_get_buffer(channel);
+ iob->callback = idx_reply_cb;
+ memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1));
+ channel->ccw.count = IDX_ACTIVATE_SIZE;
+ channel->ccw.cda = (__u32) __pa(iob->data);
+ if (channel == &card->write) {
+ memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE);
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
+ &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
+ card->seqno.trans_hdr++;
+ } else {
+ memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE);
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
+ &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
+ }
+ memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
+ &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
+ &card->info.func_level,sizeof(__u16));
+ temp = raw_devno_from_bus_id(CARD_DDEV_ID(card));
+ memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp, 2);
+ temp = (card->info.cula << 8) + card->info.unit_addr2;
+ memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
+
+ wait_event(card->wait_q,
+ atomic_compare_and_swap(0,1,&channel->irq_pending) == 0);
+ QETH_DBF_TEXT(setup, 6, "noirqpnd");
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_start(channel->ccwdev,
+ &channel->ccw,(addr_t) iob, 0, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+
+ if (rc) {
+ PRINT_ERR("qeth: Error1 in activating channel. rc=%d\n",rc);
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ atomic_set(&channel->irq_pending, 0);
+ wake_up(&card->wait_q);
+ return rc;
+ }
+ rc = wait_event_interruptible_timeout(card->wait_q,
+ channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT);
+ if (rc == -ERESTARTSYS)
+ return rc;
+ if (channel->state != CH_STATE_ACTIVATING) {
+ PRINT_WARN("qeth: IDX activate timed out!\n");
+ QETH_DBF_TEXT_(setup, 2, "2err%d", -ETIME);
+ return -ETIME;
+ }
+ return qeth_idx_activate_get_answer(channel,idx_reply_cb);
+}
+
+static int
+qeth_peer_func_level(int level)
+{
+ if ((level & 0xff) == 8)
+ return (level & 0xff) + 0x400;
+ if (((level >> 8) & 3) == 1)
+ return (level & 0xff) + 0x200;
+ return level;
+}
+
+static void
+qeth_idx_write_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob)
+{
+ struct qeth_card *card;
+ __u16 temp;
+
+ QETH_DBF_TEXT(setup ,2, "idxwrcb");
+
+ if (channel->state == CH_STATE_DOWN) {
+ channel->state = CH_STATE_ACTIVATING;
+ goto out;
+ }
+ card = CARD_FROM_CDEV(channel->ccwdev);
+
+ if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
+ PRINT_ERR("IDX_ACTIVATE on write channel device %s: negative "
+ "reply\n", CARD_WDEV_ID(card));
+ goto out;
+ }
+ memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
+ if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) {
+ PRINT_WARN("IDX_ACTIVATE on write channel device %s: "
+ "function level mismatch "
+ "(sent: 0x%x, received: 0x%x)\n",
+ CARD_WDEV_ID(card), card->info.func_level, temp);
+ goto out;
+ }
+ channel->state = CH_STATE_UP;
+out:
+ qeth_release_buffer(channel, iob);
+}
+
+static int
+qeth_check_idx_response(unsigned char *buffer)
+{
+ if (!buffer)
+ return 0;
+
+ QETH_DBF_HEX(control, 2, buffer, QETH_DBF_CONTROL_LEN);
+ if ((buffer[2] & 0xc0) == 0xc0) {
+ PRINT_WARN("received an IDX TERMINATE "
+ "with cause code 0x%02x%s\n",
+ buffer[4],
+ ((buffer[4] == 0x22) ?
+ " -- try another portname" : ""));
+ QETH_DBF_TEXT(trace, 2, "ckidxres");
+ QETH_DBF_TEXT(trace, 2, " idxterm");
+ QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+qeth_idx_read_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob)
+{
+ struct qeth_card *card;
+ __u16 temp;
+
+ QETH_DBF_TEXT(setup , 2, "idxrdcb");
+ if (channel->state == CH_STATE_DOWN) {
+ channel->state = CH_STATE_ACTIVATING;
+ goto out;
+ }
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ if (qeth_check_idx_response(iob->data)) {
+ goto out;
+ }
+ if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
+ PRINT_ERR("IDX_ACTIVATE on read channel device %s: negative "
+ "reply\n", CARD_RDEV_ID(card));
+ goto out;
+ }
+
+/**
+ * temporary fix for microcode bug
+ * to revert it,replace OR by AND
+ */
+ if ( (!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) ||
+ (card->info.type == QETH_CARD_TYPE_OSAE) )
+ card->info.portname_required = 1;
+
+ memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
+ if (temp != qeth_peer_func_level(card->info.func_level)) {
+ PRINT_WARN("IDX_ACTIVATE on read channel device %s: function "
+ "level mismatch (sent: 0x%x, received: 0x%x)\n",
+ CARD_RDEV_ID(card), card->info.func_level, temp);
+ goto out;
+ }
+ memcpy(&card->token.issuer_rm_r,
+ QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data),
+ QETH_MPC_TOKEN_LENGTH);
+ memcpy(&card->info.mcl_level[0],
+ QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH);
+ channel->state = CH_STATE_UP;
+out:
+ qeth_release_buffer(channel,iob);
+}
+
+static int
+qeth_issue_next_read(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace,5,"issnxrd");
+ if (card->read.state != CH_STATE_UP)
+ return -EIO;
+ iob = qeth_get_buffer(&card->read);
+ if (!iob) {
+ PRINT_WARN("issue_next_read failed: no iob available!\n");
+ return -ENOMEM;
+ }
+ qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE);
+ wait_event(card->wait_q,
+ atomic_compare_and_swap(0,1,&card->read.irq_pending) == 0);
+ QETH_DBF_TEXT(trace, 6, "noirqpnd");
+ rc = ccw_device_start(card->read.ccwdev, &card->read.ccw,
+ (addr_t) iob, 0, 0);
+ if (rc) {
+ PRINT_ERR("Error in starting next read ccw! rc=%i\n", rc);
+ atomic_set(&card->read.irq_pending, 0);
+ qeth_schedule_recovery(card);
+ wake_up(&card->wait_q);
+ }
+ return rc;
+}
+
+static struct qeth_reply *
+qeth_alloc_reply(struct qeth_card *card)
+{
+ struct qeth_reply *reply;
+
+ reply = kmalloc(sizeof(struct qeth_reply), GFP_KERNEL|GFP_ATOMIC);
+ if (reply){
+ memset(reply, 0, sizeof(struct qeth_reply));
+ atomic_set(&reply->refcnt, 1);
+ reply->card = card;
+ };
+ return reply;
+}
+
+static void
+qeth_get_reply(struct qeth_reply *reply)
+{
+ WARN_ON(atomic_read(&reply->refcnt) <= 0);
+ atomic_inc(&reply->refcnt);
+}
+
+static void
+qeth_put_reply(struct qeth_reply *reply)
+{
+ WARN_ON(atomic_read(&reply->refcnt) <= 0);
+ if (atomic_dec_and_test(&reply->refcnt))
+ kfree(reply);
+}
+
+static void
+qeth_cmd_timeout(unsigned long data)
+{
+ struct qeth_reply *reply, *list_reply, *r;
+ unsigned long flags;
+
+ reply = (struct qeth_reply *) data;
+ spin_lock_irqsave(&reply->card->lock, flags);
+ list_for_each_entry_safe(list_reply, r,
+ &reply->card->cmd_waiter_list, list) {
+ if (reply == list_reply){
+ qeth_get_reply(reply);
+ list_del_init(&reply->list);
+ spin_unlock_irqrestore(&reply->card->lock, flags);
+ reply->rc = -ETIME;
+ reply->received = 1;
+ wake_up(&reply->wait_q);
+ qeth_put_reply(reply);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&reply->card->lock, flags);
+}
+
+static struct qeth_ipa_cmd *
+qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
+{
+ struct qeth_ipa_cmd *cmd = NULL;
+ enum qeth_card_states old_state;
+
+ QETH_DBF_TEXT(trace,5,"chkipad");
+ if (IS_IPA(iob->data)){
+ cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data);
+ if (IS_IPA_REPLY(cmd))
+ return cmd;
+ else {
+ switch (cmd->hdr.command) {
+ case IPA_CMD_STOPLAN:
+ PRINT_WARN("Link failure on %s (CHPID 0x%X) - "
+ "there is a network problem or "
+ "someone pulled the cable or "
+ "disabled the port. Setting state "
+ "of interface to DOWN.\n",
+ card->info.if_name,
+ card->info.chpid);
+ card->lan_online = 0;
+ old_state = card->state;
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ if ((old_state == CARD_STATE_UP_LAN_ONLINE) ||
+ (old_state == CARD_STATE_UP_LAN_OFFLINE))
+ card->state = CARD_STATE_UP_LAN_OFFLINE;
+ return NULL;
+ case IPA_CMD_STARTLAN:
+ PRINT_INFO("Link reestablished on %s "
+ "(CHPID 0x%X)\n",
+ card->info.if_name,
+ card->info.chpid);
+ card->lan_online = 1;
+ if (card->state == CARD_STATE_UP_LAN_OFFLINE){
+ rtnl_lock();
+ dev_open(card->dev);
+ rtnl_unlock();
+ }
+ return NULL;
+ case IPA_CMD_REGISTER_LOCAL_ADDR:
+ QETH_DBF_TEXT(trace,3, "irla");
+ break;
+ case IPA_CMD_UNREGISTER_LOCAL_ADDR:
+ PRINT_WARN("probably problem on %s: "
+ "received IPA command 0x%X\n",
+ card->info.if_name,
+ cmd->hdr.command);
+ break;
+ default:
+ PRINT_WARN("Received data is IPA "
+ "but not a reply!\n");
+ break;
+ }
+ }
+ }
+ return cmd;
+}
+
+/**
+ * wake all waiting ipa commands
+ */
+static void
+qeth_clear_ipacmd_list(struct qeth_card *card)
+{
+ struct qeth_reply *reply, *r;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace, 4, "clipalst");
+
+ spin_lock_irqsave(&card->lock, flags);
+ list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) {
+ qeth_get_reply(reply);
+ reply->rc = -EIO;
+ reply->received = 1;
+ list_del_init(&reply->list);
+ wake_up(&reply->wait_q);
+ qeth_put_reply(reply);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+qeth_send_control_data_cb(struct qeth_channel *channel,
+ struct qeth_cmd_buffer *iob)
+{
+ struct qeth_card *card;
+ struct qeth_reply *reply, *r;
+ struct qeth_ipa_cmd *cmd;
+ unsigned long flags;
+ int keep_reply;
+
+ QETH_DBF_TEXT(trace,4,"sndctlcb");
+
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ if (qeth_check_idx_response(iob->data)) {
+ qeth_clear_ipacmd_list(card);
+ qeth_schedule_recovery(card);
+ goto out;
+ }
+
+ cmd = qeth_check_ipa_data(card, iob);
+ if ((cmd == NULL) && (card->state != CARD_STATE_DOWN))
+ goto out;
+
+ spin_lock_irqsave(&card->lock, flags);
+ list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) {
+ if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) ||
+ ((cmd) && (reply->seqno == cmd->hdr.seqno))) {
+ qeth_get_reply(reply);
+ list_del_init(&reply->list);
+ spin_unlock_irqrestore(&card->lock, flags);
+ keep_reply = 0;
+ if (reply->callback != NULL) {
+ if (cmd)
+ keep_reply = reply->callback(card,
+ reply,
+ (unsigned long)cmd);
+ else
+ keep_reply = reply->callback(card,
+ reply,
+ (unsigned long)iob);
+ }
+ if (cmd)
+ reply->rc = cmd->hdr.return_code;
+ else if (iob->rc)
+ reply->rc = iob->rc;
+ if (keep_reply) {
+ spin_lock_irqsave(&card->lock, flags);
+ list_add_tail(&reply->list,
+ &card->cmd_waiter_list);
+ spin_unlock_irqrestore(&card->lock, flags);
+ } else {
+ reply->received = 1;
+ wake_up(&reply->wait_q);
+ }
+ qeth_put_reply(reply);
+ goto out;
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+out:
+ memcpy(&card->seqno.pdu_hdr_ack,
+ QETH_PDU_HEADER_SEQ_NO(iob->data),
+ QETH_SEQ_NO_LENGTH);
+ qeth_release_buffer(channel,iob);
+}
+
+static int
+qeth_send_control_data(struct qeth_card *card, int len,
+ struct qeth_cmd_buffer *iob,
+ int (*reply_cb)
+ (struct qeth_card *, struct qeth_reply*, unsigned long),
+ void *reply_param)
+
+{
+ int rc;
+ unsigned long flags;
+ struct qeth_reply *reply;
+ struct timer_list timer;
+
+ QETH_DBF_TEXT(trace, 2, "sendctl");
+
+ qeth_setup_ccw(&card->write,iob->data,len);
+
+ memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
+ &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
+ card->seqno.trans_hdr++;
+
+ memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data),
+ &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH);
+ card->seqno.pdu_hdr++;
+ memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data),
+ &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
+ iob->callback = qeth_release_buffer;
+
+ reply = qeth_alloc_reply(card);
+ if (!reply) {
+ PRINT_WARN("Could no alloc qeth_reply!\n");
+ return -ENOMEM;
+ }
+ reply->callback = reply_cb;
+ reply->param = reply_param;
+ if (card->state == CARD_STATE_DOWN)
+ reply->seqno = QETH_IDX_COMMAND_SEQNO;
+ else
+ reply->seqno = card->seqno.ipa++;
+ init_timer(&timer);
+ timer.function = qeth_cmd_timeout;
+ timer.data = (unsigned long) reply;
+ timer.expires = jiffies + QETH_TIMEOUT;
+ init_waitqueue_head(&reply->wait_q);
+ spin_lock_irqsave(&card->lock, flags);
+ list_add_tail(&reply->list, &card->cmd_waiter_list);
+ spin_unlock_irqrestore(&card->lock, flags);
+ QETH_DBF_HEX(control, 2, iob->data, QETH_DBF_CONTROL_LEN);
+ wait_event(card->wait_q,
+ atomic_compare_and_swap(0,1,&card->write.irq_pending) == 0);
+ QETH_DBF_TEXT(trace, 6, "noirqpnd");
+ spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
+ rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
+ (addr_t) iob, 0, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
+ if (rc){
+ PRINT_WARN("qeth_send_control_data: "
+ "ccw_device_start rc = %i\n", rc);
+ QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+ spin_lock_irqsave(&card->lock, flags);
+ list_del_init(&reply->list);
+ qeth_put_reply(reply);
+ spin_unlock_irqrestore(&card->lock, flags);
+ qeth_release_buffer(iob->channel, iob);
+ atomic_set(&card->write.irq_pending, 0);
+ wake_up(&card->wait_q);
+ return rc;
+ }
+ add_timer(&timer);
+ wait_event(reply->wait_q, reply->received);
+ del_timer(&timer);
+ rc = reply->rc;
+ qeth_put_reply(reply);
+ return rc;
+}
+
+static int
+qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ int (*reply_cb)
+ (struct qeth_card *,struct qeth_reply*, unsigned long),
+ void *reply_param)
+{
+ struct qeth_ipa_cmd *cmd;
+ int rc;
+
+ QETH_DBF_TEXT(trace,4,"sendipa");
+
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+ memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+ &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+
+ rc = qeth_send_control_data(card, IPA_CMD_LENGTH, iob,
+ reply_cb, reply_param);
+ return rc;
+}
+
+
+static int
+qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup, 2, "cmenblcb");
+
+ iob = (struct qeth_cmd_buffer *) data;
+ memcpy(&card->token.cm_filter_r,
+ QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data),
+ QETH_MPC_TOKEN_LENGTH);
+ QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc);
+ return 0;
+}
+
+static int
+qeth_cm_enable(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup,2,"cmenable");
+
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE);
+ memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data),
+ &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data),
+ &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH);
+
+ rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob,
+ qeth_cm_enable_cb, NULL);
+ return rc;
+}
+
+static int
+qeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup, 2, "cmsetpcb");
+
+ iob = (struct qeth_cmd_buffer *) data;
+ memcpy(&card->token.cm_connection_r,
+ QETH_CM_SETUP_RESP_DEST_ADDR(iob->data),
+ QETH_MPC_TOKEN_LENGTH);
+ QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc);
+ return 0;
+}
+
+static int
+qeth_cm_setup(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup,2,"cmsetup");
+
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE);
+ memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data),
+ &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data),
+ &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data),
+ &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH);
+ rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob,
+ qeth_cm_setup_cb, NULL);
+ return rc;
+
+}
+
+static int
+qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+
+ __u16 mtu, framesize;
+ __u16 len;
+ __u8 link_type;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup, 2, "ulpenacb");
+
+ iob = (struct qeth_cmd_buffer *) data;
+ memcpy(&card->token.ulp_filter_r,
+ QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data),
+ QETH_MPC_TOKEN_LENGTH);
+ if (qeth_get_mtu_out_of_mpc(card->info.type)) {
+ memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2);
+ mtu = qeth_get_mtu_outof_framesize(framesize);
+ if (!mtu) {
+ iob->rc = -EINVAL;
+ QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc);
+ return 0;
+ }
+ card->info.max_mtu = mtu;
+ card->info.initial_mtu = mtu;
+ card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;
+ } else {
+ card->info.initial_mtu = qeth_get_initial_mtu_for_card(card);
+ card->info.max_mtu = qeth_get_max_mtu_for_card(card->info.type);
+ card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
+ }
+
+ memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2);
+ if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) {
+ memcpy(&link_type,
+ QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1);
+ card->info.link_type = link_type;
+ } else
+ card->info.link_type = 0;
+ QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc);
+ return 0;
+}
+
+static int
+qeth_ulp_enable(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ /*FIXME: trace view callbacks*/
+ QETH_DBF_TEXT(setup,2,"ulpenabl");
+
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE);
+
+ *(QETH_ULP_ENABLE_LINKNUM(iob->data)) =
+ (__u8) card->info.portno;
+
+ memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),
+ &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
+ &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data),
+ card->info.portname, 9);
+ rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob,
+ qeth_ulp_enable_cb, NULL);
+ return rc;
+
+}
+
+static inline __u16
+__raw_devno_from_bus_id(char *id)
+{
+ id += (strlen(id) - 4);
+ return (__u16) simple_strtoul(id, &id, 16);
+}
+
+static int
+qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup, 2, "ulpstpcb");
+
+ iob = (struct qeth_cmd_buffer *) data;
+ memcpy(&card->token.ulp_connection_r,
+ QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data),
+ QETH_MPC_TOKEN_LENGTH);
+ QETH_DBF_TEXT_(setup, 2, " rc%d", iob->rc);
+ return 0;
+}
+
+static int
+qeth_ulp_setup(struct qeth_card *card)
+{
+ int rc;
+ __u16 temp;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup,2,"ulpsetup");
+
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE);
+
+ memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data),
+ &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data),
+ &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data),
+ &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH);
+
+ temp = __raw_devno_from_bus_id(CARD_DDEV_ID(card));
+ memcpy(QETH_ULP_SETUP_CUA(iob->data), &temp, 2);
+ temp = (card->info.cula << 8) + card->info.unit_addr2;
+ memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2);
+ rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob,
+ qeth_ulp_setup_cb, NULL);
+ return rc;
+}
+
+static inline int
+qeth_check_for_inbound_error(struct qeth_qdio_buffer *buf,
+ unsigned int qdio_error,
+ unsigned int siga_error)
+{
+ int rc = 0;
+
+ if (qdio_error || siga_error) {
+ QETH_DBF_TEXT(trace, 2, "qdinerr");
+ QETH_DBF_TEXT(qerr, 2, "qdinerr");
+ QETH_DBF_TEXT_(qerr, 2, " F15=%02X",
+ buf->buffer->element[15].flags & 0xff);
+ QETH_DBF_TEXT_(qerr, 2, " F14=%02X",
+ buf->buffer->element[14].flags & 0xff);
+ QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error);
+ QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error);
+ rc = 1;
+ }
+ return rc;
+}
+
+static void
+qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
+ unsigned int qdio_err, unsigned int siga_err,
+ unsigned int queue, int first_element, int count,
+ unsigned long card_ptr)
+{
+ struct net_device *net_dev;
+ struct qeth_card *card;
+ struct qeth_qdio_buffer *buffer;
+ int i;
+
+ QETH_DBF_TEXT(trace, 6, "qdinput");
+ card = (struct qeth_card *) card_ptr;
+ net_dev = card->dev;
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_start_time = qeth_get_micros();
+#endif
+ if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
+ if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
+ QETH_DBF_TEXT(trace, 1,"qdinchk");
+ QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
+ QETH_DBF_TEXT_(trace,1,"%04X%04X",first_element,count);
+ QETH_DBF_TEXT_(trace,1,"%04X%04X", queue, status);
+ qeth_schedule_recovery(card);
+ return;
+ }
+ }
+ for (i = first_element; i < (first_element + count); ++i) {
+ buffer = &card->qdio.in_q->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
+ if ((status == QDIO_STATUS_LOOK_FOR_ERROR) &&
+ qeth_check_for_inbound_error(buffer, qdio_err, siga_err))
+ buffer->state = QETH_QDIO_BUF_ERROR;
+ else
+ buffer->state = QETH_QDIO_BUF_PRIMED;
+ }
+
+ tasklet_schedule(&card->qdio.in_tasklet);
+}
+
+static inline struct sk_buff *
+qeth_get_skb(unsigned int length)
+{
+ struct sk_buff* skb;
+#ifdef CONFIG_QETH_VLAN
+ if ((skb = dev_alloc_skb(length + VLAN_HLEN)))
+ skb_reserve(skb, VLAN_HLEN);
+#else
+ skb = dev_alloc_skb(length);
+#endif
+ return skb;
+}
+
+static inline struct sk_buff *
+qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,
+ struct qdio_buffer_element **__element, int *__offset,
+ struct qeth_hdr **hdr)
+{
+ struct qdio_buffer_element *element = *__element;
+ int offset = *__offset;
+ struct sk_buff *skb = NULL;
+ int skb_len;
+ void *data_ptr;
+ int data_len;
+
+ QETH_DBF_TEXT(trace,6,"nextskb");
+ /* qeth_hdr must not cross element boundaries */
+ if (element->length < offset + sizeof(struct qeth_hdr)){
+ if (qeth_is_last_sbale(element))
+ return NULL;
+ element++;
+ offset = 0;
+ if (element->length < sizeof(struct qeth_hdr))
+ return NULL;
+ }
+ *hdr = element->addr + offset;
+
+ offset += sizeof(struct qeth_hdr);
+ skb_len = (*hdr)->length;
+ if (!skb_len)
+ return NULL;
+ if (card->options.fake_ll){
+ if (!(skb = qeth_get_skb(skb_len + QETH_FAKE_LL_LEN)))
+ goto no_mem;
+ skb_pull(skb, QETH_FAKE_LL_LEN);
+ } else if (!(skb = qeth_get_skb(skb_len)))
+ goto no_mem;
+ data_ptr = element->addr + offset;
+ while (skb_len) {
+ data_len = min(skb_len, (int)(element->length - offset));
+ if (data_len)
+ memcpy(skb_put(skb, data_len), data_ptr, data_len);
+ skb_len -= data_len;
+ if (skb_len){
+ if (qeth_is_last_sbale(element)){
+ QETH_DBF_TEXT(trace,4,"unexeob");
+ QETH_DBF_TEXT_(trace,4,"%s",CARD_BUS_ID(card));
+ QETH_DBF_TEXT(qerr,2,"unexeob");
+ QETH_DBF_TEXT_(qerr,2,"%s",CARD_BUS_ID(card));
+ QETH_DBF_HEX(misc,4,buffer,sizeof(*buffer));
+ dev_kfree_skb_irq(skb);
+ card->stats.rx_errors++;
+ return NULL;
+ }
+ element++;
+ offset = 0;
+ data_ptr = element->addr;
+ } else {
+ offset += data_len;
+ }
+ }
+ *__element = element;
+ *__offset = offset;
+ return skb;
+no_mem:
+ if (net_ratelimit()){
+ PRINT_WARN("No memory for packet received on %s.\n",
+ card->info.if_name);
+ QETH_DBF_TEXT(trace,2,"noskbmem");
+ QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));
+ }
+ card->stats.rx_dropped++;
+ return NULL;
+}
+
+static inline unsigned short
+qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *eth;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,5,"typtrans");
+
+ card = (struct qeth_card *)dev->priv;
+#ifdef CONFIG_TR
+ if ((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR))
+ return tr_type_trans(skb,dev);
+#endif /* CONFIG_TR */
+
+ skb->mac.raw = skb->data;
+ skb_pull(skb, ETH_ALEN * 2 + sizeof (short));
+ eth = skb->mac.ethernet;
+
+ if (*eth->h_dest & 1) {
+ if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+ } else {
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+ if (*(unsigned short *) (skb->data) == 0xFFFF)
+ return htons(ETH_P_802_3);
+ return htons(ETH_P_802_2);
+}
+
+
+static inline void
+qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_hdr *hdr)
+{
+ struct ethhdr *fake_hdr;
+ struct iphdr *ip_hdr;
+
+ QETH_DBF_TEXT(trace,5,"skbfake");
+ skb->mac.raw = skb->data - QETH_FAKE_LL_LEN;
+ /* this is a fake ethernet header */
+ fake_hdr = (struct ethhdr *) skb->mac.raw;
+
+ /* the destination MAC address */
+ switch (skb->pkt_type){
+ case PACKET_MULTICAST:
+ switch (skb->protocol){
+#ifdef CONFIG_QETH_IPV6
+ case __constant_htons(ETH_P_IPV6):
+ ndisc_mc_map((struct in6_addr *)
+ skb->data + QETH_FAKE_LL_V6_ADDR_POS,
+ fake_hdr->h_dest, card->dev, 0);
+ break;
+#endif /* CONFIG_QETH_IPV6 */
+ case __constant_htons(ETH_P_IP):
+ ip_hdr = (struct iphdr *)skb->data;
+ if (card->dev->type == ARPHRD_IEEE802_TR)
+ ip_tr_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
+ else
+ ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
+ break;
+ default:
+ memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
+ }
+ break;
+ case PACKET_BROADCAST:
+ memset(fake_hdr->h_dest, 0xff, ETH_ALEN);
+ break;
+ default:
+ memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
+ }
+ /* the source MAC address */
+ if (hdr->ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+ memcpy(fake_hdr->h_source, &hdr->dest_addr[2], ETH_ALEN);
+ else
+ memset(fake_hdr->h_source, 0, ETH_ALEN);
+ /* the protocol */
+ fake_hdr->h_proto = skb->protocol;
+}
+
+static inline void
+qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_hdr *hdr)
+{
+#ifdef CONFIG_QETH_VLAN
+ u16 *vlan_tag;
+
+ if (hdr->ext_flags & QETH_HDR_EXT_VLAN_FRAME) {
+ vlan_tag = (u16 *) skb_push(skb, VLAN_HLEN);
+ *vlan_tag = hdr->vlan_id;
+ *(vlan_tag + 1) = skb->protocol;
+ skb->protocol = __constant_htons(ETH_P_8021Q);
+ }
+#endif /* CONFIG_QETH_VLAN */
+}
+
+
+static inline void
+qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_hdr *hdr)
+{
+#ifdef CONFIG_QETH_IPV6
+ if (hdr->flags & QETH_HDR_PASSTHRU){
+ skb->protocol = qeth_type_trans(skb, card->dev);
+ return;
+ }
+#endif /* CONFIG_QETH_IPV6 */
+ skb->protocol = htons((hdr->flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
+ ETH_P_IP);
+ switch (hdr->flags & QETH_HDR_CAST_MASK){
+ case QETH_CAST_UNICAST:
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case QETH_CAST_MULTICAST:
+ skb->pkt_type = PACKET_MULTICAST;
+ card->stats.multicast++;
+ break;
+ case QETH_CAST_BROADCAST:
+ skb->pkt_type = PACKET_BROADCAST;
+ card->stats.multicast++;
+ break;
+ case QETH_CAST_ANYCAST:
+ case QETH_CAST_NOCAST:
+ default:
+ skb->pkt_type = PACKET_HOST;
+ }
+ if (card->options.fake_ll)
+ qeth_rebuild_skb_fake_ll(card, skb, hdr);
+ else
+ skb->mac.raw = skb->data;
+ skb->ip_summed = card->options.checksum_type;
+ if (card->options.checksum_type == HW_CHECKSUMMING){
+ if ( (hdr->ext_flags &
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ) )
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = SW_CHECKSUMMING;
+ }
+ qeth_rebuild_skb_vlan(card, skb, hdr);
+}
+
+
+static inline struct qeth_buffer_pool_entry *
+qeth_get_buffer_pool_entry(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *entry, *tmp;
+
+ QETH_DBF_TEXT(trace, 6, "gtbfplen");
+ entry = NULL;
+ list_for_each_entry_safe(entry, tmp,
+ &card->qdio.in_buf_pool.entry_list, list){
+ list_del_init(&entry->list);
+ break;
+ }
+ return entry;
+}
+
+static inline void
+qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf)
+{
+ struct qeth_buffer_pool_entry *pool_entry;
+ int i;
+
+ pool_entry = qeth_get_buffer_pool_entry(card);
+ /*
+ * since the buffer is accessed only from the input_tasklet
+ * there shouldn't be a need to synchronize; also, since we use
+ * the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off
+ * buffers
+ */
+ BUG_ON(!pool_entry);
+
+ buf->pool_entry = pool_entry;
+ for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+ buf->buffer->element[i].length = PAGE_SIZE;
+ buf->buffer->element[i].addr = pool_entry->elements[i];
+ if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1)
+ buf->buffer->element[i].flags = SBAL_FLAGS_LAST_ENTRY;
+ else
+ buf->buffer->element[i].flags = 0;
+ }
+ buf->state = QETH_QDIO_BUF_EMPTY;
+}
+
+static void
+qeth_clear_output_buffer(struct qeth_card *card,
+ struct qeth_qdio_out_buffer *buf)
+{
+ int i;
+ struct sk_buff *skb;
+
+ for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+ buf->buffer->element[i].length = 0;
+ buf->buffer->element[i].addr = NULL;
+ buf->buffer->element[i].flags = 0;
+ while ((skb = skb_dequeue(&buf->skb_list))){
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ buf->next_element_to_fill = 0;
+ buf->state = QETH_QDIO_BUF_EMPTY;
+}
+
+static inline void
+qeth_queue_input_buffer(struct qeth_card *card, int index)
+{
+ struct qeth_qdio_q *queue = card->qdio.in_q;
+ int count;
+ int i;
+ int rc;
+
+ QETH_DBF_TEXT(trace,6,"queinbuf");
+ count = (index < queue->next_buf_to_init)?
+ card->qdio.in_buf_pool.buf_count -
+ (queue->next_buf_to_init - index) :
+ card->qdio.in_buf_pool.buf_count -
+ (queue->next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index);
+ /* only requeue at a certain threshold to avoid SIGAs */
+ if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){
+ for (i = queue->next_buf_to_init;
+ i < queue->next_buf_to_init + count; ++i)
+ qeth_init_input_buffer(card,
+ &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]);
+ /*
+ * according to old code it should be avoided to requeue all
+ * 128 buffers in order to benefit from PCI avoidance.
+ * this function keeps at least one buffer (the buffer at
+ * 'index') un-requeued -> this buffer is the first buffer that
+ * will be requeued the next time
+ */
+ rc = do_QDIO(CARD_DDEV(card),
+ QDIO_FLAG_SYNC_INPUT,
+ 0, queue->next_buf_to_init, count, NULL);
+ if (rc){
+ PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
+ "return %i (device %s).\n",
+ rc, CARD_DDEV_ID(card));
+ QETH_DBF_TEXT(trace,2,"qinberr");
+ QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));
+ }
+ queue->next_buf_to_init = (queue->next_buf_to_init + count) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ }
+}
+
+static inline void
+qeth_put_buffer_pool_entry(struct qeth_card *card,
+ struct qeth_buffer_pool_entry *entry)
+{
+ QETH_DBF_TEXT(trace, 6, "ptbfplen");
+ list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
+}
+
+static void
+qeth_qdio_input_tasklet(unsigned long data)
+{
+ struct qeth_card *card = (struct qeth_card *) data;
+ int current_buf = card->qdio.in_q->next_buf_to_process;
+ struct qeth_qdio_buffer *buf;
+ struct qdio_buffer_element *element;
+ int offset;
+ struct sk_buff *skb;
+ struct qeth_hdr *hdr;
+ int rxrc;
+
+ QETH_DBF_TEXT(trace,6,"qdintlet");
+ buf = &card->qdio.in_q->bufs[current_buf];
+ while((buf->state == QETH_QDIO_BUF_PRIMED) ||
+ (buf->state == QETH_QDIO_BUF_ERROR)){
+ if (buf->state == QETH_QDIO_BUF_ERROR)
+ goto clear_buffer;
+ if (netif_queue_stopped(card->dev))
+ goto clear_buffer;
+ /* get first element of current buffer */
+ element = (struct qdio_buffer_element *)
+ &buf->buffer->element[0];
+ offset = 0;
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.bufs_rec++;
+#endif
+ while((skb = qeth_get_next_skb(card, buf->buffer, &element,
+ &offset, &hdr))){
+
+ qeth_rebuild_skb(card, skb, hdr);
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
+ card->perf_stats.inbound_cnt++;
+#endif
+ skb->dev = card->dev;
+ if (netif_queue_stopped(card->dev)) {
+ dev_kfree_skb_irq(skb);
+ card->stats.rx_dropped++;
+ } else {
+ rxrc = netif_rx(skb);
+ card->dev->last_rx = jiffies;
+ card->stats.rx_packets++;
+ card->stats.rx_bytes += skb->len;
+ }
+ }
+clear_buffer:
+ qeth_put_buffer_pool_entry(card, buf->pool_entry);
+ /* give buffer back to hardware */
+ qeth_queue_input_buffer(card, current_buf);
+ current_buf = (current_buf + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ buf = &card->qdio.in_q->bufs[current_buf];
+ }
+ /* set index for next time the tasklet is scheduled */
+ card->qdio.in_q->next_buf_to_process = current_buf;
+}
+
+static inline int
+qeth_handle_send_error(struct qeth_card *card,
+ struct qeth_qdio_out_buffer *buffer,
+ int qdio_err, int siga_err)
+{
+ int sbalf15 = buffer->buffer->element[15].flags & 0xff;
+ int cc = siga_err & 3;
+
+ QETH_DBF_TEXT(trace, 6, "hdsnderr");
+ switch (cc) {
+ case 0:
+ if (qdio_err){
+ QETH_DBF_TEXT(trace, 1,"lnkfail");
+ QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
+ QETH_DBF_TEXT_(trace,1,"%04x %02x",
+ (u16)qdio_err, (u8)sbalf15);
+ return QETH_SEND_ERROR_LINK_FAILURE;
+ }
+ return QETH_SEND_ERROR_NONE;
+ case 2:
+ if (siga_err & QDIO_SIGA_ERROR_B_BIT_SET) {
+ QETH_DBF_TEXT(trace, 1, "SIGAcc2B");
+ QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
+ return QETH_SEND_ERROR_KICK_IT;
+ }
+ if ((sbalf15 >= 15) && (sbalf15 <= 31))
+ return QETH_SEND_ERROR_RETRY;
+ return QETH_SEND_ERROR_LINK_FAILURE;
+ /* look at qdio_error and sbalf 15 */
+ case 1:
+ QETH_DBF_TEXT(trace, 1, "SIGAcc1");
+ QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
+ return QETH_SEND_ERROR_LINK_FAILURE;
+ case 3:
+ QETH_DBF_TEXT(trace, 1, "SIGAcc3");
+ QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
+ return QETH_SEND_ERROR_KICK_IT;
+ }
+ return QETH_SEND_ERROR_LINK_FAILURE;
+}
+
+static inline void
+qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
+ int index, int count)
+{
+ struct qeth_qdio_out_buffer *buf;
+ int rc;
+ int i;
+
+ QETH_DBF_TEXT(trace, 6, "flushbuf");
+
+ for (i = index; i < index + count; ++i) {
+ buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
+ buf->buffer->element[buf->next_element_to_fill - 1].flags |=
+ SBAL_FLAGS_LAST_ENTRY;
+
+ if (!queue->do_pack){
+ if ((atomic_read(&queue->used_buffers) >=
+ (QETH_HIGH_WATERMARK_PACK -
+ QETH_WATERMARK_PACK_FUZZ)) &&
+ !atomic_read(&queue->set_pci_flags_count)){
+ /* it's likely that we'll go to packing
+ * mode soon */
+ atomic_inc(&queue->set_pci_flags_count);
+ buf->buffer->element[0].flags |= 0x40;
+ }
+ } else {
+ if (!atomic_read(&queue->set_pci_flags_count)){
+ /*
+ * there's no outstanding PCI any more, so we
+ * have to request a PCI to be sure the the PCI
+ * will wake at some time in the future then we
+ * can flush packed buffers that might still be
+ * hanging around, which can happen if no
+ * further send was requested by the stack
+ */
+ atomic_inc(&queue->set_pci_flags_count);
+ buf->buffer->element[0].flags |= 0x40;
+ }
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.bufs_sent_pack++;
+#endif
+ }
+ }
+
+ queue->card->dev->trans_start = jiffies;
+ if (under_int)
+ rc = do_QDIO(CARD_DDEV(queue->card),
+ QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT,
+ queue->queue_no, index, count, NULL);
+ else
+ rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT,
+ queue->queue_no, index, count, NULL);
+ if (rc){
+ QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO "
+ "returned error (%i) on device %s.",
+ rc, CARD_DDEV_ID(queue->card));
+ QETH_DBF_TEXT(trace, 2, "flushbuf");
+ QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+ queue->card->stats.tx_errors += count;
+ return;
+ }
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.bufs_sent += count;
+ queue->card->perf_stats.outbound_cnt++;
+#endif
+}
+
+/*
+ * switches between PACKING and non-PACKING state if needed.
+ * has to be called holding queue->lock
+ */
+static inline void
+qeth_switch_packing_state(struct qeth_qdio_out_q *queue)
+{
+ struct qeth_qdio_out_buffer *buffer;
+
+ QETH_DBF_TEXT(trace, 6, "swipack");
+ if (!queue->do_pack) {
+ if (atomic_read(&queue->used_buffers)
+ >= QETH_HIGH_WATERMARK_PACK){
+ /* switch non-PACKING -> PACKING */
+ QETH_DBF_TEXT(trace, 6, "np->pack");
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.sc_dp_p++;
+#endif
+ queue->do_pack = 1;
+ }
+ } else {
+ if (atomic_read(&queue->used_buffers)
+ <= QETH_LOW_WATERMARK_PACK) {
+ /* switch PACKING -> non-PACKING */
+ QETH_DBF_TEXT(trace, 6, "pack->np");
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.sc_p_dp++;
+#endif
+ queue->do_pack = 0;
+ /* flush packing buffers */
+ buffer = &queue->bufs[queue->next_buf_to_fill];
+ BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
+ if (buffer->next_element_to_fill > 0) {
+ buffer->state = QETH_QDIO_BUF_PRIMED;
+ atomic_inc(&queue->used_buffers);
+ queue->next_buf_to_fill =
+ (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ }
+ }
+ }
+}
+
+static void
+qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
+ unsigned int qdio_error, unsigned int siga_error,
+ unsigned int __queue, int first_element, int count,
+ unsigned long card_ptr)
+{
+ struct qeth_card *card = (struct qeth_card *) card_ptr;
+ struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue];
+ struct qeth_qdio_out_buffer *buffer;
+ int i;
+
+ QETH_DBF_TEXT(trace, 6, "qdouhdl");
+ if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
+ if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
+ QETH_DBF_SPRINTF(trace, 2, "On device %s: "
+ "received active check "
+ "condition (0x%08x).",
+ CARD_BUS_ID(card), status);
+ QETH_DBF_TEXT(trace, 2, "chkcond");
+ QETH_DBF_TEXT_(trace, 2, "%08x", status);
+ netif_stop_queue(card->dev);
+ qeth_schedule_recovery(card);
+ return;
+ }
+ }
+
+ for(i = first_element; i < (first_element + count); ++i){
+ buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
+ /*we only handle the KICK_IT error by doing a recovery */
+ if (qeth_handle_send_error(card, buffer, qdio_error, siga_error)
+ == QETH_SEND_ERROR_KICK_IT){
+ netif_stop_queue(card->dev);
+ qeth_schedule_recovery(card);
+ return;
+ }
+ /* is PCI flag set on buffer? */
+ if (buffer->buffer->element[0].flags & 0x40)
+ atomic_dec(&queue->set_pci_flags_count);
+
+ qeth_clear_output_buffer(card, buffer);
+ }
+ atomic_sub(count, &queue->used_buffers);
+
+ //if (!atomic_read(&queue->set_pci_flags_count))
+ tasklet_schedule(&queue->tasklet);
+
+ netif_wake_queue(card->dev);
+}
+
+static void
+qeth_qdio_output_tasklet(unsigned long data)
+{
+ struct qeth_qdio_out_q *queue = (struct qeth_qdio_out_q *) data;
+ struct qeth_qdio_out_buffer *buffer;
+ int index;
+ int count;
+
+ QETH_DBF_TEXT(trace, 6, "outtlet");
+
+ /* flush all PRIMED buffers */
+ index = queue->next_buf_to_flush;
+ count = 0;
+ while (queue->bufs[index].state == QETH_QDIO_BUF_PRIMED) {
+ count++;
+ index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ }
+ qeth_flush_buffers(queue, 0, queue->next_buf_to_flush, count);
+ queue->next_buf_to_flush = index;
+
+ /* flush a buffer with data, if no more PCIs are
+ * outstanding */
+ if (!atomic_read(&queue->set_pci_flags_count)){
+ spin_lock(&queue->lock);
+ buffer = &queue->bufs[index];
+ if (buffer->state == QETH_QDIO_BUF_PRIMED){
+ qeth_flush_buffers(queue, 0, index, 1);
+ index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ queue->next_buf_to_flush = index;
+ } else if (buffer->next_element_to_fill > 0){
+ /* it's a packing buffer */
+ BUG_ON(index != queue->next_buf_to_fill);
+ buffer->state = QETH_QDIO_BUF_PRIMED;
+ atomic_inc(&queue->used_buffers);
+ qeth_flush_buffers(queue, 0, index, 1);
+ index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ queue->next_buf_to_flush = index;
+ queue->next_buf_to_fill = index;
+ }
+ spin_unlock(&queue->lock);
+ }
+}
+
+static char*
+qeth_create_qib_param_field(struct qeth_card *card)
+{
+ char *param_field;
+
+ param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
+ GFP_KERNEL);
+ if (!param_field)
+ return NULL;
+
+ memset(param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
+
+ param_field[0] = _ascebc['P'];
+ param_field[1] = _ascebc['C'];
+ param_field[2] = _ascebc['I'];
+ param_field[3] = _ascebc['T'];
+ *((unsigned int *) (¶m_field[4])) = QETH_PCI_THRESHOLD_A(card);
+ *((unsigned int *) (¶m_field[8])) = QETH_PCI_THRESHOLD_B(card);
+ *((unsigned int *) (¶m_field[12])) = QETH_PCI_TIMER_VALUE(card);
+
+ return param_field;
+}
+
+static void
+qeth_initialize_working_pool_list(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *entry;
+
+ QETH_DBF_TEXT(trace,5,"inwrklst");
+
+ list_for_each_entry(entry,
+ &card->qdio.init_pool.entry_list, init_list) {
+ qeth_put_buffer_pool_entry(card,entry);
+ }
+}
+
+static void
+qeth_clear_working_pool_list(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry, *tmp;
+
+ QETH_DBF_TEXT(trace,5,"clwrklst");
+ list_for_each_entry_safe(pool_entry, tmp,
+ &card->qdio.in_buf_pool.entry_list, list){
+ list_del(&pool_entry->list);
+ }
+}
+
+static void
+qeth_free_buffer_pool(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry, *tmp;
+ int i=0;
+ QETH_DBF_TEXT(trace,5,"freepool");
+ list_for_each_entry_safe(pool_entry, tmp,
+ &card->qdio.init_pool.entry_list, init_list){
+ for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i)
+ free_page((unsigned long)pool_entry->elements[i]);
+ list_del(&pool_entry->init_list);
+ kfree(pool_entry);
+ }
+}
+
+static int
+qeth_alloc_buffer_pool(struct qeth_card *card)
+{
+ struct qeth_buffer_pool_entry *pool_entry;
+ void *ptr;
+ int i, j;
+
+ for (i = 0; i < card->qdio.init_pool.buf_count; ++i){
+ pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL);
+ if (!pool_entry){
+ qeth_free_buffer_pool(card);
+ return -ENOMEM;
+ }
+ for(j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j){
+ ptr = (void *) __get_free_page(GFP_KERNEL);
+ if (!ptr) {
+ while (j > 0)
+ free_page((unsigned long)
+ pool_entry->elements[--j]);
+ kfree(pool_entry);
+ qeth_free_buffer_pool(card);
+ return -ENOMEM;
+ }
+ pool_entry->elements[j] = ptr;
+ }
+ list_add(&pool_entry->init_list,
+ &card->qdio.init_pool.entry_list);
+ list_add(&pool_entry->list,
+ &card->qdio.in_buf_pool.entry_list);
+ }
+ return 0;
+}
+
+static int
+qeth_alloc_qdio_buffers(struct qeth_card *card)
+{
+ int i, j;
+
+ QETH_DBF_TEXT(setup, 2, "allcqdbf");
+
+ if (card->qdio.state == QETH_QDIO_ALLOCATED) {
+ qeth_initialize_working_pool_list(card);
+ return 0;
+ }
+ card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), GFP_KERNEL);
+ if (!card->qdio.in_q)
+ return - ENOMEM;
+ QETH_DBF_TEXT(setup, 2, "inq");
+ QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *));
+ memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q));
+ /* give inbound qeth_qdio_buffers their qdio_buffers */
+ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i)
+ card->qdio.in_q->bufs[i].buffer =
+ &card->qdio.in_q->qdio_bufs[i];
+ /* inbound buffer pool */
+ if (qeth_alloc_buffer_pool(card)){
+ kfree(card->qdio.in_q);
+ return -ENOMEM;
+ }
+ /* outbound */
+ card->qdio.out_qs =
+ kmalloc(card->qdio.no_out_queues *
+ sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
+ if (!card->qdio.out_qs){
+ qeth_free_buffer_pool(card);
+ return -ENOMEM;
+ }
+ for (i = 0; i < card->qdio.no_out_queues; ++i){
+ card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
+ GFP_KERNEL);
+ if (!card->qdio.out_qs[i]){
+ while (i > 0)
+ kfree(card->qdio.out_qs[--i]);
+ kfree(card->qdio.out_qs);
+ return -ENOMEM;
+ }
+ QETH_DBF_TEXT_(setup, 2, "outq %i", i);
+ QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *));
+ memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
+ card->qdio.out_qs[i]->queue_no = i;
+ /* give inbound qeth_qdio_buffers their qdio_buffers */
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
+ card->qdio.out_qs[i]->bufs[j].buffer =
+ &card->qdio.out_qs[i]->qdio_bufs[j];
+ skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j].
+ skb_list);
+ }
+ }
+ card->qdio.state = QETH_QDIO_ALLOCATED;
+ return 0;
+}
+
+static void
+qeth_free_qdio_buffers(struct qeth_card *card)
+{
+ int i, j;
+
+ QETH_DBF_TEXT(trace, 2, "freeqdbf");
+ if (card->qdio.state == QETH_QDIO_UNINITIALIZED)
+ return;
+ kfree(card->qdio.in_q);
+ /* inbound buffer pool */
+ qeth_free_buffer_pool(card);
+ /* free outbound qdio_qs */
+ for (i = 0; i < card->qdio.no_out_queues; ++i){
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
+ qeth_clear_output_buffer(card, &card->qdio.
+ out_qs[i]->bufs[j]);
+ kfree(card->qdio.out_qs[i]);
+ }
+ kfree(card->qdio.out_qs);
+ card->qdio.state = QETH_QDIO_UNINITIALIZED;
+}
+
+static void
+qeth_clear_qdio_buffers(struct qeth_card *card)
+{
+ int i, j;
+
+ QETH_DBF_TEXT(trace, 2, "clearqdbf");
+ /* clear outbound buffers to free skbs */
+ for (i = 0; i < card->qdio.no_out_queues; ++i)
+ if (card->qdio.out_qs[i]){
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
+ qeth_clear_output_buffer(card, &card->qdio.
+ out_qs[i]->bufs[j]);
+ }
+}
+
+static void
+qeth_init_qdio_info(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(setup, 4, "intqdinf");
+ card->qdio.state = QETH_QDIO_UNINITIALIZED;
+ /* inbound */
+ card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
+ card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
+ card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;
+ INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);
+ INIT_LIST_HEAD(&card->qdio.init_pool.entry_list);
+ card->qdio.in_tasklet.data = (unsigned long) card;
+ card->qdio.in_tasklet.func = qeth_qdio_input_tasklet;
+ /* outbound */
+ card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+}
+
+static int
+qeth_init_qdio_queues(struct qeth_card *card)
+{
+ int i, j;
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "initqdqs");
+
+ /* inbound queue */
+ memset(card->qdio.in_q->qdio_bufs, 0,
+ QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));
+ card->qdio.in_q->next_buf_to_process = 0;
+ card->qdio.in_q->next_buf_to_init = 0;
+ /*give only as many buffers to hardware as we have buffer pool entries*/
+ for (i = 0; i < card->qdio.in_buf_pool.buf_count; ++i)
+ qeth_init_input_buffer(card, &card->qdio.in_q->bufs[i]);
+ card->qdio.in_q->next_buf_to_init = card->qdio.in_buf_pool.buf_count;
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0,
+ card->qdio.in_buf_pool.buf_count, NULL);
+ if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ return rc;
+ }
+ rc = qdio_synchronize(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0);
+ if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ return rc;
+ }
+ /* outbound queue */
+ for (i = 0; i < card->qdio.no_out_queues; ++i){
+ memset(card->qdio.out_qs[i]->qdio_bufs, 0,
+ QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
+ qeth_clear_output_buffer(card, &card->qdio.
+ out_qs[i]->bufs[j]);
+ }
+ card->qdio.out_qs[i]->card = card;
+ card->qdio.out_qs[i]->next_buf_to_fill = 0;
+ card->qdio.out_qs[i]->next_buf_to_flush = 0;
+ card->qdio.out_qs[i]->do_pack = 0;
+ atomic_set(&card->qdio.out_qs[i]->used_buffers,0);
+ atomic_set(&card->qdio.out_qs[i]->set_pci_flags_count, 0);
+ card->qdio.out_qs[i]->tasklet.data =
+ (unsigned long) card->qdio.out_qs[i];
+ card->qdio.out_qs[i]->tasklet.func = qeth_qdio_output_tasklet;
+ spin_lock_init(&card->qdio.out_qs[i]->lock);
+ }
+ return 0;
+}
+
+static int
+qeth_qdio_establish(struct qeth_card *card)
+{
+ struct qdio_initialize init_data;
+ char *qib_param_field;
+ struct qdio_buffer **in_sbal_ptrs;
+ struct qdio_buffer **out_sbal_ptrs;
+ int i, j, k;
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "qdioest");
+ qib_param_field = qeth_create_qib_param_field(card);
+ if (!qib_param_field)
+ return -ENOMEM;
+
+ in_sbal_ptrs = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),
+ GFP_KERNEL);
+ if (!in_sbal_ptrs) {
+ kfree(qib_param_field);
+ return -ENOMEM;
+ }
+ for(i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i)
+ in_sbal_ptrs[i] = (struct qdio_buffer *)
+ virt_to_phys(card->qdio.in_q->bufs[i].buffer);
+
+ out_sbal_ptrs =
+ kmalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q *
+ sizeof(void *), GFP_KERNEL);
+ if (!out_sbal_ptrs) {
+ kfree(in_sbal_ptrs);
+ kfree(qib_param_field);
+ return -ENOMEM;
+ }
+ for(i = 0, k = 0; i < card->qdio.no_out_queues; ++i)
+ for(j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k){
+ out_sbal_ptrs[k] = (struct qdio_buffer *)
+ virt_to_phys(card->qdio.out_qs[i]->
+ bufs[j].buffer);
+ }
+
+ memset(&init_data, 0, sizeof(struct qdio_initialize));
+ init_data.cdev = CARD_DDEV(card);
+ init_data.q_format = qeth_get_qdio_q_format(card);
+ init_data.qib_param_field_format = 0;
+ init_data.qib_param_field = qib_param_field;
+ init_data.min_input_threshold = QETH_MIN_INPUT_THRESHOLD;
+ init_data.max_input_threshold = QETH_MAX_INPUT_THRESHOLD;
+ init_data.min_output_threshold = QETH_MIN_OUTPUT_THRESHOLD;
+ init_data.max_output_threshold = QETH_MAX_OUTPUT_THRESHOLD;
+ init_data.no_input_qs = 1;
+ init_data.no_output_qs = card->qdio.no_out_queues;
+ init_data.input_handler = (qdio_handler_t *)
+ qeth_qdio_input_handler;
+ init_data.output_handler = (qdio_handler_t *)
+ qeth_qdio_output_handler;
+ init_data.int_parm = (unsigned long) card;
+ init_data.flags = QDIO_INBOUND_0COPY_SBALS |
+ QDIO_OUTBOUND_0COPY_SBALS |
+ QDIO_USE_OUTBOUND_PCIS;
+ init_data.input_sbal_addr_array = (void **) in_sbal_ptrs;
+ init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
+
+ if (!(rc = qdio_initialize(&init_data)))
+ card->qdio.state = QETH_QDIO_ESTABLISHED;
+
+ kfree(out_sbal_ptrs);
+ kfree(in_sbal_ptrs);
+ kfree(qib_param_field);
+ return rc;
+}
+
+static int
+qeth_qdio_activate(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(setup,3,"qdioact");
+ return qdio_activate(CARD_DDEV(card), 0);
+}
+
+static int
+qeth_clear_channel(struct qeth_channel *channel)
+{
+ unsigned long flags;
+ struct qeth_card *card;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"clearch");
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+
+ if (rc)
+ return rc;
+ rc = wait_event_interruptible_timeout(card->wait_q,
+ channel->state==CH_STATE_STOPPED, QETH_TIMEOUT);
+ if (rc == -ERESTARTSYS)
+ return rc;
+ if (channel->state != CH_STATE_STOPPED)
+ return -ETIME;
+ channel->state = CH_STATE_DOWN;
+ return 0;
+}
+
+static int
+qeth_halt_channel(struct qeth_channel *channel)
+{
+ unsigned long flags;
+ struct qeth_card *card;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"haltch");
+ card = CARD_FROM_CDEV(channel->ccwdev);
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+
+ if (rc)
+ return rc;
+ rc = wait_event_interruptible_timeout(card->wait_q,
+ channel->state==CH_STATE_HALTED, QETH_TIMEOUT);
+ if (rc == -ERESTARTSYS)
+ return rc;
+ if (channel->state != CH_STATE_HALTED)
+ return -ETIME;
+ return 0;
+}
+
+static int
+qeth_halt_channels(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"haltchs");
+ if ((rc = qeth_halt_channel(&card->read)))
+ return rc;
+ if ((rc = qeth_halt_channel(&card->write)))
+ return rc;
+ return qeth_halt_channel(&card->data);
+}
+static int
+qeth_clear_channels(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"clearchs");
+ if ((rc = qeth_clear_channel(&card->read)))
+ return rc;
+ if ((rc = qeth_clear_channel(&card->write)))
+ return rc;
+ return qeth_clear_channel(&card->data);
+}
+
+static int
+qeth_clear_halt_card(struct qeth_card *card, int halt)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"clhacrd");
+ QETH_DBF_HEX(trace, 3, &card, sizeof(void *));
+
+ if (halt)
+ rc = qeth_halt_channels(card);
+ if (rc)
+ return rc;
+ return qeth_clear_channels(card);
+}
+
+static int
+qeth_qdio_clear_card(struct qeth_card *card, int use_halt)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"qdioclr");
+ if (card->qdio.state == QETH_QDIO_ESTABLISHED){
+ qdio_cleanup(CARD_DDEV(card),
+ (card->info.type == QETH_CARD_TYPE_IQD) ?
+ QDIO_FLAG_CLEANUP_USING_HALT :
+ QDIO_FLAG_CLEANUP_USING_CLEAR);
+ card->qdio.state = QETH_QDIO_ALLOCATED;
+ }
+ rc = qeth_clear_halt_card(card, use_halt);
+ card->state = CARD_STATE_DOWN;
+ return rc;
+}
+
+static int
+qeth_dm_act(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(setup,2,"dmact");
+
+ iob = qeth_wait_for_buffer(&card->write);
+ memcpy(iob->data, DM_ACT, DM_ACT_SIZE);
+
+ memcpy(QETH_DM_ACT_DEST_ADDR(iob->data),
+ &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
+ memcpy(QETH_DM_ACT_CONNECTION_TOKEN(iob->data),
+ &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+ rc = qeth_send_control_data(card, DM_ACT_SIZE, iob, NULL, NULL);
+ return rc;
+}
+
+static int
+qeth_mpc_initialize(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(setup,2,"mpcinit");
+
+ if ((rc = qeth_issue_next_read(card))){
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_cm_enable(card))){
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_cm_setup(card))){
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_ulp_enable(card))){
+ QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_ulp_setup(card))){
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_alloc_qdio_buffers(card))){
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ return rc;
+ }
+ if ((rc = qeth_qdio_establish(card))){
+ QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+ qeth_free_qdio_buffers(card);
+ goto out_qdio;
+ }
+ if ((rc = qeth_qdio_activate(card))){
+ QETH_DBF_TEXT_(setup, 2, "7err%d", rc);
+ goto out_qdio;
+ }
+ if ((rc = qeth_dm_act(card))){
+ QETH_DBF_TEXT_(setup, 2, "8err%d", rc);
+ goto out_qdio;
+ }
+
+ return 0;
+out_qdio:
+ qeth_qdio_clear_card(card, card->info.type==QETH_CARD_TYPE_OSAE);
+ return rc;
+}
+
+static void
+qeth_set_device_name(struct qeth_card *card)
+{
+ char buf[IF_NAME_LEN];
+
+ memset(buf, 0, IF_NAME_LEN);
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ sprintf(buf,"hsi%d", atomic_read(&qeth_hsi_count));
+ atomic_inc(&qeth_hsi_count);
+ memcpy(card->dev->name,buf,IF_NAME_LEN);
+ }
+
+}
+
+static struct net_device *
+qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
+{
+ struct net_device *dev = NULL;
+
+ switch (type) {
+ case QETH_CARD_TYPE_OSAE:
+ switch (linktype) {
+ case QETH_LINK_TYPE_LANE_TR:
+ case QETH_LINK_TYPE_HSTR:
+#ifdef CONFIG_TR
+ dev = alloc_trdev(0);
+#endif /* CONFIG_TR */
+ break;
+ default:
+ dev = alloc_etherdev(0);
+ }
+ break;
+ case QETH_CARD_TYPE_IQD:
+ default:
+ dev = alloc_etherdev(0);
+ }
+ return dev;
+}
+
+static inline int
+qeth_send_packet(struct qeth_card *, struct sk_buff *);
+
+static int
+qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int rc;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace, 6, "hrdstxmi");
+ card = (struct qeth_card *)dev->priv;
+ if (skb==NULL) {
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ return -EIO;
+ }
+ if (card->state != CARD_STATE_UP_LAN_ONLINE) {
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ card->stats.tx_carrier_errors++;
+ return -EIO;
+ }
+ if (netif_queue_stopped(dev) ) {
+ card->stats.tx_dropped++;
+ return -EBUSY;
+ }
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_start_time = qeth_get_micros();
+#endif
+ /*
+ * dev_queue_xmit should ensure that we are called packet
+ * after packet
+ */
+ netif_stop_queue(dev);
+ if (!(rc = qeth_send_packet(card, skb)))
+ netif_wake_queue(dev);
+
+ return rc;
+}
+
+static int
+qeth_verify_vlan_dev(struct net_device *dev, struct qeth_card *card)
+{
+ int rc = 0;
+#ifdef CONFIG_QETH_VLAN
+ struct vlan_group *vg;
+ int i;
+
+ if (!(vg = card->vlangrp))
+ return rc;
+
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++){
+ if (vg->vlan_devices[i] == dev){
+ rc = QETH_VLAN_CARD;
+ break;
+ }
+ }
+#endif
+ return rc;
+}
+
+static int
+qeth_verify_dev(struct net_device *dev)
+{
+ struct qeth_card *card;
+ unsigned long flags;
+ int rc = 0;
+
+ read_lock_irqsave(&qeth_card_list.rwlock, flags);
+ list_for_each_entry(card, &qeth_card_list.list, list){
+ if (card->dev == dev){
+ rc = QETH_REAL_CARD;
+ break;
+ }
+ rc = qeth_verify_vlan_dev(dev, card);
+ if (rc)
+ break;
+ }
+ read_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+
+ return rc;
+}
+
+static struct qeth_card *
+qeth_get_card_from_dev(struct net_device *dev)
+{
+ struct qeth_card *card = NULL;
+ int rc;
+
+ rc = qeth_verify_dev(dev);
+ if (rc == QETH_REAL_CARD)
+ card = (struct qeth_card *)dev->priv;
+ else if (rc == QETH_VLAN_CARD)
+ card = (struct qeth_card *)
+ VLAN_DEV_INFO(dev)->real_dev->priv;
+
+ QETH_DBF_TEXT_(trace, 4, "%d", rc);
+ return card ;
+}
+
+static void
+qeth_tx_timeout(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ card = (struct qeth_card *) dev->priv;
+ card->stats.tx_errors++;
+ qeth_schedule_recovery(card);
+}
+
+static int
+qeth_open(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace, 4, "qethopen");
+
+ card = (struct qeth_card *) dev->priv;
+
+ if ((card->state != CARD_STATE_SOFTSETUP) &&
+ (card->state != CARD_STATE_UP_LAN_OFFLINE))
+ return -ENODEV;
+ if (!card->lan_online){
+ card->state = CARD_STATE_UP_LAN_OFFLINE;
+ return -EIO;
+ }
+
+ card->dev->flags |= IFF_UP;
+ netif_start_queue(dev);
+ card->data.state = CH_STATE_UP;
+ card->state = CARD_STATE_UP_LAN_ONLINE;
+ return 0;
+}
+
+static int
+qeth_stop(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace, 4, "qethstop");
+
+ card = (struct qeth_card *) dev->priv;
+
+ netif_stop_queue(dev);
+ card->dev->flags &= ~IFF_UP;
+ if ((card->state == CARD_STATE_UP_LAN_ONLINE) ||
+ (card->state == CARD_STATE_UP_LAN_OFFLINE))
+ card->state = CARD_STATE_SOFTSETUP;
+ return 0;
+}
+
+static inline int
+qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
+{
+ int cast_type = RTN_UNSPEC;
+
+ if (skb->dst && skb->dst->neighbour){
+ cast_type = skb->dst->neighbour->type;
+ if ((cast_type == RTN_BROADCAST) ||
+ (cast_type == RTN_MULTICAST) ||
+ (cast_type == RTN_ANYCAST))
+ return cast_type;
+ else
+ return RTN_UNSPEC;
+ }
+ /* try something else */
+ if (skb->protocol == ETH_P_IPV6)
+ return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0;
+ else if (skb->protocol == ETH_P_IP)
+ return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
+ /* ... */
+ if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6))
+ return RTN_BROADCAST;
+ else {
+ u16 hdr_mac;
+
+ hdr_mac = *((u16 *)skb->nh.raw);
+ /* tr multicast? */
+ switch (card->info.link_type) {
+ case QETH_LINK_TYPE_HSTR:
+ case QETH_LINK_TYPE_LANE_TR:
+ if ((hdr_mac == QETH_TR_MAC_NC) ||
+ (hdr_mac == QETH_TR_MAC_C))
+ return RTN_MULTICAST;
+ /* eth or so multicast? */
+ default:
+ if ((hdr_mac == QETH_ETH_MAC_V4) ||
+ (hdr_mac == QETH_ETH_MAC_V6))
+ return RTN_MULTICAST;
+ }
+ }
+ return cast_type;
+}
+
+static inline int
+qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
+ int ipv, int cast_type)
+{
+ if (!ipv && (card->info.type == QETH_CARD_TYPE_OSAE))
+ return card->qdio.default_out_queue;
+ switch (card->qdio.no_out_queues) {
+ case 4:
+ if (cast_type && card->info.is_multicast_different)
+ return card->info.is_multicast_different &
+ (card->qdio.no_out_queues - 1);
+ if (card->qdio.do_prio_queueing && (ipv == 4)) {
+ if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){
+ if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT)
+ return 3;
+ if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY)
+ return 2;
+ if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT)
+ return 1;
+ if (skb->nh.iph->tos & IP_TOS_LOWDELAY)
+ return 0;
+ }
+ if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC)
+ return 3 - (skb->nh.iph->tos >> 6);
+ } else if (card->qdio.do_prio_queueing && (ipv == 6)) {
+ /* TODO: IPv6!!! */
+ }
+ return card->qdio.default_out_queue;
+ default:
+ return 0;
+ }
+}
+
+static inline int
+qeth_get_ip_version(struct sk_buff *skb)
+{
+ switch (skb->protocol) {
+ case ETH_P_IPV6:
+ return 6;
+ case ETH_P_IP:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+static inline int
+qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
+ struct qeth_hdr **hdr, int ipv)
+{
+ struct sk_buff *new_skb;
+#ifdef CONFIG_QETH_VLAN
+ u16 *tag;
+#endif
+
+ QETH_DBF_TEXT(trace, 6, "prepskb");
+ if (skb_headroom(*skb) < sizeof(struct qeth_hdr)){
+ new_skb = skb_realloc_headroom(*skb, sizeof(struct qeth_hdr));
+ if (!new_skb) {
+ PRINT_ERR("qeth_prepare_skb: could "
+ "not realloc headroom for qeth_hdr "
+ "on interface %s", card->info.if_name);
+ return -ENOMEM;
+ }
+ *skb = new_skb;
+ }
+#ifdef CONFIG_QETH_VLAN
+ if (card->vlangrp && vlan_tx_tag_present(*skb) && (ipv == 6)){
+ /*
+ * Move the mac addresses (6 bytes src, 6 bytes dest)
+ * to the beginning of the new header. We are using three
+ * memcpys instead of one memmove to save cycles.
+ */
+ skb_push(*skb, VLAN_HLEN);
+ memcpy((*skb)->data, (*skb)->data + 4, 4);
+ memcpy((*skb)->data + 4, (*skb)->data + 8, 4);
+ memcpy((*skb)->data + 8, (*skb)->data + 12, 4);
+ tag = (u16 *) (*skb)->data + 12;
+ /*
+ * first two bytes = ETH_P_8021Q (0x8100)
+ * second two bytes = VLANID
+ */
+ *tag = __constant_htons(ETH_P_8021Q);
+ *(tag + 1) = vlan_tx_tag_get(*skb);
+ *(tag + 1) = htons(*(tag + 1));
+ }
+#endif
+ *hdr = (struct qeth_hdr *) skb_push(*skb, sizeof(struct qeth_hdr));
+ /*
+ * sanity check, the Linux memory allocation scheme should
+ * never present us cases like this one (the 32bytes header plus
+ * the first 40 bytes of the paket cross a 4k boundary)
+ */
+ if ((((unsigned long) *hdr) & (~(PAGE_SIZE - 1))) !=
+ (((unsigned long) *hdr + sizeof(struct qeth_hdr) +
+ QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
+ PRINT_ERR("qeth_prepare_skb: misaligned "
+ "packet on interface %s. Discarded.",
+ card->info.if_name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline u8
+qeth_get_qeth_hdr_flags4(int cast_type)
+{
+ if (cast_type == RTN_MULTICAST)
+ return QETH_CAST_MULTICAST;
+ if (cast_type == RTN_BROADCAST)
+ return QETH_CAST_BROADCAST;
+ return QETH_CAST_UNICAST;
+}
+
+static inline u8
+qeth_get_qeth_hdr_flags6(int cast_type)
+{
+ u8 ct = QETH_HDR_PASSTHRU | QETH_HDR_IPV6;
+ if (cast_type == RTN_MULTICAST)
+ return ct | QETH_CAST_MULTICAST;
+ if (cast_type == RTN_ANYCAST)
+ return ct | QETH_CAST_ANYCAST;
+ if (cast_type == RTN_BROADCAST)
+ return ct | QETH_CAST_BROADCAST;
+ return ct | QETH_CAST_UNICAST;
+}
+
+static inline void
+qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+ struct sk_buff *skb, int ipv, int cast_type)
+{
+ hdr->id = 1;
+ hdr->ext_flags = 0;
+
+ QETH_DBF_TEXT(trace, 6, "fillhdr");
+#ifdef CONFIG_QETH_VLAN
+ /*
+ * before we're going to overwrite this location with next hop ip.
+ * v6 uses passthrough, v4 sets the tag in the QDIO header.
+ */
+ if (card->vlangrp && vlan_tx_tag_present(skb)) {
+ hdr->ext_flags = (ipv == 4)? QETH_EXT_HDR_VLAN_FRAME :
+ QETH_EXT_HDR_INCLUDE_VLAN_TAG;
+ hdr->vlan_id = vlan_tx_tag_get(skb);
+ }
+#endif /* CONFIG_QETH_VLAN */
+ hdr->length = skb->len - sizeof(struct qeth_hdr);
+ if (ipv == 4) { /* IPv4 */
+ hdr->flags = qeth_get_qeth_hdr_flags4(cast_type);
+ memset(hdr->dest_addr, 0, 12);
+ if ((skb->dst) && (skb->dst->neighbour)) {
+ *((u32 *) (&hdr->dest_addr[12])) =
+ *((u32 *) skb->dst->neighbour->primary_key);
+ } else {
+ /* fill in destination address used in ip header */
+ *((u32 *) (&hdr->dest_addr[12])) = skb->nh.iph->daddr;
+ }
+ } else if (ipv == 6) { /* IPv6 or passthru */
+ hdr->flags = qeth_get_qeth_hdr_flags6(cast_type);
+ if ((skb->dst) && (skb->dst->neighbour)) {
+ memcpy(hdr->dest_addr,
+ skb->dst->neighbour->primary_key, 16);
+ } else {
+ /* fill in destination address used in ip header */
+ memcpy(hdr->dest_addr, &skb->nh.ipv6h->daddr, 16);
+ }
+ } else { /* passthrough */
+ if (!memcmp(skb->data + sizeof(struct qeth_hdr),
+ skb->dev->broadcast, 6)) { /* broadcast? */
+ hdr->flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
+ } else {
+ hdr->flags = (cast_type == RTN_MULTICAST) ?
+ QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
+ QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
+ }
+ }
+}
+
+static inline int
+qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf,
+ char *data, struct sk_buff *skb)
+{
+ struct qdio_buffer *buffer;
+ int length = skb->len;
+ int length_here;
+ int element;
+ int first_lap = 1;
+
+ QETH_DBF_TEXT(trace, 6, "qdfillbf");
+
+ buffer = buf->buffer;
+ atomic_inc(&skb->users);
+ skb_queue_tail(&buf->skb_list, skb);
+ element = buf->next_element_to_fill;
+ while (length > 0) {
+ /* length_here is the remaining amount of data in this page */
+ length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
+ if (length < length_here)
+ length_here = length;
+ buffer->element[element].addr = data;
+ buffer->element[element].length = length_here;
+ length -= length_here;
+ if (!length){
+ if (first_lap)
+ buffer->element[element].flags = 0;
+ else
+ buffer->element[element].flags =
+ SBAL_FLAGS_LAST_FRAG;
+ } else {
+ if (first_lap)
+ buffer->element[element].flags =
+ SBAL_FLAGS_FIRST_FRAG;
+ else
+ buffer->element[element].flags =
+ SBAL_FLAGS_MIDDLE_FRAG;
+ }
+ data += length_here;
+ element++;
+ first_lap = 0;
+ }
+ buf->next_element_to_fill = element;
+ if (!queue->do_pack) {
+ QETH_DBF_TEXT(trace, 6, "fillbfnp");
+ /* set state to PRIMED -> will be flushed */
+ buf->state = QETH_QDIO_BUF_PRIMED;
+ } else {
+ QETH_DBF_TEXT(trace, 6, "fillbfpa");
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.skbs_sent_pack++;
+#endif
+ if (buf->next_element_to_fill >=
+ QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
+ /*
+ * packed buffer if full -> set state PRIMED
+ * -> will be flushed
+ */
+ buf->state = QETH_QDIO_BUF_PRIMED;
+ }
+ }
+ return 0;
+}
+
+static inline int
+qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_qdio_out_q *queue, int ipv,
+ int cast_type)
+{
+ struct qeth_hdr *hdr;
+ struct qeth_qdio_out_buffer *buffer;
+ int elements_needed;
+ int rc;
+
+ QETH_DBF_TEXT(trace, 6, "dosndpkt");
+
+ if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
+ QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
+ return rc;
+ }
+ qeth_fill_header(card, hdr, skb, ipv, cast_type);
+ elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len)
+ >> PAGE_SHIFT);
+ if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
+ PRINT_ERR("qeth_do_send_packet: invalid size of "
+ "IP packet. Discarded.");
+ return -EINVAL;
+ }
+
+ spin_lock(&queue->lock);
+ /* check if we need to switch packing state of this queue */
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ qeth_switch_packing_state(queue);
+ buffer = &queue->bufs[queue->next_buf_to_fill];
+ BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
+ if (queue->do_pack){
+ /* does packet fit in current buffer? */
+ if((QETH_MAX_BUFFER_ELEMENTS(card) - buffer->next_element_to_fill)
+ < elements_needed){
+ /* ... no -> set state PRIMED */
+ buffer->state = QETH_QDIO_BUF_PRIMED;
+ atomic_inc(&queue->used_buffers);
+ queue->next_buf_to_fill =
+ (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ buffer = &queue->bufs[queue->next_buf_to_fill];
+ }
+ }
+
+ rc = qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
+ if (rc) {
+ PRINT_WARN("qeth_do_send_packet: error during "
+ "qeth_fill_buffer.");
+ card->stats.tx_dropped++;
+ spin_unlock(&queue->lock);
+ return rc;
+ }
+ if (buffer->state == QETH_QDIO_BUF_PRIMED){
+ /* next time fill the next buffer */
+ atomic_inc(&queue->used_buffers);
+ queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ }
+ spin_unlock(&queue->lock);
+
+ tasklet_schedule(&queue->tasklet);
+
+ return rc;
+}
+
+static inline int
+qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
+{
+ int ipv;
+ int cast_type;
+ struct qeth_qdio_out_q *queue;
+ int rc;
+
+ QETH_DBF_TEXT(trace, 6, "sendpkt");
+
+ ipv = qeth_get_ip_version(skb);
+ cast_type = qeth_get_cast_type(card, skb);
+ queue = card->qdio.out_qs
+ [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+ /* do we have empty buffers? */
+ rc = (atomic_read(&queue->used_buffers) >=
+ QDIO_MAX_BUFFERS_PER_Q - 1) ? -EBUSY : 0;
+ if (rc) {
+ card->stats.tx_dropped++;
+ QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
+ return rc;
+ }
+
+ rc = qeth_do_send_packet(card, skb, queue, ipv, cast_type);
+
+ if (!rc){
+ card->stats.tx_packets++;
+ card->stats.tx_bytes += skb->len;
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
+#endif
+ }
+ return rc;
+}
+
+static int
+qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
+{
+ struct qeth_card *card = (struct qeth_card *) dev->priv;
+ int rc = 0;
+
+ switch(regnum){
+ case MII_BMCR: /* Basic mode control register */
+ rc = BMCR_FULLDPLX;
+ if(card->info.link_type != QETH_LINK_TYPE_GBIT_ETH)
+ rc |= BMCR_SPEED100;
+ break;
+ case MII_BMSR: /* Basic mode status register */
+ rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS |
+ BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL |
+ BMSR_100BASE4;
+ break;
+ case MII_PHYSID1: /* PHYS ID 1 */
+ rc = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 8) |
+ dev->dev_addr[2];
+ rc = (rc >> 5) & 0xFFFF;
+ break;
+ case MII_PHYSID2: /* PHYS ID 2 */
+ rc = (dev->dev_addr[2] << 10) & 0xFFFF;
+ break;
+ case MII_ADVERTISE: /* Advertisement control reg */
+ rc = ADVERTISE_ALL;
+ break;
+ case MII_LPA: /* Link partner ability reg */
+ rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL |
+ LPA_100BASE4 | LPA_LPACK;
+ break;
+ case MII_EXPANSION: /* Expansion register */
+ break;
+ case MII_DCOUNTER: /* disconnect counter */
+ break;
+ case MII_FCSCOUNTER: /* false carrier counter */
+ break;
+ case MII_NWAYTEST: /* N-way auto-neg test register */
+ break;
+ case MII_RERRCOUNTER: /* rx error counter */
+ rc = card->stats.rx_errors;
+ break;
+ case MII_SREVISION: /* silicon revision */
+ break;
+ case MII_RESV1: /* reserved 1 */
+ break;
+ case MII_LBRERROR: /* loopback, rx, bypass error */
+ break;
+ case MII_PHYADDR: /* physical address */
+ break;
+ case MII_RESV2: /* reserved 2 */
+ break;
+ case MII_TPISTATUS: /* TPI status for 10mbps */
+ break;
+ case MII_NCONFIG: /* network interface config */
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static void
+qeth_mdio_write(struct net_device *dev, int phy_id, int regnum, int value)
+{
+ switch(regnum){
+ case MII_BMCR: /* Basic mode control register */
+ case MII_BMSR: /* Basic mode status register */
+ case MII_PHYSID1: /* PHYS ID 1 */
+ case MII_PHYSID2: /* PHYS ID 2 */
+ case MII_ADVERTISE: /* Advertisement control reg */
+ case MII_LPA: /* Link partner ability reg */
+ case MII_EXPANSION: /* Expansion register */
+ case MII_DCOUNTER: /* disconnect counter */
+ case MII_FCSCOUNTER: /* false carrier counter */
+ case MII_NWAYTEST: /* N-way auto-neg test register */
+ case MII_RERRCOUNTER: /* rx error counter */
+ case MII_SREVISION: /* silicon revision */
+ case MII_RESV1: /* reserved 1 */
+ case MII_LBRERROR: /* loopback, rx, bypass error */
+ case MII_PHYADDR: /* physical address */
+ case MII_RESV2: /* reserved 2 */
+ case MII_TPISTATUS: /* TPI status for 10mbps */
+ case MII_NCONFIG: /* network interface config */
+ default:
+ break;
+ }
+}
+
+static inline const char *
+qeth_arp_get_error_cause(int *rc)
+{
+ switch (*rc) {
+ case QETH_IPA_ARP_RC_FAILED:
+ *rc = -EIO;
+ return "operation failed";
+ case QETH_IPA_ARP_RC_NOTSUPP:
+ *rc = -EOPNOTSUPP;
+ return "operation not supported";
+ case QETH_IPA_ARP_RC_OUT_OF_RANGE:
+ *rc = -EINVAL;
+ return "argument out of range";
+ case QETH_IPA_ARP_RC_Q_NOTSUPP:
+ *rc = -EOPNOTSUPP;
+ return "query operation not supported";
+ case QETH_IPA_ARP_RC_Q_NO_DATA:
+ *rc = -ENOENT;
+ return "no query data available";
+ default:
+ return "unknown error";
+ }
+}
+
+static int
+qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs,
+ __u16, long);
+
+static int
+qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
+{
+ int tmp;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"arpstnoe");
+
+ /* TODO: really not supported by GuestLAN? */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
+ no_entries);
+ if (rc) {
+ tmp = rc;
+ PRINT_WARN("Could not set number of ARP entries on %s: "
+ "%s (0x%x)\n",
+ card->info.if_name, qeth_arp_get_error_cause(&rc),
+ tmp);
+ }
+ return rc;
+}
+
+static int
+qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_arp_cmd *cmd;
+ struct qeth_arp_query_data *qdata;
+ struct qeth_arp_query_info *qinfo;
+ int entry_size;
+ int i;
+
+ QETH_DBF_TEXT(trace,4,"arpquecb");
+
+ qinfo = (struct qeth_arp_query_info *) reply->param;
+ cmd = (struct qeth_ipa_arp_cmd *) data;
+ if (cmd->ihdr.return_code) {
+ QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->ihdr.return_code);
+ return 0;
+ }
+ if (cmd->shdr.return_code) {
+ cmd->ihdr.return_code = cmd->shdr.return_code;
+ QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->ihdr.return_code);
+ return 0;
+ }
+ qdata = &cmd->data.query_arp;
+ switch(qdata->reply_bits){
+ case 5:
+ entry_size = sizeof(struct qeth_arp_qi_entry5);
+ break;
+ case 7:
+ entry_size = sizeof(struct qeth_arp_qi_entry7);
+ break;
+ default:
+ /* tr is the same as eth -> entry7 */
+ entry_size = sizeof(struct qeth_arp_qi_entry7);
+ break;
+ }
+ /* check if there is enough room in userspace */
+ if ((qinfo->udata_len - qinfo->udata_offset) <
+ qdata->no_entries * entry_size){
+ QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM);
+ cmd->ihdr.return_code = -ENOMEM;
+ goto out_error;
+ }
+ QETH_DBF_TEXT_(trace, 4, "anore%i", cmd->shdr.number_of_replies);
+ QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->shdr.seq_no);
+ QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries);
+ for (i = 0; i < qdata->no_entries; ++i){
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ qdata->data + i*entry_size, entry_size);
+ qinfo->no_entries++;
+ qinfo->udata_offset += entry_size;
+ }
+ /* check if all replies received ... */
+ if (cmd->shdr.seq_no < cmd->shdr.number_of_replies)
+ return 1;
+ memcpy(qinfo->udata, &qinfo->no_entries, 4);
+ memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET,&qdata->reply_bits,2);
+ return 0;
+out_error:
+ i = 0;
+ memcpy(qinfo->udata, &i, 4);
+ return 0;
+}
+
+static struct qeth_cmd_buffer *
+qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
+ enum qeth_prot_versions);
+
+struct qeth_cmd_buffer *
+qeth_get_ipa_arp_cmd_buffer(struct qeth_card *card, u16 cmd_code,
+ u32 data_len, enum qeth_prot_versions proto)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_arp_cmd *cmd;
+ u16 s1, s2;
+
+ QETH_DBF_TEXT(trace,4,"getarpcm");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, proto);
+
+ memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+ /* adjust sizes in IPA_PDU_HEADER */
+ s1 = (u32) IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + data_len;
+ s2 = (u32) QETH_ARP_CMD_BASE_LEN + data_len;
+ memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
+
+ cmd = (struct qeth_ipa_arp_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->shdr.assist_no = IPA_ARP_PROCESSING;
+ cmd->shdr.length = 8 + data_len;
+ cmd->shdr.command_code = cmd_code;
+ cmd->shdr.return_code = 0;
+ cmd->shdr.seq_no = 0;
+
+ return iob;
+}
+
+static int
+qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ char *data, int data_len,
+ int (*reply_cb)
+ (struct qeth_card *,struct qeth_reply*, unsigned long),
+ void *reply_param)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,4,"sendarp");
+
+ memcpy(QETH_IPA_ARP_DATA_POS(iob->data), data, data_len);
+ memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+ &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+
+ rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE +
+ QETH_ARP_CMD_BASE_LEN + data_len, iob,
+ reply_cb, reply_param);
+ return rc;
+}
+
+static int
+qeth_arp_query(struct qeth_card *card, char *udata)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_arp_query_data *qdata;
+ struct qeth_arp_query_info qinfo = {0, };
+ int tmp;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"arpquery");
+
+ /* TODO: really not supported by GuestLAN? */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ /* get size of userspace mem area */
+ if (copy_from_user(&qinfo.udata_len, udata, 4))
+ return -EFAULT;
+ if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL)))
+ return -ENOMEM;
+ memset(qinfo.udata, 0, qinfo.udata_len);
+ qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
+ /* alloc mem area for the actual query */
+ if (!(qdata = kmalloc(sizeof(struct qeth_arp_query_data),
+ GFP_KERNEL))){
+ kfree(qinfo.udata);
+ return -ENOMEM;
+ }
+ memset(qdata, 0, sizeof(struct qeth_arp_query_data));
+ iob = qeth_get_ipa_arp_cmd_buffer(card, IPA_CMD_ASS_ARP_QUERY_INFO,
+ sizeof(struct qeth_arp_query_data),
+ QETH_PROT_IPV4);
+ rc = qeth_send_ipa_arp_cmd(card, iob,
+ (char *) qdata,
+ sizeof(struct qeth_arp_query_data),
+ qeth_arp_query_cb,
+ (void *)&qinfo);
+ if (rc) {
+ tmp = rc;
+ PRINT_WARN("Error while querying ARP cache on %s: %s (0x%x)\n",
+ card->info.if_name, qeth_arp_get_error_cause(&rc),
+ tmp);
+ copy_to_user(udata, qinfo.udata, 4);
+ } else {
+ copy_to_user(udata, qinfo.udata, qinfo.udata_len);
+ }
+ kfree(qinfo.udata);
+ return rc;
+}
+
+static int
+qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
+ unsigned long);
+
+static struct qeth_cmd_buffer *
+qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
+ __u16, __u16, enum qeth_prot_versions);
+
+static int
+qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *,
+ __u16, long,
+ int (*reply_cb)
+ (struct qeth_card *, struct qeth_reply *, unsigned long),
+ void *reply_param);
+
+static int
+qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
+{
+ struct qeth_cmd_buffer *iob;
+ char buf[16];
+ int tmp;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"arpadent");
+
+ /* TODO: really not supported by GuestLAN? */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_ADD_ENTRY,
+ sizeof(struct qeth_arp_cache_entry),
+ QETH_PROT_IPV4);
+ rc = qeth_send_setassparms(card, iob,
+ sizeof(struct qeth_arp_cache_entry),
+ (unsigned long) entry,
+ qeth_default_setassparms_cb, NULL);
+ if (rc) {
+ tmp = rc;
+ qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
+ PRINT_WARN("Could not add ARP entry for address %s on %s: "
+ "%s (0x%x)\n",
+ buf, card->info.if_name,
+ qeth_arp_get_error_cause(&rc), tmp);
+ }
+ return rc;
+}
+
+static int
+qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
+{
+ struct qeth_cmd_buffer *iob;
+ char buf[16] = {0, };
+ int tmp;
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"arprment");
+
+ /* TODO: really not supported by GuestLAN? */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ memcpy(buf, entry, 12);
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_REMOVE_ENTRY,
+ 12,
+ QETH_PROT_IPV4);
+ rc = qeth_send_setassparms(card, iob,
+ 12, (unsigned long)buf,
+ qeth_default_setassparms_cb, NULL);
+ if (rc) {
+ tmp = rc;
+ memset(buf, 0, 16);
+ qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
+ PRINT_WARN("Could not delete ARP entry for address %s on %s: "
+ "%s (0x%x)\n",
+ buf, card->info.if_name,
+ qeth_arp_get_error_cause(&rc), tmp);
+ }
+ return rc;
+}
+
+static int
+qeth_arp_flush_cache(struct qeth_card *card)
+{
+ int rc;
+ int tmp;
+
+ QETH_DBF_TEXT(trace,3,"arpflush");
+
+ /* TODO: really not supported by GuestLAN? */
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
+ if (rc){
+ tmp = rc;
+ PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x)\n",
+ card->info.if_name, qeth_arp_get_error_cause(&rc),
+ tmp);
+ }
+ return rc;
+}
+
+static int
+qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct qeth_card *card = (struct qeth_card *)dev->priv;
+ struct qeth_arp_cache_entry arp_entry;
+ struct mii_ioctl_data *mii_data;
+ int rc = 0;
+
+ if (!card)
+ return -ENODEV;
+
+ if ((card->state != CARD_STATE_UP_LAN_ONLINE) &&
+ (card->state != CARD_STATE_UP_LAN_OFFLINE))
+ return -ENODEV;
+
+ switch (cmd){
+ case SIOCDEVPRIVATE:
+ case SIOC_QETH_ARP_SET_NO_ENTRIES:
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
+ break;
+ case SIOCDEVPRIVATE+1:
+ case SIOC_QETH_ARP_QUERY_INFO:
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
+ break;
+ case SIOCDEVPRIVATE+2:
+ case SIOC_QETH_ARP_ADD_ENTRY:
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data,
+ sizeof(struct qeth_arp_cache_entry)))
+ rc = -EFAULT;
+ else
+ rc = qeth_arp_add_entry(card, &arp_entry);
+ break;
+ case SIOCDEVPRIVATE+3:
+ case SIOC_QETH_ARP_REMOVE_ENTRY:
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data,
+ sizeof(struct qeth_arp_cache_entry)))
+ rc = -EFAULT;
+ else
+ rc = qeth_arp_remove_entry(card, &arp_entry);
+ break;
+ case SIOCDEVPRIVATE+4:
+ case SIOC_QETH_ARP_FLUSH_CACHE:
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ rc = qeth_arp_flush_cache(card);
+ break;
+ case SIOCDEVPRIVATE+5:
+ case SIOC_QETH_ADP_SET_SNMP_CONTROL:
+ break;
+ case SIOCDEVPRIVATE+6:
+ case SIOC_QETH_GET_CARD_TYPE:
+ break;
+ case SIOCGMIIPHY:
+ mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ mii_data->phy_id = 0;
+ break;
+ case SIOCGMIIREG:
+ mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ if (mii_data->phy_id != 0)
+ rc = -EINVAL;
+ else
+ mii_data->val_out = qeth_mdio_read(dev,mii_data->phy_id,
+ mii_data->reg_num);
+ break;
+ case SIOCSMIIREG:
+ rc = -EOPNOTSUPP;
+ break;
+ /* TODO: remove return if qeth_mdio_write does something */
+ if (!capable(CAP_NET_ADMIN)){
+ rc = -EPERM;
+ break;
+ }
+ mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ if (mii_data->phy_id != 0)
+ rc = -EINVAL;
+ else
+ qeth_mdio_write(dev, mii_data->phy_id, mii_data->reg_num,
+ mii_data->val_in);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ return rc;
+}
+
+static struct net_device_stats *
+qeth_get_stats(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ card = (struct qeth_card *) (dev->priv);
+
+ QETH_DBF_TEXT(trace,5,"getstat");
+
+ return &card->stats;
+}
+
+static int
+qeth_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct qeth_card *card;
+ char dbf_text[15];
+
+ card = (struct qeth_card *) (dev->priv);
+
+ QETH_DBF_TEXT(trace,4,"chgmtu");
+ sprintf(dbf_text, "%8x", new_mtu);
+ QETH_DBF_TEXT(trace,4,dbf_text);
+
+ if (new_mtu < 64)
+ return -EINVAL;
+ if (new_mtu > 65535)
+ return -EINVAL;
+ if ((!qeth_is_supported(card,IPA_IP_FRAGMENTATION)) &&
+ (!qeth_mtu_is_valid(card, new_mtu)))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+#ifdef CONFIG_QETH_VLAN
+static void
+qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,4,"vlanreg");
+
+ card = (struct qeth_card *) dev->priv;
+ spin_lock_irq(&card->vlanlock);
+ card->vlangrp = grp;
+ spin_unlock_irq(&card->vlanlock);
+}
+
+static void
+qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,4,"vlkilvid");
+
+ card = (struct qeth_card *) dev->priv;
+ spin_lock_irq(&card->vlanlock);
+ if (card->vlangrp)
+ card->vlangrp->vlan_devices[vid] = NULL;
+ spin_unlock_irq(&card->vlanlock);
+ /* delete mc addresses for this vlan dev */
+ qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+#endif
+
+static int
+qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+{
+ return 0;
+}
+
+#ifdef CONFIG_QETH_IPV6
+int
+qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
+{
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ case ARPHRD_FDDI:
+ case ARPHRD_IEEE802_TR:
+ if (dev->addr_len != ETH_ALEN)
+ return -1;
+ memcpy(eui, dev->dev_addr, 3);
+ memcpy(eui + 5, dev->dev_addr + 3, 3);
+ eui[3] = (dev->dev_id >> 8) & 0xff;
+ eui[4] = dev->dev_id & 0xff;
+ return 0;
+ }
+ return -1;
+
+}
+#endif
+
+static void
+qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
+{
+ if (dev->type == ARPHRD_IEEE802_TR)
+ ip_tr_mc_map(ipm, mac);
+ else
+ ip_eth_mc_map(ipm, mac);
+}
+
+static struct qeth_ipaddr *
+qeth_get_addr_buffer(enum qeth_prot_versions prot)
+{
+ struct qeth_ipaddr *addr;
+
+ addr = kmalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
+ if (addr == NULL) {
+ PRINT_WARN("Not enough memory to add address\n");
+ return NULL;
+ }
+ memset(addr,0,sizeof(struct qeth_ipaddr));
+ addr->type = QETH_IP_TYPE_NORMAL;
+ addr->proto = prot;
+ addr->is_multicast = 0;
+ addr->users = 0;
+ addr->set_flags = 0;
+ addr->del_flags = 0;
+ return addr;
+}
+
+static void
+qeth_delete_mc_addresses(struct qeth_card *card)
+{
+ struct qeth_ipaddr *ipm, *iptodo;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace,4,"delmc");
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipm, &card->ip_list, entry){
+ if (!ipm->is_multicast)
+ continue;
+ iptodo = qeth_get_addr_buffer(ipm->proto);
+ memcpy(iptodo, ipm, sizeof(struct qeth_ipaddr));
+ iptodo->users = iptodo->users * -1;
+ if (!__qeth_insert_ip_todo(card, iptodo, 0))
+ kfree(iptodo);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+static inline void
+qeth_add_mc(struct qeth_card *card, struct in_device *in4_dev)
+{
+ struct qeth_ipaddr *ipm;
+ struct ip_mc_list *im4;
+ char buf[MAX_ADDR_LEN];
+
+ QETH_DBF_TEXT(trace,4,"addmc");
+ for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
+ qeth_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev);
+ ipm = qeth_get_addr_buffer(QETH_PROT_IPV4);
+ if (!ipm)
+ continue;
+ ipm->u.a4.addr = im4->multiaddr;
+ memcpy(ipm->mac,buf,OSA_ADDR_LEN);
+ ipm->is_multicast = 1;
+ if (!qeth_add_ip(card,ipm))
+ kfree(ipm);
+ }
+}
+
+static inline void
+qeth_add_vlan_mc(struct qeth_card *card)
+{
+#ifdef CONFIG_QETH_VLAN
+ struct in_device *in_dev;
+ struct vlan_group *vg;
+ int i;
+
+ QETH_DBF_TEXT(trace,4,"addmcvl");
+ if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
+ (card->vlangrp == NULL))
+ return ;
+
+ vg = card->vlangrp;
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+ if (vg->vlan_devices[i] == NULL ||
+ !(vg->vlan_devices[i]->flags & IFF_UP))
+ continue;
+ in_dev = in_dev_get(vg->vlan_devices[i]);
+ if (!in_dev)
+ continue;
+ read_lock(&in_dev->lock);
+ qeth_add_mc(card,in_dev);
+ read_unlock(&in_dev->lock);
+ in_dev_put(in_dev);
+ }
+#endif
+}
+
+static void
+qeth_add_multicast_ipv4(struct qeth_card *card)
+{
+ struct in_device *in4_dev;
+
+ QETH_DBF_TEXT(trace,4,"chkmcv4");
+ in4_dev = in_dev_get(card->dev);
+ if (in4_dev == NULL)
+ return;
+ read_lock(&in4_dev->lock);
+ qeth_add_mc(card, in4_dev);
+ qeth_add_vlan_mc(card);
+ read_unlock(&in4_dev->lock);
+ in_dev_put(in4_dev);
+}
+
+#ifdef CONFIG_QETH_IPV6
+static inline void
+qeth_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev)
+{
+ struct qeth_ipaddr *ipm;
+ struct ifmcaddr6 *im6;
+ char buf[MAX_ADDR_LEN];
+
+ QETH_DBF_TEXT(trace,4,"addmc6");
+ for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) {
+ ndisc_mc_map(&im6->mca_addr, buf, in6_dev->dev, 0);
+ ipm = qeth_get_addr_buffer(QETH_PROT_IPV6);
+ if (!ipm)
+ continue;
+ ipm->is_multicast = 1;
+ memcpy(ipm->mac,buf,OSA_ADDR_LEN);
+ memcpy(&ipm->u.a6.addr,&im6->mca_addr.s6_addr,
+ sizeof(struct in6_addr));
+ if (!qeth_add_ip(card,ipm))
+ kfree(ipm);
+ }
+}
+
+static inline void
+qeth_add_vlan_mc6(struct qeth_card *card)
+{
+#ifdef CONFIG_QETH_VLAN
+ struct inet6_dev *in_dev;
+ struct vlan_group *vg;
+ int i;
+
+ QETH_DBF_TEXT(trace,4,"admc6vl");
+ if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
+ (card->vlangrp == NULL))
+ return ;
+
+ vg = card->vlangrp;
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+ if (vg->vlan_devices[i] == NULL ||
+ !(vg->vlan_devices[i]->flags & IFF_UP))
+ continue;
+ in_dev = in6_dev_get(vg->vlan_devices[i]);
+ if (!in_dev)
+ continue;
+ read_lock(&in_dev->lock);
+ qeth_add_mc6(card,in_dev);
+ read_unlock(&in_dev->lock);
+ in6_dev_put(in_dev);
+ }
+#endif /* CONFIG_QETH_VLAN */
+}
+
+static void
+qeth_add_multicast_ipv6(struct qeth_card *card)
+{
+ struct inet6_dev *in6_dev;
+
+ QETH_DBF_TEXT(trace,4,"chkmcv6");
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return ;
+
+ in6_dev = in6_dev_get(card->dev);
+ if (in6_dev == NULL)
+ return;
+ read_lock(&in6_dev->lock);
+ qeth_add_mc6(card, in6_dev);
+ qeth_add_vlan_mc6(card);
+ read_unlock(&in6_dev->lock);
+ in6_dev_put(in6_dev);
+}
+#endif /* CONFIG_QETH_IPV6 */
+
+/**
+ * set multicast address on card
+ */
+static void
+qeth_set_multicast_list(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,3,"setmulti");
+ card = (struct qeth_card *) dev->priv;
+
+ qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+
+static void
+qeth_fill_ipacmd_header(struct qeth_card *card, struct qeth_ipa_cmd *cmd,
+ __u8 command, enum qeth_prot_versions prot)
+{
+ memset(cmd, 0, sizeof (struct qeth_ipa_cmd));
+ cmd->hdr.command = command;
+ cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST;
+ cmd->hdr.seqno = card->seqno.ipa;
+ cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
+ cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
+ cmd->hdr.prim_version_no = 1;
+ cmd->hdr.param_count = 1;
+ cmd->hdr.prot_version = prot;
+ cmd->hdr.ipa_supported = 0;
+ cmd->hdr.ipa_enabled = 0;
+}
+
+static struct qeth_cmd_buffer *
+qeth_get_ipacmd_buffer(struct qeth_card *card, enum qeth_ipa_cmds ipacmd,
+ enum qeth_prot_versions prot)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ iob = qeth_wait_for_buffer(&card->write);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ qeth_fill_ipacmd_header(card, cmd, ipacmd, prot);
+
+ return iob;
+}
+
+static int
+qeth_send_setdelmc(struct qeth_card *card, struct qeth_ipaddr *addr, int ipacmd)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"setdelmc");
+
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ memcpy(&cmd->data.setdelipm.mac,addr->mac, OSA_ADDR_LEN);
+ if (addr->proto == QETH_PROT_IPV6)
+ memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr,
+ sizeof(struct in6_addr));
+ else
+ memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr,4);
+
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+
+ return rc;
+}
+static inline void
+qeth_fill_netmask(u8 *netmask, unsigned int len)
+{
+ int i,j;
+ for (i=0;i<16;i++) {
+ j=(len)-(i*8);
+ if (j >= 8)
+ netmask[i] = 0xff;
+ else if (j > 0)
+ netmask[i] = (u8)(0xFF00>>j);
+ else
+ netmask[i] = 0;
+ }
+}
+
+static int
+qeth_send_setdelip(struct qeth_card *card, struct qeth_ipaddr *addr,
+ int ipacmd, unsigned int flags)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ __u8 netmask[16];
+
+ QETH_DBF_TEXT(trace,4,"setdelip");
+ QETH_DBF_TEXT_(trace,4,"flags%02X", flags);
+
+ iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ if (addr->proto == QETH_PROT_IPV6) {
+ memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr,
+ sizeof(struct in6_addr));
+ qeth_fill_netmask(netmask,addr->u.a6.pfxlen);
+ memcpy(cmd->data.setdelip6.mask, netmask,
+ sizeof(struct in6_addr));
+ cmd->data.setdelip6.flags = flags;
+ } else {
+ memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4);
+ memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4);
+ cmd->data.setdelip4.flags = flags;
+ }
+
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+
+ return rc;
+}
+
+static int
+qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ //char buf[50];
+ int rc;
+ int cnt = 3;
+
+ if (addr->proto == QETH_PROT_IPV4) {
+ QETH_DBF_TEXT(trace, 2,"setaddr4");
+ QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, sizeof(int));
+ } else if (addr->proto == QETH_PROT_IPV6) {
+ QETH_DBF_TEXT(trace, 2, "setaddr6");
+ QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
+ QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+ } else {
+ QETH_DBF_TEXT(trace, 2, "setaddr?");
+ QETH_DBF_HEX(trace, 4, addr, sizeof(struct qeth_ipaddr));
+ }
+ do {
+ if (addr->is_multicast)
+ rc = qeth_send_setdelmc(card, addr, IPA_CMD_SETIPM);
+ else
+ rc = qeth_send_setdelip(card, addr, IPA_CMD_SETIP,
+ addr->set_flags);
+ if (rc)
+ QETH_DBF_TEXT(trace, 2, "failed");
+ } while ((--cnt > 0) && rc);
+ if (rc){
+ QETH_DBF_TEXT(trace, 2, "FAILED");
+ /* TODO: re-activate this warning as soon as we have a
+ * clean mirco code
+ qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
+ PRINT_WARN("Could not register IP address %s (rc=%x)\n",
+ buf, rc);
+ */
+ }
+ return rc;
+}
+
+static int
+qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ //char buf[50];
+ int rc;
+
+ if (addr->proto == QETH_PROT_IPV4) {
+ QETH_DBF_TEXT(trace, 2,"deladdr4");
+ QETH_DBF_HEX(trace, 2, &addr->u.a4.addr, sizeof(int));
+ } else if (addr->proto == QETH_PROT_IPV6) {
+ QETH_DBF_TEXT(trace, 2, "deladdr6");
+ QETH_DBF_HEX(trace, 2, &addr->u.a6.addr,
+ sizeof(struct in6_addr));
+ } else {
+ QETH_DBF_TEXT(trace, 2, "deladdr?");
+ QETH_DBF_HEX(trace, 2, addr, sizeof(struct qeth_ipaddr));
+ }
+ if (addr->is_multicast)
+ rc = qeth_send_setdelmc(card, addr, IPA_CMD_DELIPM);
+ else
+ rc = qeth_send_setdelip(card, addr, IPA_CMD_DELIP,
+ addr->del_flags);
+ if (rc) {
+ QETH_DBF_TEXT(trace, 2, "failed");
+ /* TODO: re-activate this warning as soon as we have a
+ * clean mirco code
+ qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
+ PRINT_WARN("Could not deregister IP address %s (rc=%x)\n",
+ buf, rc);
+ */
+ }
+ return rc;
+}
+
+static int
+qeth_netdev_init(struct net_device *dev)
+{
+ struct qeth_card *card;
+
+ card = (struct qeth_card *) dev->priv;
+
+ QETH_DBF_TEXT(trace,3,"initdev");
+
+ dev->tx_timeout = &qeth_tx_timeout;
+ dev->watchdog_timeo = QETH_TX_TIMEOUT;
+ dev->open = qeth_open;
+ dev->stop = qeth_stop;
+ dev->hard_start_xmit = qeth_hard_start_xmit;
+ dev->do_ioctl = qeth_do_ioctl;
+ dev->get_stats = qeth_get_stats;
+ dev->change_mtu = qeth_change_mtu;
+ dev->neigh_setup = qeth_neigh_setup;
+ dev->set_multicast_list = qeth_set_multicast_list;
+#ifdef CONFIG_QETH_VLAN
+ dev->vlan_rx_register = qeth_vlan_rx_register;
+ dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
+#endif
+ if (qeth_get_netdev_flags(card->info.type) & IFF_NOARP) {
+ dev->rebuild_header = NULL;
+ dev->hard_header = NULL;
+ dev->header_cache_update = NULL;
+ dev->hard_header_cache = NULL;
+ }
+#ifdef CONFIG_QETH_IPV6
+ /*IPv6 address autoconfiguration stuff*/
+ card->dev->dev_id = card->info.unique_id & 0xffff;
+ if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
+ card->dev->generate_eui64 = qeth_ipv6_generate_eui64;
+
+
+#endif
+ dev->hard_header_parse = NULL;
+ dev->set_mac_address = NULL;
+ dev->flags |= qeth_get_netdev_flags(card->info.type);
+ if ((card->options.fake_broadcast) ||
+ (card->info.broadcast_capable))
+ dev->flags |= IFF_BROADCAST;
+
+ dev->hard_header_len =
+ qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
+ dev->addr_len = OSA_ADDR_LEN;
+ dev->mtu = card->info.initial_mtu;
+
+ SET_MODULE_OWNER(dev);
+ return 0;
+}
+
+/**
+ * hardsetup card, initialize MPC and QDIO stuff
+ */
+static int
+qeth_hardsetup_card(struct qeth_card *card)
+{
+ int retries = 3;
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "hrdsetup");
+
+retry:
+ if (retries < 3){
+ PRINT_WARN("Retrying to do IDX activates.\n");
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ ccw_device_set_online(CARD_RDEV(card));
+ ccw_device_set_online(CARD_WDEV(card));
+ ccw_device_set_online(CARD_DDEV(card));
+ }
+ rc = qeth_qdio_clear_card(card,card->info.type==QETH_CARD_TYPE_OSAE);
+ if (rc == -ERESTARTSYS) {
+ QETH_DBF_TEXT(setup, 2, "break1");
+ return rc;
+ } else if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ if (--retries < 0)
+ goto out;
+ else
+ goto retry;
+ }
+ if ((rc = qeth_get_unitaddr(card))){
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ return rc;
+ }
+ qeth_init_tokens(card);
+ rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
+ if (rc == -ERESTARTSYS) {
+ QETH_DBF_TEXT(setup, 2, "break2");
+ return rc;
+ } else if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ if (--retries < 0)
+ goto out;
+ else
+ goto retry;
+ }
+ rc = qeth_idx_activate_channel(&card->write, qeth_idx_write_cb);
+ if (rc == -ERESTARTSYS) {
+ QETH_DBF_TEXT(setup, 2, "break3");
+ return rc;
+ } else if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+ if (--retries < 0)
+ goto out;
+ else
+ goto retry;
+ }
+ if ((rc = qeth_mpc_initialize(card))){
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ goto out;
+ }
+ /* at first set_online allocate netdev */
+ if (!card->dev){
+ card->dev = qeth_get_netdevice(card->info.type,
+ card->info.link_type);
+ if (!card->dev){
+ qeth_qdio_clear_card(card, card->info.type ==
+ QETH_CARD_TYPE_OSAE);
+ rc = -ENODEV;
+ QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+ goto out;
+ }
+ qeth_set_device_name(card);
+ card->dev->priv = card;
+ card->dev->type = qeth_get_arphdr_type(card->info.type,
+ card->info.link_type);
+ card->dev->init = qeth_netdev_init;
+ }
+ return 0;
+out:
+ PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
+ return rc;
+}
+
+static struct qeth_cmd_buffer *
+qeth_get_adapter_cmd(struct qeth_card *card, __u32 command)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS,
+ QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setadapterparms.cmdlength =
+ sizeof(struct qeth_ipacmd_setadpparms);
+ cmd->data.setadapterparms.command_code = command;
+ cmd->data.setadapterparms.frames_used_total = 1;
+ cmd->data.setadapterparms.frame_seq_no = 1;
+
+ return iob;
+}
+
+static int
+qeth_default_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"defadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0){
+ cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+ card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+#ifdef CONFIG_QETH_IPV6
+ if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+#endif
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.csum_mask = cmd->data.setassparms.data.flags_32bit;
+ QETH_DBF_TEXT_(trace, 3, "csum:%d", card->info.csum_mask);
+ }
+ return 0;
+}
+
+static int
+qeth_default_setadapterparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"defadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0)
+ cmd->hdr.return_code = cmd->data.setadapterparms.return_code;
+ return 0;
+}
+
+static int
+qeth_query_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,3,"quyadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f)
+ card->info.link_type =
+ cmd->data.setadapterparms.data.query_cmds_supp.lan_type;
+ card->options.adp.supported_funcs =
+ cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds;
+ return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
+}
+
+static int
+qeth_query_setadapterparms(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace,3,"queryadp");
+ iob = qeth_get_adapter_cmd(card,IPA_SETADP_QUERY_COMMANDS_SUPPORTED);
+ rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL);
+ return rc;
+}
+
+static int
+qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
+ struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"chgmaccb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ memcpy(card->dev->dev_addr,
+ &cmd->data.setadapterparms.data.change_addr.addr,OSA_ADDR_LEN);
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ return 0;
+}
+
+static int
+qeth_setadpparms_change_macaddr(struct qeth_card *card)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"chgmac");
+
+ iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC;
+ cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN;
+ memcpy(&cmd->data.setadapterparms.data.change_addr.addr,
+ card->dev->dev_addr, OSA_ADDR_LEN);
+ rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_change_macaddr_cb,
+ NULL);
+ return rc;
+}
+
+static int
+qeth_send_setadp_mode(struct qeth_card *card, __u32 command, __u32 mode)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"adpmode");
+
+ iob = qeth_get_adapter_cmd(card, command);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setadapterparms.data.mode = mode;
+ rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb,
+ NULL);
+ return rc;
+}
+
+static inline int
+qeth_setadapter_hstr(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,4,"adphstr");
+
+ if (qeth_adp_supported(card,IPA_SETADP_SET_BROADCAST_MODE)) {
+ rc = qeth_send_setadp_mode(card, IPA_SETADP_SET_BROADCAST_MODE,
+ card->options.broadcast_mode);
+ if (rc)
+ PRINT_WARN("couldn't set broadcast mode on "
+ "device %s: x%x\n",
+ CARD_BUS_ID(card), rc);
+ rc = qeth_send_setadp_mode(card, IPA_SETADP_ALTER_MAC_ADDRESS,
+ card->options.macaddr_mode);
+ if (rc)
+ PRINT_WARN("couldn't set macaddr mode on "
+ "device %s: x%x\n", CARD_BUS_ID(card), rc);
+ return rc;
+ }
+ if (card->options.broadcast_mode == QETH_TR_BROADCAST_LOCAL)
+ PRINT_WARN("set adapter parameters not available "
+ "to set broadcast mode, using ALLRINGS "
+ "on device %s:\n", CARD_BUS_ID(card));
+ if (card->options.macaddr_mode == QETH_TR_MACADDR_CANONICAL)
+ PRINT_WARN("set adapter parameters not available "
+ "to set macaddr mode, using NONCANONICAL "
+ "on device %s:\n", CARD_BUS_ID(card));
+ return 0;
+}
+
+static int
+qeth_setadapter_parms(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "setadprm");
+
+ if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)){
+ PRINT_WARN("set adapter parameters not supported "
+ "on device %s.\n",
+ CARD_BUS_ID(card));
+ QETH_DBF_TEXT(setup, 2, " notsupp");
+ return 0;
+ }
+ rc = qeth_query_setadapterparms(card);
+ if (rc) {
+ PRINT_WARN("couldn't set adapter parameters on device %s: "
+ "x%x\n", CARD_BUS_ID(card), rc);
+ return rc;
+ }
+ if (qeth_adp_supported(card,IPA_SETADP_ALTER_MAC_ADDRESS)) {
+ rc = qeth_setadpparms_change_macaddr(card);
+ if (rc)
+ PRINT_WARN("couldn't get MAC address on "
+ "device %s: x%x\n",
+ CARD_BUS_ID(card), rc);
+ }
+
+ if ((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR))
+ rc = qeth_setadapter_hstr(card);
+
+ return rc;
+}
+
+
+static int
+qeth_send_startstoplan(struct qeth_card *card, enum qeth_ipa_cmds ipacmd,
+ enum qeth_prot_versions prot)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ iob = qeth_get_ipacmd_buffer(card,ipacmd,prot);
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+
+ return rc;
+}
+
+static int
+qeth_send_startlan(struct qeth_card *card, enum qeth_prot_versions prot)
+{
+ int rc;
+
+ QETH_DBF_TEXT_(setup, 2, "strtlan%i", prot);
+
+ rc = qeth_send_startstoplan(card, IPA_CMD_STARTLAN, prot);
+ return rc;
+}
+
+static int
+qeth_send_stoplan(struct qeth_card *card)
+{
+ int rc = 0;
+
+ /*
+ * TODO: according to the IPA format document page 14,
+ * TCP/IP (we!) never issue a STOPLAN
+ * is this right ?!?
+ */
+ QETH_DBF_TEXT(trace, 2, "stoplan");
+
+ rc = qeth_send_startstoplan(card, IPA_CMD_STOPLAN, QETH_PROT_IPV4);
+ return rc;
+}
+
+static int
+qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(setup, 2, "qipasscb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV4) {
+ card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported;
+ card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+ } else {
+#ifdef CONFIG_QETH_IPV6
+ card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported;
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+#endif
+ }
+ return 0;
+}
+
+static int
+qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT_(setup, 2, "qipassi%i", prot);
+
+ iob = qeth_get_ipacmd_buffer(card,IPA_CMD_QIPASSIST,prot);
+ rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL);
+ return rc;
+}
+
+static struct qeth_cmd_buffer *
+qeth_get_setassparms_cmd(struct qeth_card *card, enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, __u16 len,
+ enum qeth_prot_versions prot)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"getasscm");
+ iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETASSPARMS,prot);
+
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setassparms.hdr.assist_no = ipa_func;
+ cmd->data.setassparms.hdr.length = 8 + len;
+ cmd->data.setassparms.hdr.command_code = cmd_code;
+ cmd->data.setassparms.hdr.return_code = 0;
+ cmd->data.setassparms.hdr.seq_no = 0;
+
+ return iob;
+}
+
+static int
+qeth_send_setassparms(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ __u16 len, long data,
+ int (*reply_cb)
+ (struct qeth_card *,struct qeth_reply *,unsigned long),
+ void *reply_param)
+{
+ int rc;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,4,"sendassp");
+
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ if (len <= sizeof(__u32))
+ cmd->data.setassparms.data.flags_32bit = (__u32) data;
+ else if (len > sizeof(__u32))
+ memcpy(&cmd->data.setassparms.data, (void *) data, len);
+
+ rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
+ return rc;
+}
+
+#ifdef CONFIG_QETH_IPV6
+static int
+qeth_send_simple_setassparms_ipv6(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
+
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace,4,"simassp6");
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
+ 0, QETH_PROT_IPV6);
+ rc = qeth_send_setassparms(card, iob, 0, 0,
+ qeth_default_setassparms_cb, NULL);
+ return rc;
+}
+#endif
+
+static int
+qeth_send_simple_setassparms(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, long data)
+{
+ int rc;
+ int length = 0;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace,4,"simassp4");
+ if (data)
+ length = sizeof(__u32);
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
+ length, QETH_PROT_IPV4);
+ rc = qeth_send_setassparms(card, iob, length, data,
+ qeth_default_setassparms_cb, NULL);
+ return rc;
+}
+
+static inline int
+qeth_start_ipa_arp_processing(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"ipaarp");
+
+ if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ PRINT_WARN("ARP processing not supported "
+ "on %s!\n", card->info.if_name);
+ return 0;
+ }
+ rc = qeth_send_simple_setassparms(card,IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ PRINT_WARN("Could not start ARP processing "
+ "assist on %s: 0x%x\n",
+ card->info.if_name, rc);
+ }
+ return rc;
+}
+
+static int
+qeth_start_ipa_ip_fragmentation(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"ipaipfrg");
+
+ if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
+ PRINT_INFO("IP fragmentation not supported on %s\n",
+ card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ PRINT_WARN("Could not start IP fragmentation "
+ "assist on %s: 0x%x\n",
+ card->info.if_name, rc);
+ } else
+ PRINT_INFO("IP fragmentation enabled \n");
+ return rc;
+}
+
+static int
+qeth_start_ipa_source_mac(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"stsrcmac");
+
+ if (!card->options.fake_ll)
+ return -EOPNOTSUPP;
+
+ if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
+ PRINT_INFO("Inbound source address not "
+ "supported on %s\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ PRINT_WARN("Could not start inbound source "
+ "assist on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+}
+
+static int
+qeth_start_ipa_vlan(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"strtvlan");
+
+#ifdef CONFIG_QETH_VLAN
+ if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
+ PRINT_WARN("VLAN not supported on %s\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
+ IPA_CMD_ASS_START,0);
+ if (rc) {
+ PRINT_WARN("Could not start vlan "
+ "assist on %s: 0x%x\n",
+ card->info.if_name, rc);
+ } else {
+ PRINT_INFO("VLAN enabled \n");
+ card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ }
+#endif /* QETH_VLAN */
+ return rc;
+}
+
+static int
+qeth_start_ipa_multicast(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"stmcast");
+
+ if (!qeth_is_supported(card, IPA_MULTICASTING)) {
+ PRINT_WARN("Multicast not supported on %s\n",
+ card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
+ IPA_CMD_ASS_START,0);
+ if (rc) {
+ PRINT_WARN("Could not start multicast "
+ "assist on %s: rc=%i\n",
+ card->info.if_name, rc);
+ } else {
+ PRINT_INFO("Multicast enabled\n");
+ card->dev->flags |= IFF_MULTICAST;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_QETH_IPV6
+static int
+qeth_softsetup_ipv6(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"softipv6");
+
+ netif_stop_queue(card->dev);
+ rc = qeth_send_startlan(card, QETH_PROT_IPV6);
+ if (rc) {
+ PRINT_ERR("IPv6 startlan failed on %s\n",
+ card->info.if_name);
+ return rc;
+ }
+ netif_wake_queue(card->dev);
+ rc = qeth_query_ipassists(card,QETH_PROT_IPV6);
+ if (rc) {
+ PRINT_ERR("IPv6 query ipassist failed on %s\n",
+ card->info.if_name);
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_IPV6,
+ IPA_CMD_ASS_START, 3);
+ if (rc) {
+ PRINT_WARN("IPv6 start assist (version 4) failed "
+ "on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms_ipv6(card, IPA_IPV6,
+ IPA_CMD_ASS_START);
+ if (rc) {
+ PRINT_WARN("IPV6 start assist (version 6) failed "
+ "on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
+ IPA_CMD_ASS_START);
+ if (rc) {
+ PRINT_WARN("Could not enable passthrough "
+ "on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ PRINT_INFO("IPV6 enabled \n");
+ return 0;
+}
+
+#endif
+
+static int
+qeth_start_ipa_ipv6(struct qeth_card *card)
+{
+ int rc = 0;
+#ifdef CONFIG_QETH_IPV6
+ QETH_DBF_TEXT(trace,3,"strtipv6");
+
+ if (!qeth_is_supported(card, IPA_IPV6)) {
+ PRINT_WARN("IPv6 not supported on %s\n",
+ card->info.if_name);
+ return 0;
+ }
+ rc = qeth_softsetup_ipv6(card);
+#endif
+ return rc ;
+}
+
+static int
+qeth_start_ipa_broadcast(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"stbrdcst");
+ if (!qeth_is_supported(card, IPA_FILTERING)) {
+ PRINT_WARN("Broadcast not supported on %s\n",
+ card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ PRINT_WARN("Could not enable broadcasting "
+ "on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_CONFIGURE, 1);
+ if (rc) {
+ PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ PRINT_INFO("Broadcast enabled \n");
+ card->dev->flags |= IFF_BROADCAST;
+ card->info.broadcast_capable = 1;
+ return 0;
+}
+
+static int
+qeth_send_checksum_command(struct qeth_card *card)
+{
+ int rc;
+
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ PRINT_WARN("Starting Inbound HW Checksumming failed on %s: "
+ "0x%x,\ncontinuing using Inbound SW Checksumming\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.csum_mask);
+ if (rc) {
+ PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: "
+ "0x%x,\ncontinuing using Inbound SW Checksumming\n",
+ card->info.if_name, rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int
+qeth_start_ipa_checksum(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"strtcsum");
+
+ if (card->options.checksum_type == NO_CHECKSUMMING) {
+ PRINT_WARN("Using no checksumming on %s.\n",
+ card->info.if_name);
+ return 0;
+ }
+ if (card->options.checksum_type == SW_CHECKSUMMING) {
+ PRINT_WARN("Using SW checksumming on %s.\n",
+ card->info.if_name);
+ return 0;
+ }
+ if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
+ PRINT_WARN("Inbound HW Checksumming not "
+ "supported on %s,\ncontinuing "
+ "using Inbound SW Checksumming\n",
+ card->info.if_name);
+ card->options.checksum_type = SW_CHECKSUMMING;
+ return 0;
+ }
+ rc = qeth_send_checksum_command(card);
+ if (!rc) {
+ PRINT_INFO("HW Checksumming (inbound) enabled \n");
+ }
+ return rc;
+}
+
+/*
+static inline void
+qeth_print_ipassist_status(struct qeth_card *card)
+{
+ char buf[255];
+ int offset = 0;
+
+ offset += sprintf(buf, "IPAssist options of %s: ", card->info.if_name);
+ if (qeth_is_enabled(card, IPA_ARP_PROCESSING))
+ offset += sprintf(buf+offset, "ARP ");
+ if (qeth_is_enabled(card, IPA_IP_FRAGMENTATION))
+ offset += sprintf(buf+offset, "IP_FRAG");
+ if (qeth_is_enabled(card, IPA_SOURCE_MAC))
+ offset += sprintf(buf+offset, "SRC_MAC");
+ if (qeth_is_enabled(card, IPA_FULL_VLAN))
+ offset += sprintf(buf+offset, "VLAN");
+ if (qeth_is_enabled(card, IPA_VLAN_PRIO))
+ offset += sprintf(buf+offset, "VLAN_PRIO");
+}
+*/
+
+static int
+qeth_start_ipassists(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(trace,3,"strtipas");
+ qeth_start_ipa_arp_processing(card); /* go on*/
+ qeth_start_ipa_ip_fragmentation(card); /* go on*/
+ qeth_start_ipa_source_mac(card); /* go on*/
+ qeth_start_ipa_vlan(card); /* go on*/
+ qeth_start_ipa_multicast(card); /* go on*/
+ qeth_start_ipa_ipv6(card); /* go on*/
+ qeth_start_ipa_broadcast(card); /* go on*/
+ qeth_start_ipa_checksum(card); /* go on*/
+ return 0;
+}
+
+static int
+qeth_send_setrouting(struct qeth_card *card, enum qeth_routing_types type,
+ enum qeth_prot_versions prot)
+{
+ int rc;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_DBF_TEXT(trace,4,"setroutg");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setrtg.type = (type);
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+
+ return rc;
+
+}
+
+int
+qeth_setrouting_v4(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(trace,3,"setrtg4");
+
+ if (card->options.route4.type == NO_ROUTER)
+ return 0;
+
+ rc = qeth_send_setrouting(card, card->options.route4.type,
+ QETH_PROT_IPV4);
+ if (rc) {
+ card->options.route4.type = NO_ROUTER;
+ PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
+ "Type set to 'no router'.\n",
+ rc, card->info.if_name);
+ }
+ return rc;
+}
+
+int
+qeth_setrouting_v6(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"setrtg6");
+#ifdef CONFIG_QETH_IPV6
+
+ if ((card->options.route6.type == NO_ROUTER) ||
+ ((card->info.type == QETH_CARD_TYPE_OSAE) &&
+ (card->options.route6.type == MULTICAST_ROUTER) &&
+ !qeth_is_supported6(card,IPA_OSA_MC_ROUTER)))
+ return 0;
+ rc = qeth_send_setrouting(card, card->options.route6.type,
+ QETH_PROT_IPV6);
+ if (rc) {
+ card->options.route6.type = NO_ROUTER;
+ PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
+ "Type set to 'no router'.\n",
+ rc, card->info.if_name);
+ }
+#endif
+ return rc;
+}
+
+/*
+ * softsetup card: init IPA stuff
+ */
+static int
+qeth_softsetup_card(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(setup, 2, "softsetp");
+
+ if ((rc = qeth_send_startlan(card, QETH_PROT_IPV4))){
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ if (rc == 0xe080){
+ PRINT_WARN("LAN on card %s if offline! "
+ "Continuing softsetup.\n",
+ CARD_BUS_ID(card));
+ card->lan_online = 0;
+ } else
+ return rc;
+ } else
+ card->lan_online = 1;
+ if ((rc = qeth_setadapter_parms(card)))
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ if ((rc = qeth_start_ipassists(card)))
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ if ((rc = qeth_setrouting_v4(card)))
+ QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+ if ((rc = qeth_setrouting_v6(card)))
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ netif_stop_queue(card->dev);
+ return 0;
+}
+
+#ifdef CONFIG_QETH_IPV6
+static int
+qeth_get_unique_id_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0)
+ card->info.unique_id = *((__u16 *)
+ &cmd->data.create_destroy_addr.unique_id[6]);
+ else {
+ card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
+ UNIQUE_ID_NOT_BY_CARD;
+ PRINT_WARN("couldn't get a unique id from the card on device "
+ "%s (result=x%x), using default id. ipv6 "
+ "autoconfig on other lpars may lead to duplicate "
+ "ip addresses. please use manually "
+ "configured ones.\n",
+ CARD_BUS_ID(card), cmd->hdr.return_code);
+ }
+ return 0;
+}
+#endif
+
+static int
+qeth_put_unique_id(struct qeth_card *card)
+{
+
+ int rc = 0;
+#ifdef CONFIG_QETH_IPV6
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(trace,2,"puniqeid");
+
+ if ((card->info.unique_id & UNIQUE_ID_NOT_BY_CARD) ==
+ UNIQUE_ID_NOT_BY_CARD)
+ return -1;
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_DESTROY_ADDR,
+ QETH_PROT_IPV6);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
+ card->info.unique_id;
+ memcpy(&cmd->data.create_destroy_addr.unique_id[0],
+ card->dev->dev_addr, OSA_ADDR_LEN);
+ rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+#else
+ card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
+ UNIQUE_ID_NOT_BY_CARD;
+#endif
+ return rc;
+}
+
+/**
+ * Clear IP List
+ */
+static void
+qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
+{
+ struct qeth_ipaddr *addr, *tmp;
+ int first_run = 1;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace,4,"clearip");
+ spin_lock_irqsave(&card->ip_lock, flags);
+ /* clear todo list */
+ list_for_each_entry_safe(addr, tmp, &card->ip_tbd_list, entry){
+ list_del(&addr->entry);
+ kfree(addr);
+ }
+again:
+ if (first_run)
+ first_run = 0;
+ else
+ spin_lock_irqsave(&card->ip_lock, flags);
+
+ list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+ list_del_init(&addr->entry);
+ if (clean){
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ qeth_deregister_addr_entry(card, addr);
+ }
+ if (!recover || addr->is_multicast)
+ kfree(addr);
+ else {
+ if (clean)
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_add_tail(&addr->entry, &card->ip_tbd_list);
+ if (clean) {
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ goto again;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+static void
+qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads,
+ int clear_start_mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ card->thread_allowed_mask = threads;
+ if (clear_start_mask)
+ card->thread_start_mask &= threads;
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ wake_up(&card->wait_q);
+}
+
+static inline int
+qeth_threads_running(struct qeth_card *card, unsigned long threads)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&card->thread_mask_lock, flags);
+ rc = (card->thread_running_mask & threads);
+ spin_unlock_irqrestore(&card->thread_mask_lock, flags);
+ return rc;
+}
+
+static int
+qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)
+{
+ return wait_event_interruptible(card->wait_q,
+ qeth_threads_running(card, threads) == 0);
+}
+
+static int
+qeth_stop_card(struct qeth_card *card)
+{
+ int recover_flag = 0;
+ int rc = 0;
+
+ QETH_DBF_TEXT(setup ,2,"stopcard");
+ QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+
+ qeth_set_allowed_threads(card, 0, 1);
+ if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD))
+ return -ERESTARTSYS;
+ if (card->read.state == CH_STATE_UP &&
+ card->write.state == CH_STATE_UP &&
+ ((card->state == CARD_STATE_UP_LAN_ONLINE) ||
+ (card->state == CARD_STATE_UP_LAN_OFFLINE))) {
+ recover_flag = 1;
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ if (!card->use_hard_stop)
+ if ((rc = qeth_send_stoplan(card)))
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ card->state = CARD_STATE_SOFTSETUP;
+ }
+ if (card->state == CARD_STATE_SOFTSETUP) {
+ qeth_clear_ip_list(card, !card->use_hard_stop, recover_flag);
+ qeth_clear_ipacmd_list(card);
+ card->state = CARD_STATE_HARDSETUP;
+ }
+ if (card->state == CARD_STATE_HARDSETUP) {
+ if (!card->use_hard_stop)
+ if ((rc = qeth_put_unique_id(card)))
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ qeth_qdio_clear_card(card, 0);
+ qeth_clear_qdio_buffers(card);
+ qeth_clear_working_pool_list(card);
+ card->state = CARD_STATE_DOWN;
+ }
+ if (card->state == CARD_STATE_DOWN) {
+ qeth_clear_cmd_buffers(&card->read);
+ qeth_clear_cmd_buffers(&card->write);
+ }
+ card->use_hard_stop = 0;
+ return rc;
+}
+
+
+static int
+qeth_get_unique_id(struct qeth_card *card)
+{
+ int rc = 0;
+#ifdef CONFIG_QETH_IPV6
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(setup, 2, "guniqeid");
+
+ if (!qeth_is_supported(card,IPA_IPV6)) {
+ card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
+ UNIQUE_ID_NOT_BY_CARD;
+ return 0;
+ }
+
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR,
+ QETH_PROT_IPV6);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
+ card->info.unique_id;
+
+ rc = qeth_send_ipa_cmd(card, iob, qeth_get_unique_id_cb, NULL);
+#else
+ card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
+ UNIQUE_ID_NOT_BY_CARD;
+#endif
+ return rc;
+}
+static void
+qeth_print_status_with_portname(struct qeth_card *card)
+{
+ char dbf_text[15];
+ int i;
+
+ sprintf(dbf_text, "%s", card->info.portname + 1);
+ for (i = 0; i < 8; i++)
+ dbf_text[i] =
+ (char) _ebcasc[(__u8) dbf_text[i]];
+ dbf_text[8] = 0;
+ printk("qeth: Device %s/%s/%s is a%s card%s%s%s\n"
+ "with link type %s (portname: %s)\n",
+ CARD_RDEV_ID(card),
+ CARD_WDEV_ID(card),
+ CARD_DDEV_ID(card),
+ qeth_get_cardname(card),
+ (card->info.mcl_level[0]) ? " (level: " : "",
+ (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+ (card->info.mcl_level[0]) ? ")" : "",
+ qeth_get_cardname_short(card),
+ dbf_text);
+
+}
+
+static void
+qeth_print_status_no_portname(struct qeth_card *card)
+{
+ if (card->info.portname[0])
+ printk("qeth: Device %s/%s/%s is a%s "
+ "card%s%s%s\nwith link type %s "
+ "(no portname needed by interface).\n",
+ CARD_RDEV_ID(card),
+ CARD_WDEV_ID(card),
+ CARD_DDEV_ID(card),
+ qeth_get_cardname(card),
+ (card->info.mcl_level[0]) ? " (level: " : "",
+ (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+ (card->info.mcl_level[0]) ? ")" : "",
+ qeth_get_cardname_short(card));
+ else
+ printk("qeth: Device %s/%s/%s is a%s "
+ "card%s%s%s\nwith link type %s.\n",
+ CARD_RDEV_ID(card),
+ CARD_WDEV_ID(card),
+ CARD_DDEV_ID(card),
+ qeth_get_cardname(card),
+ (card->info.mcl_level[0]) ? " (level: " : "",
+ (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+ (card->info.mcl_level[0]) ? ")" : "",
+ qeth_get_cardname_short(card));
+}
+
+static void
+qeth_print_status_message(struct qeth_card *card)
+{
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSAE:
+ /* VM will use a non-zero first character
+ * to indicate a HiperSockets like reporting
+ * of the level OSA sets the first character to zero
+ * */
+ if (!card->info.mcl_level[0]) {
+ sprintf(card->info.mcl_level,"%02x%02x",
+ card->info.mcl_level[2],
+ card->info.mcl_level[3]);
+
+ card->info.mcl_level[QETH_MCL_LENGTH] = 0;
+ break;
+ }
+ /* fallthrough */
+ case QETH_CARD_TYPE_IQD:
+ card->info.mcl_level[0] = (char) _ebcasc[(__u8)
+ card->info.mcl_level[0]];
+ card->info.mcl_level[1] = (char) _ebcasc[(__u8)
+ card->info.mcl_level[1]];
+ card->info.mcl_level[2] = (char) _ebcasc[(__u8)
+ card->info.mcl_level[2]];
+ card->info.mcl_level[3] = (char) _ebcasc[(__u8)
+ card->info.mcl_level[3]];
+ card->info.mcl_level[QETH_MCL_LENGTH] = 0;
+ break;
+ default:
+ memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1);
+ }
+ if (card->info.portname_required)
+ qeth_print_status_with_portname(card);
+ else
+ qeth_print_status_no_portname(card);
+}
+
+static int
+qeth_register_netdev(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(setup, 3, "regnetd");
+ if (card->dev->reg_state != NETREG_UNINITIALIZED)
+ return 0;
+ /* sysfs magic */
+ SET_NETDEV_DEV(card->dev, &card->gdev->dev);
+ rc = register_netdev(card->dev);
+ if (!rc)
+ strcpy(card->info.if_name, card->dev->name);
+
+ return rc;
+}
+
+static void
+qeth_start_again(struct qeth_card *card)
+{
+ QETH_DBF_TEXT(setup ,2, "startag");
+
+ rtnl_lock();
+ dev_open(card->dev);
+ rtnl_unlock();
+ qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+
+static int
+qeth_set_online(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = gdev->dev.driver_data;
+ int rc = 0;
+ enum qeth_card_states recover_flag;
+
+ BUG_ON(!card);
+ QETH_DBF_TEXT(setup ,2, "setonlin");
+ QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+
+ qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1);
+ if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD)){
+ PRINT_WARN("set_online of card %s interrupted by user!\n",
+ CARD_BUS_ID(card));
+ return -ERESTARTSYS;
+ }
+
+ recover_flag = card->state;
+ if (ccw_device_set_online(CARD_RDEV(card)) ||
+ ccw_device_set_online(CARD_WDEV(card)) ||
+ ccw_device_set_online(CARD_DDEV(card))){
+ QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+ return -EIO;
+ }
+
+ if ((rc = qeth_hardsetup_card(card))){
+ QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+ goto out_remove;
+ }
+ card->state = CARD_STATE_HARDSETUP;
+
+ if ((rc = qeth_query_ipassists(card,QETH_PROT_IPV4))){
+ QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+ /*TODO: rc !=0*/
+ } else
+ rc = qeth_get_unique_id(card);
+
+ if (rc) {
+ QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+ goto out_remove;
+ }
+ qeth_print_status_message(card);
+ if ((rc = qeth_register_netdev(card))){
+ QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+ goto out_remove;
+ }
+ if ((rc = qeth_softsetup_card(card))){
+ QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+ goto out_remove;
+ }
+ card->state = CARD_STATE_SOFTSETUP;
+
+ if ((rc = qeth_init_qdio_queues(card))){
+ QETH_DBF_TEXT_(setup, 2, "7err%d", rc);
+ goto out_remove;
+ }
+/*maybe it was set offline without ifconfig down
+ * we can also use this state for recovery purposes*/
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (recover_flag == CARD_STATE_RECOVER)
+ qeth_start_again(card);
+
+ return 0;
+out_remove:
+ card->use_hard_stop = 1;
+ qeth_stop_card(card);
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ if (recover_flag == CARD_STATE_RECOVER)
+ card->state = CARD_STATE_RECOVER;
+ else
+ card->state = CARD_STATE_DOWN;
+ return -ENODEV;
+}
+
+static struct ccw_device_id qeth_ids[] = {
+ {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
+ {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, qeth_ids);
+
+struct device *qeth_root_dev = NULL;
+
+struct ccwgroup_driver qeth_ccwgroup_driver = {
+ .owner = THIS_MODULE,
+ .name = "qeth",
+ .driver_id = 0xD8C5E3C8,
+ .probe = qeth_probe_device,
+ .remove = qeth_remove_device,
+ .set_online = qeth_set_online,
+ .set_offline = qeth_set_offline,
+};
+
+struct ccw_driver qeth_ccw_driver = {
+ .name = "qeth",
+ .ids = qeth_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+};
+
+
+static void
+qeth_unregister_dbf_views(void)
+{
+ if (qeth_dbf_setup)
+ debug_unregister(qeth_dbf_setup);
+ if (qeth_dbf_qerr)
+ debug_unregister(qeth_dbf_qerr);
+ if (qeth_dbf_sense)
+ debug_unregister(qeth_dbf_sense);
+ if (qeth_dbf_misc)
+ debug_unregister(qeth_dbf_misc);
+ if (qeth_dbf_data)
+ debug_unregister(qeth_dbf_data);
+ if (qeth_dbf_control)
+ debug_unregister(qeth_dbf_control);
+ if (qeth_dbf_trace)
+ debug_unregister(qeth_dbf_trace);
+}
+static int
+qeth_register_dbf_views(void)
+{
+ qeth_dbf_setup = debug_register(QETH_DBF_SETUP_NAME,
+ QETH_DBF_SETUP_INDEX,
+ QETH_DBF_SETUP_NR_AREAS,
+ QETH_DBF_SETUP_LEN);
+ qeth_dbf_misc = debug_register(QETH_DBF_MISC_NAME,
+ QETH_DBF_MISC_INDEX,
+ QETH_DBF_MISC_NR_AREAS,
+ QETH_DBF_MISC_LEN);
+ qeth_dbf_data = debug_register(QETH_DBF_DATA_NAME,
+ QETH_DBF_DATA_INDEX,
+ QETH_DBF_DATA_NR_AREAS,
+ QETH_DBF_DATA_LEN);
+ qeth_dbf_control = debug_register(QETH_DBF_CONTROL_NAME,
+ QETH_DBF_CONTROL_INDEX,
+ QETH_DBF_CONTROL_NR_AREAS,
+ QETH_DBF_CONTROL_LEN);
+ qeth_dbf_sense = debug_register(QETH_DBF_SENSE_NAME,
+ QETH_DBF_SENSE_INDEX,
+ QETH_DBF_SENSE_NR_AREAS,
+ QETH_DBF_SENSE_LEN);
+ qeth_dbf_qerr = debug_register(QETH_DBF_QERR_NAME,
+ QETH_DBF_QERR_INDEX,
+ QETH_DBF_QERR_NR_AREAS,
+ QETH_DBF_QERR_LEN);
+ qeth_dbf_trace = debug_register(QETH_DBF_TRACE_NAME,
+ QETH_DBF_TRACE_INDEX,
+ QETH_DBF_TRACE_NR_AREAS,
+ QETH_DBF_TRACE_LEN);
+
+ if ((qeth_dbf_setup == NULL) || (qeth_dbf_misc == NULL) ||
+ (qeth_dbf_data == NULL) || (qeth_dbf_control == NULL) ||
+ (qeth_dbf_sense == NULL) || (qeth_dbf_qerr == NULL) ||
+ (qeth_dbf_trace == NULL)) {
+ qeth_unregister_dbf_views();
+ return -ENOMEM;
+ }
+ debug_register_view(qeth_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_setup, QETH_DBF_SETUP_LEVEL);
+
+ debug_register_view(qeth_dbf_misc, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_misc, QETH_DBF_MISC_LEVEL);
+
+ debug_register_view(qeth_dbf_data, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_data, QETH_DBF_DATA_LEVEL);
+
+ debug_register_view(qeth_dbf_control, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_control, QETH_DBF_CONTROL_LEVEL);
+
+ debug_register_view(qeth_dbf_sense, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_sense, QETH_DBF_SENSE_LEVEL);
+
+ debug_register_view(qeth_dbf_qerr, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_qerr, QETH_DBF_QERR_LEVEL);
+
+ debug_register_view(qeth_dbf_trace, &debug_hex_ascii_view);
+ debug_set_level(qeth_dbf_trace, QETH_DBF_TRACE_LEVEL);
+
+ return 0;
+}
+
+#ifdef CONFIG_QETH_IPV6
+extern struct neigh_table arp_tbl;
+static struct neigh_ops *arp_direct_ops;
+static int (*qeth_old_arp_constructor) (struct neighbour *);
+
+static struct neigh_ops arp_direct_ops_template = {
+ .family = AF_INET,
+ .destructor = NULL,
+ .solicit = NULL,
+ .error_report = NULL,
+ .output = dev_queue_xmit,
+ .connected_output = dev_queue_xmit,
+ .hh_output = dev_queue_xmit,
+ .queue_xmit = dev_queue_xmit
+};
+
+static int
+qeth_arp_constructor(struct neighbour *neigh)
+{
+ struct net_device *dev = neigh->dev;
+ struct in_device *in_dev = in_dev_get(dev);
+
+ if (in_dev == NULL)
+ return -EINVAL;
+ if (!qeth_verify_dev(dev)) {
+ in_dev_put(in_dev);
+ return qeth_old_arp_constructor(neigh);
+ }
+
+ neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
+ if (in_dev->arp_parms)
+ neigh->parms = in_dev->arp_parms;
+ in_dev_put(in_dev);
+ neigh->nud_state = NUD_NOARP;
+ neigh->ops = arp_direct_ops;
+ neigh->output = neigh->ops->queue_xmit;
+ return 0;
+}
+#endif /*CONFIG_QETH_IPV6*/
+
+/*
+ * IP address takeover related functions
+ */
+static void
+qeth_clear_ipato_list(struct qeth_card *card)
+{
+ struct qeth_ipato_entry *ipatoe, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
+ list_del(&ipatoe->entry);
+ kfree(ipatoe);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+int
+qeth_add_ipato_entry(struct qeth_card *card, struct qeth_ipato_entry *new)
+{
+ struct qeth_ipato_entry *ipatoe;
+ unsigned long flags;
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace, 2, "addipato");
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipatoe, &card->ipato.entries, entry){
+ if (ipatoe->proto != new->proto)
+ continue;
+ if (!memcmp(ipatoe->addr, new->addr,
+ (ipatoe->proto == QETH_PROT_IPV4)? 4:16) &&
+ (ipatoe->mask_bits == new->mask_bits)){
+ PRINT_WARN("ipato entry already exists!\n");
+ rc = -EEXIST;
+ break;
+ }
+ }
+ if (!rc) {
+ list_add_tail(&new->entry, &card->ipato.entries);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ return rc;
+}
+
+void
+qeth_del_ipato_entry(struct qeth_card *card, enum qeth_prot_versions proto,
+ u8 *addr, int mask_bits)
+{
+ struct qeth_ipato_entry *ipatoe, *tmp;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace, 2, "delipato");
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry){
+ if (ipatoe->proto != proto)
+ continue;
+ if (!memcmp(ipatoe->addr, addr,
+ (proto == QETH_PROT_IPV4)? 4:16) &&
+ (ipatoe->mask_bits == mask_bits)){
+ list_del(&ipatoe->entry);
+ kfree(ipatoe);
+ }
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+}
+
+static inline void
+qeth_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
+{
+ int i, j;
+ u8 octet;
+
+ for (i = 0; i < len; ++i){
+ octet = addr[i];
+ for (j = 7; j >= 0; --j){
+ bits[i*8 + j] = octet & 1;
+ octet >>= 1;
+ }
+ }
+}
+
+static int
+qeth_is_addr_covered_by_ipato(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+ struct qeth_ipato_entry *ipatoe;
+ u8 addr_bits[128] = {0, };
+ u8 ipatoe_bits[128] = {0, };
+ int rc = 0;
+
+ if (!card->ipato.enabled)
+ return 0;
+
+ qeth_convert_addr_to_bits((u8 *) &addr->u, addr_bits,
+ (addr->proto == QETH_PROT_IPV4)? 4:16);
+ list_for_each_entry(ipatoe, &card->ipato.entries, entry){
+ if (addr->proto != ipatoe->proto)
+ continue;
+ qeth_convert_addr_to_bits(ipatoe->addr, ipatoe_bits,
+ (ipatoe->proto==QETH_PROT_IPV4) ?
+ 4:16);
+ if (addr->proto == QETH_PROT_IPV4)
+ rc = !memcmp(addr_bits, ipatoe_bits,
+ min(32, ipatoe->mask_bits));
+ else
+ rc = !memcmp(addr_bits, ipatoe_bits,
+ min(128, ipatoe->mask_bits));
+ if (rc)
+ break;
+ }
+ /* invert? */
+ if ((addr->proto == QETH_PROT_IPV4) && card->ipato.invert4)
+ rc = !rc;
+ else if ((addr->proto == QETH_PROT_IPV6) && card->ipato.invert6)
+ rc = !rc;
+
+ return rc;
+}
+
+/*
+ * VIPA related functions
+ */
+int
+qeth_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+ unsigned long flags;
+ int rc = 0;
+
+ ipaddr = qeth_get_addr_buffer(proto);
+ if (ipaddr){
+ if (proto == QETH_PROT_IPV4){
+ QETH_DBF_TEXT(trace, 2, "addvipa4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+#ifdef CONFIG_QETH_IPV6
+ } else if (proto == QETH_PROT_IPV6){
+ QETH_DBF_TEXT(trace, 2, "addvipa6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+#endif
+ }
+ ipaddr->type = QETH_IP_TYPE_VIPA;
+ ipaddr->set_flags = QETH_IPA_SETIP_VIPA_FLAG;
+ ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG;
+ } else
+ return -ENOMEM;
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
+ __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+ rc = -EEXIST;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ if (rc){
+ PRINT_WARN("Cannot add VIPA. Address already exists!\n");
+ return rc;
+ }
+ if (!qeth_add_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+ return rc;
+}
+
+void
+qeth_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+
+ ipaddr = qeth_get_addr_buffer(proto);
+ if (ipaddr){
+ if (proto == QETH_PROT_IPV4){
+ QETH_DBF_TEXT(trace, 2, "delvipa4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+#ifdef CONFIG_QETH_IPV6
+ } else if (proto == QETH_PROT_IPV6){
+ QETH_DBF_TEXT(trace, 2, "delvipa6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+#endif
+ }
+ ipaddr->type = QETH_IP_TYPE_VIPA;
+ } else
+ return;
+ if (!qeth_delete_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+
+/*
+ * proxy ARP related functions
+ */
+int
+qeth_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+ unsigned long flags;
+ int rc = 0;
+
+ ipaddr = qeth_get_addr_buffer(proto);
+ if (ipaddr){
+ if (proto == QETH_PROT_IPV4){
+ QETH_DBF_TEXT(trace, 2, "addrxip4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+#ifdef CONFIG_QETH_IPV6
+ } else if (proto == QETH_PROT_IPV6){
+ QETH_DBF_TEXT(trace, 2, "addrxip6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+#endif
+ }
+ ipaddr->type = QETH_IP_TYPE_RXIP;
+ ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG;
+ ipaddr->del_flags = 0;
+ } else
+ return -ENOMEM;
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
+ __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+ rc = -EEXIST;
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ if (rc){
+ PRINT_WARN("Cannot add RXIP. Address already exists!\n");
+ return rc;
+ }
+ if (!qeth_add_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+ return 0;
+}
+
+void
+qeth_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
+ const u8 *addr)
+{
+ struct qeth_ipaddr *ipaddr;
+
+ ipaddr = qeth_get_addr_buffer(proto);
+ if (ipaddr){
+ if (proto == QETH_PROT_IPV4){
+ QETH_DBF_TEXT(trace, 2, "addrxip4");
+ memcpy(&ipaddr->u.a4.addr, addr, 4);
+ ipaddr->u.a4.mask = 0;
+#ifdef CONFIG_QETH_IPV6
+ } else if (proto == QETH_PROT_IPV6){
+ QETH_DBF_TEXT(trace, 2, "addrxip6");
+ memcpy(&ipaddr->u.a6.addr, addr, 16);
+ ipaddr->u.a6.pfxlen = 0;
+#endif
+ }
+ ipaddr->type = QETH_IP_TYPE_RXIP;
+ } else
+ return;
+ if (!qeth_delete_ip(card, ipaddr))
+ kfree(ipaddr);
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+}
+
+/**
+ * IP event handler
+ */
+static int
+qeth_ip_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct net_device *dev =(struct net_device *) ifa->ifa_dev->dev;
+ struct qeth_ipaddr *addr;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,3,"ipevent");
+ card = qeth_get_card_from_dev(dev);
+ if (!card)
+ return NOTIFY_DONE;
+
+ addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
+ if (addr != NULL) {
+ addr->u.a4.addr = ifa->ifa_address;
+ addr->u.a4.mask = ifa->ifa_mask;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ }
+ switch(event) {
+ case NETDEV_UP:
+ if (addr) {
+ if (!qeth_add_ip(card, addr))
+ kfree(addr);
+ }
+ break;
+ case NETDEV_DOWN:
+ if (addr) {
+ if (!qeth_delete_ip(card, addr))
+ kfree(addr);
+ }
+ break;
+ default:
+ break;
+ }
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block qeth_ip_notifier = {
+ qeth_ip_event,
+ 0
+};
+
+#ifdef CONFIG_QETH_IPV6
+/**
+ * IPv6 event handler
+ */
+static int
+qeth_ip6_event(struct notifier_block *this,
+ unsigned long event,void *ptr)
+{
+
+ struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
+ struct net_device *dev = (struct net_device *)ifa->idev->dev;
+ struct qeth_ipaddr *addr;
+ struct qeth_card *card;
+
+ QETH_DBF_TEXT(trace,3,"ip6event");
+
+ card = qeth_get_card_from_dev(dev);
+ if (!card)
+ return NOTIFY_DONE;
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return NOTIFY_DONE;
+
+ addr = qeth_get_addr_buffer(QETH_PROT_IPV6);
+ if (addr != NULL) {
+ memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr));
+ addr->u.a6.pfxlen = ifa->prefix_len;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ }
+ switch(event) {
+ case NETDEV_UP:
+ if (addr){
+ if (!qeth_add_ip(card, addr))
+ kfree(addr);
+ }
+ break;
+ case NETDEV_DOWN:
+ if (addr){
+ if (!qeth_delete_ip(card, addr))
+ kfree(addr);
+ }
+ break;
+ default:
+ break;
+ }
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
+ schedule_work(&card->kernel_thread_starter);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block qeth_ip6_notifier = {
+ qeth_ip6_event,
+ 0
+};
+#endif
+
+static int
+qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+
+ struct device *entry;
+ struct qeth_card *card;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+ list_for_each_entry(entry, &qeth_ccwgroup_driver.driver.devices,
+ driver_list) {
+ card = (struct qeth_card *) entry->driver_data;
+ qeth_clear_ip_list(card, 0, 0);
+ qeth_qdio_clear_card(card, 0);
+ }
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block qeth_reboot_notifier = {
+ qeth_reboot_event,
+ 0
+};
+
+static int
+qeth_register_notifiers(void)
+{
+ int r;
+
+ QETH_DBF_TEXT(trace,5,"regnotif");
+ if ((r = register_reboot_notifier(&qeth_reboot_notifier)))
+ return r;
+ if ((r = register_inetaddr_notifier(&qeth_ip_notifier)))
+ goto out_reboot;
+#ifdef CONFIG_QETH_IPV6
+ if ((r = register_inet6addr_notifier(&qeth_ip6_notifier)))
+ goto out_ipv4;
+#endif
+ return 0;
+
+#ifdef CONFIG_QETH_IPV6
+out_ipv4:
+ unregister_inetaddr_notifier(&qeth_ip_notifier);
+#endif
+out_reboot:
+ unregister_reboot_notifier(&qeth_reboot_notifier);
+ return r;
+}
+
+/**
+ * unregister all event notifiers
+ */
+static void
+qeth_unregister_notifiers(void)
+{
+
+ QETH_DBF_TEXT(trace,5,"unregnot");
+ BUG_ON(unregister_reboot_notifier(&qeth_reboot_notifier));
+ BUG_ON(unregister_inetaddr_notifier(&qeth_ip_notifier));
+#ifdef CONFIG_QETH_IPV6
+ BUG_ON(unregister_inet6addr_notifier(&qeth_ip6_notifier));
+#endif /* QETH_IPV6 */
+
+}
+
+#ifdef CONFIG_QETH_IPV6
+static int
+qeth_ipv6_init(void)
+{
+ qeth_old_arp_constructor = arp_tbl.constructor;
+ write_lock(&arp_tbl.lock);
+ arp_tbl.constructor = qeth_arp_constructor;
+ write_unlock(&arp_tbl.lock);
+
+ arp_direct_ops = (struct neigh_ops*)
+ kmalloc(sizeof(struct neigh_ops), GFP_KERNEL);
+ if (!arp_direct_ops)
+ return -ENOMEM;
+
+ memcpy(arp_direct_ops, &arp_direct_ops_template,
+ sizeof(struct neigh_ops));
+
+ return 0;
+}
+
+static void
+qeth_ipv6_uninit(void)
+{
+ write_lock(&arp_tbl.lock);
+ arp_tbl.constructor = qeth_old_arp_constructor;
+ write_unlock(&arp_tbl.lock);
+ kfree(arp_direct_ops);
+}
+#endif /* CONFIG_QETH_IPV6 */
+
+static void
+qeth_sysfs_unregister(void)
+{
+ qeth_remove_driver_attributes();
+ ccw_driver_unregister(&qeth_ccw_driver);
+ ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
+ s390_root_dev_unregister(qeth_root_dev);
+}
+/**
+ * register qeth at sysfs
+ */
+static int
+qeth_sysfs_register(void)
+{
+ int rc=0;
+
+ rc = ccwgroup_driver_register(&qeth_ccwgroup_driver);
+ if (rc)
+ return rc;
+ rc = ccw_driver_register(&qeth_ccw_driver);
+ if (rc)
+ return rc;
+ rc = qeth_create_driver_attributes();
+ if (rc)
+ return rc;
+ qeth_root_dev = s390_root_dev_register("qeth");
+ if (IS_ERR(qeth_root_dev)) {
+ rc = PTR_ERR(qeth_root_dev);
+ return rc;
+ }
+ return 0;
+}
+
+/***
+ * init function
+ */
+static int __init
+qeth_init(void)
+{
+ int rc=0;
+
+ qeth_eyecatcher();
+ printk(KERN_INFO "qeth: loading %s\n",version);
+
+ INIT_LIST_HEAD(&qeth_card_list.list);
+ rwlock_init(&qeth_card_list.rwlock);
+
+ atomic_set(&qeth_hsi_count, 0);
+ if (qeth_register_dbf_views())
+ goto out_err;
+ if (qeth_sysfs_register())
+ goto out_sysfs;
+
+#ifdef CONFIG_QETH_IPV6
+ if (qeth_ipv6_init()) {
+ PRINT_ERR("Out of memory during ipv6 init.\n");
+ goto out_sysfs;
+ }
+#endif /* QETH_IPV6 */
+ if (qeth_register_notifiers())
+ goto out_ipv6;
+ if (qeth_create_procfs_entries())
+ goto out_notifiers;
+
+ return rc;
+
+out_notifiers:
+ qeth_unregister_notifiers();
+out_ipv6:
+#ifdef CONFIG_QETH_IPV6
+ qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+out_sysfs:
+ qeth_sysfs_unregister();
+ qeth_unregister_dbf_views();
+out_err:
+ PRINT_ERR("Initialization failed");
+ return rc;
+}
+
+static void
+__exit qeth_exit(void)
+{
+ struct qeth_card *card, *tmp;
+ unsigned long flags;
+
+ QETH_DBF_TEXT(trace,1, "cleanup.");
+
+ /*
+ * Weed would not need to clean up our devices here, because the
+ * common device layer calls qeth_remove_device for each device
+ * as soon as we unregister our driver (done in qeth_sysfs_unregister).
+ * But we do cleanup here so we can do a "soft" shutdown of our cards.
+ * qeth_remove_device called by the common device layer would otherwise
+ * do a "hard" shutdown (card->use_hard_stop is set to one in
+ * qeth_remove_device).
+ */
+again:
+ read_lock_irqsave(&qeth_card_list.rwlock, flags);
+ list_for_each_entry_safe(card, tmp, &qeth_card_list.list, list){
+ read_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+ qeth_set_offline(card->gdev);
+ qeth_remove_device(card->gdev);
+ goto again;
+ }
+ read_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+#ifdef CONFIG_QETH_IPV6
+ qeth_ipv6_uninit();
+#endif
+ qeth_unregister_notifiers();
+ qeth_remove_procfs_entries();
+ qeth_sysfs_unregister();
+ qeth_unregister_dbf_views();
+ printk("qeth: removed\n");
+}
+
+EXPORT_SYMBOL(qeth_eyecatcher);
+module_init(qeth_init);
+module_exit(qeth_exit);
+MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
+MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
+ "Copyright 2000,2003 IBM Corporation\n");
+
+MODULE_LICENSE("GPL");
* Linux on zSeries OSA Express and HiperSockets support
*
* Copyright 2000,2003 IBM Corporation
- * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Author(s): Frank Pavlic <pavlic@de.ibm.com>
+ * Thomas Spatzier <tspat@de.ibm.com>
*
*/
#include <asm/cio.h>
unsigned char IPA_PDU_HEADER[]={
0x00,0xe0,0x00,0x00, 0x77,0x77,0x77,0x77,
0x00,0x00,0x00,0x14, 0x00,0x00,
- (IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd))/256,
- (IPA_PDU_HEADER_SIZE+sizeof(struct ipa_cmd))%256,
+ (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd))/256,
+ (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd))%256,
0x10,0x00,0x00,0x01,
0x00,0x00,0x00,0x00,
0xc1,0x03,0x00,0x01, 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00, 0x00,0x24,0x00,sizeof(struct ipa_cmd),
- 0x00,0x00,sizeof(struct ipa_cmd),0x05, 0x77,0x77,0x77,0x77,
+ 0x00,0x00,0x00,0x00, 0x00,0x24,
+ sizeof(struct qeth_ipa_cmd)/256,
+ sizeof(struct qeth_ipa_cmd)%256,
+ 0x00,
+ sizeof(struct qeth_ipa_cmd)/256,
+ sizeof(struct qeth_ipa_cmd),0x05, 0x77,0x77,0x77,0x77,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
- 0x01,0x00,sizeof(struct ipa_cmd)/256,sizeof(struct ipa_cmd)%256,
- 0x00,0x00,0x00,0x40,
+ 0x01,0x00,
+ sizeof(struct qeth_ipa_cmd)/256,
+ sizeof(struct qeth_ipa_cmd)%256,
+ 0x00,0x00,0x00,0x40,
};
unsigned char WRITE_CCW[]={
-
*
* Copyright 2000,2003 IBM Corporation
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Thomas Spatzier <tspat@de.ibm.com>
+ * Frank Pavlic <pavlic@de.ibm.com>
*
*/
#ifndef __QETH_MPC_H__
#define __QETH_MPC_H__
-#define VERSION_QETH_MPC_H "$Revision: 1.18 $"
+#include <asm/qeth.h>
-#define QETH_IPA_TIMEOUT (card->ipa_timeout)
-#define QETH_MPC_TIMEOUT 2000
-#define QETH_ADDR_TIMEOUT 1000
+#define VERSION_QETH_MPC_H "$Revision: 1.27 $"
-#define QETH_SETIP_RETRIES 2
-
-#define IDX_ACTIVATE_SIZE 0x22
-#define CM_ENABLE_SIZE 0x63
-#define CM_SETUP_SIZE 0x64
-#define ULP_ENABLE_SIZE 0x6b
-#define ULP_SETUP_SIZE 0x6c
-#define DM_ACT_SIZE 0x55
+#define IPA_PDU_HEADER_SIZE 0x40
+#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e)
+#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26)
+#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a)
+#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a)
-#define QETH_MPC_TOKEN_LENGTH 4
-#define QETH_SEQ_NO_LENGTH 4
-#define QETH_IPA_SEQ_NO_LENGTH 2
+extern unsigned char IPA_PDU_HEADER[];
+#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
-#define QETH_TRANSPORT_HEADER_SEQ_NO(buffer) (buffer+4)
-#define QETH_PDU_HEADER_SEQ_NO(buffer) (buffer+0x1c)
-#define QETH_PDU_HEADER_ACK_SEQ_NO(buffer) (buffer+0x20)
+#define IPA_CMD_LENGTH (IPA_PDU_HEADER_SIZE + sizeof(struct qeth_ipa_cmd))
-extern unsigned char IDX_ACTIVATE_READ[];
+#define QETH_SEQ_NO_LENGTH 4
+#define QETH_MPC_TOKEN_LENGTH 4
+#define QETH_MCL_LENGTH 4
+#define OSA_ADDR_LEN 6
-extern unsigned char IDX_ACTIVATE_WRITE[];
+#define QETH_TIMEOUT (10 * HZ)
+#define QETH_IDX_COMMAND_SEQNO -1
+#define SR_INFO_LEN 16
-#define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer+0x0c)
-#define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b]&0x80)
-#define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer+0x10)
-#define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer+0x16)
-#define QETH_IDX_ACT_QDIO_DEV_CUA(buffer) (buffer+0x1e)
-#define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20)
+#define QETH_CLEAR_CHANNEL_PARM -10
+#define QETH_HALT_CHANNEL_PARM -11
-#define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2)
+/*****************************************************************************/
+/* IP Assist related definitions */
+/*****************************************************************************/
+#define IPA_CMD_INITIATOR_HOST 0x00
+#define IPA_CMD_INITIATOR_HYDRA 0x01
+#define IPA_CMD_PRIM_VERSION_NO 0x01
-#define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12)
-#define QETH_MCL_LENGTH 4
+enum qeth_card_types {
+ QETH_CARD_TYPE_UNKNOWN = 0,
+ QETH_CARD_TYPE_OSAE = 10,
+ QETH_CARD_TYPE_IQD = 1234,
+};
-extern unsigned char CM_ENABLE[];
+#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
+/* only the first two bytes are looked at in qeth_get_cardname_short */
+enum qeth_link_types {
+ QETH_LINK_TYPE_FAST_ETH = 0x01,
+ QETH_LINK_TYPE_HSTR = 0x02,
+ QETH_LINK_TYPE_GBIT_ETH = 0x03,
+ QETH_LINK_TYPE_10GBIT_ETH = 0x10,
+ QETH_LINK_TYPE_LANE_ETH100 = 0x81,
+ QETH_LINK_TYPE_LANE_TR = 0x82,
+ QETH_LINK_TYPE_LANE_ETH1000 = 0x83,
+ QETH_LINK_TYPE_LANE = 0x88,
+ QETH_LINK_TYPE_ATM_NATIVE = 0x90,
+};
-#define QETH_CM_ENABLE_ISSUER_RM_TOKEN(buffer) (buffer+0x2c)
-#define QETH_CM_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
-#define QETH_CM_ENABLE_USER_DATA(buffer) (buffer+0x5b)
+enum qeth_tr_macaddr_modes {
+ QETH_TR_MACADDR_NONCANONICAL = 0,
+ QETH_TR_MACADDR_CANONICAL = 1,
+};
-#define QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
- 0x13)
+enum qeth_tr_broadcast_modes {
+ QETH_TR_BROADCAST_ALLRINGS = 0,
+ QETH_TR_BROADCAST_LOCAL = 1,
+};
-extern unsigned char CM_SETUP[];
+/* these values match CHECKSUM_* in include/linux/skbuff.h */
+enum qeth_checksum_types {
+ SW_CHECKSUMMING = 0, /* TODO: set to bit flag used in IPA Command */
+ HW_CHECKSUMMING = 1,
+ NO_CHECKSUMMING = 2,
+};
+#define QETH_CHECKSUM_DEFAULT SW_CHECKSUMMING
-#define QETH_CM_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
-#define QETH_CM_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
-#define QETH_CM_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
+/*
+ * Routing stuff
+ */
+#define RESET_ROUTING_FLAG 0x10 /* indicate that routing type shall be set */
+enum qeth_routing_types {
+ NO_ROUTER = 0, /* TODO: set to bit flag used in IPA Command */
+ PRIMARY_ROUTER = 1,
+ SECONDARY_ROUTER = 2,
+ MULTICAST_ROUTER = 3,
+ PRIMARY_CONNECTOR = 4,
+ SECONDARY_CONNECTOR = 5,
+};
-#define QETH_CM_SETUP_RESP_DEST_ADDR(buffer) (PDU_ENCAPSULATION(buffer)+ \
- 0x1a)
-extern unsigned char ULP_ENABLE[];
+/* IPA Commands */
+enum qeth_ipa_cmds {
+ IPA_CMD_STARTLAN = 0x01,
+ IPA_CMD_STOPLAN = 0x02,
+ IPA_CMD_SETIP = 0xb1,
+ IPA_CMD_DELIP = 0xb7,
+ IPA_CMD_QIPASSIST = 0xb2,
+ IPA_CMD_SETASSPARMS = 0xb3,
+ IPA_CMD_SETIPM = 0xb4,
+ IPA_CMD_DELIPM = 0xb5,
+ IPA_CMD_SETRTG = 0xb6,
+ IPA_CMD_SETADAPTERPARMS = 0xb8,
+ IPA_CMD_IPFRAME = 0xb9,
+ IPA_CMD_ADD_ADDR_ENTRY = 0xc1,
+ IPA_CMD_DELETE_ADDR_ENTRY = 0xc2,
+ IPA_CMD_CREATE_ADDR = 0xc3,
+ IPA_CMD_DESTROY_ADDR = 0xc4,
+ IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1,
+ IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2,
+};
-#define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61)
-#define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c)
-#define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
-#define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62)
+enum qeth_ip_ass_cmds {
+ IPA_CMD_ASS_START = 0x0001,
+ IPA_CMD_ASS_STOP = 0x0002,
+ IPA_CMD_ASS_CONFIGURE = 0x0003,
+ IPA_CMD_ASS_ENABLE = 0x0004,
+};
-#define QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
- 0x13)
-#define QETH_ULP_ENABLE_RESP_MAX_MTU(buffer) (PDU_ENCAPSULATION(buffer)+ 0x1f)
-#define QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
- 0x17)
-#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) (PDU_ENCAPSULATION(buffer)+ \
- 0x2b)
+enum qeth_arp_process_subcmds {
+ IPA_CMD_ASS_ARP_SET_NO_ENTRIES = 0x0003,
+ IPA_CMD_ASS_ARP_QUERY_CACHE = 0x0004,
+ IPA_CMD_ASS_ARP_ADD_ENTRY = 0x0005,
+ IPA_CMD_ASS_ARP_REMOVE_ENTRY = 0x0006,
+ IPA_CMD_ASS_ARP_FLUSH_CACHE = 0x0007,
+ IPA_CMD_ASS_ARP_QUERY_INFO = 0x0104,
+ IPA_CMD_ASS_ARP_QUERY_STATS = 0x0204,
+};
-extern unsigned char ULP_SETUP[];
+/* Return Codes for IPA Commands */
+enum qeth_ipa_return_codes {
+ IPA_RC_SUCCESS = 0x0000,
+ IPA_RC_NOTSUPP = 0x0001,
+ IPA_RC_NO_ACCESS = 0x0002,
+ IPA_RC_FAILED = 0x0003,
+ IPA_RC_DATA_MISMATCH = 0xe001,
+ IPA_RC_INVALID_LAN_TYPE = 0xe003,
+ IPA_RC_INVALID_LAN_NO = 0xe004,
+ IPA_RC_IPADDR_ALREADY_REG = 0xe005,
+ IPA_RC_IPADDR_TABLE_FULL = 0xe006,
+ IPA_RC_IPADDR_ALREADY_USED = 0xe00a,
+ IPA_RC_ASSNO_NOT_SUPP = 0xe00d,
+ IPA_RC_ASSCMD_START_FAILED = 0xe00e,
+ IPA_RC_ASSCMD_PART_SUCCESS = 0xe00f,
+ IPA_RC_IPADDR_NOT_DEFINED = 0xe010,
+ IPA_RC_LAN_OFFLINE = 0xe080,
+};
-#define QETH_ULP_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
-#define QETH_ULP_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
-#define QETH_ULP_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
-#define QETH_ULP_SETUP_CUA(buffer) (buffer+0x68)
-#define QETH_ULP_SETUP_REAL_DEVADDR(buffer) (buffer+0x6a)
+/* IPA function flags; each flag marks availability of respective function */
+enum qeth_ipa_funcs {
+ IPA_ARP_PROCESSING = 0x00000001L,
+ IPA_INBOUND_CHECKSUM = 0x00000002L,
+ IPA_OUTBOUND_CHECKSUM = 0x00000004L,
+ IPA_IP_FRAGMENTATION = 0x00000008L,
+ IPA_FILTERING = 0x00000010L,
+ IPA_IPV6 = 0x00000020L,
+ IPA_MULTICASTING = 0x00000040L,
+ IPA_IP_REASSEMBLY = 0x00000080L,
+ IPA_QUERY_ARP_COUNTERS = 0x00000100L,
+ IPA_QUERY_ARP_ADDR_INFO = 0x00000200L,
+ IPA_SETADAPTERPARMS = 0x00000400L,
+ IPA_VLAN_PRIO = 0x00000800L,
+ IPA_PASSTHRU = 0x00001000L,
+ IPA_FULL_VLAN = 0x00004000L,
+ IPA_SOURCE_MAC = 0x00010000L,
+ IPA_OSA_MC_ROUTER = 0x00020000L,
+};
-#define QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer) (PDU_ENCAPSULATION \
- (buffer)+0x1a)
+/* SETIP/DELIP IPA Command: ***************************************************/
+enum qeth_ipa_setdelip_flags {
+ QETH_IPA_SETDELIP_DEFAULT = 0x00L, /* default */
+ QETH_IPA_SETIP_VIPA_FLAG = 0x01L, /* no grat. ARP */
+ QETH_IPA_SETIP_TAKEOVER_FLAG = 0x02L, /* nofail on grat. ARP */
+ QETH_IPA_DELIP_ADDR_2_B_TAKEN_OVER = 0x20L,
+ QETH_IPA_DELIP_VIPA_FLAG = 0x40L,
+ QETH_IPA_DELIP_ADDR_NEEDS_SETIP = 0x80L,
+};
-extern unsigned char DM_ACT[];
+/* SETADAPTER IPA Command: ****************************************************/
+enum qeth_ipa_setadp_cmd {
+ IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x01,
+ IPA_SETADP_ALTER_MAC_ADDRESS = 0x02,
+ IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x04,
+ IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x08,
+ IPA_SETADP_SET_ADDRESSING_MODE = 0x10,
+ IPA_SETADP_SET_CONFIG_PARMS = 0x20,
+ IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x40,
+ IPA_SETADP_SET_BROADCAST_MODE = 0x80,
+ IPA_SETADP_SEND_OSA_MESSAGE = 0x0100,
+ IPA_SETADP_SET_SNMP_CONTROL = 0x0200,
+ IPA_SETADP_READ_SNMP_PARMS = 0x0400,
+ IPA_SETADP_WRITE_SNMP_PARMS = 0x0800,
+ IPA_SETADP_QUERY_CARD_INFO = 0x1000,
+};
+enum qeth_ipa_mac_ops {
+ CHANGE_ADDR_READ_MAC = 0,
+ CHANGE_ADDR_REPLACE_MAC = 1,
+ CHANGE_ADDR_ADD_MAC = 2,
+ CHANGE_ADDR_DEL_MAC = 4,
+ CHANGE_ADDR_RESET_MAC = 8,
+};
+enum qeth_ipa_addr_ops {
+ CHANGE_ADDR_READ_ADDR = 0,
+ CHANGE_ADDR_ADD_ADDR = 1,
+ CHANGE_ADDR_DEL_ADDR = 2,
+ CHANGE_ADDR_FLUSH_ADDR_TABLE = 4,
-#define QETH_DM_ACT_DEST_ADDR(buffer) (buffer+0x2c)
-#define QETH_DM_ACT_CONNECTION_TOKEN(buffer) (buffer+0x51)
-#define IPA_CMD_STARTLAN 0x01
-#define IPA_CMD_STOPLAN 0x02
-#define IPA_CMD_SETIP 0xb1
-#define IPA_CMD_DELIP 0xb7
-#define IPA_CMD_QIPASSIST 0xb2
-#define IPA_CMD_SETASSPARMS 0xb3
-#define IPA_CMD_SETIPM 0xb4
-#define IPA_CMD_DELIPM 0xb5
-#define IPA_CMD_SETRTG 0xb6
-#define IPA_CMD_SETADAPTERPARMS 0xb8
-#define IPA_CMD_ADD_ADDR_ENTRY 0xc1
-#define IPA_CMD_DELETE_ADDR_ENTRY 0xc2
-#define IPA_CMD_CREATE_ADDR 0xc3
-#define IPA_CMD_DESTROY_ADDR 0xc4
-#define IPA_CMD_REGISTER_LOCAL_ADDR 0xd1
-#define IPA_CMD_UNREGISTER_LOCAL_ADDR 0xd2
-
-#define INITIATOR_HOST 0
-#define INITIATOR_HYDRA 1
-
-#define PRIM_VERSION_IPA 1
-
-#define PROT_VERSION_SNA 1 /* hahaha */
-#define PROT_VERSION_IPv4 4
-#define PROT_VERSION_IPv6 6
-
-#define OSA_ADDR_LEN 6
-#define IPA_SETADAPTERPARMS_IP_VERSION PROT_VERSION_IPv4
-#define SR_INFO_LEN 16
-
-#define IPA_ARP_PROCESSING 0x00000001L
-#define IPA_INBOUND_CHECKSUM 0x00000002L
-#define IPA_OUTBOUND_CHECKSUM 0x00000004L
-#define IPA_IP_FRAGMENTATION 0x00000008L
-#define IPA_FILTERING 0x00000010L
-#define IPA_IPv6 0x00000020L
-#define IPA_MULTICASTING 0x00000040L
-#define IPA_IP_REASSEMBLY 0x00000080L
-#define IPA_QUERY_ARP_COUNTERS 0x00000100L
-#define IPA_QUERY_ARP_ADDR_INFO 0x00000200L
-#define IPA_SETADAPTERPARMS 0x00000400L
-#define IPA_VLAN_PRIO 0x00000800L
-#define IPA_PASSTHRU 0x00001000L
-#define IPA_FULL_VLAN 0x00004000L
-#define IPA_SOURCE_MAC_AVAIL 0x00010000L
-#define IPA_OSA_MC_ROUTER_AVAIL 0x00020000L
-
-#define IPA_SETADP_QUERY_COMMANDS_SUPPORTED 0x01
-#define IPA_SETADP_ALTER_MAC_ADDRESS 0x02
-#define IPA_SETADP_ADD_DELETE_GROUP_ADDRESS 0x04
-#define IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR 0x08
-#define IPA_SETADP_SET_ADDRESSING_MODE 0x10
-#define IPA_SETADP_SET_CONFIG_PARMS 0x20
-#define IPA_SETADP_SET_CONFIG_PARMS_EXTENDED 0x40
-#define IPA_SETADP_SET_BROADCAST_MODE 0x80
-#define IPA_SETADP_SEND_OSA_MESSAGE 0x0100
-#define IPA_SETADP_SET_SNMP_CONTROL 0x0200
-#define IPA_SETADP_READ_SNMP_PARMS 0x0400
-#define IPA_SETADP_WRITE_SNMP_PARMS 0x0800
-#define IPA_SETADP_QUERY_CARD_INFO 0x1000
-
-#define CHANGE_ADDR_READ_MAC 0
-#define CHANGE_ADDR_REPLACE_MAC 1
-#define CHANGE_ADDR_ADD_MAC 2
-#define CHANGE_ADDR_DEL_MAC 4
-#define CHANGE_ADDR_RESET_MAC 8
-#define CHANGE_ADDR_READ_ADDR 0
-#define CHANGE_ADDR_ADD_ADDR 1
-#define CHANGE_ADDR_DEL_ADDR 2
-#define CHANGE_ADDR_FLUSH_ADDR_TABLE 4
-
-/* we assumed, that the card is named card */
-#define qeth_is_supported(str) (card->ipa_supported&str)
-#define qeth_is_supported6(str) (card->ipa6_supported&str)
-#define qeth_is_adp_supported(str) (card->adp_supported&str)
-
-/* the same for all assist parms: */
-#define IPA_CMD_ASS_START 0x0001
-#define IPA_CMD_ASS_STOP 0x0002
-
-#define IPA_CMD_ASS_CONFIGURE 0x0003
-#define IPA_CMD_ASS_ENABLE 0x0004
-
-#define IPA_CMD_ASS_ARP_SET_NO_ENTRIES 0x0003
-#define IPA_CMD_ASS_ARP_QUERY_CACHE 0x0004
-#define IPA_CMD_ASS_ARP_ADD_ENTRY 0x0005
-#define IPA_CMD_ASS_ARP_REMOVE_ENTRY 0x0006
-#define IPA_CMD_ASS_ARP_FLUSH_CACHE 0x0007
-#define IPA_CMD_ASS_ARP_QUERY_INFO 0x0104
-#define IPA_CMD_ASS_ARP_QUERY_STATS 0x0204
-
-#define IPA_CHECKSUM_DEFAULT_ENABLE_MASK 0x001a
-
-#define IPA_CMD_ASS_FILTER_SET_TYPES 0x0003
-
-#define IPA_CMD_ASS_IPv6_SET_FUNCTIONS 0x0003
-
-#define IPA_REPLY_SUCCESS 0
-#define IPA_REPLY_FAILED 1
-#define IPA_REPLY_OPNOTSUPP 2
-#define IPA_REPLY_OPNOTSUPP2 4
-#define IPA_REPLY_NOINFO 8
-
-#define IPA_SETIP_FLAGS 0
-#define IPA_SETIP_VIPA_FLAGS 1
-#define IPA_SETIP_TAKEOVER_FLAGS 2
-
-#define VIPA_2_B_ADDED 0
-#define VIPA_ESTABLISHED 1
-#define VIPA_2_B_REMOVED 2
-
-#define IPA_DELIP_FLAGS 0
-
-#define IPA_SETADP_CMDSIZE 40
-
-struct ipa_setadp_cmd {
- __u32 supp_hw_cmds;
- __u32 reserved1;
- __u16 cmdlength;
- __u16 reserved2;
- __u32 command_code;
+};
+/* (SET)DELIP(M) IPA stuff ***************************************************/
+struct qeth_ipacmd_setdelip4 {
+ __u8 ip_addr[4];
+ __u8 mask[4];
+ __u32 flags;
+} __attribute__ ((packed));
+
+struct qeth_ipacmd_setdelip6 {
+ __u8 ip_addr[16];
+ __u8 mask[16];
+ __u32 flags;
+} __attribute__ ((packed));
+
+struct qeth_ipacmd_setdelipm {
+ __u8 mac[6];
+ __u8 padding[2];
+ __u8 ip6[12];
+ __u8 ip4[4];
+} __attribute__ ((packed));
+
+struct qeth_ipacmd_setassparms_hdr {
+ __u32 assist_no;
+ __u16 length;
+ __u16 command_code;
__u16 return_code;
- __u8 frames_used_total;
- __u8 frame_seq_no;
- __u32 reserved3;
+ __u8 number_of_replies;
+ __u8 seq_no;
+} __attribute__((packed));
+
+/* SETASSPARMS IPA Command: */
+struct qeth_ipacmd_setassparms {
+ struct qeth_ipacmd_setassparms_hdr hdr;
union {
- struct {
- __u32 no_lantypes_supp;
- __u8 lan_type;
- __u8 reserved1[3];
- __u32 supported_cmds;
- __u8 reserved2[8];
- } query_cmds_supp;
- struct {
- __u32 cmd;
- __u32 addr_size;
- __u32 no_macs;
- __u8 addr[OSA_ADDR_LEN];
- } change_addr;
- __u32 mode;
+ __u32 flags_32bit;
+ struct qeth_arp_cache_entry add_arp_entry;
+ __u8 ip[16];
} data;
+} __attribute__ ((packed));
+
+
+/* SETRTG IPA Command: ****************************************************/
+struct qeth_set_routing {
+ __u8 type;
};
-struct ipa_cmd{
- __u8 command;
- __u8 initiator;
- __u16 seq_no;
- __u16 return_code;
- __u8 adapter_type;
- __u8 rel_adapter_no;
- __u8 prim_version_no;
- __u8 param_count;
- __u16 prot_version;
- __u32 ipa_supported;
- __u32 ipa_enabled;
- union {
- struct {
- __u8 ip[4];
- __u8 netmask[4];
- __u32 flags;
- } setdelip4;
- struct {
- __u8 ip[16];
- __u8 netmask[16];
- __u32 flags;
- } setdelip6;
- struct {
- __u32 assist_no;
- __u16 length;
- __u16 command_code;
- __u16 return_code;
- __u8 number_of_replies;
- __u8 seq_no;
- union {
- __u32 flags_32bit;
- struct {
- __u8 mac[6];
- __u8 reserved[2];
- __u8 ip[16];
- __u8 reserved2[32];
- } add_arp_entry;
- __u8 ip[16];
- } data;
- } setassparms;
- struct {
- __u8 mac[6];
- __u8 padding[2];
- __u8 ip6[12];
- __u8 ip4_6[4];
- } setdelipm;
- struct {
- __u8 type;
- } setrtg;
- struct ipa_setadp_cmd setadapterparms;
- struct {
- __u32 command;
-#define ADDR_FRAME_TYPE_DIX 1
-#define ADDR_FRAME_TYPE_802_3 2
-#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10
-#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20
- __u32 frame_type;
- __u32 cmd_flags;
- __u8 ip_addr[16];
- __u32 tag_field;
- __u8 mac_addr[6];
- __u8 reserved[10];
- __u32 sr_len;
- __u8 sr_info[SR_INFO_LEN];
- } add_addr_entry;
- struct {
- __u32 command;
- __u32 cmd_flags;
- __u8 ip_addr[16];
- __u32 tag_field;
- } delete_addr_entry;
- struct {
- __u8 unique_id[8];
- } create_destroy_addr;
- } data;
-}__attribute__ ((packed));
-
-#define QETH_IOC_MAGIC 0x22
-/* these don't really have 'unsigned long' arguments but were defined that way */
-#define QETH_IOCPROC_OSAEINTERFACES _IOWR(QETH_IOC_MAGIC, 1, unsigned long)
-#define QETH_IOCPROC_INTERFACECHANGES _IOWR(QETH_IOC_MAGIC, 2, unsigned long)
-
-#define SNMP_QUERY_CARD_INFO 0x00000002L
-#define SNMP_REGISETER_MIB 0x00000004L
-#define SNMP_GET_OID 0x00000010L
-#define SNMP_SET_OID 0x00000011L
-#define SNMP_GET_NEXT_OID 0x00000012L
-#define SNMP_QUERY_ALERTS 0x00000020L
-#define SNMP_SET_TRAP 0x00000021L
-
-
-#define ARP_DATA_SIZE 3968
-#define ARP_FLUSH -3
-#define ARP_RETURNCODE_NOARPDATA -2
-#define ARP_RETURNCODE_ERROR -1
-#define ARP_RETURNCODE_SUCCESS 0
-#define ARP_RETURNCODE_LASTREPLY 1
-
-#define SNMP_BASE_CMDLENGTH 44
-#define SNMP_SETADP_CMDLENGTH 16
-#define SNMP_REQUEST_DATA_OFFSET 16
-
-struct snmp_ipa_setadp_cmd {
+/* SETADAPTERPARMS IPA Command: *******************************************/
+struct qeth_query_cmds_supp {
+ __u32 no_lantypes_supp;
+ __u8 lan_type;
+ __u8 reserved1[3];
+ __u32 supported_cmds;
+ __u8 reserved2[8];
+} __attribute__ ((packed));
+
+struct qeth_change_addr {
+ __u32 cmd;
+ __u32 addr_size;
+ __u32 no_macs;
+ __u8 addr[OSA_ADDR_LEN];
+} __attribute__ ((packed));
+
+struct qeth_ipacmd_setadpparms {
__u32 supp_hw_cmds;
__u32 reserved1;
__u16 cmdlength;
__u8 frames_used_total;
__u8 frame_seq_no;
__u32 reserved3;
- __u8 snmp_token[16];
union {
- struct {
- __u32 snmp_request;
- __u32 snmp_interface;
- __u32 snmp_returncode;
- __u32 snmp_firmwarelevel;
- __u32 snmp_seqno;
- __u8 snmp_data[ARP_DATA_SIZE];
- } snmp_subcommand;
+ struct qeth_query_cmds_supp query_cmds_supp;
+ struct qeth_change_addr change_addr;
+ __u32 mode;
} data;
-}__attribute__ ((packed));
-
-
-struct arp_cmd {
- __u8 command;
- __u8 initiator;
- __u16 seq_no;
- __u16 return_code;
- __u8 adapter_type;
- __u8 rel_adapter_no;
- __u8 prim_version_no;
- __u8 param_count;
- __u16 prot_version;
- __u32 ipa_supported;
- __u32 ipa_enabled;
+} __attribute__ ((packed));
+
+/* IPFRAME IPA Command: ***************************************************/
+/* TODO: define in analogy to commands define above */
+
+/* ADD_ADDR_ENTRY IPA Command: ********************************************/
+/* TODO: define in analogy to commands define above */
+
+/* DELETE_ADDR_ENTRY IPA Command: *****************************************/
+/* TODO: define in analogy to commands define above */
+
+/* CREATE_ADDR IPA Command: ***********************************************/
+struct qeth_create_destroy_address {
+ __u8 unique_id[8];
+} __attribute__ ((packed));
+
+/* REGISTER_LOCAL_ADDR IPA Command: ***************************************/
+/* TODO: define in analogy to commands define above */
+
+/* UNREGISTER_LOCAL_ADDR IPA Command: *************************************/
+/* TODO: define in analogy to commands define above */
+
+/* Header for each IPA command */
+struct qeth_ipacmd_hdr {
+ __u8 command;
+ __u8 initiator;
+ __u16 seqno;
+ __u16 return_code;
+ __u8 adapter_type;
+ __u8 rel_adapter_no;
+ __u8 prim_version_no;
+ __u8 param_count;
+ __u16 prot_version;
+ __u32 ipa_supported;
+ __u32 ipa_enabled;
+} __attribute__ ((packed));
+
+/* The IPA command itself */
+struct qeth_ipa_cmd {
+ struct qeth_ipacmd_hdr hdr;
union {
- struct {
- __u32 assist_no;
- __u16 length;
- __u16 command_code;
- __u16 return_code;
- __u8 number_of_replies;
- __u8 seq_no;
- union {
- struct {
- __u16 tcpip_requestbitmask;
- __u16 osa_setbitmask;
- __u32 number_of_entries;
- __u8 arp_data[ARP_DATA_SIZE];
- } queryarp_data;
- } data;
- } setassparms;
- struct snmp_ipa_setadp_cmd setadapterparms;
+ struct qeth_ipacmd_setdelip4 setdelip4;
+ struct qeth_ipacmd_setdelip6 setdelip6;
+ struct qeth_ipacmd_setdelipm setdelipm;
+ struct qeth_ipacmd_setassparms setassparms;
+ struct qeth_create_destroy_address create_destroy_addr;
+ struct qeth_ipacmd_setadpparms setadapterparms;
+ struct qeth_set_routing setrtg;
} data;
-}__attribute__ ((packed));
+} __attribute__ ((packed));
+/*
+ * special command for ARP processing.
+ * this is not included in setassparms command before, because we get
+ * problem with the size of struct qeth_ipacmd_setassparms otherwise
+ */
+enum qeth_ipa_arp_return_codes {
+ QETH_IPA_ARP_RC_SUCCESS = 0x0000,
+ QETH_IPA_ARP_RC_FAILED = 0x0001,
+ QETH_IPA_ARP_RC_NOTSUPP = 0x0002,
+ QETH_IPA_ARP_RC_OUT_OF_RANGE = 0x0003,
+ QETH_IPA_ARP_RC_Q_NOTSUPP = 0x0004,
+ QETH_IPA_ARP_RC_Q_NO_DATA = 0x0008,
+};
+#define QETH_QARP_DATA_SIZE 3968
+struct qeth_arp_query_data {
+ __u16 request_bits;
+ __u16 reply_bits;
+ __u32 no_entries;
+ char data[QETH_QARP_DATA_SIZE];
+} __attribute__((packed));
+
+/* used as parameter for arp_query reply */
+struct qeth_arp_query_info {
+ __u32 udata_len;
+ __u32 udata_offset;
+ __u32 no_entries;
+ char *udata;
+};
-#define IPA_PDU_HEADER_SIZE 0x40
-#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e)
-#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26)
-#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a)
-#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a)
+#define IPA_ARP_CMD_LEN (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_arp_cmd))
+#define QETH_ARP_CMD_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \
+ sizeof(struct qeth_ipacmd_setassparms_hdr))
+#define QETH_IPA_ARP_DATA_POS(buffer) (buffer + IPA_PDU_HEADER_SIZE + \
+ QETH_ARP_CMD_BASE_LEN)
+struct qeth_ipa_arp_cmd {
+ struct qeth_ipacmd_hdr ihdr;
+ struct qeth_ipacmd_setassparms_hdr shdr;
+ union {
+ struct qeth_arp_query_data query_arp;
+ } data;
+} __attribute__((packed));
-extern unsigned char IPA_PDU_HEADER[];
-#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
+/* Helper functions */
+#define IS_IPA_REPLY(cmd) (cmd->hdr.initiator == IPA_CMD_INITIATOR_HOST)
-#define PDU_ENCAPSULATION(buffer) \
- (buffer+ \
- *(buffer+ (*(buffer+0x0b))+ *(buffer+*(buffer+0x0b)+0x11) +0x07))
+/*****************************************************************************/
+/* END OF IP Assist related definitions */
+/*****************************************************************************/
-#define IS_IPA(buffer) ((buffer) && ( *(buffer+ ((*(buffer+0x0b))+4) )==0xc1) )
-#define IS_IPA_REPLY(buffer) ( (buffer) && ( (*(PDU_ENCAPSULATION(buffer)+1))==INITIATOR_HOST ) )
+extern unsigned char WRITE_CCW[];
+extern unsigned char READ_CCW[];
+
+extern unsigned char CM_ENABLE[];
+#define CM_ENABLE_SIZE 0x63
+#define QETH_CM_ENABLE_ISSUER_RM_TOKEN(buffer) (buffer+0x2c)
+#define QETH_CM_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
+#define QETH_CM_ENABLE_USER_DATA(buffer) (buffer+0x5b)
-#define CCW_NOP_CMD 0x03
-#define CCW_NOP_COUNT 1
+#define QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) \
+ (PDU_ENCAPSULATION(buffer)+ 0x13)
-extern unsigned char WRITE_CCW[];
-extern unsigned char READ_CCW[];
+extern unsigned char CM_SETUP[];
+#define CM_SETUP_SIZE 0x64
+#define QETH_CM_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_CM_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
+#define QETH_CM_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
-#endif /* __QETH_MPC_H__ */
+#define QETH_CM_SETUP_RESP_DEST_ADDR(buffer) \
+ (PDU_ENCAPSULATION(buffer) + 0x1a)
+extern unsigned char ULP_ENABLE[];
+#define ULP_ENABLE_SIZE 0x6b
+#define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61)
+#define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
+#define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62)
+#define QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer) \
+ (PDU_ENCAPSULATION(buffer) + 0x13)
+#define QETH_ULP_ENABLE_RESP_MAX_MTU(buffer) \
+ (PDU_ENCAPSULATION(buffer)+ 0x1f)
+#define QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer) \
+ (PDU_ENCAPSULATION(buffer) + 0x17)
+#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) \
+ (PDU_ENCAPSULATION(buffer)+ 0x2b)
+extern unsigned char ULP_SETUP[];
+#define ULP_SETUP_SIZE 0x6c
+#define QETH_ULP_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_ULP_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
+#define QETH_ULP_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
+#define QETH_ULP_SETUP_CUA(buffer) (buffer+0x68)
+#define QETH_ULP_SETUP_REAL_DEVADDR(buffer) (buffer+0x6a)
+#define QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer) \
+ (PDU_ENCAPSULATION(buffer)+0x1a)
+
+extern unsigned char DM_ACT[];
+#define DM_ACT_SIZE 0x55
+#define QETH_DM_ACT_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_DM_ACT_CONNECTION_TOKEN(buffer) (buffer+0x51)
+#define QETH_TRANSPORT_HEADER_SEQ_NO(buffer) (buffer+4)
+#define QETH_PDU_HEADER_SEQ_NO(buffer) (buffer+0x1c)
+#define QETH_PDU_HEADER_ACK_SEQ_NO(buffer) (buffer+0x20)
+extern unsigned char IDX_ACTIVATE_READ[];
+extern unsigned char IDX_ACTIVATE_WRITE[];
+#define IDX_ACTIVATE_SIZE 0x22
+#define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer+0x0c)
+#define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b]&0x80)
+#define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer+0x10)
+#define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer+0x16)
+#define QETH_IDX_ACT_QDIO_DEV_CUA(buffer) (buffer+0x1e)
+#define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20)
+#define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2)
+#define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12)
+#define PDU_ENCAPSULATION(buffer) \
+ (buffer + *(buffer + (*(buffer+0x0b)) + \
+ *(buffer + *(buffer+0x0b)+0x11) +0x07))
+#define IS_IPA(buffer) \
+ ((buffer) && \
+ ( *(buffer + ((*(buffer+0x0b))+4) )==0xc1) )
+#define ADDR_FRAME_TYPE_DIX 1
+#define ADDR_FRAME_TYPE_802_3 2
+#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10
+#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20
+#endif
--- /dev/null
+/*
+ *
+ * linux/drivers/s390/net/qeth_fs.c ($Revision: 1.5 $)
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ * This file contains code related to procfs.
+ *
+ * Copyright 2000,2003 IBM Corporation
+ *
+ * Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include <linux/rwsem.h>
+
+#include "qeth.h"
+#include "qeth_mpc.h"
+#include "qeth_fs.h"
+
+/***** /proc/qeth *****/
+#define QETH_PROCFILE_NAME "qeth"
+static struct proc_dir_entry *qeth_procfile;
+
+static void *
+qeth_procfile_seq_start(struct seq_file *s, loff_t *offset)
+{
+ struct list_head *next_card = NULL;
+ int i = 0;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+
+ if (*offset == 0)
+ return SEQ_START_TOKEN;
+
+ /* get card at pos *offset */
+ list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices)
+ if (++i == *offset)
+ return next_card;
+
+ return NULL;
+}
+
+static void
+qeth_procfile_seq_stop(struct seq_file *s, void* it)
+{
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+}
+
+static void *
+qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
+{
+ struct list_head *next_card = NULL;
+ struct list_head *current_card;
+
+ if (it == SEQ_START_TOKEN) {
+ next_card = qeth_ccwgroup_driver.driver.devices.next;
+ if (next_card->next == next_card) /* list empty */
+ return NULL;
+ (*offset)++;
+ } else {
+ current_card = (struct list_head *)it;
+ if (current_card->next == &qeth_ccwgroup_driver.driver.devices)
+ return NULL; /* end of list reached */
+ next_card = current_card->next;
+ (*offset)++;
+ }
+
+ return next_card;
+}
+
+static inline const char *
+qeth_get_router_str(struct qeth_card *card, int ipv)
+{
+ int routing_type = 0;
+
+ if (ipv == 4){
+ routing_type = card->options.route4.type;
+ } else {
+#ifdef CONFIG_QETH_IPV6
+ routing_type = card->options.route6.type;
+#else
+ return "n/a";
+#endif /* CONFIG_QETH_IPV6 */
+ }
+
+ if (routing_type == PRIMARY_ROUTER)
+ return "pri";
+ else if (routing_type == SECONDARY_ROUTER)
+ return "sec";
+ else if (routing_type == MULTICAST_ROUTER)
+ return "mc";
+ else if (routing_type == PRIMARY_CONNECTOR)
+ return "p.c";
+ else if (routing_type == SECONDARY_CONNECTOR)
+ return "s.c";
+ else if (routing_type == NO_ROUTER)
+ return "no";
+ else
+ return "unk";
+}
+
+static int
+qeth_procfile_seq_show(struct seq_file *s, void *it)
+{
+ struct device *device;
+ struct qeth_card *card;
+ char tmp[12]; /* for qeth_get_prioq_str */
+
+ if (it == SEQ_START_TOKEN){
+ seq_printf(s, "devices CHPID interface "
+ "cardtype port chksum prio-q'ing rtr4 "
+ "rtr6 fsz cnt\n");
+ seq_printf(s, "-------------------------- ----- ---------- "
+ "-------------- ---- ------ ---------- ---- "
+ "---- ----- -----\n");
+ } else {
+ device = list_entry(it, struct device, driver_list);
+ card = device->driver_data;
+ seq_printf(s, "%s/%s/%s x%02X %-10s %-14s %-4i ",
+ CARD_RDEV_ID(card),
+ CARD_WDEV_ID(card),
+ CARD_DDEV_ID(card),
+ card->info.chpid,
+ card->info.if_name,
+ qeth_get_cardname_short(card),
+ card->info.portno);
+ if (card->lan_online)
+ seq_printf(s, "%-6s %-10s %-4s %-4s %-5s %-5i\n",
+ qeth_get_checksum_str(card),
+ qeth_get_prioq_str(card, tmp),
+ qeth_get_router_str(card, 4),
+ qeth_get_router_str(card, 6),
+ qeth_get_bufsize_str(card),
+ card->qdio.in_buf_pool.buf_count);
+ else
+ seq_printf(s, " +++ LAN OFFLINE +++\n");
+ }
+ return 0;
+}
+
+static struct seq_operations qeth_procfile_seq_ops = {
+ .start = qeth_procfile_seq_start,
+ .stop = qeth_procfile_seq_stop,
+ .next = qeth_procfile_seq_next,
+ .show = qeth_procfile_seq_show,
+};
+
+static int
+qeth_procfile_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &qeth_procfile_seq_ops);
+}
+
+static struct file_operations qeth_procfile_fops = {
+ .owner = THIS_MODULE,
+ .open = qeth_procfile_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/***** /proc/qeth_perf *****/
+#define QETH_PERF_PROCFILE_NAME "qeth_perf"
+static struct proc_dir_entry *qeth_perf_procfile;
+
+#ifdef CONFIG_QETH_PERF_STATS
+
+static void *
+qeth_perf_procfile_seq_start(struct seq_file *s, loff_t *offset)
+{
+ struct list_head *next_card = NULL;
+ int i = 0;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+ /* get card at pos *offset */
+ list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){
+ if (i == *offset)
+ return next_card;
+ i++;
+ }
+ return NULL;
+}
+
+static void
+qeth_perf_procfile_seq_stop(struct seq_file *s, void* it)
+{
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+}
+
+static void *
+qeth_perf_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
+{
+ struct list_head *current_card = (struct list_head *)it;
+
+ if (current_card->next == &qeth_ccwgroup_driver.driver.devices)
+ return NULL; /* end of list reached */
+ (*offset)++;
+ return current_card->next;
+}
+
+static int
+qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
+{
+ struct device *device;
+ struct qeth_card *card;
+
+ device = list_entry(it, struct device, driver_list);
+ card = device->driver_data;
+ seq_printf(s, "For card with devnos %s/%s/%s (%s):\n",
+ CARD_RDEV_ID(card),
+ CARD_WDEV_ID(card),
+ CARD_DDEV_ID(card),
+ card->info.if_name
+ );
+ seq_printf(s, " Skb's/buffers received : %li/%i\n"
+ " Skb's/buffers sent : %li/%i\n\n",
+ card->stats.rx_packets, card->perf_stats.bufs_rec,
+ card->stats.tx_packets, card->perf_stats.bufs_sent
+ );
+ seq_printf(s, " Skb's/buffers sent without packing : %li/%i\n"
+ " Skb's/buffers sent with packing : %i/%i\n\n",
+ card->stats.tx_packets - card->perf_stats.skbs_sent_pack,
+ card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack,
+ card->perf_stats.skbs_sent_pack,
+ card->perf_stats.bufs_sent_pack
+ );
+ seq_printf(s, " Packing state changes no pkg.->packing : %i/%i\n"
+ " Current buffer usage (outbound q's) : "
+ "%i/%i/%i/%i\n\n",
+ card->perf_stats.sc_dp_p, card->perf_stats.sc_p_dp,
+ atomic_read(&card->qdio.out_qs[0]->used_buffers),
+ (card->qdio.no_out_queues > 1)?
+ atomic_read(&card->qdio.out_qs[1]->used_buffers)
+ : 0,
+ (card->qdio.no_out_queues > 2)?
+ atomic_read(&card->qdio.out_qs[2]->used_buffers)
+ : 0,
+ (card->qdio.no_out_queues > 3)?
+ atomic_read(&card->qdio.out_qs[3]->used_buffers)
+ : 0
+ );
+ seq_printf(s, " Inbound time (in us) : %i\n"
+ " Inbound cnt : %i\n"
+ " Outbound time (in us, incl QDIO) : %i\n"
+ " Outbound cnt : %i\n"
+ " Watermarks L/H : %i/%i\n\n",
+ card->perf_stats.inbound_time,
+ card->perf_stats.inbound_cnt,
+ card->perf_stats.outbound_time,
+ card->perf_stats.outbound_cnt,
+ QETH_LOW_WATERMARK_PACK, QETH_HIGH_WATERMARK_PACK
+ );
+
+ return 0;
+}
+
+static struct seq_operations qeth_perf_procfile_seq_ops = {
+ .start = qeth_perf_procfile_seq_start,
+ .stop = qeth_perf_procfile_seq_stop,
+ .next = qeth_perf_procfile_seq_next,
+ .show = qeth_perf_procfile_seq_show,
+};
+
+static int
+qeth_perf_procfile_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &qeth_perf_procfile_seq_ops);
+}
+
+static struct file_operations qeth_perf_procfile_fops = {
+ .owner = THIS_MODULE,
+ .open = qeth_perf_procfile_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+#define qeth_perf_procfile_created qeth_perf_procfile
+#else
+#define qeth_perf_procfile_created 1
+#endif /* CONFIG_QETH_PERF_STATS */
+
+/***** /proc/qeth_ipa_takeover *****/
+#define QETH_IPATO_PROCFILE_NAME "qeth_ipa_takeover"
+static struct proc_dir_entry *qeth_ipato_procfile;
+
+static void *
+qeth_ipato_procfile_seq_start(struct seq_file *s, loff_t *offset)
+{
+ struct list_head *next_card = NULL;
+ int i = 0;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+ /* TODO: finish this */
+ /*
+ * maybe SEQ_SATRT_TOKEN can be returned for offset 0
+ * output driver settings then;
+ * else output setting for respective card
+ */
+ /* get card at pos *offset */
+ list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){
+ if (i == *offset)
+ return next_card;
+ i++;
+ }
+ return NULL;
+}
+
+static void
+qeth_ipato_procfile_seq_stop(struct seq_file *s, void* it)
+{
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+}
+
+static void *
+qeth_ipato_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
+{
+ struct list_head *current_card = (struct list_head *)it;
+
+ /* TODO: finish this */
+ /*
+ * maybe SEQ_SATRT_TOKEN can be returned for offset 0
+ * output driver settings then;
+ * else output setting for respective card
+ */
+ if (current_card->next == &qeth_ccwgroup_driver.driver.devices)
+ return NULL; /* end of list reached */
+ (*offset)++;
+ return current_card->next;
+}
+
+static int
+qeth_ipato_procfile_seq_show(struct seq_file *s, void *it)
+{
+ struct device *device;
+ struct qeth_card *card;
+
+ /* TODO: finish this */
+ /*
+ * maybe SEQ_SATRT_TOKEN can be returned for offset 0
+ * output driver settings then;
+ * else output setting for respective card
+ */
+ device = list_entry(it, struct device, driver_list);
+ card = device->driver_data;
+
+ return 0;
+}
+
+static struct seq_operations qeth_ipato_procfile_seq_ops = {
+ .start = qeth_ipato_procfile_seq_start,
+ .stop = qeth_ipato_procfile_seq_stop,
+ .next = qeth_ipato_procfile_seq_next,
+ .show = qeth_ipato_procfile_seq_show,
+};
+
+static int
+qeth_ipato_procfile_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &qeth_ipato_procfile_seq_ops);
+}
+
+static struct file_operations qeth_ipato_procfile_fops = {
+ .owner = THIS_MODULE,
+ .open = qeth_ipato_procfile_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+int __init
+qeth_create_procfs_entries(void)
+{
+ qeth_procfile = create_proc_entry(QETH_PROCFILE_NAME,
+ S_IFREG | 0444, NULL);
+ if (qeth_procfile)
+ qeth_procfile->proc_fops = &qeth_procfile_fops;
+
+#ifdef CONFIG_QETH_PERF_STATS
+ qeth_perf_procfile = create_proc_entry(QETH_PERF_PROCFILE_NAME,
+ S_IFREG | 0444, NULL);
+ if (qeth_perf_procfile)
+ qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops;
+#endif /* CONFIG_QETH_PERF_STATS */
+
+ qeth_ipato_procfile = create_proc_entry(QETH_IPATO_PROCFILE_NAME,
+ S_IFREG | 0444, NULL);
+ if (qeth_ipato_procfile)
+ qeth_ipato_procfile->proc_fops = &qeth_ipato_procfile_fops;
+
+ if (qeth_procfile &&
+ qeth_ipato_procfile &&
+ qeth_perf_procfile_created)
+ return 0;
+ else
+ return -ENOMEM;
+}
+
+void __exit
+qeth_remove_procfs_entries(void)
+{
+ if (qeth_procfile)
+ remove_proc_entry(QETH_PROCFILE_NAME, NULL);
+ if (qeth_perf_procfile)
+ remove_proc_entry(QETH_PERF_PROCFILE_NAME, NULL);
+ if (qeth_ipato_procfile)
+ remove_proc_entry(QETH_IPATO_PROCFILE_NAME, NULL);
+}
+
+
+/* ONLY FOR DEVELOPMENT! -> make it as module */
+/*
+static void
+qeth_create_sysfs_entries(void)
+{
+ struct device *dev;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+
+ list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices,
+ driver_list)
+ qeth_create_device_attributes(dev);
+
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+}
+
+static void
+qeth_remove_sysfs_entries(void)
+{
+ struct device *dev;
+
+ down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+
+ list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices,
+ driver_list)
+ qeth_remove_device_attributes(dev);
+
+ up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+}
+
+static int __init
+qeth_fs_init(void)
+{
+ printk(KERN_INFO "qeth_fs_init\n");
+ qeth_create_procfs_entries();
+ qeth_create_sysfs_entries();
+
+ return 0;
+}
+
+static void __exit
+qeth_fs_exit(void)
+{
+ printk(KERN_INFO "qeth_fs_exit\n");
+ qeth_remove_procfs_entries();
+ qeth_remove_sysfs_entries();
+}
+
+
+module_init(qeth_fs_init);
+module_exit(qeth_fs_exit);
+
+MODULE_LICENSE("GPL");
+*/
--- /dev/null
+/*
+ *
+ * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.19 $)
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ * This file contains code related to sysfs.
+ *
+ * Copyright 2000,2003 IBM Corporation
+ *
+ * Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ * Frank Pavlic <pavlic@de.ibm.com>
+ *
+ */
+#include <linux/list.h>
+#include <linux/rwsem.h>
+
+#include <asm/ebcdic.h>
+
+#include "qeth.h"
+#include "qeth_mpc.h"
+#include "qeth_fs.h"
+
+/*****************************************************************************/
+/* */
+/* /sys-fs stuff UNDER DEVELOPMENT !!! */
+/* */
+/*****************************************************************************/
+//low/high watermark
+
+static ssize_t
+qeth_dev_state_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ if (!card)
+ return -EINVAL;
+
+ switch (card->state) {
+ case CARD_STATE_DOWN:
+ return sprintf(buf, "DOWN\n");
+ case CARD_STATE_HARDSETUP:
+ return sprintf(buf, "HARDSETUP\n");
+ case CARD_STATE_SOFTSETUP:
+ return sprintf(buf, "SOFTSETUP\n");
+ case CARD_STATE_UP_LAN_OFFLINE:
+ return sprintf(buf, "UP (LAN OFFLINE)\n");
+ case CARD_STATE_UP_LAN_ONLINE:
+ return sprintf(buf, "UP (LAN ONLINE)\n");
+ case CARD_STATE_RECOVER:
+ return sprintf(buf, "RECOVER\n");
+ default:
+ return sprintf(buf, "UNKNOWN\n");
+ }
+}
+
+static DEVICE_ATTR(state, 0444, qeth_dev_state_show, NULL);
+
+static ssize_t
+qeth_dev_chpid_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%02X\n", card->info.chpid);
+}
+
+static DEVICE_ATTR(chpid, 0444, qeth_dev_chpid_show, NULL);
+
+static ssize_t
+qeth_dev_if_name_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%s\n", card->info.if_name);
+}
+
+static DEVICE_ATTR(if_name, 0444, qeth_dev_if_name_show, NULL);
+
+static ssize_t
+qeth_dev_card_type_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%s\n", qeth_get_cardname_short(card));
+}
+
+static DEVICE_ATTR(card_type, 0444, qeth_dev_card_type_show, NULL);
+
+static ssize_t
+qeth_dev_portno_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->info.portno);
+}
+
+static ssize_t
+qeth_dev_portno_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ unsigned int portno;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ portno = simple_strtoul(buf, &tmp, 16);
+ if ((portno < 0) || (portno > MAX_PORTNO)){
+ PRINT_WARN("portno 0x%X is out of range\n", portno);
+ return -EINVAL;
+ }
+
+ card->info.portno = portno;
+ return count;
+}
+
+static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store);
+
+static ssize_t
+qeth_dev_portname_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+ char portname[9] = {0, };
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->info.portname_required) {
+ memcpy(portname, card->info.portname + 1, 8);
+ EBCASC(portname, 8);
+ return sprintf(buf, "%s\n", portname);
+ } else
+ return sprintf(buf, "no portname required\n");
+}
+
+static ssize_t
+qeth_dev_portname_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ tmp = strsep((char **) &buf, "\n");
+ if ((strlen(tmp) > 8) || (strlen(tmp) < 2))
+ return -EINVAL;
+
+ card->info.portname[0] = strlen(tmp);
+ /* for beauty reasons */
+ for (i = 1; i < 9; i++)
+ card->info.portname[i] = ' ';
+ strcpy(card->info.portname + 1, tmp);
+ ASCEBC(card->info.portname + 1, 8);
+
+ return count;
+}
+
+static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show,
+ qeth_dev_portname_store);
+
+static ssize_t
+qeth_dev_checksum_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%s checksumming\n", qeth_get_checksum_str(card));
+}
+
+static ssize_t
+qeth_dev_checksum_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "sw_checksumming"))
+ card->options.checksum_type = SW_CHECKSUMMING;
+ else if (!strcmp(tmp, "hw_checksumming"))
+ card->options.checksum_type = HW_CHECKSUMMING;
+ else if (!strcmp(tmp, "no_checksumming"))
+ card->options.checksum_type = NO_CHECKSUMMING;
+ else {
+ PRINT_WARN("Unknown checksumming type '%s'\n", tmp);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(checksumming, 0644, qeth_dev_checksum_show,
+ qeth_dev_checksum_store);
+
+static ssize_t
+qeth_dev_prioqing_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ switch (card->qdio.do_prio_queueing) {
+ case QETH_PRIO_Q_ING_PREC:
+ return sprintf(buf, "%s\n", "by precedence");
+ case QETH_PRIO_Q_ING_TOS:
+ return sprintf(buf, "%s\n", "by type of service");
+ default:
+ return sprintf(buf, "always queue %i\n",
+ card->qdio.default_out_queue);
+ }
+}
+
+static ssize_t
+qeth_dev_prioqing_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "prio_queueing_prec"))
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC;
+ else if (!strcmp(tmp, "prio_queueing_tos"))
+ card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS;
+ else if (!strcmp(tmp, "no_prio_queueing:0")) {
+ card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
+ card->qdio.default_out_queue = 0;
+ } else if (!strcmp(tmp, "no_prio_queueing:1")) {
+ card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
+ card->qdio.default_out_queue = 1;
+ } else if (!strcmp(tmp, "no_prio_queueing:2")) {
+ card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
+ card->qdio.default_out_queue = 2;
+ } else if (!strcmp(tmp, "no_prio_queueing:3")) {
+ card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
+ card->qdio.default_out_queue = 3;
+ } else if (!strcmp(tmp, "no_prio_queueing")) {
+ card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
+ card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+ } else {
+ PRINT_WARN("Unknown queueing type '%s'\n", tmp);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(priority_queueing, 0644, qeth_dev_prioqing_show,
+ qeth_dev_prioqing_store);
+
+static ssize_t
+qeth_dev_bufcnt_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->qdio.in_buf_pool.buf_count);
+}
+
+static ssize_t
+qeth_dev_bufcnt_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ unsigned int cnt;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ cnt = simple_strtoul(buf, &tmp, 16);
+ cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN :
+ ((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
+ card->qdio.in_buf_pool.buf_count = cnt;
+ /* TODO: steel/add buffers from/to a running card's buffer pool (?) */
+
+ return count;
+}
+
+static DEVICE_ATTR(buffer_count, 0644, qeth_dev_bufcnt_show,
+ qeth_dev_bufcnt_store);
+
+static inline ssize_t
+qeth_dev_route_show(struct qeth_routing_info *route, char *buf)
+{
+ switch (route->type) {
+ case PRIMARY_ROUTER:
+ return sprintf(buf, "%s\n", "primary router");
+ case SECONDARY_ROUTER:
+ return sprintf(buf, "%s\n", "secondary router");
+ case MULTICAST_ROUTER:
+ return sprintf(buf, "%s\n", "multicast router");
+ case PRIMARY_CONNECTOR:
+ return sprintf(buf, "%s\n", "primary connector");
+ case SECONDARY_CONNECTOR:
+ return sprintf(buf, "%s\n", "secondary connector");
+ default:
+ return sprintf(buf, "%s\n", "no");
+ }
+}
+
+static ssize_t
+qeth_dev_route4_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_route_show(&card->options.route4, buf);
+}
+
+static inline ssize_t
+qeth_dev_route_store(struct qeth_card *card, struct qeth_routing_info *route,
+ enum qeth_prot_versions prot, const char *buf, size_t count)
+{
+ enum qeth_routing_types old_route_type = route->type;
+ char *tmp;
+ int rc;
+
+ tmp = strsep((char **) &buf, "\n");
+
+ if (!strcmp(tmp, "no_router")){
+ route->type = NO_ROUTER;
+ goto check_reset;
+ }
+
+ if (card->info.type == QETH_CARD_TYPE_IQD) {
+ if (!strcmp(tmp, "primary_connector")) {
+ route->type = PRIMARY_CONNECTOR;
+ } else if (!strcmp(tmp, "secondary_connector")) {
+ route->type = SECONDARY_CONNECTOR;
+ } else if (!strcmp(tmp, "multicast_router")) {
+ route->type = MULTICAST_ROUTER;
+ } else
+ goto out_inval;
+ } else {
+ if (!strcmp(tmp, "primary_router")) {
+ route->type = PRIMARY_ROUTER;
+ } else if (!strcmp(tmp, "secondary_router")) {
+ route->type = SECONDARY_ROUTER;
+ } else if (!strcmp(tmp, "multicast_router")) {
+ if (qeth_is_ipafunc_supported(card, prot,
+ IPA_OSA_MC_ROUTER))
+ route->type = MULTICAST_ROUTER;
+ else
+ goto out_inval;
+ } else
+ goto out_inval;
+ }
+check_reset:
+ if (old_route_type != route->type){
+ if (prot == QETH_PROT_IPV4)
+ rc = qeth_setrouting_v4(card);
+ else if (prot == QETH_PROT_IPV6)
+ rc = qeth_setrouting_v6(card);
+ }
+ return count;
+out_inval:
+ PRINT_WARN("Routing type '%s' not supported for interface %s.\n"
+ "Router status not changed.\n",
+ tmp, card->info.if_name);
+ return -EINVAL;
+}
+
+static ssize_t
+qeth_dev_route4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_route_store(card, &card->options.route4,
+ QETH_PROT_IPV4, buf, count);
+}
+
+static DEVICE_ATTR(route4, 0644, qeth_dev_route4_show, qeth_dev_route4_store);
+
+#ifdef CONFIG_QETH_IPV6
+static ssize_t
+qeth_dev_route6_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return sprintf(buf, "%s\n", "n/a");
+
+ return qeth_dev_route_show(&card->options.route6, buf);
+}
+
+static ssize_t
+qeth_dev_route6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ if (!qeth_is_supported(card, IPA_IPV6)){
+ PRINT_WARN("IPv6 not supported for interface %s.\n"
+ "Routing status no changed.\n",
+ card->info.if_name);
+ return -ENOTSUPP;
+ }
+
+ return qeth_dev_route_store(card, &card->options.route6,
+ QETH_PROT_IPV6, buf, count);
+}
+
+static DEVICE_ATTR(route6, 0644, qeth_dev_route6_show, qeth_dev_route6_store);
+#endif
+
+static ssize_t
+qeth_dev_add_hhlen_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.add_hhlen);
+}
+
+static ssize_t
+qeth_dev_add_hhlen_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i < 0) || (i > MAX_ADD_HHLEN)) {
+ PRINT_WARN("add_hhlen out of range\n");
+ return -EINVAL;
+ }
+ card->options.add_hhlen = i;
+
+ return count;
+}
+
+static DEVICE_ATTR(add_hhlen, 0644, qeth_dev_add_hhlen_show,
+ qeth_dev_add_hhlen_store);
+
+static ssize_t
+qeth_dev_fake_ll_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.fake_ll? 1:0);
+}
+
+static ssize_t
+qeth_dev_fake_ll_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1))
+ card->options.fake_ll = i;
+ else {
+ PRINT_WARN("fake_ll: write 0 or 1 to this file!\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(fake_ll, 0644, qeth_dev_fake_ll_show,
+ qeth_dev_fake_ll_store);
+
+static ssize_t
+qeth_dev_fake_broadcast_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0);
+}
+
+static ssize_t
+qeth_dev_fake_broadcast_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1))
+ card->options.fake_broadcast = i;
+ else {
+ PRINT_WARN("fake_broadcast: write 0 or 1 to this file!\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(fake_broadcast, 0644, qeth_dev_fake_broadcast_show,
+ qeth_dev_fake_broadcast_store);
+
+static ssize_t
+qeth_dev_recover_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_UP_LAN_ONLINE) &&
+ (card->state != CARD_STATE_UP_LAN_OFFLINE))
+ return -EPERM;
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if (i == 1)
+ qeth_schedule_recovery(card);
+
+ return count;
+}
+
+static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store);
+
+/* TODO */
+static ssize_t
+qeth_dev_broadcast_mode_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR)))
+ return sprintf(buf, "n/a\n");
+
+ return sprintf(buf, "%s\n", (card->options.broadcast_mode ==
+ QETH_TR_BROADCAST_ALLRINGS)?
+ "all rings":"local");
+}
+
+/* TODO */
+static ssize_t
+qeth_dev_broadcast_mode_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){
+ PRINT_WARN("Device is not a tokenring device!\n");
+ return -EINVAL;
+ }
+
+ tmp = strsep((char **) &buf, "\n");
+
+ if (!strcmp(tmp, "local")){
+ card->options.broadcast_mode = QETH_TR_BROADCAST_LOCAL;
+ return count;
+ } else if (!strcmp(tmp, "all_rings")) {
+ card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
+ return count;
+ } else {
+ PRINT_WARN("broadcast_mode: invalid mode %s!\n",
+ tmp);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(broadcast_mode, 0644, qeth_dev_broadcast_mode_show,
+ qeth_dev_broadcast_mode_store);
+
+/* TODO */
+static ssize_t
+qeth_dev_canonical_macaddr_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR)))
+ return sprintf(buf, "n/a\n");
+
+ return sprintf(buf, "%i\n", (card->options.macaddr_mode ==
+ QETH_TR_MACADDR_CANONICAL)? 1:0);
+}
+
+/* TODO */
+static ssize_t
+qeth_dev_canonical_macaddr_store(struct device *dev, const char *buf,
+ size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
+ (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){
+ PRINT_WARN("Device is not a tokenring device!\n");
+ return -EINVAL;
+ }
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1))
+ card->options.macaddr_mode = i?
+ QETH_TR_MACADDR_CANONICAL :
+ QETH_TR_MACADDR_NONCANONICAL;
+ else {
+ PRINT_WARN("canonical_macaddr: write 0 or 1 to this file!\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(canonical_macaddr, 0644, qeth_dev_canonical_macaddr_show,
+ qeth_dev_canonical_macaddr_store);
+
+static struct device_attribute * qeth_device_attrs[] = {
+ &dev_attr_state,
+ &dev_attr_chpid,
+ &dev_attr_if_name,
+ &dev_attr_card_type,
+ &dev_attr_portno,
+ &dev_attr_portname,
+ &dev_attr_checksumming,
+ &dev_attr_priority_queueing,
+ &dev_attr_buffer_count,
+ &dev_attr_route4,
+#ifdef CONFIG_QETH_IPV6
+ &dev_attr_route6,
+#endif
+ &dev_attr_add_hhlen,
+ &dev_attr_fake_ll,
+ &dev_attr_fake_broadcast,
+ &dev_attr_recover,
+ &dev_attr_broadcast_mode,
+ &dev_attr_canonical_macaddr,
+ NULL,
+};
+
+static struct attribute_group qeth_device_attr_group = {
+ .attrs = (struct attribute **)qeth_device_attrs,
+};
+
+
+#define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \
+struct device_attribute dev_attr_##_id = { \
+ .attr = {.name=__stringify(_name), .mode=_mode, .owner=THIS_MODULE },\
+ .show = _show, \
+ .store = _store, \
+};
+
+static ssize_t
+qeth_dev_ipato_enable_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.enabled? 1:0);
+}
+
+static ssize_t
+qeth_dev_ipato_enable_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "toggle")){
+ card->ipato.enabled = (card->ipato.enabled)? 0 : 1;
+ } else if (!strcmp(tmp, "1")){
+ card->ipato.enabled = 1;
+ } else if (!strcmp(tmp, "0")){
+ card->ipato.enabled = 0;
+ } else {
+ PRINT_WARN("ipato_enable: write 0, 1 or 'toggle' to "
+ "this file\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static QETH_DEVICE_ATTR(ipato_enable, enable, 0644,
+ qeth_dev_ipato_enable_show,
+ qeth_dev_ipato_enable_store);
+
+static ssize_t
+qeth_dev_ipato_invert4_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.invert4? 1:0);
+}
+
+static ssize_t
+qeth_dev_ipato_invert4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "toggle")){
+ card->ipato.invert4 = (card->ipato.invert4)? 0 : 1;
+ } else if (!strcmp(tmp, "1")){
+ card->ipato.invert4 = 1;
+ } else if (!strcmp(tmp, "0")){
+ card->ipato.invert4 = 0;
+ } else {
+ PRINT_WARN("ipato_invert4: write 0, 1 or 'toggle' to "
+ "this file\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644,
+ qeth_dev_ipato_invert4_show,
+ qeth_dev_ipato_invert4_store);
+
+static inline ssize_t
+qeth_dev_ipato_add_show(char *buf, struct qeth_card *card,
+ enum qeth_prot_versions proto)
+{
+ struct qeth_ipato_entry *ipatoe;
+ unsigned long flags;
+ char addr_str[49];
+ int i = 0;
+
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipatoe, &card->ipato.entries, entry){
+ if (ipatoe->proto != proto)
+ continue;
+ qeth_ipaddr_to_string(proto, ipatoe->addr, addr_str);
+ i += sprintf(buf + i, "%s/%i\n", addr_str, ipatoe->mask_bits);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += sprintf(buf + i, "\n");
+
+ return i;
+}
+
+static ssize_t
+qeth_dev_ipato_add4_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static inline int
+qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto,
+ u8 *addr, int *mask_bits)
+{
+ const char *start, *end;
+ char *tmp;
+ char buffer[49] = {0, };
+
+ start = buf;
+ /* get address string */
+ end = strchr(start, '/');
+ if (!end){
+ PRINT_WARN("Invalid format for ipato_addx/delx. "
+ "Use <ip addr>/<mask bits>\n");
+ return -EINVAL;
+ }
+ strncpy(buffer, start, end - start);
+ if (qeth_string_to_ipaddr(buffer, proto, addr)){
+ PRINT_WARN("Invalid IP address format!\n");
+ return -EINVAL;
+ }
+ start = end + 1;
+ *mask_bits = simple_strtoul(start, &tmp, 10);
+
+ return 0;
+}
+
+static inline ssize_t
+qeth_dev_ipato_add_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ struct qeth_ipato_entry *ipatoe;
+ u8 addr[16];
+ int mask_bits;
+ int rc;
+
+ if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits)))
+ return rc;
+
+ if (!(ipatoe = kmalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL))){
+ PRINT_WARN("No memory to allocate ipato entry\n");
+ return -ENOMEM;
+ }
+ memset(ipatoe, 0, sizeof(struct qeth_ipato_entry));
+ ipatoe->proto = proto;
+ memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16);
+ ipatoe->mask_bits = mask_bits;
+
+ if ((rc = qeth_add_ipato_entry(card, ipatoe))){
+ kfree(ipatoe);
+ return rc;
+ }
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_ipato_add4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(ipato_add4, add4, 0644,
+ qeth_dev_ipato_add4_show,
+ qeth_dev_ipato_add4_store);
+
+static inline ssize_t
+qeth_dev_ipato_del_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16];
+ int mask_bits;
+ int rc;
+
+ if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits)))
+ return rc;
+
+ qeth_del_ipato_entry(card, proto, addr, mask_bits);
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_ipato_del4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL,
+ qeth_dev_ipato_del4_store);
+
+#ifdef CONFIG_QETH_IPV6
+static ssize_t
+qeth_dev_ipato_invert6_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->ipato.invert6? 1:0);
+}
+
+static ssize_t
+qeth_dev_ipato_invert6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "toggle")){
+ card->ipato.invert6 = (card->ipato.invert6)? 0 : 1;
+ } else if (!strcmp(tmp, "1")){
+ card->ipato.invert6 = 1;
+ } else if (!strcmp(tmp, "0")){
+ card->ipato.invert6 = 0;
+ } else {
+ PRINT_WARN("ipato_invert6: write 0, 1 or 'toggle' to "
+ "this file\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644,
+ qeth_dev_ipato_invert6_show,
+ qeth_dev_ipato_invert6_store);
+
+
+static ssize_t
+qeth_dev_ipato_add6_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t
+qeth_dev_ipato_add6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(ipato_add6, add6, 0644,
+ qeth_dev_ipato_add6_show,
+ qeth_dev_ipato_add6_store);
+
+static ssize_t
+qeth_dev_ipato_del6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL,
+ qeth_dev_ipato_del6_store);
+#endif /* CONFIG_QETH_IPV6 */
+
+static struct device_attribute * qeth_ipato_device_attrs[] = {
+ &dev_attr_ipato_enable,
+ &dev_attr_ipato_invert4,
+ &dev_attr_ipato_add4,
+ &dev_attr_ipato_del4,
+#ifdef CONFIG_QETH_IPV6
+ &dev_attr_ipato_invert6,
+ &dev_attr_ipato_add6,
+ &dev_attr_ipato_del6,
+#endif
+ NULL,
+};
+
+static struct attribute_group qeth_device_ipato_group = {
+ .name = "ipa_takeover",
+ .attrs = (struct attribute **)qeth_ipato_device_attrs,
+};
+
+static inline ssize_t
+qeth_dev_vipa_add_show(char *buf, struct qeth_card *card,
+ enum qeth_prot_versions proto)
+{
+ struct qeth_ipaddr *ipaddr;
+ char addr_str[49];
+ unsigned long flags;
+ int i = 0;
+
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipaddr, &card->ip_list, entry){
+ if (ipaddr->proto != proto)
+ continue;
+ if (ipaddr->type != QETH_IP_TYPE_VIPA)
+ continue;
+ qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str);
+ i += sprintf(buf + i, "%s\n", addr_str);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += sprintf(buf + i, "\n");
+
+ return i;
+}
+
+static ssize_t
+qeth_dev_vipa_add4_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static inline int
+qeth_parse_vipae(const char* buf, enum qeth_prot_versions proto,
+ u8 *addr)
+{
+ if (qeth_string_to_ipaddr(buf, proto, addr)){
+ PRINT_WARN("Invalid IP address format!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline ssize_t
+qeth_dev_vipa_add_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16] = {0, };
+ int rc;
+
+ if ((rc = qeth_parse_vipae(buf, proto, addr)))
+ return rc;
+
+ if ((rc = qeth_add_vipa(card, proto, addr)))
+ return rc;
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_vipa_add4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(vipa_add4, add4, 0644,
+ qeth_dev_vipa_add4_show,
+ qeth_dev_vipa_add4_store);
+
+static inline ssize_t
+qeth_dev_vipa_del_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16];
+ int rc;
+
+ if ((rc = qeth_parse_vipae(buf, proto, addr)))
+ return rc;
+
+ qeth_del_vipa(card, proto, addr);
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_vipa_del4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL,
+ qeth_dev_vipa_del4_store);
+
+#ifdef CONFIG_QETH_IPV6
+static ssize_t
+qeth_dev_vipa_add6_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t
+qeth_dev_vipa_add6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(vipa_add6, add6, 0644,
+ qeth_dev_vipa_add6_show,
+ qeth_dev_vipa_add6_store);
+
+static ssize_t
+qeth_dev_vipa_del6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL,
+ qeth_dev_vipa_del6_store);
+#endif /* CONFIG_QETH_IPV6 */
+
+static struct device_attribute * qeth_vipa_device_attrs[] = {
+ &dev_attr_vipa_add4,
+ &dev_attr_vipa_del4,
+#ifdef CONFIG_QETH_IPV6
+ &dev_attr_vipa_add6,
+ &dev_attr_vipa_del6,
+#endif
+ NULL,
+};
+
+static struct attribute_group qeth_device_vipa_group = {
+ .name = "vipa",
+ .attrs = (struct attribute **)qeth_vipa_device_attrs,
+};
+
+static inline ssize_t
+qeth_dev_rxip_add_show(char *buf, struct qeth_card *card,
+ enum qeth_prot_versions proto)
+{
+ struct qeth_ipaddr *ipaddr;
+ char addr_str[49];
+ unsigned long flags;
+ int i = 0;
+
+ spin_lock_irqsave(&card->ip_lock, flags);
+ list_for_each_entry(ipaddr, &card->ip_list, entry){
+ if (ipaddr->proto != proto)
+ continue;
+ if (ipaddr->type != QETH_IP_TYPE_RXIP)
+ continue;
+ qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str);
+ i += sprintf(buf + i, "%s\n", addr_str);
+ }
+ spin_unlock_irqrestore(&card->ip_lock, flags);
+ i += sprintf(buf + i, "\n");
+
+ return i;
+}
+
+static ssize_t
+qeth_dev_rxip_add4_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV4);
+}
+
+static inline int
+qeth_parse_rxipe(const char* buf, enum qeth_prot_versions proto,
+ u8 *addr)
+{
+ if (qeth_string_to_ipaddr(buf, proto, addr)){
+ PRINT_WARN("Invalid IP address format!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline ssize_t
+qeth_dev_rxip_add_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16] = {0, };
+ int rc;
+
+ if ((rc = qeth_parse_rxipe(buf, proto, addr)))
+ return rc;
+
+ if ((rc = qeth_add_rxip(card, proto, addr)))
+ return rc;
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_rxip_add4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(rxip_add4, add4, 0644,
+ qeth_dev_rxip_add4_show,
+ qeth_dev_rxip_add4_store);
+
+static inline ssize_t
+qeth_dev_rxip_del_store(const char *buf, size_t count,
+ struct qeth_card *card, enum qeth_prot_versions proto)
+{
+ u8 addr[16];
+ int rc;
+
+ if ((rc = qeth_parse_rxipe(buf, proto, addr)))
+ return rc;
+
+ qeth_del_rxip(card, proto, addr);
+
+ return count;
+}
+
+static ssize_t
+qeth_dev_rxip_del4_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4);
+}
+
+static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL,
+ qeth_dev_rxip_del4_store);
+
+#ifdef CONFIG_QETH_IPV6
+static ssize_t
+qeth_dev_rxip_add6_show(struct device *dev, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV6);
+}
+
+static ssize_t
+qeth_dev_rxip_add6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(rxip_add6, add6, 0644,
+ qeth_dev_rxip_add6_show,
+ qeth_dev_rxip_add6_store);
+
+static ssize_t
+qeth_dev_rxip_del6_store(struct device *dev, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6);
+}
+
+static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL,
+ qeth_dev_rxip_del6_store);
+#endif /* CONFIG_QETH_IPV6 */
+
+static struct device_attribute * qeth_rxip_device_attrs[] = {
+ &dev_attr_rxip_add4,
+ &dev_attr_rxip_del4,
+#ifdef CONFIG_QETH_IPV6
+ &dev_attr_rxip_add6,
+ &dev_attr_rxip_del6,
+#endif
+ NULL,
+};
+
+static struct attribute_group qeth_device_rxip_group = {
+ .name = "rxip",
+ .attrs = (struct attribute **)qeth_rxip_device_attrs,
+};
+
+int
+qeth_create_device_attributes(struct device *dev)
+{
+ int ret;
+
+ if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_attr_group)))
+ return ret;
+ if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_ipato_group))){
+ sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
+ return ret;
+ }
+ if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_vipa_group))){
+ sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ return ret;
+ }
+ if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_rxip_group))){
+ sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group);
+ }
+
+ return ret;
+}
+
+void
+qeth_remove_device_attributes(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_rxip_group);
+}
+
+/**********************/
+/* DRIVER ATTRIBUTES */
+/**********************/
+static ssize_t
+qeth_driver_group_store(struct device_driver *ddrv, const char *buf,
+ size_t count)
+{
+ const char *start, *end;
+ char bus_ids[3][BUS_ID_SIZE], *argv[3];
+ int i;
+ int err;
+
+ start = buf;
+ for (i = 0; i < 3; i++) {
+ static const char delim[] = { ',', ',', '\n' };
+ int len;
+
+ if (!(end = strchr(start, delim[i])))
+ return -EINVAL;
+ len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start);
+ strncpy(bus_ids[i], start, len);
+ bus_ids[i][len] = '\0';
+ start = end + 1;
+ argv[i] = bus_ids[i];
+ }
+ err = ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id,
+ &qeth_ccw_driver, 3, argv);
+ if (err)
+ return err;
+ else
+ return count;
+}
+
+
+static DRIVER_ATTR(group, 0200, 0, qeth_driver_group_store);
+
+static ssize_t
+qeth_driver_snmp_register_show(struct device_driver *ddrv, char *buf)
+{
+ /* TODO */
+ return 0;
+}
+
+static ssize_t
+qeth_driver_snmp_register_store(struct device_driver *ddrv, const char *buf,
+ size_t count)
+{
+ /* TODO */
+ return count;
+}
+
+static DRIVER_ATTR(snmp_register, 0644, qeth_driver_snmp_register_show,
+ qeth_driver_snmp_register_store);
+
+int
+qeth_create_driver_attributes(void)
+{
+ int rc;
+
+ if ((rc = driver_create_file(&qeth_ccwgroup_driver.driver,
+ &driver_attr_group)))
+ return rc;
+ return driver_create_file(&qeth_ccwgroup_driver.driver,
+ &driver_attr_snmp_register);
+}
+
+void
+qeth_remove_driver_attributes(void)
+{
+ driver_remove_file(&qeth_ccwgroup_driver.driver,
+ &driver_attr_group);
+ driver_remove_file(&qeth_ccwgroup_driver.driver,
+ &driver_attr_snmp_register);
+}
*/
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_AUX_REVISION "$Revision: 1.101 $"
-
-/********************** INCLUDES *********************************************/
-
-#include <linux/init.h>
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ctype.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/time.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/workqueue.h>
-#include <linux/syscalls.h>
+#define ZFCP_AUX_REVISION "$Revision: 1.105 $"
#include "zfcp_ext.h"
-#include <asm/semaphore.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/ebcdic.h>
-#include <asm/cpcmd.h> /* Debugging only */
-#include <asm/processor.h> /* Debugging only */
-
-#include <linux/miscdevice.h>
-#include <linux/major.h>
-
/* accumulated log level (module parameter) */
static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
static char *device;
zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req,
void *add_data, int add_length)
{
-#ifdef ZFCP_DEBUG_COMMANDS
struct zfcp_adapter *adapter = fsf_req->adapter;
struct scsi_cmnd *scsi_cmnd;
int level = 3;
min(ZFCP_CMD_DBF_LENGTH, add_length - i));
}
write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
-#endif
}
/* XXX additionally log unit if available */
void
zfcp_cmd_dbf_event_scsi(const char *text, struct scsi_cmnd *scsi_cmnd)
{
-#ifdef ZFCP_DEBUG_COMMANDS
struct zfcp_adapter *adapter;
union zfcp_req_data *req_data;
struct zfcp_fsf_req *fsf_req;
debug_text_event(adapter->cmd_dbf, level, "");
}
write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
-#endif
}
void
zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text,
struct fsf_status_read_buffer *status_buffer, int length)
{
-#ifdef ZFCP_DEBUG_INCOMING_ELS
int level = 1;
int i;
level,
(char *) status_buffer->payload + i,
min(ZFCP_IN_ELS_DBF_LENGTH, length - i));
-#endif
}
/**
zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size)
{
struct scatterlist *sg;
- int i;
+ unsigned int i;
int retval = 0;
sg_list->count = size >> PAGE_SHIFT;
zfcp_sg_list_free(struct zfcp_sg_list *sg_list)
{
struct scatterlist *sg;
- int i;
+ unsigned int i;
int retval = 0;
BUG_ON((sg_list->sg == NULL) || (sg_list == NULL));
mempool_destroy(adapter->pool.data_gid_pn);
}
+/**
+ * zfcp_adapter_debug_register - registers debug feature for an adapter
+ * @adapter: pointer to adapter for which debug features should be registered
+ * return: -ENOMEM on error, 0 otherwise
+ */
+int
+zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
+{
+ char dbf_name[20];
+
+ /* debug feature area which records fsf request sequence numbers */
+ sprintf(dbf_name, ZFCP_REQ_DBF_NAME "%s",
+ zfcp_get_busid_by_adapter(adapter));
+ adapter->req_dbf = debug_register(dbf_name,
+ ZFCP_REQ_DBF_INDEX,
+ ZFCP_REQ_DBF_AREAS,
+ ZFCP_REQ_DBF_LENGTH);
+ debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
+ debug_text_event(adapter->req_dbf, 1, "zzz");
+
+ /* debug feature area which records SCSI command failures (hostbyte) */
+ rwlock_init(&adapter->cmd_dbf_lock);
+ sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s",
+ zfcp_get_busid_by_adapter(adapter));
+ adapter->cmd_dbf = debug_register(dbf_name,
+ ZFCP_CMD_DBF_INDEX,
+ ZFCP_CMD_DBF_AREAS,
+ ZFCP_CMD_DBF_LENGTH);
+ debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
+
+ /* debug feature area which records SCSI command aborts */
+ sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s",
+ zfcp_get_busid_by_adapter(adapter));
+ adapter->abort_dbf = debug_register(dbf_name,
+ ZFCP_ABORT_DBF_INDEX,
+ ZFCP_ABORT_DBF_AREAS,
+ ZFCP_ABORT_DBF_LENGTH);
+ debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
+
+ /* debug feature area which records SCSI command aborts */
+ sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s",
+ zfcp_get_busid_by_adapter(adapter));
+ adapter->in_els_dbf = debug_register(dbf_name,
+ ZFCP_IN_ELS_DBF_INDEX,
+ ZFCP_IN_ELS_DBF_AREAS,
+ ZFCP_IN_ELS_DBF_LENGTH);
+ debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
+
+
+ /* debug feature area which records erp events */
+ sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s",
+ zfcp_get_busid_by_adapter(adapter));
+ adapter->erp_dbf = debug_register(dbf_name,
+ ZFCP_ERP_DBF_INDEX,
+ ZFCP_ERP_DBF_AREAS,
+ ZFCP_ERP_DBF_LENGTH);
+ debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
+ debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
+
+ if (adapter->req_dbf && adapter->cmd_dbf && adapter->abort_dbf &&
+ adapter->in_els_dbf && adapter->erp_dbf)
+ return 0;
+
+ zfcp_adapter_debug_unregister(adapter);
+ return -ENOMEM;
+}
+
+/**
+ * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter
+ * @adapter: pointer to adapter for which debug features should be unregistered
+ */
+void
+zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
+{
+ debug_unregister(adapter->erp_dbf);
+ debug_unregister(adapter->req_dbf);
+ debug_unregister(adapter->cmd_dbf);
+ debug_unregister(adapter->abort_dbf);
+ debug_unregister(adapter->in_els_dbf);
+}
+
/*
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
{
int retval = 0;
struct zfcp_adapter *adapter;
- char dbf_name[20];
/*
* Note: It is safe to release the list_lock, as any list changes
if (zfcp_sysfs_adapter_create_files(&ccw_device->dev))
goto sysfs_failed;
-#ifdef ZFCP_DEBUG_REQUESTS
- /* debug feature area which records fsf request sequence numbers */
- sprintf(dbf_name, ZFCP_REQ_DBF_NAME "%s",
- zfcp_get_busid_by_adapter(adapter));
- adapter->req_dbf = debug_register(dbf_name,
- ZFCP_REQ_DBF_INDEX,
- ZFCP_REQ_DBF_AREAS,
- ZFCP_REQ_DBF_LENGTH);
- if (!adapter->req_dbf) {
- ZFCP_LOG_INFO("registration of dbf for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto failed_req_dbf;
- }
- debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
- debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
- debug_text_event(adapter->req_dbf, 1, "zzz");
-#endif /* ZFCP_DEBUG_REQUESTS */
-
-#ifdef ZFCP_DEBUG_COMMANDS
- /* debug feature area which records SCSI command failures (hostbyte) */
- rwlock_init(&adapter->cmd_dbf_lock);
- sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s",
- zfcp_get_busid_by_adapter(adapter));
- adapter->cmd_dbf = debug_register(dbf_name,
- ZFCP_CMD_DBF_INDEX,
- ZFCP_CMD_DBF_AREAS,
- ZFCP_CMD_DBF_LENGTH);
- if (!adapter->cmd_dbf) {
- ZFCP_LOG_INFO("registration of dbf for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto failed_cmd_dbf;
- }
- debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
- debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
-#endif /* ZFCP_DEBUG_COMMANDS */
-
-#ifdef ZFCP_DEBUG_ABORTS
- /* debug feature area which records SCSI command aborts */
- sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s",
- zfcp_get_busid_by_adapter(adapter));
- adapter->abort_dbf = debug_register(dbf_name,
- ZFCP_ABORT_DBF_INDEX,
- ZFCP_ABORT_DBF_AREAS,
- ZFCP_ABORT_DBF_LENGTH);
- if (!adapter->abort_dbf) {
- ZFCP_LOG_INFO("registration of dbf for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto failed_abort_dbf;
- }
- debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
- debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
-#endif /* ZFCP_DEBUG_ABORTS */
-
-#ifdef ZFCP_DEBUG_INCOMING_ELS
- /* debug feature area which records SCSI command aborts */
- sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s",
- zfcp_get_busid_by_adapter(adapter));
- adapter->in_els_dbf = debug_register(dbf_name,
- ZFCP_IN_ELS_DBF_INDEX,
- ZFCP_IN_ELS_DBF_AREAS,
- ZFCP_IN_ELS_DBF_LENGTH);
- if (!adapter->in_els_dbf) {
- ZFCP_LOG_INFO("registration of dbf for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto failed_in_els_dbf;
- }
- debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
- debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
-#endif /* ZFCP_DEBUG_INCOMING_ELS */
-
- sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s",
- zfcp_get_busid_by_adapter(adapter));
- adapter->erp_dbf = debug_register(dbf_name,
- ZFCP_ERP_DBF_INDEX,
- ZFCP_ERP_DBF_AREAS,
- ZFCP_ERP_DBF_LENGTH);
- if (!adapter->erp_dbf) {
- ZFCP_LOG_INFO("registration of dbf for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto failed_erp_dbf;
- }
- debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
- debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
-
/* put allocated adapter at list tail */
write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
goto out;
- failed_erp_dbf:
-#ifdef ZFCP_DEBUG_INCOMING_ELS
- debug_unregister(adapter->in_els_dbf);
- failed_in_els_dbf:
-#endif
-
-#ifdef ZFCP_DEBUG_ABORTS
- debug_unregister(adapter->abort_dbf);
- failed_abort_dbf:
-#endif
-
-#ifdef ZFCP_DEBUG_COMMANDS
- debug_unregister(adapter->cmd_dbf);
- failed_cmd_dbf:
-#endif
-
-#ifdef ZFCP_DEBUG_REQUESTS
- debug_unregister(adapter->req_dbf);
- failed_req_dbf:
-#endif
- zfcp_sysfs_adapter_remove_files(&ccw_device->dev);
sysfs_failed:
dev_set_drvdata(&ccw_device->dev, NULL);
failed_low_mem_buffers:
ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
zfcp_get_busid_by_adapter(adapter));
- debug_unregister(adapter->erp_dbf);
-
-#ifdef ZFCP_DEBUG_REQUESTS
- debug_unregister(adapter->req_dbf);
-#endif
-
-#ifdef ZFCP_DEBUG_COMMANDS
- debug_unregister(adapter->cmd_dbf);
-#endif
-#ifdef ZFCP_DEBUG_ABORTS
- debug_unregister(adapter->abort_dbf);
-#endif
-
-#ifdef ZFCP_DEBUG_INCOMING_ELS
- debug_unregister(adapter->in_els_dbf);
-#endif
-
zfcp_free_low_mem_buffers(adapter);
/* free memory of adapter data structure and queues */
zfcp_qdio_free_queues(adapter);
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define ZFCP_CCW_C_REVISION "$Revision: 1.51 $"
+#define ZFCP_CCW_C_REVISION "$Revision: 1.52 $"
#include <linux/init.h>
#include <linux/module.h>
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
+ retval = zfcp_adapter_debug_register(adapter);
+ if (retval)
+ goto out;
retval = zfcp_erp_thread_setup(adapter);
if (retval) {
ZFCP_LOG_INFO("error: start of error recovery thread for "
"adapter %s failed\n",
zfcp_get_busid_by_adapter(adapter));
- goto out;
+ goto out_erp_thread;
}
retval = zfcp_adapter_scsi_register(adapter);
out_scsi_register:
zfcp_erp_thread_kill(adapter);
+ out_erp_thread:
+ zfcp_adapter_debug_unregister(adapter);
out:
up(&zfcp_data.config_sema);
return retval;
zfcp_erp_wait(adapter);
zfcp_adapter_scsi_unregister(adapter);
zfcp_erp_thread_kill(adapter);
+ zfcp_adapter_debug_unregister(adapter);
up(&zfcp_data.config_sema);
return 0;
}
#define ZFCP_DEF_H
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_DEF_REVISION "$Revision: 1.66 $"
+#define ZFCP_DEF_REVISION "$Revision: 1.71 $"
/*************************** INCLUDES *****************************************/
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
#include <linux/blkdev.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
-#include "../../scsi/scsi.h"
#include "../../fc4/fc.h"
-#include "zfcp_fsf.h" /* FSF SW Interface */
+#include "zfcp_fsf.h"
#include <asm/ccwdev.h>
#include <asm/qdio.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <linux/reboot.h>
#include <linux/mempool.h>
+#include <linux/syscalls.h>
#include <linux/ioctl.h>
#ifdef CONFIG_S390_SUPPORT
#include <linux/ioctl32.h>
/************************ DEBUG FLAGS *****************************************/
#define ZFCP_PRINT_FLAGS
-#define ZFCP_DEBUG_REQUESTS /* fsf_req tracing */
-#define ZFCP_DEBUG_COMMANDS /* host_byte tracing */
-#define ZFCP_DEBUG_ABORTS /* scsi_cmnd abort tracing */
-#define ZFCP_DEBUG_INCOMING_ELS /* incoming ELS tracing */
#define ZFCP_STAT_REQSIZES
#define ZFCP_STAT_QUEUES
#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024
-static const char zfcp_act_subtable_type[5][8] = {
- {"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"}
-};
-
-
/************************* STRUCTURE DEFINITIONS *****************************/
struct zfcp_fsf_req;
refcount drop to zero */
struct zfcp_port *port; /* remote port of unit */
atomic_t status; /* status of this logical unit */
- u32 lun_access; /* access flags for this unit */
scsi_lun_t scsi_lun; /* own SCSI LUN */
fcp_lun_t fcp_lun; /* own FCP_LUN */
u32 handle; /* handle assigned by FSF */
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_ERP_REVISION "$Revision: 1.45 $"
+#define ZFCP_ERP_REVISION "$Revision: 1.49 $"
#include "zfcp_ext.h"
* returns: 0 - there was an action to handle
* !0 - otherwise
*/
-static int
+int
zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
unsigned long set_mask)
{
}
/*
- * purpose: is called for finished FSF requests related to erp,
- * moves concerned erp action to 'ready' queue and
- * signals erp thread to process it,
- * besides it cancels a timeout
- */
-void
-zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *fsf_req)
-{
- struct zfcp_erp_action *erp_action = fsf_req->erp_action;
- struct zfcp_adapter *adapter = fsf_req->adapter;
-
- debug_text_event(adapter->erp_dbf, 3, "a_frh");
- debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
-
- if (erp_action) {
- debug_event(adapter->erp_dbf, 3, &erp_action->action,
- sizeof (int));
- zfcp_erp_async_handler(erp_action, 0);
- }
-}
-
-/*
* purpose: is called for erp_action which was slept waiting for
* memory becoming avaliable,
* will trigger that this action will be continued
"unit 0x%016Lx on port 0x%016Lx on "
"adapter %s\n", unit->fcp_lun, unit->port->wwpn,
zfcp_get_busid_by_unit(unit));
- atomic_set(&p->unit->scsi_add_work, 0);
+ atomic_set(&unit->scsi_add_work, 0);
return -ENOMEM;
}
ZFCP_LOG_NORMAL("bug: shutdown of QDIO queues failed "
"(retval=%d)\n", retval_cleanup);
}
-#ifdef ZFCP_DEBUG_REQUESTS
else
debug_text_event(adapter->req_dbf, 1, "q_clean");
-#endif /* ZFCP_DEBUG_REQUESTS */
failed_qdio_establish:
atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
zfcp_get_busid_by_adapter(adapter));
} else {
ZFCP_LOG_DEBUG("queues cleaned up\n");
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 1, "q_clean");
-#endif /* ZFCP_DEBUG_REQUESTS */
}
/*
#ifndef ZFCP_EXT_H
#define ZFCP_EXT_H
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_EXT_REVISION "$Revision: 1.47 $"
+#define ZFCP_EXT_REVISION "$Revision: 1.49 $"
#include "zfcp_def.h"
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
wwn_t wwpn);
extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
+extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32);
extern void zfcp_port_dequeue(struct zfcp_port *);
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
extern int zfcp_erp_thread_kill(struct zfcp_adapter *);
extern int zfcp_erp_wait(struct zfcp_adapter *);
-extern void zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *);
+extern int zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
extern int zfcp_test_link(struct zfcp_port *);
*/
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_FSF_C_REVISION "$Revision: 1.36 $"
+#define ZFCP_FSF_C_REVISION "$Revision: 1.43 $"
#include "zfcp_ext.h"
[FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND
};
+static const char zfcp_act_subtable_type[5][8] = {
+ {"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"}
+};
+
+
/****************************************************************/
/*************** FSF related Functions *************************/
/****************************************************************/
sizeof(struct fsf_qtcb));
goto forget_log;
}
- if ((fsf_req->qtcb->header.log_start +
+ if ((size_t) (fsf_req->qtcb->header.log_start +
fsf_req->qtcb->header.log_length)
> sizeof(struct fsf_qtcb)) {
ZFCP_LOG_NORMAL("bug: ULP (FSF logging) log data ends "
zfcp_get_busid_by_adapter(adapter),
fsf_req->qtcb->prefix.prot_status_qual.
sequence_error.exp_req_seq_no);
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 1, "exp_seq!");
debug_event(adapter->req_dbf, 1,
&fsf_req->qtcb->prefix.prot_status_qual.
debug_text_event(adapter->req_dbf, 1, "qtcb_seq!");
debug_exception(adapter->req_dbf, 1,
&fsf_req->qtcb->prefix.req_seq_no, 4);
-#endif /* ZFCP_DEBUG_REQUESTS */
debug_text_exception(adapter->erp_dbf, 0, "prot_seq_err");
/* restart operation on this adapter */
zfcp_erp_adapter_reopen(adapter, 0);
static int
zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req)
{
+ struct zfcp_erp_action *erp_action = fsf_req->erp_action;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
int retval = 0;
if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
fsf_req->qtcb->header.fsf_command);
}
- zfcp_erp_fsf_req_handler(fsf_req);
+ if (!erp_action)
+ return retval;
+
+ debug_text_event(adapter->erp_dbf, 3, "a_frh");
+ debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
+ zfcp_erp_async_handler(erp_action, 0);
+
return retval;
}
ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n",
zfcp_get_busid_by_adapter(adapter));
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 1, "unso");
-#endif
goto out;
failed_req_send:
case FSF_STATUS_READ_LINK_DOWN:
ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_LINK_DOWN\n");
-
- /* Unneccessary, ignoring.... */
+ debug_text_event(adapter->erp_dbf, 0, "unsol_link_down:");
+ ZFCP_LOG_INFO("Local link to adapter %s is down\n",
+ zfcp_get_busid_by_adapter(adapter));
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &adapter->status);
+ zfcp_erp_adapter_failed(adapter);
break;
case FSF_STATUS_READ_LINK_UP:
break;
- case FSF_STATUS_READ_NOTIFICATION_LOST:
- ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_NOTIFICATION_LOST\n");
- debug_text_event(adapter->erp_dbf, 2, "unsol_not_lost:");
- switch (status_buffer->status_subtype) {
- case FSF_STATUS_READ_SUB_LOST_CFDC_UPDATED:
- ZFCP_LOG_NORMAL(
- "The unsolicited status information about "
- "CFDC update on the adapter %s is lost "
- "due to the lack of internal resources\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- case FSF_STATUS_READ_SUB_LOST_CFDC_HARDENED:
- ZFCP_LOG_NORMAL(
- "The unsolicited status information about "
- "CFDC harden on the adapter %s is lost "
- "due to the lack of internal resources\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- }
- break;
-
case FSF_STATUS_READ_CFDC_UPDATED:
ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_UPDATED\n");
debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_update:");
case FSF_FCP_COMMAND_DOES_NOT_EXIST:
ZFCP_LOG_FLAGS(2, "FSF_FCP_COMMAND_DOES_NOT_EXIST\n");
retval = 0;
-#ifdef ZFCP_DEBUG_REQUESTS
- /*
- * debug feature area which records
- * fsf request sequence numbers
- */
debug_text_event(new_fsf_req->adapter->req_dbf, 3, "no_exist");
debug_event(new_fsf_req->adapter->req_dbf, 3,
&new_fsf_req->qtcb->bottom.support.req_handle,
sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
debug_text_event(new_fsf_req->adapter->erp_dbf, 3,
"fsf_s_no_exist");
new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
return retval;
}
+/**
+ * zfcp_fsf_exchange_config_evaluate
+ * @fsf_req: fsf_req which belongs to xchg config data request
+ * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
+ *
+ * returns: -EIO on error, 0 otherwise
+ */
+static int
+zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
+{
+ struct fsf_qtcb_bottom_config *bottom;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+
+ bottom = &fsf_req->qtcb->bottom.config;
+ ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n",
+ bottom->low_qtcb_version, bottom->high_qtcb_version);
+ adapter->fsf_lic_version = bottom->lic_version;
+ adapter->supported_features = bottom->supported_features;
+
+ if (xchg_ok) {
+ adapter->wwnn = bottom->nport_serv_param.wwnn;
+ adapter->wwpn = bottom->nport_serv_param.wwpn;
+ adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
+ adapter->fc_topology = bottom->fc_topology;
+ adapter->fc_link_speed = bottom->fc_link_speed;
+ adapter->hydra_version = bottom->adapter_type;
+ } else {
+ adapter->wwnn = 0;
+ adapter->wwpn = 0;
+ adapter->s_id = 0;
+ adapter->fc_topology = 0;
+ adapter->fc_link_speed = 0;
+ adapter->hydra_version = 0;
+ }
+
+ if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
+ adapter->hardware_version = bottom->hardware_version;
+ memcpy(adapter->serial_number, bottom->serial_number, 17);
+ EBCASC(adapter->serial_number, sizeof(adapter->serial_number));
+ }
+
+ ZFCP_LOG_INFO("The adapter %s reported the following characteristics:\n"
+ "WWNN 0x%016Lx, "
+ "WWPN 0x%016Lx, "
+ "S_ID 0x%08x,\n"
+ "adapter version 0x%x, "
+ "LIC version 0x%x, "
+ "FC link speed %d Gb/s\n",
+ zfcp_get_busid_by_adapter(adapter),
+ adapter->wwnn,
+ adapter->wwpn,
+ (unsigned int) adapter->s_id,
+ adapter->hydra_version,
+ adapter->fsf_lic_version,
+ adapter->fc_link_speed);
+ if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
+ ZFCP_LOG_NORMAL("error: the adapter %s "
+ "only supports newer control block "
+ "versions in comparison to this device "
+ "driver (try updated device driver)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 0, "low_qtcb_ver");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ return -EIO;
+ }
+ if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
+ ZFCP_LOG_NORMAL("error: the adapter %s "
+ "only supports older control block "
+ "versions than this device driver uses"
+ "(consider a microcode upgrade)\n",
+ zfcp_get_busid_by_adapter(adapter));
+ debug_text_event(adapter->erp_dbf, 0, "high_qtcb_ver");
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ return -EIO;
+ }
+ return 0;
+}
+
/*
* function: zfcp_fsf_exchange_config_data_handler
*
static int
zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req)
{
- int retval = -EIO;
struct fsf_qtcb_bottom_config *bottom;
struct zfcp_adapter *adapter = fsf_req->adapter;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't set any value, stay with the old (unitialized) ones */
- goto skip_fsfstatus;
- }
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return -EIO;
- /* evaluate FSF status in QTCB */
switch (fsf_req->qtcb->header.fsf_status) {
case FSF_GOOD:
ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
- bottom = &fsf_req->qtcb->bottom.config;
- /* only log QTCB versions for now */
- ZFCP_LOG_DEBUG("low QTCB version 0x%x of FSF, "
- "high QTCB version 0x%x of FSF, \n",
- bottom->low_qtcb_version,
- bottom->high_qtcb_version);
- adapter->wwnn = bottom->nport_serv_param.wwnn;
- adapter->wwpn = bottom->nport_serv_param.wwpn;
- adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
- adapter->hydra_version = bottom->adapter_type;
- adapter->fsf_lic_version = bottom->lic_version;
- adapter->fc_topology = bottom->fc_topology;
- adapter->fc_link_speed = bottom->fc_link_speed;
- adapter->supported_features = bottom->supported_features;
-
- if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
- adapter->hardware_version = bottom->hardware_version;
- /* copy just first 17 bytes */
- memcpy(adapter->serial_number,
- bottom->serial_number, 17);
- EBCASC(adapter->serial_number,
- sizeof(adapter->serial_number));
- }
- ZFCP_LOG_INFO("The adapter %s reported "
- "the following characteristics:\n"
- "WWNN 0x%016Lx, "
- "WWPN 0x%016Lx, "
- "S_ID 0x%08x,\n"
- "adapter version 0x%x, "
- "LIC version 0x%x, "
- "FC link speed %d Gb/s\n",
- zfcp_get_busid_by_adapter(adapter),
- adapter->wwnn,
- adapter->wwpn,
- (unsigned int) adapter->s_id,
- adapter->hydra_version,
- adapter->fsf_lic_version,
- adapter->fc_link_speed);
- if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
- ZFCP_LOG_NORMAL("error: the adapter %s "
- "only supports newer control block "
- "versions in comparison to this device "
- "driver (try updated device driver)\n",
- zfcp_get_busid_by_adapter(adapter));
- debug_text_event(fsf_req->adapter->erp_dbf, 0,
- "low_qtcb_ver");
- zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
- }
- if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
- ZFCP_LOG_NORMAL("error: the adapter %s "
- "only supports older control block "
- "versions than this device driver uses"
- "(consider a microcode upgrade)\n",
- zfcp_get_busid_by_adapter(adapter));
- debug_text_event(fsf_req->adapter->erp_dbf, 0,
- "high_qtcb_ver");
- zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
- }
+ if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
+ return -EIO;
+
switch (adapter->fc_topology) {
case FSF_TOPO_P2P:
ZFCP_LOG_FLAGS(1, "FSF_TOPO_P2P\n");
debug_text_event(fsf_req->adapter->erp_dbf, 0,
"top-p-to-p");
zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
+ return -EIO;
case FSF_TOPO_AL:
ZFCP_LOG_FLAGS(1, "FSF_TOPO_AL\n");
ZFCP_LOG_NORMAL("error: Arbitrated loop fibrechannel "
debug_text_event(fsf_req->adapter->erp_dbf, 0,
"top-al");
zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
+ return -EIO;
case FSF_TOPO_FABRIC:
ZFCP_LOG_FLAGS(1, "FSF_TOPO_FABRIC\n");
ZFCP_LOG_INFO("Switched fabric fibrechannel "
debug_text_exception(fsf_req->adapter->erp_dbf, 0,
"unknown-topo");
zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
+ return -EIO;
}
+ bottom = &fsf_req->qtcb->bottom.config;
if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
"allowed by the adapter %s "
debug_event(fsf_req->adapter->erp_dbf, 0,
&bottom->max_qtcb_size, sizeof (u32));
zfcp_erp_adapter_shutdown(adapter, 0);
- goto skip_fsfstatus;
+ return -EIO;
}
atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
&adapter->status);
- retval = 0;
-
break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ debug_text_event(adapter->erp_dbf, 0, "xchg-inco");
+
+ if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
+ return -EIO;
+ ZFCP_LOG_INFO("Local link to adapter %s is down\n",
+ zfcp_get_busid_by_adapter(adapter));
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &adapter->status);
+ zfcp_erp_adapter_failed(adapter);
+ break;
default:
- /* retval is -EIO by default */
debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf-stat-ng");
debug_event(fsf_req->adapter->erp_dbf, 0,
&fsf_req->qtcb->header.fsf_status, sizeof (u32));
+ zfcp_erp_adapter_shutdown(adapter, 0);
+ return -EIO;
}
- skip_fsfstatus:
- return retval;
+ return 0;
}
/*
atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
erp_action->fsf_req->data.open_unit.unit = erp_action->unit;
erp_action->fsf_req->erp_action = erp_action;
-// erp_action->fsf_req->qtcb->bottom.support.option =
-// FSF_OPEN_LUN_UNSOLICITED_SENSE_DATA;
/* start QDIO request for this FSF request */
retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
case FSF_LUN_SHARING_VIOLATION :
ZFCP_LOG_FLAGS(2, "FSF_LUN_SHARING_VIOLATION\n");
- subtable = header->fsf_status_qual.halfword[4];
- rule = header->fsf_status_qual.halfword[5];
- if (rule == 0xFFFF) {
+ if (header->fsf_status_qual.word[0] != 0) {
ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port "
- "with WWPN 0x%Lx connected to the "
- "adapter %s is already in use\n",
+ "with WWPN 0x%Lx "
+ "connected to the adapter %s "
+ "is already in use in LPAR%d\n",
unit->fcp_lun,
unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
+ zfcp_get_busid_by_unit(unit),
+ header->fsf_status_qual.fsf_queue_designator.hla);
} else {
+ subtable = header->fsf_status_qual.halfword[4];
+ rule = header->fsf_status_qual.halfword[5];
switch (subtable) {
case FSF_SQ_CFDC_SUBTABLE_OS:
case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_NORMAL("Access to unit 0x%016Lx on "
- "port 0x%016Lx on adapter %s "
+ ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
+ "remote port with WWPN 0x%Lx "
+ "connected to the adapter %s "
"is denied (%s rule %d)\n",
unit->fcp_lun,
unit->port->wwpn,
}
}
ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
+ ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
(char *) &header->fsf_status_qual,
sizeof (union fsf_status_qual));
debug_text_event(adapter->erp_dbf, 2,
/* save LUN handle assigned by FSF */
unit->handle = header->lun_handle;
ZFCP_LOG_TRACE("unit 0x%016Lx on remote port 0x%016Lx on "
- "adapter %s opened, port handle 0x%x, "
- "access flag 0x%02x\n",
+ "adapter %s opened, port handle 0x%x\n",
unit->fcp_lun,
unit->port->wwpn,
zfcp_get_busid_by_unit(unit),
- unit->handle,
- bottom->lun_access);
+ unit->handle);
/* mark unit as open */
atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
- if (adapter->supported_features & FSF_FEATURE_CFDC)
- unit->lun_access = bottom->lun_access;
retval = 0;
break;
* (need this for look up on normal command completion)
*/
fsf_req->data.send_fcp_command_task.scsi_cmnd = scsi_cmnd;
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 3, "fsf/sc");
debug_event(adapter->req_dbf, 3, &fsf_req, sizeof (unsigned long));
debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
-#ifdef ZFCP_DEBUG_ABORTS
- fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
-#endif
+ fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
fsf_req->data.send_fcp_command_task.unit = unit;
ZFCP_LOG_DEBUG("unit=%p, fcp_lun=0x%016Lx\n", unit, unit->fcp_lun);
no_fit:
failed_scsi_cmnd:
/* dequeue new FSF request previously enqueued */
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 3, "fail_sc");
debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
zfcp_fsf_req_free(fsf_req);
fsf_req = NULL;
* the new eh
*/
/* always call back */
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(fsf_req->adapter->req_dbf, 2, "ok_done:");
debug_event(fsf_req->adapter->req_dbf, 2, &scpnt,
sizeof (unsigned long));
sizeof (unsigned long));
debug_event(fsf_req->adapter->req_dbf, 2, &fsf_req,
sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
(scpnt->scsi_done) (scpnt);
/*
* We must hold this lock until scsi_done has been called.
"to request queue.\n");
} else {
req_queue->distance_from_int = new_distance_from_int;
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 1, "o:a/seq");
debug_event(adapter->req_dbf, 1, &fsf_req,
sizeof (unsigned long));
} else {
debug_text_event(adapter->req_dbf, 1, "nocb");
}
-#endif /* ZFCP_DEBUG_REQUESTS */
/*
* increase FSF sequence counter -
* this must only be done for request successfully enqueued to
#define FSF_CONFLICTS_OVERRULED 0x00000058
#define FSF_PORT_BOXED 0x00000059
#define FSF_LUN_BOXED 0x0000005A
+#define FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE 0x0000005B
#define FSF_PAYLOAD_SIZE_MISMATCH 0x00000060
#define FSF_REQUEST_SIZE_TOO_LARGE 0x00000061
#define FSF_RESPONSE_SIZE_TOO_LARGE 0x00000062
#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD 0x00000004
#define FSF_STATUS_READ_LINK_DOWN 0x00000005 /* FIXME: really? */
#define FSF_STATUS_READ_LINK_UP 0x00000006
-#define FSF_STATUS_READ_NOTIFICATION_LOST 0x00000009
#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A
#define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B
#define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002
/* status subtypes for CFDC */
-#define FSF_STATUS_READ_SUB_LOST_CFDC_UPDATED 0x00000020
-#define FSF_STATUS_READ_SUB_LOST_CFDC_HARDENED 0x00000040
#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE 0x00000002
#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2 0x0000000F
/* option */
#define FSF_OPEN_LUN_SUPPRESS_BOXING 0x00000001
-#define FSF_OPEN_LUN_UNSOLICITED_SENSE_DATA 0x00000002
/* adapter types */
#define FSF_ADAPTER_TYPE_FICON 0x00000001
#define FSF_ADAPTER_TYPE_FICON_EXPRESS 0x00000002
-/* flags */
-#define FSF_CFDC_OPEN_LUN_ALLOWED 0x01
-#define FSF_CFDC_EXCLUSIVE_ACCESS 0x02
-#define FSF_CFDC_OUTBOUND_TRANSFER_ALLOWED 0x10
-
/* port types */
#define FSF_HBA_PORTTYPE_UNKNOWN 0x00000001
#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003
u8 byte[FSF_STATUS_QUALIFIER_SIZE];
u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)];
u32 word[FSF_STATUS_QUALIFIER_SIZE / sizeof (u32)];
- struct {
- u32 this_cmd;
- u32 aborted_cmd;
- } port_handle;
- struct {
- u32 this_cmd;
- u32 aborted_cmd;
- } lun_handle;
- struct {
- u64 found;
- u64 expected;
- } fcp_lun;
+ struct fsf_queue_designator fsf_queue_designator;
} __attribute__ ((packed));
struct fsf_qtcb_header {
u32 service_class;
u8 res3[3];
u8 timeout;
- u32 lun_access;
- u8 res4[180];
+ u8 res4[184];
u32 els1_length;
u32 els2_length;
u32 req_buf_length;
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define ZFCP_QDIO_C_REVISION "$Revision: 1.15 $"
+#define ZFCP_QDIO_C_REVISION "$Revision: 1.16 $"
#include "zfcp_ext.h"
struct zfcp_fsf_req *fsf_req;
int retval = 0;
-#ifdef ZFCP_DEBUG_REQUESTS
/* Note: seq is entered later */
debug_text_event(adapter->req_dbf, 1, "i:a/seq");
debug_event(adapter->req_dbf, 1, &sbale_addr, sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
/* invalid (per convention used in this driver) */
if (unlikely(!sbale_addr)) {
retval = -EINVAL;
goto out;
}
-#ifdef ZFCP_DEBUG_REQUESTS
/* debug feature stuff (test for QTCB: remember new unsol. status!) */
if (likely(fsf_req->qtcb)) {
debug_event(adapter->req_dbf, 1,
&fsf_req->qtcb->prefix.req_seq_no, sizeof (u32));
}
-#endif /* ZFCP_DEBUG_REQUESTS */
ZFCP_LOG_TRACE("fsf_req at %p, QTCB at %p\n", fsf_req, fsf_req->qtcb);
if (likely(fsf_req->qtcb)) {
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI
/* this drivers version (do not edit !!! generated and updated by cvs) */
-#define ZFCP_SCSI_REVISION "$Revision: 1.57 $"
+#define ZFCP_SCSI_REVISION "$Revision: 1.59 $"
#include <linux/blkdev.h>
static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *);
static int zfcp_task_management_function(struct zfcp_unit *, u8);
-static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, int, int);
+static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, scsi_id_t,
+ scsi_lun_t);
static struct device_attribute *zfcp_sysfs_sdev_attrs[];
ZFCP_LOG_DEBUG("error: initiation of Send FCP Cmnd failed\n");
retval = SCSI_MLQUEUE_HOST_BUSY;
} else {
-
-#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 3, "q_scpnt");
debug_event(adapter->req_dbf, 3, &scpnt,
sizeof (unsigned long));
-#endif /* ZFCP_DEBUG_REQUESTS */
}
out:
* context:
*/
static struct zfcp_unit *
-zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, int id, int lun)
+zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id,
+ scsi_lun_t lun)
{
struct zfcp_port *port;
struct zfcp_unit *unit, *retval = NULL;
unsigned long flags;
u32 status = 0;
-
-#ifdef ZFCP_DEBUG_ABORTS
/* the components of a abort_dbf record (fixed size record) */
u64 dbf_scsi_cmnd = (unsigned long) scpnt;
char dbf_opcode[ZFCP_ABORT_DBF_LENGTH];
memcpy(dbf_opcode,
scpnt->cmnd,
min(scpnt->cmd_len, (unsigned char) ZFCP_ABORT_DBF_LENGTH));
-#endif
ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n",
scpnt, zfcp_get_busid_by_adapter(adapter));
/* Figure out which fsf_req needs to be aborted. */
old_fsf_req = req_data->send_fcp_command_task.fsf_req;
-#ifdef ZFCP_DEBUG_ABORTS
+
dbf_fsf_req = (unsigned long) old_fsf_req;
dbf_timeout =
(jiffies - req_data->send_fcp_command_task.start_jiffies) / HZ;
-#endif
+
ZFCP_LOG_DEBUG("old_fsf_req=%p\n", old_fsf_req);
if (!old_fsf_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
/* wait for completion of abort */
ZFCP_LOG_DEBUG("waiting for cleanup...\n");
-#ifdef ZFCP_DEBUG_ABORTS
+#if 1
/*
* FIXME:
* copying zfcp_fsf_req_wait_and_cleanup code is not really nice
strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH);
}
- out:
-#ifdef ZFCP_DEBUG_ABORTS
+ out:
debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH);
debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof (wwn_t));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof (u64));
debug_text_event(adapter->abort_dbf, 1, dbf_result);
-#endif
+
spin_lock_irq(scsi_host->host_lock);
return retval;
}
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.13 $"
+#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.13.2.1 $"
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#define L1_CACHE_SHIFT 8
#define L1_CACHE_SHIFT_MAX 8 /* largest L1 which this arch supports */
-#define __ARCH_FORCE_KMALLOCALIGN 8
#endif
static inline struct page *
pte_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
{
- return virt_to_page(pte_alloc_one_kernel(mm, vmaddr));
+ pte_t *pte = pte_alloc_one_kernel(mm, vmaddr);
+ if (pte)
+ return virt_to_page(pte);
+ return 0;
}
static inline void pte_free_kernel(pte_t *pte)
--- /dev/null
+/*
+ * include/asm-s390/qeth.h
+ *
+ * ioctl definitions for qeth driver
+ *
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ *
+ */
+#ifndef __ASM_S390_IOCTL_H__
+#define __ASM_S390_IOCTL_H__
+#include <linux/ioctl.h>
+
+#define QETH_IOCTL_LETTER 'Q'
+
+#define SIOC_QETH_ARP_SET_NO_ENTRIES _IOWR(QETH_IOCTL_LETTER, 1, int)
+#define SIOC_QETH_ARP_QUERY_INFO _IOWR(QETH_IOCTL_LETTER, 2, int)
+#define SIOC_QETH_ARP_ADD_ENTRY _IOWR(QETH_IOCTL_LETTER, 3, int)
+#define SIOC_QETH_ARP_REMOVE_ENTRY _IOWR(QETH_IOCTL_LETTER, 4, int)
+#define SIOC_QETH_ARP_FLUSH_CACHE _IOWR(QETH_IOCTL_LETTER, 5, int)
+#define SIOC_QETH_ADP_SET_SNMP_CONTROL _IOWR(QETH_IOCTL_LETTER, 6, int)
+#define SIOC_QETH_GET_CARD_TYPE _IOWR(QETH_IOCTL_LETTER, 7, int)
+
+struct qeth_arp_cache_entry {
+ __u8 macaddr[6];
+ __u8 reserved1[2];
+ __u8 ipaddr[16]; /* for both IPv4 and IPv6 */
+ __u8 reserved2[32];
+} __attribute__ ((packed));
+
+struct qeth_arp_qi_entry7 {
+ __u8 media_specific[32];
+ __u8 macaddr_type;
+ __u8 ipaddr_type;
+ __u8 macaddr[6];
+ __u8 ipaddr[4];
+} __attribute__((packed));
+
+struct qeth_arp_qi_entry5 {
+ __u8 media_specific[32];
+ __u8 macaddr_type;
+ __u8 ipaddr_type;
+ __u8 ipaddr[4];
+} __attribute__((packed));
+
+/* data sent to user space as result of query arp ioctl */
+#define QETH_QARP_USER_DATA_SIZE 20000
+#define QETH_QARP_MASK_OFFSET 4
+#define QETH_QARP_ENTRIES_OFFSET 6
+struct qeth_arp_query_user_data {
+ union {
+ __u32 data_len; /* set by user space program */
+ __u32 no_entries; /* set by kernel */
+ } u;
+ __u16 mask_bits;
+ char *entries;
+} __attribute__((packed));
+
+#endif /* __ASM_S390_IOCTL_H__ */
extern inline int _raw_spin_trylock(spinlock_t *lp)
{
-#ifndef __s390x__
- unsigned long result, reg;
-#else /* __s390x__ */
- unsigned int result, reg;
-#endif /* __s390x__ */
+ unsigned long reg;
+ unsigned int result;
+
__asm__ __volatile(" basr %1,0\n"
"0: cs %0,%1,0(%3)"
: "=d" (result), "=&d" (reg), "=m" (lp->lock)
"0: csg %0,%1,0(%3)\n"
#endif /* __s390x__ */
: "=d" (result), "=&d" (reg), "=m" (rw->lock)
- : "a" (&rw->lock), "m" (rw->lock), "0" (0)
+ : "a" (&rw->lock), "m" (rw->lock), "0" (0UL)
: "cc", "memory" );
return result == 0;
}
unsigned char *haddr);
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
int (*accept_fastpath)(struct net_device *, struct dst_entry*);
+ int (*generate_eui64)(u8 *eui, struct net_device *dev);
#ifdef CONFIG_NETPOLL_RX
int netpoll_rx;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*poll_controller)(struct net_device *dev);
#endif
- int (*generate_eui64)(u8 *eui, struct net_device *dev);
/* bridge stuff */
struct net_bridge_port *br_port;
return 0;
}
+#ifdef CONFIG_NO_IDLE_HZ
+extern cpumask_t idle_cpu_mask;
+#endif
+
+/*
+ * RCU is build for ticking systems. Without the HZ timer
+ * we have not enought state changes which may result in a
+ * never finished RCU request.
+ * In a tickless system we don't want to wake idle CPUs just
+ * to finish the RCU request. That is possible because the
+ * idle CPUs satisfy the quiescilant RCU condition anyway.
+ */
+static inline void rcu_set_active_cpu_map(cpumask_t *mask)
+{
+#ifdef CONFIG_NO_IDLE_HZ
+ cpumask_t active = idle_cpu_mask;
+ cpus_complement(active);
+ cpus_and(*mask, cpu_online_map, active);
+#else
+ *mask = cpu_online_map;
+#endif
+}
+
#define rcu_read_lock() preempt_disable()
#define rcu_read_unlock() preempt_enable()
#include <linux/rcupdate.h>
#include <linux/cpu.h>
-#ifdef CONFIG_NO_IDLE_HZ
-extern cpumask_t idle_cpu_mask;
-#endif
-
/* Definition for rcupdate control block. */
struct rcu_ctrlblk rcu_ctrlblk =
{ .mutex = SPIN_LOCK_UNLOCKED, .curbatch = 1,
!cpus_empty(rcu_ctrlblk.rcu_cpu_mask)) {
return;
}
-
-#ifdef CONFIG_NO_IDLE_HZ
- /*
- * RCU is build for ticking systems. Without the HZ timer
- * we have not enought state changes which may result in a
- * never finished RCU request.
- * In a tickless system we don't want to wake idle CPUs just
- * to finish the RCU request. That is possible because the
- * idle CPUs satisfy the quiescilant RCU condition anyway.
- * FIXME: Is this correct?
- */
- rcu_ctrlblk.rcu_cpu_mask = cpu_online_map & ~idle_cpu_mask;
-#else
/* Can't change, since spin lock held. */
- rcu_ctrlblk.rcu_cpu_mask = cpu_online_map;
-#endif
+ rcu_set_active_cpu_map(&rcu_ctrlblk.rcu_cpu_mask);
}
/*
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
-#ifdef __ARCH_FORCE_KMALLOCALIGN
-#define KMALLOC_ALIGN __ARCH_FORCE_KMALLOCALIGN
-#else
-#define KMALLOC_ALIGN 0
-#endif
-
/*
* DEBUG - 1 for kmem_cache_create() to honour; SLAB_DEBUG_INITIAL,
* SLAB_RED_ZONE & SLAB_POISON.
unsigned int colour_off; /* colour offset */
unsigned int colour_next; /* cache colouring */
kmem_cache_t *slabp_cache;
- unsigned int slab_size;
unsigned int dflags; /* dynamic flags */
/* constructor func */
.objsize = sizeof(kmem_cache_t),
.flags = SLAB_NO_REAP,
.spinlock = SPIN_LOCK_UNLOCKED,
- .colour_off = SMP_CACHE_BYTES,
+ .colour_off = L1_CACHE_BYTES,
.name = "kmem_cache",
-#if DEBUG
- .reallen = sizeof(kmem_cache_t),
-#endif
};
/* Guard access to the cache-chain. */
}
/* Cal the num objs, wastage, and bytes left over for a given slab size. */
-static void cache_estimate (unsigned long gfporder, size_t size, size_t align,
+static void cache_estimate (unsigned long gfporder, size_t size,
int flags, size_t *left_over, unsigned int *num)
{
int i;
extra = sizeof(kmem_bufctl_t);
}
i = 0;
- while (i*size + ALIGN(base+i*extra, align) <= wastage)
+ while (i*size + L1_CACHE_ALIGN(base+i*extra) <= wastage)
i++;
if (i > 0)
i--;
*num = i;
wastage -= i*size;
- wastage -= ALIGN(base+i*extra, align);
+ wastage -= L1_CACHE_ALIGN(base+i*extra);
*left_over = wastage;
}
list_add(&cache_cache.next, &cache_chain);
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
- cache_estimate(0, cache_cache.objsize, SMP_CACHE_BYTES, 0,
- &left_over, &cache_cache.num);
+ cache_estimate(0, cache_cache.objsize, 0,
+ &left_over, &cache_cache.num);
if (!cache_cache.num)
BUG();
cache_cache.colour = left_over/cache_cache.colour_off;
cache_cache.colour_next = 0;
- cache_cache.slab_size = ALIGN(cache_cache.num*sizeof(kmem_bufctl_t) + sizeof(struct slab),
- SMP_CACHE_BYTES);
+
/* 2+3) create the kmalloc caches */
sizes = malloc_sizes;
* allow tighter packing of the smaller caches. */
sizes->cs_cachep = kmem_cache_create(
names->name, sizes->cs_size,
- KMALLOC_ALIGN, 0, NULL, NULL);
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!sizes->cs_cachep)
BUG();
sizes->cs_dmacachep = kmem_cache_create(
names->name_dma, sizes->cs_size,
- KMALLOC_ALIGN, SLAB_CACHE_DMA, NULL, NULL);
+ 0, SLAB_CACHE_DMA|SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!sizes->cs_dmacachep)
BUG();
* kmem_cache_create - Create a cache.
* @name: A string which is used in /proc/slabinfo to identify this cache.
* @size: The size of objects to be created in this cache.
- * @align: The required alignment for the objects.
+ * @offset: The offset to use within the page.
* @flags: SLAB flags
* @ctor: A constructor for the objects.
* @dtor: A destructor for the objects.
* %SLAB_NO_REAP - Don't automatically reap this cache when we're under
* memory pressure.
*
- * %SLAB_HWCACHE_ALIGN - This flag has no effect and will be removed soon.
+ * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
+ * cacheline. This can be beneficial if you're counting cycles as closely
+ * as davem.
*/
kmem_cache_t *
-kmem_cache_create (const char *name, size_t size, size_t align,
+kmem_cache_create (const char *name, size_t size, size_t offset,
unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),
void (*dtor)(void*, kmem_cache_t *, unsigned long))
{
- size_t left_over, slab_size;
+ size_t left_over, align, slab_size;
kmem_cache_t *cachep = NULL;
/*
(size < BYTES_PER_WORD) ||
(size > (1<<MAX_OBJ_ORDER)*PAGE_SIZE) ||
(dtor && !ctor) ||
- (align < 0)) {
+ (offset < 0 || offset > size)) {
printk(KERN_ERR "%s: Early error in slab %s\n",
__FUNCTION__, name);
BUG();
- }
+ }
#if DEBUG
WARN_ON(strchr(name, ' ')); /* It confuses parsers */
#if FORCED_DEBUG
/*
- * Enable redzoning and last user accounting, except for caches with
- * large objects, if the increased size would increase the object size
- * above the next power of two: caches with object sizes just above a
- * power of two have a significant amount of internal fragmentation.
+ * Enable redzoning and last user accounting, except
+ * - for caches with forced alignment: redzoning would violate the
+ * alignment
+ * - for caches with large objects, if the increased size would
+ * increase the object size above the next power of two: caches
+ * with object sizes just above a power of two have a significant
+ * amount of internal fragmentation
*/
- if ((size < 4096 || fls(size-1) == fls(size-1+3*BYTES_PER_WORD)))
+ if ((size < 4096 || fls(size-1) == fls(size-1+3*BYTES_PER_WORD))
+ && !(flags & SLAB_MUST_HWCACHE_ALIGN)) {
flags |= SLAB_RED_ZONE|SLAB_STORE_USER;
+ }
flags |= SLAB_POISON;
#endif
#endif
+
/*
* Always checks flags, a caller might be expecting debug
* support which isn't available.
if (flags & ~CREATE_MASK)
BUG();
- if (align) {
- /* minimum supported alignment: */
- if (align < BYTES_PER_WORD)
- align = BYTES_PER_WORD;
-
- /* combinations of forced alignment and advanced debugging is
- * not yet implemented.
- */
- flags &= ~(SLAB_RED_ZONE|SLAB_STORE_USER);
- }
-
/* Get cache's description obj. */
cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
if (!cachep)
goto opps;
memset(cachep, 0, sizeof(kmem_cache_t));
+#if DEBUG
+ cachep->reallen = size;
+#endif
/* Check that size is in terms of words. This is needed to avoid
* unaligned accesses for some archs when redzoning is used, and makes
* sure any on-slab bufctl's are also correctly aligned.
}
#if DEBUG
- cachep->reallen = size;
-
if (flags & SLAB_RED_ZONE) {
- /* redzoning only works with word aligned caches */
- align = BYTES_PER_WORD;
-
+ /*
+ * There is no point trying to honour cache alignment
+ * when redzoning.
+ */
+ flags &= ~SLAB_HWCACHE_ALIGN;
/* add space for red zone words */
cachep->dbghead += BYTES_PER_WORD;
size += 2*BYTES_PER_WORD;
}
if (flags & SLAB_STORE_USER) {
- /* user store requires word alignment and
- * one word storage behind the end of the real
- * object.
- */
- align = BYTES_PER_WORD;
- size += BYTES_PER_WORD;
+ flags &= ~SLAB_HWCACHE_ALIGN;
+ size += BYTES_PER_WORD; /* add space */
}
-
#if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
if (size > 128 && cachep->reallen > L1_CACHE_BYTES && size < PAGE_SIZE) {
cachep->dbghead += PAGE_SIZE - size;
}
#endif
#endif
+ align = BYTES_PER_WORD;
+ if (flags & SLAB_HWCACHE_ALIGN)
+ align = L1_CACHE_BYTES;
/* Determine if the slab management is 'on' or 'off' slab. */
if (size >= (PAGE_SIZE>>3))
*/
flags |= CFLGS_OFF_SLAB;
- if (!align) {
- /* Default alignment: compile time specified l1 cache size.
- * But if an object is really small, then squeeze multiple
- * into one cacheline.
- */
- align = L1_CACHE_BYTES;
+ if (flags & SLAB_HWCACHE_ALIGN) {
+ /* Need to adjust size so that objs are cache aligned. */
+ /* Small obj size, can get at least two per cache line. */
while (size <= align/2)
align /= 2;
+ size = (size+align-1)&(~(align-1));
}
- size = ALIGN(size, align);
/* Cal size (in pages) of slabs, and the num of objs per slab.
* This could be made much more intelligent. For now, try to avoid
do {
unsigned int break_flag = 0;
cal_wastage:
- cache_estimate(cachep->gfporder, size, align, flags,
+ cache_estimate(cachep->gfporder, size, flags,
&left_over, &cachep->num);
if (break_flag)
break;
cachep = NULL;
goto opps;
}
- slab_size = ALIGN(cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab),align);
+ slab_size = L1_CACHE_ALIGN(cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab));
/*
* If the slab has been placed off-slab, and we have enough space then
left_over -= slab_size;
}
- if (flags & CFLGS_OFF_SLAB) {
- /* really off slab. No need for manual alignment */
- slab_size = cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab);
- }
-
- cachep->colour_off = L1_CACHE_BYTES;
/* Offset must be a multiple of the alignment. */
- if (cachep->colour_off < align)
- cachep->colour_off = align;
- cachep->colour = left_over/cachep->colour_off;
- cachep->slab_size = slab_size;
+ offset += (align-1);
+ offset &= ~(align-1);
+ if (!offset)
+ offset = L1_CACHE_BYTES;
+ cachep->colour_off = offset;
+ cachep->colour = left_over/offset;
+
cachep->flags = flags;
cachep->gfpflags = 0;
if (flags & SLAB_CACHE_DMA)
return NULL;
} else {
slabp = objp+colour_off;
- colour_off += cachep->slab_size;
+ colour_off += L1_CACHE_ALIGN(cachep->num *
+ sizeof(kmem_bufctl_t) + sizeof(struct slab));
}
slabp->inuse = 0;
slabp->colouroff = colour_off;
case NETDEV_UNREGISTER:
fib6_run_gc(0);
break;
+ case NETDEV_DOWN:
+ neigh_ifdown(&nd_tbl, dev);
+ fib6_run_gc(0);
+ break;
default:
break;
}